Feature: Reserve Ports for cooperative use of shared drone. If a port is reserved the GUI will display the username of the owner but others can still view and control the port; in other words, we just potentially prevent unintentional usage of other people's ports, users still need to play nice with each other!
Fixes issue 144
38
.hgignore
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
syntax: glob
|
||||||
|
|
||||||
|
# generated object files
|
||||||
|
*.pyc
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.exe
|
||||||
|
*.app
|
||||||
|
drone
|
||||||
|
ostinato
|
||||||
|
|
||||||
|
# Qt generated files
|
||||||
|
ui_*.h
|
||||||
|
moc_*.cpp
|
||||||
|
qrc_*.cpp
|
||||||
|
|
||||||
|
# QMake generated files
|
||||||
|
Makefile*
|
||||||
|
*\object_script.*
|
||||||
|
|
||||||
|
# protobuf generated files
|
||||||
|
*.pb.h
|
||||||
|
*.pb.cc
|
||||||
|
*_pb2.py
|
||||||
|
|
||||||
|
# ostinato generated files
|
||||||
|
version.cpp
|
||||||
|
pkg_info.json
|
||||||
|
|
||||||
|
# vim swap files
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# ctags
|
||||||
|
tags
|
||||||
|
|
5
.vimrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
set shiftwidth=4
|
||||||
|
set tabstop=8
|
||||||
|
set softtabstop=4
|
||||||
|
set expandtab
|
||||||
|
set cindent
|
674
COPYING
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
674
binding/LICENSE.txt
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
9
binding/README.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
===============
|
||||||
|
python-ostinato
|
||||||
|
===============
|
||||||
|
|
||||||
|
python-ostinato provides python bindings for Ostinato.
|
||||||
|
|
||||||
|
Ostinato is a network packet/traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark.
|
||||||
|
|
||||||
|
Documentation is available in the wiki at http://ostinato.org
|
28
binding/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Copyright (C) 2014 Srivats P.
|
||||||
|
#
|
||||||
|
# This file is part of "Ostinato"
|
||||||
|
#
|
||||||
|
# This is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from os.path import dirname
|
||||||
|
|
||||||
|
with open(dirname(__file__) + '/pkg_info.json') as f:
|
||||||
|
_info = json.load(f)
|
||||||
|
|
||||||
|
__version__ = _info['version']
|
||||||
|
__revision__ = _info['revision']
|
||||||
|
|
||||||
|
__log__ = logging.getLogger(__name__)
|
4
binding/binding.pro
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += pkg_info
|
||||||
|
|
||||||
|
include(../version.pri)
|
67
binding/core.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Copyright (C) 2014 Srivats P.
|
||||||
|
#
|
||||||
|
# This file is part of "Ostinato"
|
||||||
|
#
|
||||||
|
# This is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
import os
|
||||||
|
from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError
|
||||||
|
import protocols.protocol_pb2 as ost_pb
|
||||||
|
from __init__ import __version__
|
||||||
|
|
||||||
|
class DroneProxy(object):
|
||||||
|
|
||||||
|
def __init__(self, host_name, port_number=7878):
|
||||||
|
self.host = host_name
|
||||||
|
self.port = port_number
|
||||||
|
self.channel = OstinatoRpcChannel()
|
||||||
|
self.stub = ost_pb.OstService_Stub(self.channel)
|
||||||
|
self.void = ost_pb.Void()
|
||||||
|
|
||||||
|
for method in self.stub.GetDescriptor().methods:
|
||||||
|
fn = lambda request=self.void, method_name=method.name: \
|
||||||
|
self.callRpcMethod(method_name, request)
|
||||||
|
self.__dict__[method.name] = fn
|
||||||
|
|
||||||
|
def hostName(self):
|
||||||
|
return self.host
|
||||||
|
|
||||||
|
def portNumber(self):
|
||||||
|
return self.port
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
self.channel.connect(self.host, self.port)
|
||||||
|
ver = ost_pb.VersionInfo()
|
||||||
|
ver.version = __version__
|
||||||
|
compat = self.checkVersion(ver)
|
||||||
|
if compat.result == ost_pb.VersionCompatibility.kIncompatible:
|
||||||
|
raise RpcError('incompatible version %s (%s)' %
|
||||||
|
(ver.version, compat.notes))
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
self.channel.disconnect()
|
||||||
|
|
||||||
|
def callRpcMethod(self, method_name, request):
|
||||||
|
controller = OstinatoRpcController()
|
||||||
|
ost_pb.OstService_Stub.__dict__[method_name](
|
||||||
|
self.stub, controller, request, None)
|
||||||
|
return controller.response
|
||||||
|
|
||||||
|
def saveCaptureBuffer(self, buffer, file_name):
|
||||||
|
f= open(file_name, 'wb')
|
||||||
|
f.write(buffer)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno())
|
||||||
|
f.close()
|
||||||
|
|
188
binding/example.py
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
# standard modules
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
# ostinato modules
|
||||||
|
# (user scripts using the installed package should prepend ostinato. i.e
|
||||||
|
# ostinato.core and ostinato.protocols)
|
||||||
|
from core import ost_pb, DroneProxy
|
||||||
|
from protocols.mac_pb2 import mac
|
||||||
|
from protocols.ip4_pb2 import ip4, Ip4
|
||||||
|
|
||||||
|
# initialize defaults
|
||||||
|
use_defaults = False
|
||||||
|
host_name = '127.0.0.1'
|
||||||
|
tx_port_number = 0
|
||||||
|
rx_port_number = 0
|
||||||
|
|
||||||
|
# setup logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# command-line option/arg processing
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
if sys.argv[1] in ('-d', '--use-defaults'):
|
||||||
|
use_defaults = True
|
||||||
|
if sys.argv[1] in ('-h', '--help'):
|
||||||
|
print('%s [OPTION]...' % (sys.argv[0]))
|
||||||
|
print('Options:')
|
||||||
|
print(' -d --use-defaults run using default values')
|
||||||
|
print(' -h --help show this help')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
print('')
|
||||||
|
print('This example expects the following topology -')
|
||||||
|
print('')
|
||||||
|
print(' +-------+ +-------+')
|
||||||
|
print(' | |Tx--->----| |')
|
||||||
|
print(' | Drone | | DUT |')
|
||||||
|
print(' | |Rx---<----| |')
|
||||||
|
print(' +-------+ +-------+')
|
||||||
|
print('')
|
||||||
|
print('Drone has 2 ports connected to DUT. Packets sent on the Tx port')
|
||||||
|
print('are expected to be received back on the Rx port')
|
||||||
|
print('')
|
||||||
|
print('An easy way to simulate the above topology is to select the loopback')
|
||||||
|
print('port as both Tx and Rx ports')
|
||||||
|
print('')
|
||||||
|
|
||||||
|
if not use_defaults:
|
||||||
|
s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name))
|
||||||
|
host_name = s or host_name
|
||||||
|
|
||||||
|
drone = DroneProxy(host_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# connect to drone
|
||||||
|
log.info('connecting to drone(%s:%d)'
|
||||||
|
% (drone.hostName(), drone.portNumber()))
|
||||||
|
drone.connect()
|
||||||
|
|
||||||
|
# retreive port id list
|
||||||
|
log.info('retreiving port list')
|
||||||
|
port_id_list = drone.getPortIdList()
|
||||||
|
|
||||||
|
# retreive port config list
|
||||||
|
log.info('retreiving port config for all ports')
|
||||||
|
port_config_list = drone.getPortConfig(port_id_list)
|
||||||
|
|
||||||
|
if len(port_config_list.port) == 0:
|
||||||
|
log.warning('drone has no ports!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# print port list and get tx/rx port id
|
||||||
|
print('Port List')
|
||||||
|
print('---------')
|
||||||
|
for port in port_config_list.port:
|
||||||
|
print('%d.%s (%s)' % (port.port_id.id, port.name, port.description))
|
||||||
|
# use a loopback port as default tx/rx port
|
||||||
|
if ('lo' in port.name or 'loopback' in port.description.lower()):
|
||||||
|
tx_port_number = port.port_id.id
|
||||||
|
rx_port_number = port.port_id.id
|
||||||
|
|
||||||
|
if not use_defaults:
|
||||||
|
p = raw_input('Tx Port Id [%d]: ' % (tx_port_number))
|
||||||
|
if p:
|
||||||
|
tx_port_number = int(p)
|
||||||
|
|
||||||
|
p = raw_input('Rx Port Id [%d]: ' % (rx_port_number))
|
||||||
|
if p:
|
||||||
|
rx_port_number = int(p)
|
||||||
|
|
||||||
|
tx_port = ost_pb.PortIdList()
|
||||||
|
tx_port.port_id.add().id = tx_port_number;
|
||||||
|
|
||||||
|
rx_port = ost_pb.PortIdList()
|
||||||
|
rx_port.port_id.add().id = rx_port_number;
|
||||||
|
|
||||||
|
# add a stream
|
||||||
|
stream_id = ost_pb.StreamIdList()
|
||||||
|
stream_id.port_id.CopyFrom(tx_port.port_id[0])
|
||||||
|
stream_id.stream_id.add().id = 1
|
||||||
|
log.info('adding tx_stream %d' % stream_id.stream_id[0].id)
|
||||||
|
drone.addStream(stream_id)
|
||||||
|
|
||||||
|
# configure the stream
|
||||||
|
stream_cfg = ost_pb.StreamConfigList()
|
||||||
|
stream_cfg.port_id.CopyFrom(tx_port.port_id[0])
|
||||||
|
s = stream_cfg.stream.add()
|
||||||
|
s.stream_id.id = stream_id.stream_id[0].id
|
||||||
|
s.core.is_enabled = True
|
||||||
|
s.control.num_packets = 5
|
||||||
|
|
||||||
|
# setup stream protocols as mac:eth2:ip4:udp:payload
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
|
||||||
|
p.Extensions[mac].dst_mac = 0x001122334455
|
||||||
|
p.Extensions[mac].src_mac = 0x00aabbccddee
|
||||||
|
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
|
||||||
|
|
||||||
|
p = s.protocol.add()
|
||||||
|
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
|
||||||
|
# reduce typing by creating a shorter reference to p.Extensions[ip4]
|
||||||
|
ip = p.Extensions[ip4]
|
||||||
|
ip.src_ip = 0x01020304
|
||||||
|
ip.dst_ip = 0x05060708
|
||||||
|
ip.dst_ip_mode = Ip4.e_im_inc_host
|
||||||
|
|
||||||
|
s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber
|
||||||
|
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
|
||||||
|
|
||||||
|
log.info('configuring tx_stream %d' % stream_id.stream_id[0].id)
|
||||||
|
drone.modifyStream(stream_cfg)
|
||||||
|
|
||||||
|
# clear tx/rx stats
|
||||||
|
log.info('clearing tx/rx stats')
|
||||||
|
drone.clearStats(tx_port)
|
||||||
|
drone.clearStats(rx_port)
|
||||||
|
|
||||||
|
# start capture and transmit
|
||||||
|
log.info('starting capture')
|
||||||
|
drone.startCapture(rx_port)
|
||||||
|
log.info('starting transmit')
|
||||||
|
drone.startTransmit(tx_port)
|
||||||
|
|
||||||
|
# wait for transmit to finish
|
||||||
|
log.info('waiting for transmit to finish ...')
|
||||||
|
time.sleep(7)
|
||||||
|
|
||||||
|
# stop transmit and capture
|
||||||
|
log.info('stopping transmit')
|
||||||
|
drone.stopTransmit(tx_port)
|
||||||
|
log.info('stopping capture')
|
||||||
|
drone.stopCapture(rx_port)
|
||||||
|
|
||||||
|
# get tx/rx stats
|
||||||
|
log.info('retreiving stats')
|
||||||
|
tx_stats = drone.getStats(tx_port)
|
||||||
|
rx_stats = drone.getStats(rx_port)
|
||||||
|
|
||||||
|
#log.info('--> (tx_stats)' + tx_stats.__str__())
|
||||||
|
#log.info('--> (rx_stats)' + rx_stats.__str__())
|
||||||
|
log.info('tx pkts = %d, rx pkts = %d' %
|
||||||
|
(tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts))
|
||||||
|
|
||||||
|
# retrieve and dump received packets
|
||||||
|
log.info('getting Rx capture buffer')
|
||||||
|
buff = drone.getCaptureBuffer(rx_port.port_id[0])
|
||||||
|
drone.saveCaptureBuffer(buff, 'capture.pcap')
|
||||||
|
log.info('dumping Rx capture buffer')
|
||||||
|
os.system('tshark -r capture.pcap')
|
||||||
|
os.remove('capture.pcap')
|
||||||
|
|
||||||
|
# delete streams
|
||||||
|
log.info('deleting tx_stream %d' % stream_id.stream_id[0].id)
|
||||||
|
drone.deleteStream(stream_id)
|
||||||
|
|
||||||
|
# bye for now
|
||||||
|
drone.disconnect()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
log.exception(ex)
|
||||||
|
sys.exit(1)
|
17
binding/protocols/__init__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Copyright (C) 2014 Srivats P.
|
||||||
|
#
|
||||||
|
# This file is part of "Ostinato"
|
||||||
|
#
|
||||||
|
# This is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
167
binding/rpc.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# Copyright (C) 2014 Srivats P.
|
||||||
|
#
|
||||||
|
# This file is part of "Ostinato"
|
||||||
|
#
|
||||||
|
# This is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
from google.protobuf.message import EncodeError, DecodeError
|
||||||
|
from google.protobuf.service import RpcChannel
|
||||||
|
from google.protobuf.service import RpcController
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class PeerClosedConnError(Exception):
|
||||||
|
def __init__(self, msg):
|
||||||
|
self.msg = msg
|
||||||
|
def __str__(self):
|
||||||
|
return self.msg
|
||||||
|
|
||||||
|
class RpcError(Exception):
|
||||||
|
def __init__(self, msg):
|
||||||
|
self.msg = msg
|
||||||
|
def __str__(self):
|
||||||
|
return self.msg
|
||||||
|
|
||||||
|
class RpcMismatchError(Exception):
|
||||||
|
def __init__(self, msg, received, expected):
|
||||||
|
self.msg = msg
|
||||||
|
self.received = received
|
||||||
|
self.expected = expected
|
||||||
|
def __str__(self):
|
||||||
|
return '%s - Expected method %d, Received method %d' % (
|
||||||
|
self.msg, self.expected, self.received)
|
||||||
|
|
||||||
|
class OstinatoRpcController(RpcController):
|
||||||
|
def __init__(self):
|
||||||
|
super(OstinatoRpcController, self).__init__()
|
||||||
|
|
||||||
|
class OstinatoRpcChannel(RpcChannel):
|
||||||
|
def __init__(self):
|
||||||
|
self.log = logging.getLogger(__name__)
|
||||||
|
self.log.debug('opening socket')
|
||||||
|
|
||||||
|
def connect(self, host, port):
|
||||||
|
self.peer = '%s:%d' % (host, port)
|
||||||
|
self.log.debug('connecting to %s', self.peer)
|
||||||
|
try:
|
||||||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.sock.connect((host, port))
|
||||||
|
except socket.error as e:
|
||||||
|
error = 'ERROR: Unable to connect to Drone %s (%s)' % (
|
||||||
|
self.peer, str(e))
|
||||||
|
print(error)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
self.log.debug('closing socket')
|
||||||
|
self.sock.close()
|
||||||
|
|
||||||
|
def CallMethod(self, method, controller, request, response_class, done):
|
||||||
|
MSG_HDR_SIZE = 8
|
||||||
|
MSG_TYPE_REQUEST = 1
|
||||||
|
MSG_TYPE_RESPONSE = 2
|
||||||
|
MSG_TYPE_BLOB = 3
|
||||||
|
MSG_TYPE_ERROR = 4
|
||||||
|
|
||||||
|
error = ''
|
||||||
|
try:
|
||||||
|
self.log.info('invoking RPC %s(%s): %s', method.name,
|
||||||
|
type(request).__name__, response_class.__name__)
|
||||||
|
self.log.debug('serializing request arg %s', request)
|
||||||
|
req = request.SerializeToString()
|
||||||
|
hdr = struct.pack('>HHI', MSG_TYPE_REQUEST, method.index, len(req))
|
||||||
|
self.log.debug('req.hdr = %r', hdr)
|
||||||
|
self.sock.sendall(hdr + req)
|
||||||
|
|
||||||
|
# receive and parse header
|
||||||
|
self.log.debug('receiving response hdr')
|
||||||
|
hdr = ''
|
||||||
|
while len(hdr) < MSG_HDR_SIZE:
|
||||||
|
chunk = self.sock.recv(MSG_HDR_SIZE - len(hdr))
|
||||||
|
if chunk == '':
|
||||||
|
raise PeerClosedConnError('connection closed by peer')
|
||||||
|
hdr = hdr + chunk
|
||||||
|
|
||||||
|
(msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr)
|
||||||
|
self.log.debug('resp hdr: type = %d, method = %d, len = %d',
|
||||||
|
msg_type, method_index, resp_len)
|
||||||
|
|
||||||
|
# receive and parse the actual response message
|
||||||
|
self.log.debug('receiving response data')
|
||||||
|
resp = ''
|
||||||
|
while len(resp) < resp_len:
|
||||||
|
chunk = self.sock.recv(resp_len - len(resp))
|
||||||
|
if chunk == '':
|
||||||
|
raise PeerClosedConnError('connection closed by peer')
|
||||||
|
resp = resp + chunk
|
||||||
|
|
||||||
|
# verify response method is same as the one requested
|
||||||
|
if method_index != method.index:
|
||||||
|
raise RpcMismatchError('RPC mismatch',
|
||||||
|
expected = method.index, received = method_index)
|
||||||
|
|
||||||
|
if msg_type == MSG_TYPE_RESPONSE:
|
||||||
|
response = response_class()
|
||||||
|
response.ParseFromString(resp)
|
||||||
|
self.log.debug('parsed response %s', response)
|
||||||
|
elif msg_type == MSG_TYPE_BLOB:
|
||||||
|
response = resp
|
||||||
|
elif msg_type == MSG_TYPE_ERROR:
|
||||||
|
raise RpcError(unicode(resp, 'utf-8'))
|
||||||
|
else:
|
||||||
|
raise RpcError('unknown RPC msg type %d' % msg_type)
|
||||||
|
|
||||||
|
controller.response = response
|
||||||
|
|
||||||
|
except socket.error as e:
|
||||||
|
error = 'ERROR: RPC %s() to Drone %s failed (%s)' % (
|
||||||
|
method.name, self.peer, e)
|
||||||
|
self.log.exception(error+e)
|
||||||
|
raise
|
||||||
|
except PeerClosedConnError as e:
|
||||||
|
error = 'ERROR: Drone %s closed connection receiving reply ' \
|
||||||
|
'for RPC %s() (%s)' % (
|
||||||
|
self.peer, method.name, e)
|
||||||
|
self.log.exception(error)
|
||||||
|
raise
|
||||||
|
except EncodeError as e:
|
||||||
|
error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \
|
||||||
|
'to Drone %s (%s)' % (
|
||||||
|
type(request).__name__, method.name, self.peer, e)
|
||||||
|
self.log.exception(error)
|
||||||
|
raise
|
||||||
|
except DecodeError as e:
|
||||||
|
error = 'ERROR: Failed to parse %s response for RPC %s() ' \
|
||||||
|
'from Drone %s (%s)' % (
|
||||||
|
type(response).__name__, method.name, self.peer, e)
|
||||||
|
self.log.exception(error)
|
||||||
|
raise
|
||||||
|
except RpcMismatchError as e:
|
||||||
|
error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % (
|
||||||
|
method.name, e)
|
||||||
|
self.log.exception(error)
|
||||||
|
raise
|
||||||
|
except RpcError as e:
|
||||||
|
error = 'ERROR: error received for RPC %s() (%s) ' % (
|
||||||
|
method.name, e)
|
||||||
|
self.log.exception(error)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
if error:
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
|
||||||
|
|
84
binding/setup.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Copyright (C) 2014 Srivats P.
|
||||||
|
#
|
||||||
|
# This file is part of "Ostinato"
|
||||||
|
#
|
||||||
|
# This is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from setuptools import Command, setup
|
||||||
|
from setuptools.command.sdist import sdist as _sdist
|
||||||
|
|
||||||
|
def read(fname):
|
||||||
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
def ensure_cwd():
|
||||||
|
if os.path.split(os.getcwd())[1] != 'binding':
|
||||||
|
print('ERROR: This script needs to be run from the binding directory')
|
||||||
|
print('Current Working Directory is %s' % os.getcwd())
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
class sdist(_sdist):
|
||||||
|
def run(self):
|
||||||
|
ensure_cwd()
|
||||||
|
_sdist.run(self)
|
||||||
|
|
||||||
|
class sdist_clean(Command):
|
||||||
|
description = 'clean stuff generated by sdist'
|
||||||
|
user_options = []
|
||||||
|
def initialize_options(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
ensure_cwd()
|
||||||
|
shutil.rmtree('dist', ignore_errors = True)
|
||||||
|
shutil.rmtree('python-ostinato.egg-info', ignore_errors = True)
|
||||||
|
shutil.rmtree('python_ostinato.egg-info', ignore_errors = True)
|
||||||
|
|
||||||
|
# ------- script starts from here ------- #
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'pkg_info.json')) as f:
|
||||||
|
pkg_info = json.load(f)
|
||||||
|
|
||||||
|
setup(name = 'python-ostinato',
|
||||||
|
version = pkg_info['version'],
|
||||||
|
author = 'Srivats P',
|
||||||
|
author_email = 'pstavirs@gmail.com',
|
||||||
|
license = "GPLv3+",
|
||||||
|
url = 'http://ostinato.org',
|
||||||
|
description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer',
|
||||||
|
long_description = read('README.txt'),
|
||||||
|
install_requires = ['protobuf>=2.3.0'],
|
||||||
|
packages = ['ostinato', 'ostinato.protocols'],
|
||||||
|
package_dir = {'ostinato': '.'},
|
||||||
|
package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']},
|
||||||
|
platforms = ['Any'],
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Intended Audience :: Telecommunications Industry',
|
||||||
|
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||||
|
'Topic :: Software Development :: Testing :: Traffic Generation',
|
||||||
|
'Topic :: System :: Networking'],
|
||||||
|
cmdclass={
|
||||||
|
'sdist': sdist,
|
||||||
|
'sdist_clean': sdist_clean},
|
||||||
|
)
|
||||||
|
|
211
client/about.ui
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>About</class>
|
||||||
|
<widget class="QDialog" name="About" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>500</width>
|
||||||
|
<height>327</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>About Ostinato</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget" >
|
||||||
|
<property name="currentIndex" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab" >
|
||||||
|
<attribute name="title" >
|
||||||
|
<string>Ostinato</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap" >
|
||||||
|
<pixmap resource="ostinato.qrc" >:/icons/logo.png</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>21</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4" >
|
||||||
|
<property name="text" >
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap" >
|
||||||
|
<pixmap resource="ostinato.qrc" >:/icons/name.png</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="versionLabel" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Version/Revision Placeholder</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="CopyrightLabel" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Copyright (c) 2007-2014 Srivats P.</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="LinksLabel" >
|
||||||
|
<property name="text" >
|
||||||
|
<string><a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat" >
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>21</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Logo (c): Dhiman Sengupta
|
||||||
|
Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/)</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2" >
|
||||||
|
<attribute name="title" >
|
||||||
|
<string>License</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string><p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat" >
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons" >
|
||||||
|
<set>QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>About</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>353</x>
|
||||||
|
<y>280</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>286</x>
|
||||||
|
<y>262</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
408
client/dumpview.cpp
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dumpview.h"
|
||||||
|
|
||||||
|
//! \todo Enable Scrollbars
|
||||||
|
|
||||||
|
DumpView::DumpView(QWidget *parent)
|
||||||
|
: QAbstractItemView(parent)
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
// NOTE: Monospaced fonts only !!!!!!!!!!!
|
||||||
|
setFont(QFont("Courier"));
|
||||||
|
w = fontMetrics().width('X');
|
||||||
|
h = fontMetrics().height();
|
||||||
|
|
||||||
|
mLineHeight = h;
|
||||||
|
mCharWidth = w;
|
||||||
|
|
||||||
|
mSelectedRow = mSelectedCol = -1;
|
||||||
|
|
||||||
|
// calculate width for offset column and the whitespace that follows it
|
||||||
|
// 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
|
||||||
|
mOffsetPaneTopRect = QRect(0, 0, w*4, h);
|
||||||
|
mDumpPaneTopRect = QRect(mOffsetPaneTopRect.right()+w*3, 0,
|
||||||
|
w*((8*3-1)+2+(8*3-1)), h);
|
||||||
|
mAsciiPaneTopRect = QRect(mDumpPaneTopRect.right()+w*3, 0,
|
||||||
|
w*(8+1+8), h);
|
||||||
|
qDebug("DumpView::DumpView");
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DumpView::indexAt(const QPoint &/*point*/) const
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
int x = point.x();
|
||||||
|
int row, col;
|
||||||
|
|
||||||
|
if (x > mAsciiPaneTopRect.left())
|
||||||
|
{
|
||||||
|
col = (x - mAsciiPaneTopRect.left()) / mCharWidth;
|
||||||
|
if (col == 8) // don't select whitespace
|
||||||
|
goto _exit;
|
||||||
|
else if (col > 8) // adjust for whitespace
|
||||||
|
col--;
|
||||||
|
}
|
||||||
|
else if (x > mDumpPaneTopRect.left())
|
||||||
|
{
|
||||||
|
col = (x - mDumpPaneTopRect.left()) / (mCharWidth*3);
|
||||||
|
}
|
||||||
|
row = point.y()/mLineHeight;
|
||||||
|
|
||||||
|
if ((col < 16) && (row < ((data.size()+16)/16)))
|
||||||
|
{
|
||||||
|
selrow = row;
|
||||||
|
selcol = col;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
// last row check col
|
||||||
|
if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16)))
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
qDebug("dumpview::selection(%d, %d)", selrow, selcol);
|
||||||
|
|
||||||
|
offset = selrow * 16 + selcol;
|
||||||
|
#if 0
|
||||||
|
for(int i = 0; i < model()->rowCount(parent); i++)
|
||||||
|
{
|
||||||
|
QModelIndex index = model()->index(i, 0, parent);
|
||||||
|
|
||||||
|
if (model()->hasChildren(index))
|
||||||
|
indexAtOffset(offset, index); // Non Leaf
|
||||||
|
else
|
||||||
|
if (
|
||||||
|
dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf
|
||||||
|
// FIXME: Use RawValueRole instead of UserRole
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
// Clear existing selection
|
||||||
|
selrow = -1;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/)
|
||||||
|
{
|
||||||
|
// FIXME: implement scrolling
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect DumpView::visualRect(const QModelIndex &/*index*/) const
|
||||||
|
{
|
||||||
|
// FIXME: calculate actual rect
|
||||||
|
return rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
//protected:
|
||||||
|
int DumpView::horizontalOffset() const
|
||||||
|
{
|
||||||
|
return horizontalScrollBar()->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/,
|
||||||
|
Qt::KeyboardModifiers /*modifiers*/)
|
||||||
|
{
|
||||||
|
// FIXME(MED): need to implement movement using cursor
|
||||||
|
return currentIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpView::setSelection(const QRect &/*rect*/,
|
||||||
|
QItemSelectionModel::SelectionFlags flags)
|
||||||
|
{
|
||||||
|
// FIXME(HI): calculate indexes using rect
|
||||||
|
selectionModel()->select(QModelIndex(), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DumpView::verticalOffset() const
|
||||||
|
{
|
||||||
|
return verticalScrollBar()->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegion DumpView::visualRegionForSelection(
|
||||||
|
const QItemSelection &/*selection*/) const
|
||||||
|
{
|
||||||
|
// FIXME(HI)
|
||||||
|
return QRegion(rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
//protected slots:
|
||||||
|
void DumpView::dataChanged(const QModelIndex &/*topLeft*/,
|
||||||
|
const QModelIndex &/*bottomRight*/)
|
||||||
|
{
|
||||||
|
// FIXME(HI)
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpView::selectionChanged(const QItemSelection &/*selected*/,
|
||||||
|
const QItemSelection &/*deselected*/)
|
||||||
|
{
|
||||||
|
// FIXME(HI)
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize,
|
||||||
|
QModelIndex parent)
|
||||||
|
{
|
||||||
|
// FIXME: Use new enum instead of Qt::UserRole
|
||||||
|
//! \todo (low): generalize this for any model not just our pkt model
|
||||||
|
|
||||||
|
Q_ASSERT(!parent.isValid());
|
||||||
|
|
||||||
|
qDebug("!!!! %d $$$$", dump.size());
|
||||||
|
|
||||||
|
for(int i = 0; i < model()->rowCount(parent); i++)
|
||||||
|
{
|
||||||
|
QModelIndex index = model()->index(i, 0, parent);
|
||||||
|
|
||||||
|
Q_ASSERT(index.isValid());
|
||||||
|
|
||||||
|
// Assumption: protocol data is in bytes (not bits)
|
||||||
|
qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size());
|
||||||
|
dump.append(model()->data(index, Qt::UserRole).toByteArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionModel()->selectedIndexes().size())
|
||||||
|
{
|
||||||
|
int j, bits;
|
||||||
|
QModelIndex index;
|
||||||
|
|
||||||
|
Q_ASSERT(selectionModel()->selectedIndexes().size() == 1);
|
||||||
|
index = selectionModel()->selectedIndexes().at(0);
|
||||||
|
|
||||||
|
if (index.parent().isValid())
|
||||||
|
{
|
||||||
|
// Field
|
||||||
|
|
||||||
|
// SelOfs = SUM(protocol sizes before selected field's protocol) +
|
||||||
|
// SUM(field sizes before selected field)
|
||||||
|
|
||||||
|
selOfs = 0;
|
||||||
|
j = index.parent().row() - 1;
|
||||||
|
while (j >= 0)
|
||||||
|
{
|
||||||
|
selOfs += model()->data(index.parent().sibling(j,0),
|
||||||
|
Qt::UserRole).toByteArray().size();
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
|
||||||
|
bits = 0;
|
||||||
|
j = index.row() - 1;
|
||||||
|
while (j >= 0)
|
||||||
|
{
|
||||||
|
bits += model()->data(index.sibling(j,0), Qt::UserRole+1).
|
||||||
|
toInt();
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
selOfs += bits/8;
|
||||||
|
selSize = model()->data(index, Qt::UserRole).toByteArray().size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Protocol
|
||||||
|
selOfs = 0;
|
||||||
|
j = index.row() - 1;
|
||||||
|
while (j >= 0)
|
||||||
|
{
|
||||||
|
selOfs += model()->data(index.sibling(j,0), Qt::UserRole).
|
||||||
|
toByteArray().size();
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
selSize = model()->data(index, Qt::UserRole).toByteArray().size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(LOW): rewrite this function - it's a mess!
|
||||||
|
void DumpView::paintEvent(QPaintEvent* /*event*/)
|
||||||
|
{
|
||||||
|
QStylePainter painter(viewport());
|
||||||
|
QRect offsetRect = mOffsetPaneTopRect;
|
||||||
|
QRect dumpRect = mDumpPaneTopRect;
|
||||||
|
QRect asciiRect = mAsciiPaneTopRect;
|
||||||
|
QPalette pal = palette();
|
||||||
|
static QByteArray data;
|
||||||
|
//QByteArray ba;
|
||||||
|
int selOfs = -1, selSize;
|
||||||
|
int curSelOfs, curSelSize;
|
||||||
|
|
||||||
|
qDebug("dumpview::paintEvent");
|
||||||
|
|
||||||
|
// FIXME(LOW): unable to set the self widget's font in constructor
|
||||||
|
painter.setFont(QFont("Courier"));
|
||||||
|
|
||||||
|
// set a white background
|
||||||
|
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
|
||||||
|
|
||||||
|
if (model())
|
||||||
|
{
|
||||||
|
data.clear();
|
||||||
|
populateDump(data, selOfs, selSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// display the offset, dump and ascii panes 8 + 8 bytes on a line
|
||||||
|
for (int i = 0; i < data.size(); i+=16)
|
||||||
|
{
|
||||||
|
QString dumpStr, asciiStr;
|
||||||
|
|
||||||
|
//ba = data.mid(i, 16);
|
||||||
|
|
||||||
|
// display offset
|
||||||
|
painter.drawItemText(offsetRect, Qt::AlignLeft | Qt::AlignTop, pal,
|
||||||
|
true, QString("%1").arg(i, 4, 16, QChar('0')), QPalette::WindowText);
|
||||||
|
// construct the dumpStr and asciiStr
|
||||||
|
for (int j = i; (j < (i+16)) && (j < data.size()); j++)
|
||||||
|
{
|
||||||
|
unsigned char c = data.at(j);
|
||||||
|
|
||||||
|
// extra space after 8 bytes
|
||||||
|
if (((j+8) % 16) == 0)
|
||||||
|
{
|
||||||
|
dumpStr.append(" ");
|
||||||
|
asciiStr.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
dumpStr.append(QString("%1").arg((uint)c, 2, 16, QChar('0')).
|
||||||
|
toUpper()).append(" ");
|
||||||
|
|
||||||
|
if (isPrintable(c))
|
||||||
|
asciiStr.append(QChar(c));
|
||||||
|
else
|
||||||
|
asciiStr.append(QChar('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// display dump
|
||||||
|
painter.drawItemText(dumpRect, Qt::AlignLeft | Qt::AlignTop, pal,
|
||||||
|
true, dumpStr, QPalette::WindowText);
|
||||||
|
|
||||||
|
// display ascii
|
||||||
|
painter.drawItemText(asciiRect, Qt::AlignLeft | Qt::AlignTop, pal,
|
||||||
|
true, asciiStr, QPalette::WindowText);
|
||||||
|
|
||||||
|
// if no selection, skip selection painting
|
||||||
|
if (selOfs < 0)
|
||||||
|
goto _next;
|
||||||
|
|
||||||
|
// Check overlap between current row and selection
|
||||||
|
{
|
||||||
|
QRect r1(i, 0, qMin(16, data.size()-i), 8);
|
||||||
|
QRect s1(selOfs, 0, selSize, 8);
|
||||||
|
if (r1.intersects(s1))
|
||||||
|
{
|
||||||
|
QRect t = r1.intersected(s1);
|
||||||
|
|
||||||
|
curSelOfs = t.x();
|
||||||
|
curSelSize = t.width();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
curSelSize = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// overpaint selection on current row (if any)
|
||||||
|
if (curSelSize > 0)
|
||||||
|
{
|
||||||
|
QRect r;
|
||||||
|
QString selectedAsciiStr, selectedDumpStr;
|
||||||
|
|
||||||
|
qDebug("dumpview::paintEvent - Highlighted (%d, %d)",
|
||||||
|
curSelOfs, curSelSize);
|
||||||
|
|
||||||
|
// construct the dumpStr and asciiStr
|
||||||
|
for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++)
|
||||||
|
{
|
||||||
|
unsigned char c = data.at(k);
|
||||||
|
|
||||||
|
// extra space after 8 bytes
|
||||||
|
if (((k+8) % 16) == 0)
|
||||||
|
{
|
||||||
|
// Avoid adding space at the start for fields starting
|
||||||
|
// at second column 8 byte boundary
|
||||||
|
if (k!=curSelOfs)
|
||||||
|
{
|
||||||
|
selectedDumpStr.append(" ");
|
||||||
|
selectedAsciiStr.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16,
|
||||||
|
QChar('0')).toUpper()).append(" ");
|
||||||
|
|
||||||
|
if (isPrintable(c))
|
||||||
|
selectedAsciiStr.append(QChar(c));
|
||||||
|
else
|
||||||
|
selectedAsciiStr.append(QChar('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// display dump
|
||||||
|
r = dumpRect;
|
||||||
|
if ((curSelOfs - i) < 8)
|
||||||
|
r.translate(mCharWidth*(curSelOfs-i)*3, 0);
|
||||||
|
else
|
||||||
|
r.translate(mCharWidth*((curSelOfs-i)*3+1), 0);
|
||||||
|
|
||||||
|
// adjust width taking care of selection stretching between
|
||||||
|
// the two 8byte columns
|
||||||
|
if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 ))
|
||||||
|
r.setWidth((curSelSize * 3 + 1) * mCharWidth);
|
||||||
|
else
|
||||||
|
r.setWidth((curSelSize * 3) * mCharWidth);
|
||||||
|
|
||||||
|
painter.fillRect(r, pal.highlight());
|
||||||
|
painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal,
|
||||||
|
true, selectedDumpStr, QPalette::HighlightedText);
|
||||||
|
|
||||||
|
// display ascii
|
||||||
|
r = asciiRect;
|
||||||
|
if ((curSelOfs - i) < 8)
|
||||||
|
r.translate(mCharWidth*(curSelOfs-i)*1, 0);
|
||||||
|
else
|
||||||
|
r.translate(mCharWidth*((curSelOfs-i)*1+1), 0);
|
||||||
|
|
||||||
|
// adjust width taking care of selection stretching between
|
||||||
|
// the two 8byte columns
|
||||||
|
if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 ))
|
||||||
|
r.setWidth((curSelSize * 1 + 1) * mCharWidth);
|
||||||
|
else
|
||||||
|
r.setWidth((curSelSize * 1) * mCharWidth);
|
||||||
|
|
||||||
|
painter.fillRect(r, pal.highlight());
|
||||||
|
painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal,
|
||||||
|
true, selectedAsciiStr, QPalette::HighlightedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
_next:
|
||||||
|
// move the rects down
|
||||||
|
offsetRect.translate(0, mLineHeight);
|
||||||
|
dumpRect.translate(0, mLineHeight);
|
||||||
|
asciiRect.translate(0, mLineHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
61
client/dumpview.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtGui> // FIXME: High
|
||||||
|
|
||||||
|
|
||||||
|
class DumpView: public QAbstractItemView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DumpView(QWidget *parent=0);
|
||||||
|
|
||||||
|
QModelIndex indexAt( const QPoint &point ) const;
|
||||||
|
void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible );
|
||||||
|
QRect visualRect( const QModelIndex &index ) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int horizontalOffset() const;
|
||||||
|
bool isIndexHidden( const QModelIndex &index ) const;
|
||||||
|
QModelIndex moveCursor( CursorAction cursorAction,
|
||||||
|
Qt::KeyboardModifiers modifiers );
|
||||||
|
void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags );
|
||||||
|
int verticalOffset() const;
|
||||||
|
QRegion visualRegionForSelection( const QItemSelection &selection ) const;
|
||||||
|
protected slots:
|
||||||
|
void dataChanged( const QModelIndex &topLeft,
|
||||||
|
const QModelIndex &bottomRight );
|
||||||
|
void selectionChanged( const QItemSelection &selected,
|
||||||
|
const QItemSelection &deselected );
|
||||||
|
void paintEvent(QPaintEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void populateDump(QByteArray &dump, int &selOfs, int &selSize,
|
||||||
|
QModelIndex parent = QModelIndex());
|
||||||
|
bool inline isPrintable(char c)
|
||||||
|
{if ((c > 48) && (c < 126)) return true; else return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRect mOffsetPaneTopRect;
|
||||||
|
QRect mDumpPaneTopRect;
|
||||||
|
QRect mAsciiPaneTopRect;
|
||||||
|
int mSelectedRow, mSelectedCol;
|
||||||
|
int mLineHeight;
|
||||||
|
int mCharWidth;
|
||||||
|
};
|
||||||
|
|
91
client/hexlineedit.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hexlineedit.h"
|
||||||
|
#include "qdebug.h"
|
||||||
|
|
||||||
|
QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets);
|
||||||
|
|
||||||
|
HexLineEdit::HexLineEdit( QWidget * parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
//QLineEdit::QLineEdit(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
const QValidator *v = validator();
|
||||||
|
if ( v )
|
||||||
|
{
|
||||||
|
int curpos = cursorPosition();
|
||||||
|
QString str = text();
|
||||||
|
if ( v->validate( str, curpos ) == QValidator::Acceptable )
|
||||||
|
{
|
||||||
|
if ( curpos != cursorPosition() )
|
||||||
|
setCursorPosition( curpos );
|
||||||
|
if ( str != text() )
|
||||||
|
setText( str );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( curpos != cursorPosition() )
|
||||||
|
setCursorPosition( curpos );
|
||||||
|
str = text();
|
||||||
|
v->fixup( str );
|
||||||
|
if ( str != text() )
|
||||||
|
{
|
||||||
|
setText( str );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QLineEdit::focusOutEvent( e );
|
||||||
|
emit focusOut();
|
||||||
|
#else
|
||||||
|
#define uintToHexStr(num, bytesize) \
|
||||||
|
QString("%1").arg((num), (bytesize)*2 , 16, QChar('0'))
|
||||||
|
|
||||||
|
bool isOk;
|
||||||
|
ulong num;
|
||||||
|
|
||||||
|
qDebug("before = %s\n", text().toAscii().data());
|
||||||
|
num = text().remove(QChar(' ')).toULong(&isOk, 16);
|
||||||
|
setText(uintToHexStr(num, 4));
|
||||||
|
qDebug("after = %s\n", text().toAscii().data());
|
||||||
|
#undef uintToHexStr
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void HexLineEdit::focusInEvent( QFocusEvent *e )
|
||||||
|
{
|
||||||
|
QLineEdit::focusInEvent( e );
|
||||||
|
emit focusIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HexLineEdit::keyPressEvent( QKeyEvent *e )
|
||||||
|
{
|
||||||
|
QLineEdit::keyPressEvent( e );
|
||||||
|
if ( e->key() == Key_Enter || e->key() == Key_Return )
|
||||||
|
{
|
||||||
|
setSelection( 0, text().length() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
43
client/hexlineedit.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HEXLINEEDIT
|
||||||
|
#define _HEXLINEEDIT
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class HexLineEdit : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
HexLineEdit ( QWidget * parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void focusOutEvent( QFocusEvent *e );
|
||||||
|
//void focusInEvent( QFocusEvent *e );
|
||||||
|
//void keyPressEvent( QKeyEvent *e );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
//void focusIn();
|
||||||
|
void focusOut();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
BIN
client/icons/about.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
client/icons/arrow_down.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
client/icons/arrow_left.png
Normal file
After Width: | Height: | Size: 345 B |
BIN
client/icons/arrow_right.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
client/icons/arrow_up.png
Normal file
After Width: | Height: | Size: 372 B |
BIN
client/icons/bullet_error.png
Normal file
After Width: | Height: | Size: 454 B |
BIN
client/icons/bullet_green.png
Normal file
After Width: | Height: | Size: 295 B |
BIN
client/icons/bullet_orange.png
Normal file
After Width: | Height: | Size: 283 B |
BIN
client/icons/bullet_red.png
Normal file
After Width: | Height: | Size: 287 B |
BIN
client/icons/bullet_white.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
client/icons/bullet_yellow.png
Normal file
After Width: | Height: | Size: 287 B |
BIN
client/icons/control_play.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
client/icons/control_stop.png
Normal file
After Width: | Height: | Size: 403 B |
BIN
client/icons/deco_exclusive.png
Normal file
After Width: | Height: | Size: 793 B |
BIN
client/icons/delete.png
Normal file
After Width: | Height: | Size: 715 B |
BIN
client/icons/exit.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
client/icons/gaps.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
client/icons/logo.icns
Normal file
BIN
client/icons/logo.ico
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
client/icons/logo.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
client/icons/magnifier.png
Normal file
After Width: | Height: | Size: 615 B |
BIN
client/icons/name.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
client/icons/portgroup_add.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
client/icons/portgroup_connect.png
Normal file
After Width: | Height: | Size: 748 B |
BIN
client/icons/portgroup_delete.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
client/icons/portgroup_disconnect.png
Normal file
After Width: | Height: | Size: 796 B |
BIN
client/icons/portstats_clear.png
Normal file
After Width: | Height: | Size: 367 B |
BIN
client/icons/portstats_clear_all.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
client/icons/portstats_filter.png
Normal file
After Width: | Height: | Size: 432 B |
BIN
client/icons/preferences.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
client/icons/qt.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
client/icons/sound_mute.png
Normal file
After Width: | Height: | Size: 474 B |
BIN
client/icons/sound_none.png
Normal file
After Width: | Height: | Size: 417 B |
BIN
client/icons/stream_add.png
Normal file
After Width: | Height: | Size: 814 B |
BIN
client/icons/stream_delete.png
Normal file
After Width: | Height: | Size: 847 B |
BIN
client/icons/stream_duplicate.png
Normal file
After Width: | Height: | Size: 859 B |
BIN
client/icons/stream_edit.png
Normal file
After Width: | Height: | Size: 865 B |
94
client/main.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "../common/ostprotolib.h"
|
||||||
|
#include "../common/protocolmanager.h"
|
||||||
|
#include "../common/protocolwidgetfactory.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h>
|
||||||
|
|
||||||
|
extern const char* version;
|
||||||
|
extern const char* revision;
|
||||||
|
extern ProtocolManager *OstProtocolManager;
|
||||||
|
extern ProtocolWidgetFactory *OstProtocolWidgetFactory;
|
||||||
|
|
||||||
|
QSettings *appSettings;
|
||||||
|
QMainWindow *mainWindow;
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
QString kGzipPathDefaultValue;
|
||||||
|
QString kDiffPathDefaultValue;
|
||||||
|
QString kAwkPathDefaultValue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
int exitCode;
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe";
|
||||||
|
kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe";
|
||||||
|
kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
app.setApplicationName("Ostinato");
|
||||||
|
app.setOrganizationName("Ostinato");
|
||||||
|
app.setProperty("version", version);
|
||||||
|
app.setProperty("revision", revision);
|
||||||
|
|
||||||
|
OstProtocolManager = new ProtocolManager();
|
||||||
|
OstProtocolWidgetFactory = new ProtocolWidgetFactory();
|
||||||
|
|
||||||
|
/* (Portable Mode) If we have a .ini file in the same directory as the
|
||||||
|
executable, we use that instead of the platform specific location
|
||||||
|
and format for the settings */
|
||||||
|
QString portableIni = QCoreApplication::applicationDirPath()
|
||||||
|
+ "/ostinato.ini";
|
||||||
|
if (QFile::exists(portableIni))
|
||||||
|
appSettings = new QSettings(portableIni, QSettings::IniFormat);
|
||||||
|
else
|
||||||
|
appSettings = new QSettings();
|
||||||
|
|
||||||
|
OstProtoLib::setExternalApplicationPaths(
|
||||||
|
appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
Preferences::initDefaults();
|
||||||
|
|
||||||
|
mainWindow = new MainWindow;
|
||||||
|
mainWindow->show();
|
||||||
|
exitCode = app.exec();
|
||||||
|
|
||||||
|
delete mainWindow;
|
||||||
|
delete appSettings;
|
||||||
|
delete OstProtocolManager;
|
||||||
|
google::protobuf::ShutdownProtobufLibrary();
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
|
}
|
153
client/mainwindow.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#include "dbgthread.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "portstatswindow.h"
|
||||||
|
#include "portswindow.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "ui_about.h"
|
||||||
|
#include "updater.h"
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
extern const char* version;
|
||||||
|
extern const char* revision;
|
||||||
|
|
||||||
|
PortGroupList *pgl;
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow (parent)
|
||||||
|
{
|
||||||
|
QString serverApp = QCoreApplication::applicationDirPath();
|
||||||
|
Updater *updater = new Updater();
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// applicationDirPath() does not return bundle, but executable inside bundle
|
||||||
|
serverApp.replace("Ostinato.app", "drone.app");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
serverApp.append("/drone.exe");
|
||||||
|
#else
|
||||||
|
serverApp.append("/drone");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
localServer_ = new QProcess(this);
|
||||||
|
localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
localServer_->start(serverApp, QStringList());
|
||||||
|
|
||||||
|
pgl = new PortGroupList;
|
||||||
|
|
||||||
|
portsWindow = new PortsWindow(pgl, this);
|
||||||
|
statsWindow = new PortStatsWindow(pgl, this);
|
||||||
|
portsDock = new QDockWidget(tr("Ports and Streams"), this);
|
||||||
|
portsDock->setObjectName("portsDock");
|
||||||
|
portsDock->setFeatures(
|
||||||
|
portsDock->features() & ~QDockWidget::DockWidgetClosable);
|
||||||
|
statsDock = new QDockWidget(tr("Statistics"), this);
|
||||||
|
statsDock->setObjectName("statsDock");
|
||||||
|
statsDock->setFeatures(
|
||||||
|
statsDock->features() & ~QDockWidget::DockWidgetClosable);
|
||||||
|
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
menuFile->insertActions(menuFile->actions().at(0), portsWindow->actions());
|
||||||
|
|
||||||
|
statsDock->setWidget(statsWindow);
|
||||||
|
addDockWidget(Qt::BottomDockWidgetArea, statsDock);
|
||||||
|
portsDock->setWidget(portsWindow);
|
||||||
|
addDockWidget(Qt::TopDockWidgetArea, portsDock);
|
||||||
|
|
||||||
|
QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect();
|
||||||
|
if (!geom.isNull())
|
||||||
|
setGeometry(geom);
|
||||||
|
QByteArray layout = appSettings->value(kApplicationWindowLayout)
|
||||||
|
.toByteArray();
|
||||||
|
if (layout.size())
|
||||||
|
restoreState(layout, 0);
|
||||||
|
|
||||||
|
connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close()));
|
||||||
|
connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
|
||||||
|
|
||||||
|
connect(updater, SIGNAL(newVersionAvailable(QString)),
|
||||||
|
this, SLOT(onNewVersion(QString)));
|
||||||
|
updater->checkForNewVersion();
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
DbgThread *dbg = new DbgThread(pgl);
|
||||||
|
dbg->start();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
//! \todo - find a way to terminate cleanly
|
||||||
|
localServer_->kill();
|
||||||
|
#else
|
||||||
|
localServer_->terminate();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete pgl;
|
||||||
|
|
||||||
|
QByteArray layout = saveState(0);
|
||||||
|
appSettings->setValue(kApplicationWindowLayout, layout);
|
||||||
|
appSettings->setValue(kApplicationWindowGeometryKey, geometry());
|
||||||
|
|
||||||
|
localServer_->waitForFinished();
|
||||||
|
delete localServer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionPreferences_triggered()
|
||||||
|
{
|
||||||
|
Preferences *preferences = new Preferences();
|
||||||
|
|
||||||
|
preferences->exec();
|
||||||
|
|
||||||
|
delete preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionHelpAbout_triggered()
|
||||||
|
{
|
||||||
|
QDialog *aboutDialog = new QDialog;
|
||||||
|
|
||||||
|
Ui::About about;
|
||||||
|
about.setupUi(aboutDialog);
|
||||||
|
about.versionLabel->setText(
|
||||||
|
QString("Version: %1 Revision: %2").arg(version).arg(revision));
|
||||||
|
|
||||||
|
aboutDialog->exec();
|
||||||
|
|
||||||
|
delete aboutDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onNewVersion(QString newVersion)
|
||||||
|
{
|
||||||
|
statusBar()->showMessage(QString("New Ostinato version %1 available. "
|
||||||
|
"Visit http://ostinato.org to download").arg(newVersion));
|
||||||
|
}
|
56
client/mainwindow.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MAIN_WINDOW_H
|
||||||
|
#define _MAIN_WINDOW_H
|
||||||
|
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
class PortsWindow;
|
||||||
|
class PortStatsWindow;
|
||||||
|
|
||||||
|
class QDockWidget;
|
||||||
|
class QProcess;
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
QProcess *localServer_;
|
||||||
|
PortsWindow *portsWindow;
|
||||||
|
PortStatsWindow *statsWindow;
|
||||||
|
QDockWidget *portsDock;
|
||||||
|
QDockWidget *statsDock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void on_actionPreferences_triggered();
|
||||||
|
void on_actionHelpAbout_triggered();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onNewVersion(QString version);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
84
client/mainwindow.ui
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>700</width>
|
||||||
|
<height>550</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Ostinato</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/about.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget" />
|
||||||
|
<widget class="QMenuBar" name="menubar" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>700</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuFile" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="separator" />
|
||||||
|
<addaction name="actionPreferences" />
|
||||||
|
<addaction name="actionFileExit" />
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuHelp" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>Help</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionHelpAbout" />
|
||||||
|
<addaction name="actionAboutQt" />
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuFile" />
|
||||||
|
<addaction name="menuHelp" />
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar" />
|
||||||
|
<action name="actionFileExit" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/exit.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>E&xit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHelpAbout" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/about.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>&About</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionPreferences" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/preferences.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Preferences</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionAboutQt" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/qt.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>About Qt</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
542
client/modeltest.cpp
Normal file
@ -0,0 +1,542 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2007 Trolltech ASA. All rights reserved.
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Concurrent project on Trolltech Labs.
|
||||||
|
**
|
||||||
|
** This file may be used under the terms of the GNU General Public
|
||||||
|
** License version 2.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of
|
||||||
|
** this file. Please review the following information to ensure GNU
|
||||||
|
** General Public Licensing requirements will be met:
|
||||||
|
** http://www.trolltech.com/products/qt/opensource.html
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** review the following information:
|
||||||
|
** http://www.trolltech.com/products/qt/licensing.html or contact the
|
||||||
|
** sales department at sales@trolltech.com.
|
||||||
|
**
|
||||||
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include <QtGui/QtGui>
|
||||||
|
|
||||||
|
#include "modeltest.h"
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QModelIndex)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Connect to all of the models signals. Whenever anything happens recheck everything.
|
||||||
|
*/
|
||||||
|
ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false)
|
||||||
|
{
|
||||||
|
Q_ASSERT(model);
|
||||||
|
|
||||||
|
connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(runAllTests()));
|
||||||
|
|
||||||
|
// Special checks for inserting/removing
|
||||||
|
connect(model, SIGNAL(layoutAboutToBeChanged()),
|
||||||
|
this, SLOT(layoutAboutToBeChanged()));
|
||||||
|
connect(model, SIGNAL(layoutChanged()),
|
||||||
|
this, SLOT(layoutChanged()));
|
||||||
|
|
||||||
|
connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int)));
|
||||||
|
connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
|
||||||
|
connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(rowsInserted(const QModelIndex &, int, int)));
|
||||||
|
connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
|
||||||
|
this, SLOT(rowsRemoved(const QModelIndex &, int, int)));
|
||||||
|
|
||||||
|
runAllTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelTest::runAllTests()
|
||||||
|
{
|
||||||
|
if (fetchingMore)
|
||||||
|
return;
|
||||||
|
nonDestructiveBasicTest();
|
||||||
|
rowCount();
|
||||||
|
columnCount();
|
||||||
|
hasIndex();
|
||||||
|
index();
|
||||||
|
parent();
|
||||||
|
data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
|
||||||
|
to make sure the model doesn't outright segfault, testing the functions that makes sense.
|
||||||
|
*/
|
||||||
|
void ModelTest::nonDestructiveBasicTest()
|
||||||
|
{
|
||||||
|
Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex());
|
||||||
|
model->canFetchMore(QModelIndex());
|
||||||
|
Q_ASSERT(model->columnCount(QModelIndex()) >= 0);
|
||||||
|
Q_ASSERT(model->data(QModelIndex()) == QVariant());
|
||||||
|
fetchingMore = true;
|
||||||
|
model->fetchMore(QModelIndex());
|
||||||
|
fetchingMore = false;
|
||||||
|
Qt::ItemFlags flags = model->flags(QModelIndex());
|
||||||
|
Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0);
|
||||||
|
model->hasChildren(QModelIndex());
|
||||||
|
model->hasIndex(0, 0);
|
||||||
|
model->headerData(0, Qt::Horizontal);
|
||||||
|
model->index(0, 0);
|
||||||
|
Q_ASSERT(model->index(-1, -1) == QModelIndex());
|
||||||
|
model->itemData(QModelIndex());
|
||||||
|
QVariant cache;
|
||||||
|
model->match(QModelIndex(), -1, cache);
|
||||||
|
model->mimeTypes();
|
||||||
|
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
|
||||||
|
Q_ASSERT(model->rowCount() >= 0);
|
||||||
|
QVariant variant;
|
||||||
|
model->setData(QModelIndex(), variant, -1);
|
||||||
|
model->setHeaderData(-1, Qt::Horizontal, QVariant());
|
||||||
|
model->setHeaderData(0, Qt::Horizontal, QVariant());
|
||||||
|
model->setHeaderData(999999, Qt::Horizontal, QVariant());
|
||||||
|
QMap<int, QVariant> roles;
|
||||||
|
model->sibling(0, 0, QModelIndex());
|
||||||
|
model->span(QModelIndex());
|
||||||
|
model->supportedDropActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
|
||||||
|
|
||||||
|
Models that are dynamically populated are not as fully tested here.
|
||||||
|
*/
|
||||||
|
void ModelTest::rowCount()
|
||||||
|
{
|
||||||
|
// check top row
|
||||||
|
QModelIndex topIndex = model->index(0, 0, QModelIndex());
|
||||||
|
int rows = model->rowCount(topIndex);
|
||||||
|
Q_ASSERT(rows >= 0);
|
||||||
|
if (rows > 0)
|
||||||
|
Q_ASSERT(model->hasChildren(topIndex) == true);
|
||||||
|
|
||||||
|
QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
|
||||||
|
if (secondLevelIndex.isValid()) { // not the top level
|
||||||
|
// check a row count where parent is valid
|
||||||
|
rows = model->rowCount(secondLevelIndex);
|
||||||
|
Q_ASSERT(rows >= 0);
|
||||||
|
if (rows > 0)
|
||||||
|
Q_ASSERT(model->hasChildren(secondLevelIndex) == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The models rowCount() is tested more extensively in checkChildren(),
|
||||||
|
// but this catches the big mistakes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
|
||||||
|
*/
|
||||||
|
void ModelTest::columnCount()
|
||||||
|
{
|
||||||
|
// check top row
|
||||||
|
QModelIndex topIndex = model->index(0, 0, QModelIndex());
|
||||||
|
Q_ASSERT(model->columnCount(topIndex) >= 0);
|
||||||
|
|
||||||
|
// check a column count where parent is valid
|
||||||
|
QModelIndex childIndex = model->index(0, 0, topIndex);
|
||||||
|
if (childIndex.isValid())
|
||||||
|
Q_ASSERT(model->columnCount(childIndex) >= 0);
|
||||||
|
|
||||||
|
// columnCount() is tested more extensively in checkChildren(),
|
||||||
|
// but this catches the big mistakes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::hasIndex()
|
||||||
|
*/
|
||||||
|
void ModelTest::hasIndex()
|
||||||
|
{
|
||||||
|
// Make sure that invalid values returns an invalid index
|
||||||
|
Q_ASSERT(model->hasIndex(-2, -2) == false);
|
||||||
|
Q_ASSERT(model->hasIndex(-2, 0) == false);
|
||||||
|
Q_ASSERT(model->hasIndex(0, -2) == false);
|
||||||
|
|
||||||
|
int rows = model->rowCount();
|
||||||
|
int columns = model->columnCount();
|
||||||
|
|
||||||
|
// check out of bounds
|
||||||
|
Q_ASSERT(model->hasIndex(rows, columns) == false);
|
||||||
|
Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false);
|
||||||
|
|
||||||
|
if (rows > 0)
|
||||||
|
Q_ASSERT(model->hasIndex(0, 0) == true);
|
||||||
|
|
||||||
|
// hasIndex() is tested more extensively in checkChildren(),
|
||||||
|
// but this catches the big mistakes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::index()
|
||||||
|
*/
|
||||||
|
void ModelTest::index()
|
||||||
|
{
|
||||||
|
// Make sure that invalid values returns an invalid index
|
||||||
|
Q_ASSERT(model->index(-2, -2) == QModelIndex());
|
||||||
|
Q_ASSERT(model->index(-2, 0) == QModelIndex());
|
||||||
|
Q_ASSERT(model->index(0, -2) == QModelIndex());
|
||||||
|
|
||||||
|
int rows = model->rowCount();
|
||||||
|
int columns = model->columnCount();
|
||||||
|
|
||||||
|
if (rows == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Catch off by one errors
|
||||||
|
Q_ASSERT(model->index(rows, columns) == QModelIndex());
|
||||||
|
Q_ASSERT(model->index(0, 0).isValid() == true);
|
||||||
|
|
||||||
|
// Make sure that the same index is *always* returned
|
||||||
|
QModelIndex a = model->index(0, 0);
|
||||||
|
QModelIndex b = model->index(0, 0);
|
||||||
|
Q_ASSERT(a == b);
|
||||||
|
|
||||||
|
// index() is tested more extensively in checkChildren(),
|
||||||
|
// but this catches the big mistakes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::parent()
|
||||||
|
*/
|
||||||
|
void ModelTest::parent()
|
||||||
|
{
|
||||||
|
// Make sure the model wont crash and will return an invalid QModelIndex
|
||||||
|
// when asked for the parent of an invalid index.
|
||||||
|
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
|
||||||
|
|
||||||
|
if (model->rowCount() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Column 0 | Column 1 |
|
||||||
|
// QModelIndex() | |
|
||||||
|
// \- topIndex | topIndex1 |
|
||||||
|
// \- childIndex | childIndex1 |
|
||||||
|
|
||||||
|
// Common error test #1, make sure that a top level index has a parent
|
||||||
|
// that is a invalid QModelIndex.
|
||||||
|
QModelIndex topIndex = model->index(0, 0, QModelIndex());
|
||||||
|
Q_ASSERT(model->parent(topIndex) == QModelIndex());
|
||||||
|
|
||||||
|
// Common error test #2, make sure that a second level index has a parent
|
||||||
|
// that is the first level index.
|
||||||
|
if (model->rowCount(topIndex) > 0) {
|
||||||
|
QModelIndex childIndex = model->index(0, 0, topIndex);
|
||||||
|
qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId());
|
||||||
|
qDebug("topIndex I %llx", topIndex.internalId());
|
||||||
|
qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId());
|
||||||
|
Q_ASSERT(model->parent(childIndex) == topIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common error test #3, the second column should NOT have the same children
|
||||||
|
// as the first column in a row.
|
||||||
|
// Usually the second column shouldn't have children.
|
||||||
|
QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
|
||||||
|
if (model->rowCount(topIndex1) > 0) {
|
||||||
|
QModelIndex childIndex = model->index(0, 0, topIndex);
|
||||||
|
QModelIndex childIndex1 = model->index(0, 0, topIndex1);
|
||||||
|
Q_ASSERT(childIndex != childIndex1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full test, walk n levels deep through the model making sure that all
|
||||||
|
// parent's children correctly specify their parent.
|
||||||
|
checkChildren(QModelIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Called from the parent() test.
|
||||||
|
|
||||||
|
A model that returns an index of parent X should also return X when asking
|
||||||
|
for the parent of the index.
|
||||||
|
|
||||||
|
This recursive function does pretty extensive testing on the whole model in an
|
||||||
|
effort to catch edge cases.
|
||||||
|
|
||||||
|
This function assumes that rowCount(), columnCount() and index() already work.
|
||||||
|
If they have a bug it will point it out, but the above tests should have already
|
||||||
|
found the basic bugs because it is easier to figure out the problem in
|
||||||
|
those tests then this one.
|
||||||
|
*/
|
||||||
|
void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth)
|
||||||
|
{
|
||||||
|
// First just try walking back up the tree.
|
||||||
|
QModelIndex p = parent;
|
||||||
|
while (p.isValid())
|
||||||
|
p = p.parent();
|
||||||
|
|
||||||
|
// For models that are dynamically populated
|
||||||
|
if (model->canFetchMore(parent)) {
|
||||||
|
fetchingMore = true;
|
||||||
|
model->fetchMore(parent);
|
||||||
|
fetchingMore = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rows = model->rowCount(parent);
|
||||||
|
int columns = model->columnCount(parent);
|
||||||
|
|
||||||
|
if (rows > 0)
|
||||||
|
Q_ASSERT(model->hasChildren(parent));
|
||||||
|
|
||||||
|
// Some further testing against rows(), columns(), and hasChildren()
|
||||||
|
Q_ASSERT(rows >= 0);
|
||||||
|
Q_ASSERT(columns >= 0);
|
||||||
|
if (rows > 0)
|
||||||
|
Q_ASSERT(model->hasChildren(parent) == true);
|
||||||
|
|
||||||
|
//qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
|
||||||
|
// << "columns:" << columns << "parent column:" << parent.column();
|
||||||
|
|
||||||
|
Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false);
|
||||||
|
for (int r = 0; r < rows; ++r) {
|
||||||
|
if (model->canFetchMore(parent)) {
|
||||||
|
fetchingMore = true;
|
||||||
|
model->fetchMore(parent);
|
||||||
|
fetchingMore = false;
|
||||||
|
}
|
||||||
|
Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false);
|
||||||
|
for (int c = 0; c < columns; ++c) {
|
||||||
|
Q_ASSERT(model->hasIndex(r, c, parent) == true);
|
||||||
|
QModelIndex index = model->index(r, c, parent);
|
||||||
|
// rowCount() and columnCount() said that it existed...
|
||||||
|
Q_ASSERT(index.isValid() == true);
|
||||||
|
|
||||||
|
// index() should always return the same index when called twice in a row
|
||||||
|
QModelIndex modifiedIndex = model->index(r, c, parent);
|
||||||
|
Q_ASSERT(index == modifiedIndex);
|
||||||
|
|
||||||
|
// Make sure we get the same index if we request it twice in a row
|
||||||
|
QModelIndex a = model->index(r, c, parent);
|
||||||
|
QModelIndex b = model->index(r, c, parent);
|
||||||
|
Q_ASSERT(a == b);
|
||||||
|
|
||||||
|
// Some basic checking on the index that is returned
|
||||||
|
Q_ASSERT(index.model() == model);
|
||||||
|
Q_ASSERT(index.row() == r);
|
||||||
|
Q_ASSERT(index.column() == c);
|
||||||
|
// While you can technically return a QVariant usually this is a sign
|
||||||
|
// of an bug in data() Disable if this really is ok in your model.
|
||||||
|
//Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true);
|
||||||
|
|
||||||
|
// If the next test fails here is some somewhat useful debug you play with.
|
||||||
|
/*
|
||||||
|
if (model->parent(index) != parent) {
|
||||||
|
qDebug() << r << c << currentDepth << model->data(index).toString()
|
||||||
|
<< model->data(parent).toString();
|
||||||
|
qDebug() << index << parent << model->parent(index);
|
||||||
|
// And a view that you can even use to show the model.
|
||||||
|
//QTreeView view;
|
||||||
|
//view.setModel(model);
|
||||||
|
//view.show();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Check that we can get back our real parent.
|
||||||
|
QModelIndex p = model->parent(index);
|
||||||
|
//qDebug() << "child:" << index;
|
||||||
|
//qDebug() << p;
|
||||||
|
//qDebug() << parent;
|
||||||
|
Q_ASSERT(model->parent(index) == parent);
|
||||||
|
|
||||||
|
// recursively go down the children
|
||||||
|
if (model->hasChildren(index) && currentDepth < 10 ) {
|
||||||
|
//qDebug() << r << c << "has children" << model->rowCount(index);
|
||||||
|
checkChildren(index, ++currentDepth);
|
||||||
|
}/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
|
||||||
|
|
||||||
|
// make sure that after testing the children that the index doesn't change.
|
||||||
|
QModelIndex newerIndex = model->index(r, c, parent);
|
||||||
|
Q_ASSERT(index == newerIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests model's implementation of QAbstractItemModel::data()
|
||||||
|
*/
|
||||||
|
void ModelTest::data()
|
||||||
|
{
|
||||||
|
// Invalid index should return an invalid qvariant
|
||||||
|
Q_ASSERT(!model->data(QModelIndex()).isValid());
|
||||||
|
|
||||||
|
if (model->rowCount() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// A valid index should have a valid QVariant data
|
||||||
|
Q_ASSERT(model->index(0, 0).isValid());
|
||||||
|
|
||||||
|
// shouldn't be able to set data on an invalid index
|
||||||
|
Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false);
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QString
|
||||||
|
QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
|
||||||
|
if (variant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QString>(variant));
|
||||||
|
}
|
||||||
|
variant = model->data(model->index(0, 0), Qt::StatusTipRole);
|
||||||
|
if (variant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QString>(variant));
|
||||||
|
}
|
||||||
|
variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
|
||||||
|
if (variant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QString>(variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QSize
|
||||||
|
variant = model->data(model->index(0, 0), Qt::SizeHintRole);
|
||||||
|
if (variant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QSize>(variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QFont
|
||||||
|
QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole);
|
||||||
|
if (fontVariant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QFont>(fontVariant));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the alignment is one we know about
|
||||||
|
QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
|
||||||
|
if (textAlignmentVariant.isValid()) {
|
||||||
|
int alignment = textAlignmentVariant.toInt();
|
||||||
|
Q_ASSERT(alignment == Qt::AlignLeft ||
|
||||||
|
alignment == Qt::AlignRight ||
|
||||||
|
alignment == Qt::AlignHCenter ||
|
||||||
|
alignment == Qt::AlignJustify ||
|
||||||
|
alignment == Qt::AlignTop ||
|
||||||
|
alignment == Qt::AlignBottom ||
|
||||||
|
alignment == Qt::AlignVCenter ||
|
||||||
|
alignment == Qt::AlignCenter ||
|
||||||
|
alignment == Qt::AlignAbsolute ||
|
||||||
|
alignment == Qt::AlignLeading ||
|
||||||
|
alignment == Qt::AlignTrailing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// General Purpose roles that should return a QColor
|
||||||
|
QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole);
|
||||||
|
if (colorVariant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QColor>(colorVariant));
|
||||||
|
}
|
||||||
|
|
||||||
|
colorVariant = model->data(model->index(0, 0), Qt::TextColorRole);
|
||||||
|
if (colorVariant.isValid()) {
|
||||||
|
Q_ASSERT(qVariantCanConvert<QColor>(colorVariant));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the "check state" is one we know about.
|
||||||
|
QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
|
||||||
|
if (checkStateVariant.isValid()) {
|
||||||
|
int state = checkStateVariant.toInt();
|
||||||
|
Q_ASSERT(state == Qt::Unchecked ||
|
||||||
|
state == Qt::PartiallyChecked ||
|
||||||
|
state == Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Store what is about to be inserted to make sure it actually happens
|
||||||
|
|
||||||
|
\sa rowsInserted()
|
||||||
|
*/
|
||||||
|
void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
Q_UNUSED(end);
|
||||||
|
Changing c;
|
||||||
|
c.parent = parent;
|
||||||
|
c.oldSize = model->rowCount(parent);
|
||||||
|
c.last = model->data(model->index(start - 1, 0, parent));
|
||||||
|
c.next = model->data(model->index(start, 0, parent));
|
||||||
|
insert.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Confirm that what was said was going to happen actually did
|
||||||
|
|
||||||
|
\sa rowsAboutToBeInserted()
|
||||||
|
*/
|
||||||
|
void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end)
|
||||||
|
{
|
||||||
|
Changing c = insert.pop();
|
||||||
|
Q_ASSERT(c.parent == parent);
|
||||||
|
Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent));
|
||||||
|
Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
|
||||||
|
/*
|
||||||
|
if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
|
||||||
|
qDebug() << start << end;
|
||||||
|
for (int i=0; i < model->rowCount(); ++i)
|
||||||
|
qDebug() << model->index(i, 0).data().toString();
|
||||||
|
qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelTest::layoutAboutToBeChanged()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i)
|
||||||
|
changing.append(QPersistentModelIndex(model->index(i, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelTest::layoutChanged()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < changing.count(); ++i) {
|
||||||
|
QPersistentModelIndex p = changing[i];
|
||||||
|
Q_ASSERT(p == model->index(p.row(), p.column(), p.parent()));
|
||||||
|
}
|
||||||
|
changing.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Store what is about to be inserted to make sure it actually happens
|
||||||
|
|
||||||
|
\sa rowsRemoved()
|
||||||
|
*/
|
||||||
|
void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
Changing c;
|
||||||
|
c.parent = parent;
|
||||||
|
c.oldSize = model->rowCount(parent);
|
||||||
|
c.last = model->data(model->index(start - 1, 0, parent));
|
||||||
|
c.next = model->data(model->index(end + 1, 0, parent));
|
||||||
|
remove.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Confirm that what was said was going to happen actually did
|
||||||
|
|
||||||
|
\sa rowsAboutToBeRemoved()
|
||||||
|
*/
|
||||||
|
void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end)
|
||||||
|
{
|
||||||
|
Changing c = remove.pop();
|
||||||
|
Q_ASSERT(c.parent == parent);
|
||||||
|
Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent));
|
||||||
|
Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
|
||||||
|
Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent)));
|
||||||
|
}
|
||||||
|
|
76
client/modeltest.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2007 Trolltech ASA. All rights reserved.
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Concurrent project on Trolltech Labs.
|
||||||
|
**
|
||||||
|
** This file may be used under the terms of the GNU General Public
|
||||||
|
** License version 2.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of
|
||||||
|
** this file. Please review the following information to ensure GNU
|
||||||
|
** General Public Licensing requirements will be met:
|
||||||
|
** http://www.trolltech.com/products/qt/opensource.html
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** review the following information:
|
||||||
|
** http://www.trolltech.com/products/qt/licensing.html or contact the
|
||||||
|
** sales department at sales@trolltech.com.
|
||||||
|
**
|
||||||
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MODELTEST_H
|
||||||
|
#define MODELTEST_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QAbstractItemModel>
|
||||||
|
#include <QtCore/QStack>
|
||||||
|
|
||||||
|
class ModelTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModelTest(QAbstractItemModel *model, QObject *parent = 0);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void nonDestructiveBasicTest();
|
||||||
|
void rowCount();
|
||||||
|
void columnCount();
|
||||||
|
void hasIndex();
|
||||||
|
void index();
|
||||||
|
void parent();
|
||||||
|
void data();
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void runAllTests();
|
||||||
|
void layoutAboutToBeChanged();
|
||||||
|
void layoutChanged();
|
||||||
|
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
|
||||||
|
void rowsInserted(const QModelIndex & parent, int start, int end);
|
||||||
|
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
|
||||||
|
void rowsRemoved(const QModelIndex & parent, int start, int end);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkChildren(const QModelIndex &parent, int currentDepth = 0);
|
||||||
|
|
||||||
|
QAbstractItemModel *model;
|
||||||
|
|
||||||
|
struct Changing
|
||||||
|
{
|
||||||
|
QModelIndex parent;
|
||||||
|
int oldSize;
|
||||||
|
QVariant last;
|
||||||
|
QVariant next;
|
||||||
|
};
|
||||||
|
QStack<Changing> insert;
|
||||||
|
QStack<Changing> remove;
|
||||||
|
|
||||||
|
bool fetchingMore;
|
||||||
|
|
||||||
|
QList<QPersistentModelIndex> changing;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
4
client/modeltest.pri
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
SOURCES += $$PWD/modeltest.cpp
|
||||||
|
HEADERS += $$PWD/modeltest.h
|
95
client/ostinato.pro
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += qt ver_info
|
||||||
|
macx: TARGET = Ostinato
|
||||||
|
win32:RC_FILE = ostinato.rc
|
||||||
|
macx:ICON = icons/logo.icns
|
||||||
|
QT += network script xml
|
||||||
|
INCLUDEPATH += "../rpc/" "../common/"
|
||||||
|
win32 {
|
||||||
|
CONFIG(debug, debug|release) {
|
||||||
|
LIBS += -L"../common/debug" -lostprotogui -lostproto
|
||||||
|
LIBS += -L"../rpc/debug" -lpbrpc
|
||||||
|
POST_TARGETDEPS += \
|
||||||
|
"../common/debug/libostprotogui.a" \
|
||||||
|
"../common/debug/libostproto.a" \
|
||||||
|
"../rpc/debug/libpbrpc.a"
|
||||||
|
} else {
|
||||||
|
LIBS += -L"../common/release" -lostprotogui -lostproto
|
||||||
|
LIBS += -L"../rpc/release" -lpbrpc
|
||||||
|
POST_TARGETDEPS += \
|
||||||
|
"../common/release/libostprotogui.a" \
|
||||||
|
"../common/release/libostproto.a" \
|
||||||
|
"../rpc/release/libpbrpc.a"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LIBS += -L"../common" -lostprotogui -lostproto
|
||||||
|
LIBS += -L"../rpc" -lpbrpc
|
||||||
|
POST_TARGETDEPS += \
|
||||||
|
"../common/libostprotogui.a" \
|
||||||
|
"../common/libostproto.a" \
|
||||||
|
"../rpc/libpbrpc.a"
|
||||||
|
}
|
||||||
|
LIBS += -lprotobuf
|
||||||
|
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
|
||||||
|
RESOURCES += ostinato.qrc
|
||||||
|
HEADERS += \
|
||||||
|
dumpview.h \
|
||||||
|
hexlineedit.h \
|
||||||
|
mainwindow.h \
|
||||||
|
packetmodel.h \
|
||||||
|
port.h \
|
||||||
|
portconfigdialog.h \
|
||||||
|
portgroup.h \
|
||||||
|
portgrouplist.h \
|
||||||
|
portmodel.h \
|
||||||
|
portstatsmodel.h \
|
||||||
|
portstatsfilterdialog.h \
|
||||||
|
portstatswindow.h \
|
||||||
|
portswindow.h \
|
||||||
|
preferences.h \
|
||||||
|
settings.h \
|
||||||
|
streamconfigdialog.h \
|
||||||
|
streamlistdelegate.h \
|
||||||
|
streammodel.h \
|
||||||
|
updater.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
about.ui \
|
||||||
|
mainwindow.ui \
|
||||||
|
portconfigdialog.ui \
|
||||||
|
portstatsfilter.ui \
|
||||||
|
portstatswindow.ui \
|
||||||
|
portswindow.ui \
|
||||||
|
preferences.ui \
|
||||||
|
streamconfigdialog.ui
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
dumpview.cpp \
|
||||||
|
stream.cpp \
|
||||||
|
hexlineedit.cpp \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
packetmodel.cpp \
|
||||||
|
port.cpp \
|
||||||
|
portconfigdialog.cpp \
|
||||||
|
portgroup.cpp \
|
||||||
|
portgrouplist.cpp \
|
||||||
|
portmodel.cpp \
|
||||||
|
portstatsmodel.cpp \
|
||||||
|
portstatsfilterdialog.cpp \
|
||||||
|
portstatswindow.cpp \
|
||||||
|
portswindow.cpp \
|
||||||
|
preferences.cpp \
|
||||||
|
streamconfigdialog.cpp \
|
||||||
|
streamlistdelegate.cpp \
|
||||||
|
streammodel.cpp \
|
||||||
|
updater.cpp
|
||||||
|
|
||||||
|
|
||||||
|
QMAKE_DISTCLEAN += object_script.*
|
||||||
|
|
||||||
|
include(../install.pri)
|
||||||
|
include(../version.pri)
|
||||||
|
|
||||||
|
# TODO(LOW): Test only
|
||||||
|
CONFIG(debug, debug|release):include(modeltest.pri)
|
39
client/ostinato.qrc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/" >
|
||||||
|
<file>icons/about.png</file>
|
||||||
|
<file>icons/arrow_down.png</file>
|
||||||
|
<file>icons/arrow_left.png</file>
|
||||||
|
<file>icons/arrow_right.png</file>
|
||||||
|
<file>icons/arrow_up.png</file>
|
||||||
|
<file>icons/bullet_error.png</file>
|
||||||
|
<file>icons/bullet_green.png</file>
|
||||||
|
<file>icons/bullet_orange.png</file>
|
||||||
|
<file>icons/bullet_red.png</file>
|
||||||
|
<file>icons/bullet_white.png</file>
|
||||||
|
<file>icons/bullet_yellow.png</file>
|
||||||
|
<file>icons/control_play.png</file>
|
||||||
|
<file>icons/control_stop.png</file>
|
||||||
|
<file>icons/deco_exclusive.png</file>
|
||||||
|
<file>icons/delete.png</file>
|
||||||
|
<file>icons/exit.png</file>
|
||||||
|
<file>icons/gaps.png</file>
|
||||||
|
<file>icons/logo.png</file>
|
||||||
|
<file>icons/magnifier.png</file>
|
||||||
|
<file>icons/name.png</file>
|
||||||
|
<file>icons/portgroup_add.png</file>
|
||||||
|
<file>icons/portgroup_connect.png</file>
|
||||||
|
<file>icons/portgroup_delete.png</file>
|
||||||
|
<file>icons/portgroup_disconnect.png</file>
|
||||||
|
<file>icons/portstats_clear.png</file>
|
||||||
|
<file>icons/portstats_clear_all.png</file>
|
||||||
|
<file>icons/portstats_filter.png</file>
|
||||||
|
<file>icons/preferences.png</file>
|
||||||
|
<file>icons/qt.png</file>
|
||||||
|
<file>icons/sound_mute.png</file>
|
||||||
|
<file>icons/sound_none.png</file>
|
||||||
|
<file>icons/stream_add.png</file>
|
||||||
|
<file>icons/stream_delete.png</file>
|
||||||
|
<file>icons/stream_duplicate.png</file>
|
||||||
|
<file>icons/stream_edit.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
1
client/ostinato.rc
Normal file
@ -0,0 +1 @@
|
|||||||
|
IDI_ICON1 ICON DISCARDABLE "icons/logo.ico"
|
244
client/packetmodel.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
#include "packetmodel.h"
|
||||||
|
#include "../common/protocollistiterator.h"
|
||||||
|
#include "../common/abstractprotocol.h"
|
||||||
|
|
||||||
|
PacketModel::PacketModel(QObject *parent)
|
||||||
|
: QAbstractItemModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketModel::setSelectedProtocols(ProtocolListIterator &iter)
|
||||||
|
{
|
||||||
|
QList<const AbstractProtocol*> currentProtocols;
|
||||||
|
|
||||||
|
iter.toFront();
|
||||||
|
while (iter.hasNext())
|
||||||
|
currentProtocols.append(iter.next());
|
||||||
|
|
||||||
|
if (mSelectedProtocols != currentProtocols)
|
||||||
|
{
|
||||||
|
mSelectedProtocols = currentProtocols;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
|
emit layoutChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PacketModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
IndexId parentId;
|
||||||
|
|
||||||
|
// qDebug("in %s", __FUNCTION__);
|
||||||
|
|
||||||
|
// Parent == Invalid i.e. Invisible Root.
|
||||||
|
// ==> Children are Protocol (Top Level) Items
|
||||||
|
if (!parent.isValid())
|
||||||
|
return mSelectedProtocols.size();
|
||||||
|
|
||||||
|
// Parent - Valid Item
|
||||||
|
parentId.w = parent.internalId();
|
||||||
|
switch(parentId.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount();
|
||||||
|
case ITYP_FIELD:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1 == 0); // Unreachable code
|
||||||
|
qWarning("%s: Catch all - need to investigate", __FUNCTION__);
|
||||||
|
return 0; // catch all
|
||||||
|
}
|
||||||
|
|
||||||
|
int PacketModel::columnCount(const QModelIndex &/*parent*/) const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
QModelIndex index;
|
||||||
|
IndexId id, parentId;
|
||||||
|
|
||||||
|
if (!hasIndex(row, col, parent))
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
// Parent is Invisible Root
|
||||||
|
// Request for a Protocol Item
|
||||||
|
if (!parent.isValid())
|
||||||
|
{
|
||||||
|
id.w = 0;
|
||||||
|
id.ws.type = ITYP_PROTOCOL;
|
||||||
|
id.ws.protocol = row;
|
||||||
|
index = createIndex(row, col, id.w);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent is a Valid Item
|
||||||
|
parentId.w = parent.internalId();
|
||||||
|
id.w = parentId.w;
|
||||||
|
switch(parentId.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
id.ws.type = ITYP_FIELD;
|
||||||
|
index = createIndex(row, col, id.w);
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
case ITYP_FIELD:
|
||||||
|
Q_ASSERT(1 == 0); // Unreachable code
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1 == 0); // Unreachable code
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PacketModel::parent(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QModelIndex parentIndex;
|
||||||
|
IndexId id, parentId;
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
id.w = index.internalId();
|
||||||
|
parentId.w = id.w;
|
||||||
|
switch(id.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
// return invalid index for invisible root
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
case ITYP_FIELD:
|
||||||
|
parentId.ws.type = ITYP_PROTOCOL;
|
||||||
|
parentIndex = createIndex(id.ws.protocol, 0, parentId.w);
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1 == 1); // Unreachable code
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return parentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PacketModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
IndexId id;
|
||||||
|
int fieldIdx = 0;
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
id.w = index.internalId();
|
||||||
|
|
||||||
|
if (id.ws.type == ITYP_FIELD)
|
||||||
|
{
|
||||||
|
const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol);
|
||||||
|
int n = index.row() + 1;
|
||||||
|
|
||||||
|
while (n)
|
||||||
|
{
|
||||||
|
if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField))
|
||||||
|
n--;
|
||||||
|
fieldIdx++;
|
||||||
|
}
|
||||||
|
fieldIdx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(HI): Relook at this completely
|
||||||
|
if (role == Qt::UserRole)
|
||||||
|
{
|
||||||
|
switch(id.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size());
|
||||||
|
return mSelectedProtocols.at(id.ws.protocol)->
|
||||||
|
protocolFrameValue();
|
||||||
|
|
||||||
|
case ITYP_FIELD:
|
||||||
|
return mSelectedProtocols.at(id.ws.protocol)->fieldData(
|
||||||
|
fieldIdx, AbstractProtocol::FieldFrameValue);
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Use a new enum here instead of UserRole
|
||||||
|
if (role == (Qt::UserRole+1))
|
||||||
|
{
|
||||||
|
switch(id.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
return mSelectedProtocols.at(id.ws.protocol)->
|
||||||
|
protocolFrameValue().size();
|
||||||
|
|
||||||
|
case ITYP_FIELD:
|
||||||
|
return mSelectedProtocols.at(id.ws.protocol)->fieldData(
|
||||||
|
fieldIdx, AbstractProtocol::FieldBitSize);
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch(id.ws.type)
|
||||||
|
{
|
||||||
|
case ITYP_PROTOCOL:
|
||||||
|
return QString("%1 (%2)")
|
||||||
|
.arg(mSelectedProtocols.at(id.ws.protocol)->shortName())
|
||||||
|
.arg(mSelectedProtocols.at(id.ws.protocol)->name());
|
||||||
|
|
||||||
|
case ITYP_FIELD:
|
||||||
|
return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx,
|
||||||
|
AbstractProtocol::FieldName).toString() + QString(" : ") +
|
||||||
|
mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx,
|
||||||
|
AbstractProtocol::FieldTextValue).toString();
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled ItemType", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1 == 1); // Unreachable code
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
61
client/packetmodel.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PACKET_MODEL_H
|
||||||
|
#define _PACKET_MODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
class ProtocolListIterator;
|
||||||
|
class AbstractProtocol;
|
||||||
|
|
||||||
|
class PacketModel: public QAbstractItemModel
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
PacketModel(QObject *parent = 0);
|
||||||
|
void setSelectedProtocols(ProtocolListIterator &iter);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/,
|
||||||
|
int /*role= Qt::DisplayRole*/) const {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const;
|
||||||
|
QModelIndex parent(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef union _IndexId
|
||||||
|
{
|
||||||
|
quint32 w;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint16 type;
|
||||||
|
#define ITYP_PROTOCOL 1
|
||||||
|
#define ITYP_FIELD 2
|
||||||
|
quint16 protocol; // protocol is valid for both ITYPs
|
||||||
|
} ws;
|
||||||
|
} IndexId;
|
||||||
|
|
||||||
|
QList<const AbstractProtocol*> mSelectedProtocols;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
598
client/port.cpp
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#include "abstractfileformat.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <google/protobuf/descriptor.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern QMainWindow *mainWindow;
|
||||||
|
|
||||||
|
uint Port::mAllocStreamId = 0;
|
||||||
|
|
||||||
|
static const int kEthOverhead = 20;
|
||||||
|
|
||||||
|
uint Port::newStreamId()
|
||||||
|
{
|
||||||
|
return mAllocStreamId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Port::Port(quint32 id, quint32 portGroupId)
|
||||||
|
{
|
||||||
|
mPortId = id;
|
||||||
|
d.mutable_port_id()->set_id(id);
|
||||||
|
stats.mutable_port_id()->set_id(id);
|
||||||
|
mPortGroupId = portGroupId;
|
||||||
|
capFile_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Port::~Port()
|
||||||
|
{
|
||||||
|
qDebug("%s", __FUNCTION__);
|
||||||
|
while (!mStreams.isEmpty())
|
||||||
|
delete mStreams.takeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::updatePortConfig(OstProto::Port *port)
|
||||||
|
{
|
||||||
|
d.MergeFrom(*port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::updateStreamOrdinalsFromIndex()
|
||||||
|
{
|
||||||
|
for (int i=0; i < mStreams.size(); i++)
|
||||||
|
mStreams[i]->setOrdinal(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::reorderStreamsByOrdinals()
|
||||||
|
{
|
||||||
|
qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::recalculateAverageRates()
|
||||||
|
{
|
||||||
|
double pps = 0;
|
||||||
|
double bps = 0;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
foreach (Stream* s, mStreams)
|
||||||
|
{
|
||||||
|
if (!s->isEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double r = s->averagePacketRate();
|
||||||
|
pps += r;
|
||||||
|
bps += r * (s->frameLenAvg() + kEthOverhead) * 8;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if ((transmitMode() == OstProto::kSequentialTransmit)
|
||||||
|
&& (s->nextWhat() == Stream::e_nw_stop))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
switch (transmitMode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
avgPacketsPerSec_ = pps/n;
|
||||||
|
avgBitsPerSec_ = bps/n;
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
avgPacketsPerSec_ = pps;
|
||||||
|
avgBitsPerSec_ = bps;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!
|
||||||
|
}
|
||||||
|
numActiveStreams_ = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||||
|
|
||||||
|
qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__,
|
||||||
|
avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_);
|
||||||
|
|
||||||
|
emit portRateChanged(mPortGroupId, mPortId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::setAveragePacketRate(double packetsPerSec)
|
||||||
|
{
|
||||||
|
double rate = 0;
|
||||||
|
double pps = 0;
|
||||||
|
double bps = 0;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec);
|
||||||
|
qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__,
|
||||||
|
avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_);
|
||||||
|
foreach (Stream* s, mStreams)
|
||||||
|
{
|
||||||
|
if (!s->isEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (transmitMode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_);
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
rate = s->averagePacketRate() +
|
||||||
|
((s->averagePacketRate()/avgPacketsPerSec_) *
|
||||||
|
(packetsPerSec - avgPacketsPerSec_));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("cur stream pps = %g", s->averagePacketRate());
|
||||||
|
|
||||||
|
s->setAveragePacketRate(rate);
|
||||||
|
|
||||||
|
qDebug("new stream pps = %g", s->averagePacketRate());
|
||||||
|
|
||||||
|
double r = s->averagePacketRate();
|
||||||
|
pps += r;
|
||||||
|
bps += r * (s->frameLenAvg() + kEthOverhead) * 8;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if ((transmitMode() == OstProto::kSequentialTransmit)
|
||||||
|
&& (s->nextWhat() == Stream::e_nw_stop))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
switch (transmitMode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
avgPacketsPerSec_ = pps/n;
|
||||||
|
avgBitsPerSec_ = bps/n;
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
avgPacketsPerSec_ = pps;
|
||||||
|
avgBitsPerSec_ = bps;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!
|
||||||
|
}
|
||||||
|
numActiveStreams_ = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||||
|
|
||||||
|
qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__,
|
||||||
|
avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_);
|
||||||
|
|
||||||
|
emit portRateChanged(mPortGroupId, mPortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::setAverageBitRate(double bitsPerSec)
|
||||||
|
{
|
||||||
|
double rate = 0;
|
||||||
|
double pps = 0;
|
||||||
|
double bps = 0;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec);
|
||||||
|
qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__,
|
||||||
|
avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_);
|
||||||
|
foreach (Stream* s, mStreams)
|
||||||
|
{
|
||||||
|
if (!s->isEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (transmitMode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_);
|
||||||
|
qDebug("rate = %g", rate);
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
rate = s->averagePacketRate()
|
||||||
|
+ ((s->averagePacketRate()/avgPacketsPerSec_)
|
||||||
|
* ((bitsPerSec - avgBitsPerSec_)
|
||||||
|
/ ((s->frameLenAvg()+kEthOverhead)*8)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("cur stream pps = %g", s->averagePacketRate());
|
||||||
|
|
||||||
|
s->setAveragePacketRate(rate);
|
||||||
|
|
||||||
|
qDebug("new stream pps = %g", s->averagePacketRate());
|
||||||
|
|
||||||
|
double r = s->averagePacketRate();
|
||||||
|
pps += r;
|
||||||
|
bps += r * (s->frameLenAvg() + kEthOverhead) * 8;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if ((transmitMode() == OstProto::kSequentialTransmit)
|
||||||
|
&& (s->nextWhat() == Stream::e_nw_stop))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
switch (transmitMode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
avgPacketsPerSec_ = pps/n;
|
||||||
|
avgBitsPerSec_ = bps/n;
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
avgPacketsPerSec_ = pps;
|
||||||
|
avgBitsPerSec_ = bps;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!
|
||||||
|
}
|
||||||
|
numActiveStreams_ = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||||
|
|
||||||
|
qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__,
|
||||||
|
avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_);
|
||||||
|
emit portRateChanged(mPortGroupId, mPortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::newStreamAt(int index, OstProto::Stream const *stream)
|
||||||
|
{
|
||||||
|
Stream *s = new Stream;
|
||||||
|
|
||||||
|
if (index > mStreams.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream)
|
||||||
|
s->protoDataCopyFrom(*stream);
|
||||||
|
|
||||||
|
s->setId(newStreamId());
|
||||||
|
mStreams.insert(index, s);
|
||||||
|
updateStreamOrdinalsFromIndex();
|
||||||
|
recalculateAverageRates();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::deleteStreamAt(int index)
|
||||||
|
{
|
||||||
|
if (index >= mStreams.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delete mStreams.takeAt(index);
|
||||||
|
updateStreamOrdinalsFromIndex();
|
||||||
|
recalculateAverageRates();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::insertStream(uint streamId)
|
||||||
|
{
|
||||||
|
Stream *s = new Stream;
|
||||||
|
|
||||||
|
s->setId(streamId);
|
||||||
|
|
||||||
|
// FIXME(MED): If a stream with id already exists, what do we do?
|
||||||
|
mStreams.append(s);
|
||||||
|
|
||||||
|
// Update mAllocStreamId to take into account the stream id received
|
||||||
|
// from server
|
||||||
|
if (mAllocStreamId <= streamId)
|
||||||
|
mAllocStreamId = streamId + 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::updateStream(uint streamId, OstProto::Stream *stream)
|
||||||
|
{
|
||||||
|
int i, streamIndex;
|
||||||
|
|
||||||
|
for (i = 0; i < mStreams.size(); i++)
|
||||||
|
{
|
||||||
|
if (streamId == mStreams[i]->id())
|
||||||
|
goto _found;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_found:
|
||||||
|
streamIndex = i;
|
||||||
|
|
||||||
|
mStreams[streamIndex]->protoDataCopyFrom(*stream);
|
||||||
|
reorderStreamsByOrdinals();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::getDeletedStreamsSinceLastSync(
|
||||||
|
OstProto::StreamIdList &streamIdList)
|
||||||
|
{
|
||||||
|
streamIdList.clear_stream_id();
|
||||||
|
for (int i = 0; i < mLastSyncStreamList.size(); i++)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < mStreams.size(); j++)
|
||||||
|
{
|
||||||
|
if (mLastSyncStreamList[i] == mStreams[j]->id())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j < mStreams.size())
|
||||||
|
{
|
||||||
|
// stream still exists!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// stream has been deleted since last sync
|
||||||
|
OstProto::StreamId *s;
|
||||||
|
|
||||||
|
s = streamIdList.add_stream_id();
|
||||||
|
s->set_id(mLastSyncStreamList.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::getNewStreamsSinceLastSync(
|
||||||
|
OstProto::StreamIdList &streamIdList)
|
||||||
|
{
|
||||||
|
streamIdList.clear_stream_id();
|
||||||
|
for (int i = 0; i < mStreams.size(); i++)
|
||||||
|
{
|
||||||
|
if (mLastSyncStreamList.contains(mStreams[i]->id()))
|
||||||
|
{
|
||||||
|
// existing stream!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// new stream!
|
||||||
|
OstProto::StreamId *s;
|
||||||
|
|
||||||
|
s = streamIdList.add_stream_id();
|
||||||
|
s->set_id(mStreams[i]->id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::getModifiedStreamsSinceLastSync(
|
||||||
|
OstProto::StreamConfigList &streamConfigList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
//streamConfigList.mutable_port_id()->set_id(mPortId);
|
||||||
|
for (int i = 0; i < mStreams.size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::Stream *s;
|
||||||
|
|
||||||
|
s = streamConfigList.add_stream();
|
||||||
|
mStreams[i]->protoDataCopyInto(*s);
|
||||||
|
}
|
||||||
|
qDebug("Done %s", __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::when_syncComplete()
|
||||||
|
{
|
||||||
|
//reorderStreamsByOrdinals();
|
||||||
|
|
||||||
|
mLastSyncStreamList.clear();
|
||||||
|
for (int i=0; i<mStreams.size(); i++)
|
||||||
|
mLastSyncStreamList.append(mStreams[i]->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::updateStats(OstProto::PortStats *portStats)
|
||||||
|
{
|
||||||
|
OstProto::PortState oldState;
|
||||||
|
|
||||||
|
oldState = stats.state();
|
||||||
|
stats.MergeFrom(*portStats);
|
||||||
|
|
||||||
|
if (oldState.link_state() != stats.state().link_state())
|
||||||
|
{
|
||||||
|
qDebug("portstate changed");
|
||||||
|
emit portDataChanged(mPortGroupId, mPortId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Port::duplicateStreams(const QList<int> &list, int count)
|
||||||
|
{
|
||||||
|
QList<OstProto::Stream> sources;
|
||||||
|
foreach(int index, list) {
|
||||||
|
OstProto::Stream stream;
|
||||||
|
Q_ASSERT(index < mStreams.size());
|
||||||
|
mStreams.at(index)->protoDataCopyInto(stream);
|
||||||
|
sources.append(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
int insertAt = mStreams.size();
|
||||||
|
for (int i=0; i < count; i++) {
|
||||||
|
for (int j=0; j < sources.size(); j++) {
|
||||||
|
newStreamAt(insertAt, &sources.at(j));
|
||||||
|
// Rename stream by appending the copy count
|
||||||
|
mStreams.at(insertAt)->setName(QString("%1 (%2)")
|
||||||
|
.arg(mStreams.at(insertAt)->name())
|
||||||
|
.arg(i+1));
|
||||||
|
insertAt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit streamListChanged(mPortGroupId, mPortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::openStreams(QString fileName, bool append, QString &error)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
QDialog *optDialog;
|
||||||
|
QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow);
|
||||||
|
OstProto::StreamConfigList streams;
|
||||||
|
AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName);
|
||||||
|
|
||||||
|
if (fmt == NULL)
|
||||||
|
goto _fail;
|
||||||
|
|
||||||
|
if ((optDialog = fmt->openOptionsDialog()))
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
optDialog->setParent(mainWindow, Qt::Dialog);
|
||||||
|
ret = optDialog->exec();
|
||||||
|
optDialog->setParent(0, Qt::Dialog);
|
||||||
|
if (ret == QDialog::Rejected)
|
||||||
|
goto _user_opt_cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.setAutoReset(false);
|
||||||
|
progress.setAutoClose(false);
|
||||||
|
progress.setMinimumDuration(0);
|
||||||
|
progress.show();
|
||||||
|
|
||||||
|
mainWindow->setDisabled(true);
|
||||||
|
progress.setEnabled(true); // to override the mainWindow disable
|
||||||
|
|
||||||
|
connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString)));
|
||||||
|
connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int)));
|
||||||
|
connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int)));
|
||||||
|
connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel()));
|
||||||
|
|
||||||
|
fmt->openStreamsOffline(fileName, streams, error);
|
||||||
|
qDebug("after open offline");
|
||||||
|
|
||||||
|
while (!fmt->isFinished())
|
||||||
|
qApp->processEvents();
|
||||||
|
qDebug("wait over for offline operation");
|
||||||
|
|
||||||
|
if (!fmt->result())
|
||||||
|
goto _fail;
|
||||||
|
|
||||||
|
// process any remaining events posted from the thread
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
qApp->processEvents();
|
||||||
|
|
||||||
|
if (!append)
|
||||||
|
{
|
||||||
|
int n = numStreams();
|
||||||
|
|
||||||
|
progress.setLabelText("Deleting existing streams...");
|
||||||
|
progress.setRange(0, n);
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
if (progress.wasCanceled())
|
||||||
|
goto _user_cancel;
|
||||||
|
deleteStreamAt(0);
|
||||||
|
progress.setValue(i);
|
||||||
|
if (i % 32 == 0)
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.setLabelText("Constructing new streams...");
|
||||||
|
progress.setRange(0, streams.stream_size());
|
||||||
|
for (int i = 0; i < streams.stream_size(); i++)
|
||||||
|
{
|
||||||
|
if (progress.wasCanceled())
|
||||||
|
goto _user_cancel;
|
||||||
|
newStreamAt(mStreams.size(), &streams.stream(i));
|
||||||
|
progress.setValue(i);
|
||||||
|
if (i % 32 == 0)
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
_user_cancel:
|
||||||
|
emit streamListChanged(mPortGroupId, mPortId);
|
||||||
|
_user_opt_cancel:
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
_fail:
|
||||||
|
progress.close();
|
||||||
|
mainWindow->setEnabled(true);
|
||||||
|
recalculateAverageRates();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Port::saveStreams(QString fileName, QString fileType, QString &error)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow);
|
||||||
|
AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromType(fileType);
|
||||||
|
OstProto::StreamConfigList streams;
|
||||||
|
|
||||||
|
if (fmt == NULL)
|
||||||
|
goto _fail;
|
||||||
|
|
||||||
|
progress.setAutoReset(false);
|
||||||
|
progress.setAutoClose(false);
|
||||||
|
progress.setMinimumDuration(0);
|
||||||
|
progress.show();
|
||||||
|
|
||||||
|
mainWindow->setDisabled(true);
|
||||||
|
progress.setEnabled(true); // to override the mainWindow disable
|
||||||
|
|
||||||
|
connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString)));
|
||||||
|
connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int)));
|
||||||
|
connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int)));
|
||||||
|
connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel()));
|
||||||
|
|
||||||
|
progress.setLabelText("Preparing Streams...");
|
||||||
|
progress.setRange(0, mStreams.size());
|
||||||
|
streams.mutable_port_id()->set_id(0);
|
||||||
|
for (int i = 0; i < mStreams.size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::Stream *s = streams.add_stream();
|
||||||
|
mStreams[i]->protoDataCopyInto(*s);
|
||||||
|
|
||||||
|
if (progress.wasCanceled())
|
||||||
|
goto _user_cancel;
|
||||||
|
progress.setValue(i);
|
||||||
|
if (i % 32 == 0)
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt->saveStreamsOffline(streams, fileName, error);
|
||||||
|
qDebug("after save offline");
|
||||||
|
|
||||||
|
while (!fmt->isFinished())
|
||||||
|
qApp->processEvents();
|
||||||
|
qDebug("wait over for offline operation");
|
||||||
|
|
||||||
|
ret = fmt->result();
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
_user_cancel:
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
_fail:
|
||||||
|
error = QString("Unsupported File Type - %1").arg(fileType);
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
progress.close();
|
||||||
|
mainWindow->setEnabled(true);
|
||||||
|
return ret;
|
||||||
|
}
|
155
client/port.h
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_H
|
||||||
|
#define _PORT_H
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
//class StreamModel;
|
||||||
|
|
||||||
|
class Port : public QObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
static uint mAllocStreamId;
|
||||||
|
|
||||||
|
OstProto::Port d;
|
||||||
|
OstProto::PortStats stats;
|
||||||
|
QTemporaryFile *capFile_;
|
||||||
|
|
||||||
|
// FIXME(HI): consider removing mPortId as it is duplicated inside 'd'
|
||||||
|
quint32 mPortId;
|
||||||
|
quint32 mPortGroupId;
|
||||||
|
QString mUserAlias; // user defined
|
||||||
|
|
||||||
|
double avgPacketsPerSec_;
|
||||||
|
double avgBitsPerSec_;
|
||||||
|
int numActiveStreams_;
|
||||||
|
|
||||||
|
QList<quint32> mLastSyncStreamList;
|
||||||
|
QList<Stream*> mStreams; // sorted by stream's ordinal value
|
||||||
|
|
||||||
|
uint newStreamId();
|
||||||
|
void updateStreamOrdinalsFromIndex();
|
||||||
|
void reorderStreamsByOrdinals();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum AdminStatus { AdminDisable, AdminEnable };
|
||||||
|
|
||||||
|
// FIXME(HIGH): default args is a hack for QList operations on Port
|
||||||
|
Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF);
|
||||||
|
~Port();
|
||||||
|
|
||||||
|
quint32 portGroupId() const { return mPortGroupId; }
|
||||||
|
const QString& userAlias() const { return mUserAlias; }
|
||||||
|
|
||||||
|
quint32 id() const
|
||||||
|
{ return d.port_id().id(); }
|
||||||
|
const QString name() const
|
||||||
|
{ return QString().fromStdString(d.name()); }
|
||||||
|
const QString description() const
|
||||||
|
{ return QString().fromStdString(d.description()); }
|
||||||
|
const QString notes() const
|
||||||
|
{ return QString().fromStdString(d.notes()); }
|
||||||
|
const QString userName() const
|
||||||
|
{ return QString().fromStdString(d.user_name()); }
|
||||||
|
AdminStatus adminStatus()
|
||||||
|
{ return (d.is_enabled()?AdminEnable:AdminDisable); }
|
||||||
|
bool hasExclusiveControl()
|
||||||
|
{ return d.is_exclusive_control(); }
|
||||||
|
OstProto::TransmitMode transmitMode()
|
||||||
|
{ return d.transmit_mode(); }
|
||||||
|
double averagePacketRate()
|
||||||
|
{ return avgPacketsPerSec_; }
|
||||||
|
double averageBitRate()
|
||||||
|
{ return avgBitsPerSec_; }
|
||||||
|
|
||||||
|
//void setAdminEnable(AdminStatus status) { mAdminStatus = status; }
|
||||||
|
void setAlias(QString &alias) { mUserAlias = alias; }
|
||||||
|
//void setExclusive(bool flag);
|
||||||
|
|
||||||
|
int numStreams() { return mStreams.size(); }
|
||||||
|
Stream* streamByIndex(int index)
|
||||||
|
{
|
||||||
|
Q_ASSERT(index < mStreams.size());
|
||||||
|
return mStreams[index];
|
||||||
|
}
|
||||||
|
OstProto::LinkState linkState()
|
||||||
|
{ return stats.state().link_state(); }
|
||||||
|
|
||||||
|
OstProto::PortStats getStats() { return stats; }
|
||||||
|
QTemporaryFile* getCaptureFile()
|
||||||
|
{
|
||||||
|
delete capFile_;
|
||||||
|
capFile_ = new QTemporaryFile(QString(QDir::tempPath())
|
||||||
|
.append("/")
|
||||||
|
.append(name())
|
||||||
|
.append(".XXXXXX"));
|
||||||
|
return capFile_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal
|
||||||
|
void updatePortConfig(OstProto::Port *port);
|
||||||
|
|
||||||
|
//! Used by StreamModel
|
||||||
|
//@{
|
||||||
|
bool newStreamAt(int index, OstProto::Stream const *stream = NULL);
|
||||||
|
bool deleteStreamAt(int index);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! Used by MyService::Stub to update from config received from server
|
||||||
|
//@{
|
||||||
|
bool insertStream(uint streamId);
|
||||||
|
bool updateStream(uint streamId, OstProto::Stream *stream);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||||
|
void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||||
|
void getModifiedStreamsSinceLastSync(
|
||||||
|
OstProto::StreamConfigList &streamConfigList);
|
||||||
|
|
||||||
|
void when_syncComplete();
|
||||||
|
|
||||||
|
void setAveragePacketRate(double packetsPerSec);
|
||||||
|
void setAverageBitRate(double bitsPerSec);
|
||||||
|
// FIXME(MED): Bad Hack! port should not need an external trigger to
|
||||||
|
// recalculate - refactor client side domain objects and model objects
|
||||||
|
void recalculateAverageRates();
|
||||||
|
void updateStats(OstProto::PortStats *portStats);
|
||||||
|
|
||||||
|
void duplicateStreams(const QList<int> &list, int count);
|
||||||
|
|
||||||
|
bool openStreams(QString fileName, bool append, QString &error);
|
||||||
|
bool saveStreams(QString fileName, QString fileType, QString &error);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void portRateChanged(int portGroupId, int portId);
|
||||||
|
void portDataChanged(int portGroupId, int portId);
|
||||||
|
void streamListChanged(int portGroupId, int portId);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
99
client/portconfigdialog.cpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2011 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portconfigdialog.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent)
|
||||||
|
: QDialog(parent), portConfig_(portConfig)
|
||||||
|
{
|
||||||
|
QString currentUser(portConfig_.user_name().c_str());
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
switch(portConfig_.transmit_mode())
|
||||||
|
{
|
||||||
|
case OstProto::kSequentialTransmit:
|
||||||
|
sequentialStreamsButton->setChecked(true);
|
||||||
|
break;
|
||||||
|
case OstProto::kInterleavedTransmit:
|
||||||
|
interleavedStreamsButton->setChecked(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false); // Unreachable!!!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port Reservation
|
||||||
|
myself_ = appSettings->value(kUserKey, kUserDefaultValue).toString();
|
||||||
|
// XXX: what if myself_ is empty?
|
||||||
|
if (currentUser.isEmpty()) {
|
||||||
|
reservedBy_ = kNone;
|
||||||
|
reservedBy->setText("Unreserved");
|
||||||
|
reserveButton->setText("Reserve");
|
||||||
|
}
|
||||||
|
else if (currentUser == myself_) {
|
||||||
|
reservedBy_ = kSelf;
|
||||||
|
reservedBy->setText("Reserved by: me <i>("+currentUser+")</i>");
|
||||||
|
reserveButton->setText("Reserve (uncheck to unreserve)");
|
||||||
|
reserveButton->setChecked(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reservedBy_ = kOther;
|
||||||
|
reservedBy->setText("Reserved by: <i>"+currentUser+"</i>");
|
||||||
|
reserveButton->setText("Force reserve");
|
||||||
|
}
|
||||||
|
qDebug("reservedBy_ = %d", reservedBy_);
|
||||||
|
|
||||||
|
exclusiveControlButton->setChecked(portConfig_.is_exclusive_control());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortConfigDialog::accept()
|
||||||
|
{
|
||||||
|
if (sequentialStreamsButton->isChecked())
|
||||||
|
portConfig_.set_transmit_mode(OstProto::kSequentialTransmit);
|
||||||
|
else if (interleavedStreamsButton->isChecked())
|
||||||
|
portConfig_.set_transmit_mode(OstProto::kInterleavedTransmit);
|
||||||
|
else
|
||||||
|
Q_ASSERT(false); // Unreachable!!!
|
||||||
|
|
||||||
|
switch (reservedBy_) {
|
||||||
|
case kSelf:
|
||||||
|
if (!reserveButton->isChecked())
|
||||||
|
portConfig_.set_user_name(""); // unreserve
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kOther:
|
||||||
|
case kNone:
|
||||||
|
if (reserveButton->isChecked())
|
||||||
|
portConfig_.set_user_name(
|
||||||
|
myself_.toStdString()); // (force) reserve
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("Unreachable code");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
portConfig_.set_is_exclusive_control(exclusiveControlButton->isChecked());
|
||||||
|
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
41
client/portconfigdialog.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2011 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_CONFIG_DIALOG_H
|
||||||
|
#define _PORT_CONFIG_DIALOG_H
|
||||||
|
|
||||||
|
#include "ui_portconfigdialog.h"
|
||||||
|
#include "protocol.pb.h"
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class PortConfigDialog : public QDialog, public Ui::PortConfigDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PortConfigDialog(OstProto::Port &portConfig, QWidget *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void accept();
|
||||||
|
|
||||||
|
OstProto::Port &portConfig_;
|
||||||
|
enum { kNone, kSelf, kOther } reservedBy_;
|
||||||
|
QString myself_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
132
client/portconfigdialog.ui
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>PortConfigDialog</class>
|
||||||
|
<widget class="QDialog" name="PortConfigDialog" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>244</width>
|
||||||
|
<height>233</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Port Config</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="transmitModeBox" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>Transmit Mode</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="sequentialStreamsButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Sequential Streams</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="interleavedStreamsButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Interleaved Streams</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox" >
|
||||||
|
<property name="title" >
|
||||||
|
<string>Reservation</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="reservedBy" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Reserved by: </string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="reserveButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Reserve</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="exclusiveControlButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Exclusive Control</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>226</width>
|
||||||
|
<height>71</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons" >
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>PortConfigDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>234</x>
|
||||||
|
<y>205</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>157</x>
|
||||||
|
<y>214</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>PortConfigDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>234</x>
|
||||||
|
<y>205</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>243</x>
|
||||||
|
<y>214</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
896
client/portgroup.cpp
Normal file
@ -0,0 +1,896 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portgroup.h"
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCursor>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
using ::google::protobuf::NewCallback;
|
||||||
|
|
||||||
|
extern QMainWindow *mainWindow;
|
||||||
|
extern char *version;
|
||||||
|
|
||||||
|
quint32 PortGroup::mPortGroupAllocId = 0;
|
||||||
|
|
||||||
|
PortGroup::PortGroup(QHostAddress ip, quint16 port)
|
||||||
|
{
|
||||||
|
// Allocate an id for self
|
||||||
|
mPortGroupId = PortGroup::mPortGroupAllocId++;
|
||||||
|
|
||||||
|
portIdList_ = new OstProto::PortIdList;
|
||||||
|
portStatsList_ = new OstProto::PortStatsList;
|
||||||
|
|
||||||
|
statsController = new PbRpcController(portIdList_, portStatsList_);
|
||||||
|
isGetStatsPending_ = false;
|
||||||
|
|
||||||
|
compat = kUnknown;
|
||||||
|
|
||||||
|
reconnect = false;
|
||||||
|
reconnectAfter = kMinReconnectWaitTime;
|
||||||
|
reconnectTimer = new QTimer(this);
|
||||||
|
reconnectTimer->setSingleShot(true);
|
||||||
|
connect(reconnectTimer, SIGNAL(timeout()),
|
||||||
|
this, SLOT(on_reconnectTimer_timeout()));
|
||||||
|
|
||||||
|
rpcChannel = new PbRpcChannel(ip, port);
|
||||||
|
serviceStub = new OstProto::OstService::Stub(rpcChannel);
|
||||||
|
|
||||||
|
// FIXME(LOW):Can't for my life figure out why this ain't working!
|
||||||
|
//QMetaObject::connectSlotsByName(this);
|
||||||
|
connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
|
||||||
|
this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState)));
|
||||||
|
connect(rpcChannel, SIGNAL(connected()),
|
||||||
|
this, SLOT(on_rpcChannel_connected()));
|
||||||
|
connect(rpcChannel, SIGNAL(disconnected()),
|
||||||
|
this, SLOT(on_rpcChannel_disconnected()));
|
||||||
|
connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||||
|
this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError)));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(portListChanged(quint32)),
|
||||||
|
this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
PortGroup::~PortGroup()
|
||||||
|
{
|
||||||
|
qDebug("PortGroup Destructor");
|
||||||
|
// Disconnect and free rpc channel etc.
|
||||||
|
PortGroup::disconnectFromHost();
|
||||||
|
delete serviceStub;
|
||||||
|
delete rpcChannel;
|
||||||
|
delete statsController;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Slots
|
||||||
|
// ------------------------------------------------
|
||||||
|
void PortGroup::on_reconnectTimer_timeout()
|
||||||
|
{
|
||||||
|
reconnectAfter *= 2;
|
||||||
|
if (reconnectAfter > kMaxReconnectWaitTime)
|
||||||
|
reconnectAfter = kMaxReconnectWaitTime;
|
||||||
|
|
||||||
|
connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state)
|
||||||
|
{
|
||||||
|
qDebug("state changed %d", state);
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case QAbstractSocket::UnconnectedState:
|
||||||
|
case QAbstractSocket::ClosingState:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::on_rpcChannel_connected()
|
||||||
|
{
|
||||||
|
OstProto::VersionInfo *verInfo = new OstProto::VersionInfo;
|
||||||
|
OstProto::VersionCompatibility *verCompat =
|
||||||
|
new OstProto::VersionCompatibility;
|
||||||
|
|
||||||
|
qDebug("connected\n");
|
||||||
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
|
reconnectAfter = kMinReconnectWaitTime;
|
||||||
|
|
||||||
|
qDebug("requesting version check ...");
|
||||||
|
verInfo->set_version(version);
|
||||||
|
|
||||||
|
PbRpcController *controller = new PbRpcController(verInfo, verCompat);
|
||||||
|
serviceStub->checkVersion(controller, verInfo, verCompat,
|
||||||
|
NewCallback(this, &PortGroup::processVersionCompatibility,
|
||||||
|
controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processVersionCompatibility(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::VersionCompatibility *verCompat
|
||||||
|
= static_cast<OstProto::VersionCompatibility*>(controller->response());
|
||||||
|
|
||||||
|
Q_ASSERT(verCompat != NULL);
|
||||||
|
|
||||||
|
qDebug("got version result ...");
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verCompat->result() == OstProto::VersionCompatibility::kIncompatible) {
|
||||||
|
qWarning("incompatible version %s (%s)", version,
|
||||||
|
qPrintable(QString::fromStdString(verCompat->notes())));
|
||||||
|
compat = kIncompatible;
|
||||||
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
goto _error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
compat = kCompatible;
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::Void *void_ = new OstProto::Void;
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
|
||||||
|
qDebug("requesting portlist ...");
|
||||||
|
PbRpcController *controller = new PbRpcController(void_, portIdList);
|
||||||
|
serviceStub->getPortIdList(controller, void_, portIdList,
|
||||||
|
NewCallback(this, &PortGroup::processPortIdList, controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
_error_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::on_rpcChannel_disconnected()
|
||||||
|
{
|
||||||
|
qDebug("disconnected\n");
|
||||||
|
emit portListAboutToBeChanged(mPortGroupId);
|
||||||
|
|
||||||
|
while (!mPorts.isEmpty())
|
||||||
|
delete mPorts.takeFirst();
|
||||||
|
|
||||||
|
emit portListChanged(mPortGroupId);
|
||||||
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
|
isGetStatsPending_ = false;
|
||||||
|
|
||||||
|
if (reconnect)
|
||||||
|
{
|
||||||
|
qDebug("starting reconnect timer for %d ms ...", reconnectAfter);
|
||||||
|
reconnectTimer->start(reconnectAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError)
|
||||||
|
{
|
||||||
|
qDebug("%s: error %d", __FUNCTION__, socketError);
|
||||||
|
emit portGroupDataChanged(mPortGroupId);
|
||||||
|
|
||||||
|
if (socketError == QAbstractSocket::RemoteHostClosedError)
|
||||||
|
reconnect = false;
|
||||||
|
|
||||||
|
qDebug("%s: state %d", __FUNCTION__, rpcChannel->state());
|
||||||
|
if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect)
|
||||||
|
{
|
||||||
|
qDebug("starting reconnect timer for %d ms...", reconnectAfter);
|
||||||
|
reconnectTimer->start(reconnectAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::when_portListChanged(quint32 /*portGroupId*/)
|
||||||
|
{
|
||||||
|
if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(NULL, tr("No ports in portgroup"),
|
||||||
|
QString("The portgroup %1:%2 does not contain any ports!\n\n"
|
||||||
|
"Packet Transmit/Capture requires elevated privileges. "
|
||||||
|
"Please ensure that you are running 'drone' - the server "
|
||||||
|
"component of Ostinato with admin/root OR setuid privilege.\n\n"
|
||||||
|
"For more information see "
|
||||||
|
"http://code.google.com/p/ostinato/wiki/FAQ#"
|
||||||
|
"Q._Port_group_has_no_interfaces")
|
||||||
|
.arg(serverAddress().toString())
|
||||||
|
.arg(int(serverPort())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processPortIdList(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList
|
||||||
|
= static_cast<OstProto::PortIdList*>(controller->response());
|
||||||
|
|
||||||
|
Q_ASSERT(portIdList != NULL);
|
||||||
|
|
||||||
|
qDebug("got a portlist ...");
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit portListAboutToBeChanged(mPortGroupId);
|
||||||
|
|
||||||
|
for(int i = 0; i < portIdList->port_id_size(); i++)
|
||||||
|
{
|
||||||
|
Port *p;
|
||||||
|
|
||||||
|
p = new Port(portIdList->port_id(i).id(), mPortGroupId);
|
||||||
|
connect(p, SIGNAL(portDataChanged(int, int)),
|
||||||
|
this, SIGNAL(portGroupDataChanged(int, int)));
|
||||||
|
qDebug("before port append\n");
|
||||||
|
mPorts.append(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit portListChanged(mPortGroupId);
|
||||||
|
|
||||||
|
portIdList_->CopyFrom(*portIdList);
|
||||||
|
|
||||||
|
// Request PortConfigList
|
||||||
|
{
|
||||||
|
qDebug("requesting port config list ...");
|
||||||
|
OstProto::PortIdList *portIdList2 = new OstProto::PortIdList();
|
||||||
|
OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList();
|
||||||
|
PbRpcController *controller2 = new PbRpcController(portIdList2,
|
||||||
|
portConfigList);
|
||||||
|
|
||||||
|
portIdList2->CopyFrom(*portIdList);
|
||||||
|
|
||||||
|
serviceStub->getPortConfig(controller, portIdList2, portConfigList,
|
||||||
|
NewCallback(this, &PortGroup::processPortConfigList, controller2));
|
||||||
|
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
_error_exit:
|
||||||
|
_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processPortConfigList(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::PortConfigList *portConfigList
|
||||||
|
= static_cast<OstProto::PortConfigList*>(controller->response());
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
//emit portListAboutToBeChanged(mPortGroupId);
|
||||||
|
|
||||||
|
for(int i = 0; i < portConfigList->port_size(); i++)
|
||||||
|
{
|
||||||
|
uint id;
|
||||||
|
|
||||||
|
id = portConfigList->port(i).port_id().id();
|
||||||
|
// FIXME: don't mix port id & index into mPorts[]
|
||||||
|
mPorts[id]->updatePortConfig(portConfigList->mutable_port(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
//emit portListChanged(mPortGroupId);
|
||||||
|
|
||||||
|
// FIXME: check if we need new signals since we are not changing the
|
||||||
|
// number of ports, just the port data
|
||||||
|
|
||||||
|
if (numPorts() > 0)
|
||||||
|
getStreamIdList();
|
||||||
|
|
||||||
|
_error_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::when_configApply(int portIndex)
|
||||||
|
{
|
||||||
|
OstProto::StreamIdList *streamIdList;
|
||||||
|
OstProto::StreamConfigList *streamConfigList;
|
||||||
|
OstProto::Ack *ack;
|
||||||
|
PbRpcController *controller;
|
||||||
|
|
||||||
|
Q_ASSERT(portIndex < mPorts.size());
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
|
mainWindow->setDisabled(true);
|
||||||
|
|
||||||
|
qDebug("applying 'deleted streams' ...");
|
||||||
|
streamIdList = new OstProto::StreamIdList;
|
||||||
|
ack = new OstProto::Ack;
|
||||||
|
controller = new PbRpcController(streamIdList, ack);
|
||||||
|
|
||||||
|
streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
|
||||||
|
mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList);
|
||||||
|
|
||||||
|
serviceStub->deleteStream(controller, streamIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processDeleteStreamAck, controller));
|
||||||
|
|
||||||
|
qDebug("applying 'new streams' ...");
|
||||||
|
streamIdList = new OstProto::StreamIdList;
|
||||||
|
ack = new OstProto::Ack;
|
||||||
|
controller = new PbRpcController(streamIdList, ack);
|
||||||
|
|
||||||
|
streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
|
||||||
|
mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList);
|
||||||
|
|
||||||
|
serviceStub->addStream(controller, streamIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processAddStreamAck, controller));
|
||||||
|
|
||||||
|
qDebug("applying 'modified streams' ...");
|
||||||
|
streamConfigList = new OstProto::StreamConfigList;
|
||||||
|
ack = new OstProto::Ack;
|
||||||
|
controller = new PbRpcController(streamConfigList, ack);
|
||||||
|
|
||||||
|
streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id());
|
||||||
|
mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList);
|
||||||
|
|
||||||
|
serviceStub->modifyStream(controller, streamConfigList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processModifyStreamAck,
|
||||||
|
portIndex, controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processAddStreamAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processDeleteStreamAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processModifyStreamAck(int portIndex,
|
||||||
|
PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
qDebug("apply completed");
|
||||||
|
mPorts[portIndex]->when_syncComplete();
|
||||||
|
|
||||||
|
mainWindow->setEnabled(true);
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig)
|
||||||
|
{
|
||||||
|
OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
|
||||||
|
qDebug("%s: portIndex = %d", __FUNCTION__, portIndex);
|
||||||
|
|
||||||
|
Q_ASSERT(portIndex < mPorts.size());
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||||
|
mainWindow->setDisabled(true);
|
||||||
|
|
||||||
|
OstProto::Port *port = portConfigList->add_port();
|
||||||
|
port->CopyFrom(portConfig);
|
||||||
|
port->mutable_port_id()->set_id(mPorts[portIndex]->id());
|
||||||
|
|
||||||
|
PbRpcController *controller = new PbRpcController(portConfigList, ack);
|
||||||
|
serviceStub->modifyPort(controller, portConfigList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processModifyPortAck, controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processModifyPortAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList;
|
||||||
|
PbRpcController *controller2 = new PbRpcController(portIdList,
|
||||||
|
portConfigList);
|
||||||
|
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->CopyFrom(static_cast<OstProto::PortConfigList*>
|
||||||
|
(controller->request())->mutable_port(0)->port_id());
|
||||||
|
|
||||||
|
serviceStub->getPortConfig(controller, portIdList, portConfigList,
|
||||||
|
NewCallback(this, &PortGroup::processUpdatedPortConfig,
|
||||||
|
controller2));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processUpdatedPortConfig(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::PortConfigList *portConfigList
|
||||||
|
= static_cast<OstProto::PortConfigList*>(controller->response());
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (portConfigList->port_size() != 1)
|
||||||
|
qDebug("port size = %d (expected = 1)", portConfigList->port_size());
|
||||||
|
|
||||||
|
for(int i = 0; i < portConfigList->port_size(); i++)
|
||||||
|
{
|
||||||
|
uint id;
|
||||||
|
|
||||||
|
id = portConfigList->port(i).port_id().id();
|
||||||
|
// FIXME: don't mix port id & index into mPorts[]
|
||||||
|
mPorts[id]->updatePortConfig(portConfigList->mutable_port(i));
|
||||||
|
|
||||||
|
emit portGroupDataChanged(mPortGroupId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
mainWindow->setEnabled(true);
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::getStreamIdList()
|
||||||
|
{
|
||||||
|
for (int portIndex = 0; portIndex < numPorts(); portIndex++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = new OstProto::PortId;
|
||||||
|
OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList;
|
||||||
|
PbRpcController *controller = new PbRpcController(portId, streamIdList);
|
||||||
|
|
||||||
|
portId->set_id(mPorts[portIndex]->id());
|
||||||
|
|
||||||
|
serviceStub->getStreamIdList(controller, portId, streamIdList,
|
||||||
|
NewCallback(this, &PortGroup::processStreamIdList,
|
||||||
|
portIndex, controller));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::StreamIdList *streamIdList
|
||||||
|
= static_cast<OstProto::StreamIdList*>(controller->response());
|
||||||
|
|
||||||
|
qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex);
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(portIndex < numPorts());
|
||||||
|
|
||||||
|
if (streamIdList->port_id().id() != mPorts[portIndex]->id())
|
||||||
|
{
|
||||||
|
qDebug("Invalid portId %d (expected %d) received for portIndex %d",
|
||||||
|
streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < streamIdList->stream_id_size(); i++)
|
||||||
|
{
|
||||||
|
uint streamId;
|
||||||
|
|
||||||
|
streamId = streamIdList->stream_id(i).id();
|
||||||
|
mPorts[portIndex]->insertStream(streamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPorts[portIndex]->when_syncComplete();
|
||||||
|
|
||||||
|
// Are we done for all ports?
|
||||||
|
if (numPorts() && portIndex >= (numPorts()-1))
|
||||||
|
{
|
||||||
|
// FIXME(HI): some way to reset streammodel
|
||||||
|
getStreamConfigList();
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::getStreamConfigList()
|
||||||
|
{
|
||||||
|
qDebug("requesting stream config list ...");
|
||||||
|
|
||||||
|
for (int portIndex = 0; portIndex < numPorts(); portIndex++)
|
||||||
|
{
|
||||||
|
OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList;
|
||||||
|
OstProto::StreamConfigList *streamConfigList
|
||||||
|
= new OstProto::StreamConfigList;
|
||||||
|
PbRpcController *controller = new PbRpcController(
|
||||||
|
streamIdList, streamConfigList);
|
||||||
|
|
||||||
|
streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id());
|
||||||
|
for (int j = 0; j < mPorts[portIndex]->numStreams(); j++)
|
||||||
|
{
|
||||||
|
OstProto::StreamId *s = streamIdList->add_stream_id();
|
||||||
|
s->set_id(mPorts[portIndex]->streamByIndex(j)->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->getStreamConfig(controller, streamIdList, streamConfigList,
|
||||||
|
NewCallback(this, &PortGroup::processStreamConfigList,
|
||||||
|
portIndex, controller));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStreamConfigList(int portIndex,
|
||||||
|
PbRpcController *controller)
|
||||||
|
{
|
||||||
|
OstProto::StreamConfigList *streamConfigList
|
||||||
|
= static_cast<OstProto::StreamConfigList*>(controller->response());
|
||||||
|
|
||||||
|
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||||
|
|
||||||
|
Q_ASSERT(portIndex < numPorts());
|
||||||
|
|
||||||
|
if (controller->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(controller->ErrorString()));
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(portIndex < numPorts());
|
||||||
|
|
||||||
|
if (streamConfigList->port_id().id() != mPorts[portIndex]->id())
|
||||||
|
{
|
||||||
|
qDebug("Invalid portId %d (expected %d) received for portIndex %d",
|
||||||
|
streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < streamConfigList->stream_size(); i++)
|
||||||
|
{
|
||||||
|
uint streamId;
|
||||||
|
|
||||||
|
streamId = streamConfigList->stream(i).stream_id().id();
|
||||||
|
mPorts[portIndex]->updateStream(streamId,
|
||||||
|
streamConfigList->mutable_stream(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we done for all ports?
|
||||||
|
if (portIndex >= numPorts())
|
||||||
|
{
|
||||||
|
// FIXME(HI): some way to reset streammodel
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::startTx(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (portList == NULL)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
PbRpcController *controller = new PbRpcController(portIdList, ack);
|
||||||
|
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->startTransmit(controller, portIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processStartTxAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStartTxAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::stopTx(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if ((portList == NULL) || (portList->size() == 0))
|
||||||
|
goto _exit;
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
PbRpcController *controller = new PbRpcController(portIdList, ack);
|
||||||
|
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->stopTransmit(controller, portIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processStopTxAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStopTxAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::startCapture(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((portList == NULL) || (portList->size() == 0))
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
PbRpcController *controller = new PbRpcController(portIdList, ack);
|
||||||
|
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->startCapture(controller, portIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processStartCaptureAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStartCaptureAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::stopCapture(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((portList == NULL) || (portList->size() == 0))
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
PbRpcController *controller = new PbRpcController(portIdList, ack);
|
||||||
|
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->stopCapture(controller, portIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processStopCaptureAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processStopCaptureAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::viewCapture(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if ((portList == NULL) || (portList->size() != 1))
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = new OstProto::PortId;
|
||||||
|
OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer;
|
||||||
|
PbRpcController *controller = new PbRpcController(portId, buf);
|
||||||
|
QFile *capFile = mPorts[portList->at(i)]->getCaptureFile();
|
||||||
|
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
|
||||||
|
capFile->open(QIODevice::ReadWrite|QIODevice::Truncate);
|
||||||
|
qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData());
|
||||||
|
controller->setBinaryBlob(capFile);
|
||||||
|
|
||||||
|
serviceStub->getCaptureBuffer(controller, portId, buf,
|
||||||
|
NewCallback(this, &PortGroup::processViewCaptureAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processViewCaptureAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
QFile *capFile = static_cast<QFile*>(controller->binaryBlob());
|
||||||
|
|
||||||
|
QString viewer = appSettings->value(kWiresharkPathKey,
|
||||||
|
kWiresharkPathDefaultValue).toString();
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
capFile->flush();
|
||||||
|
capFile->close();
|
||||||
|
|
||||||
|
if (!QFile::exists(viewer))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(NULL, "Can't find Wireshark",
|
||||||
|
viewer + QString(" does not exist!\n\nPlease correct the path"
|
||||||
|
" to Wireshark in the Preferences."));
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName()))
|
||||||
|
qDebug("Failed starting Wireshark");
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::getPortStats()
|
||||||
|
{
|
||||||
|
//qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (numPorts() <= 0)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (isGetStatsPending_)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
statsController->Reset();
|
||||||
|
isGetStatsPending_ = true;
|
||||||
|
serviceStub->getStats(statsController,
|
||||||
|
static_cast<OstProto::PortIdList*>(statsController->request()),
|
||||||
|
static_cast<OstProto::PortStatsList*>(statsController->response()),
|
||||||
|
NewCallback(this, &PortGroup::processPortStatsList));
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processPortStatsList()
|
||||||
|
{
|
||||||
|
//qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (statsController->Failed())
|
||||||
|
{
|
||||||
|
qDebug("%s: rpc failed(%s)", __FUNCTION__,
|
||||||
|
qPrintable(statsController->ErrorString()));
|
||||||
|
goto _error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < portStatsList_->port_stats_size(); i++)
|
||||||
|
{
|
||||||
|
uint id = portStatsList_->port_stats(i).port_id().id();
|
||||||
|
// FIXME: don't mix port id & index into mPorts[]
|
||||||
|
mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
emit statsChanged(mPortGroupId);
|
||||||
|
|
||||||
|
_error_exit:
|
||||||
|
isGetStatsPending_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::clearPortStats(QList<uint> *portList)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (state() != QAbstractSocket::ConnectedState)
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
{
|
||||||
|
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||||
|
OstProto::Ack *ack = new OstProto::Ack;
|
||||||
|
PbRpcController *controller = new PbRpcController(portIdList, ack);
|
||||||
|
|
||||||
|
if (portList == NULL)
|
||||||
|
portIdList->CopyFrom(*portIdList_);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < portList->size(); i++)
|
||||||
|
{
|
||||||
|
OstProto::PortId *portId = portIdList->add_port_id();
|
||||||
|
portId->set_id(portList->at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceStub->clearStats(controller, portIdList, ack,
|
||||||
|
NewCallback(this, &PortGroup::processClearStatsAck, controller));
|
||||||
|
}
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroup::processClearStatsAck(PbRpcController *controller)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
// Refresh stats immediately after a stats clear/reset
|
||||||
|
getPortStats();
|
||||||
|
|
||||||
|
delete controller;
|
||||||
|
}
|
||||||
|
|
157
client/portgroup.h
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_GROUP_H
|
||||||
|
#define _PORT_GROUP_H
|
||||||
|
|
||||||
|
#include "port.h"
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "../common/protocol.pb.h"
|
||||||
|
#include "pbrpcchannel.h"
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
HIGH
|
||||||
|
MED
|
||||||
|
LOW
|
||||||
|
- Allow hostnames in addition to IP Address as "server address"
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DEFAULT_SERVER_PORT 7878
|
||||||
|
|
||||||
|
class QFile;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
class PortGroup : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum { kIncompatible, kCompatible, kUnknown } compat;
|
||||||
|
static quint32 mPortGroupAllocId;
|
||||||
|
quint32 mPortGroupId;
|
||||||
|
QString mUserAlias; // user defined
|
||||||
|
|
||||||
|
bool reconnect;
|
||||||
|
int reconnectAfter; // time in milliseconds
|
||||||
|
static const int kMinReconnectWaitTime = 2000; // ms
|
||||||
|
static const int kMaxReconnectWaitTime = 60000; // ms
|
||||||
|
QTimer *reconnectTimer;
|
||||||
|
PbRpcChannel *rpcChannel;
|
||||||
|
PbRpcController *statsController;
|
||||||
|
bool isGetStatsPending_;
|
||||||
|
|
||||||
|
OstProto::OstService::Stub *serviceStub;
|
||||||
|
|
||||||
|
OstProto::PortIdList *portIdList_;
|
||||||
|
OstProto::PortStatsList *portStatsList_;
|
||||||
|
|
||||||
|
public: // FIXME(HIGH): member access
|
||||||
|
QList<Port*> mPorts;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortGroup(QHostAddress ip = QHostAddress::LocalHost,
|
||||||
|
quint16 port = DEFAULT_SERVER_PORT);
|
||||||
|
~PortGroup();
|
||||||
|
|
||||||
|
void connectToHost() {
|
||||||
|
reconnect = true;
|
||||||
|
compat = kUnknown;
|
||||||
|
rpcChannel->establish();
|
||||||
|
}
|
||||||
|
void connectToHost(QHostAddress ip, quint16 port) {
|
||||||
|
reconnect = true;
|
||||||
|
compat = kUnknown;
|
||||||
|
rpcChannel->establish(ip, port);
|
||||||
|
}
|
||||||
|
void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); }
|
||||||
|
|
||||||
|
int numPorts() const { return mPorts.size(); }
|
||||||
|
quint32 id() const { return mPortGroupId; }
|
||||||
|
|
||||||
|
const QString& userAlias() const { return mUserAlias; }
|
||||||
|
void setUserAlias(QString alias) { mUserAlias = alias; };
|
||||||
|
|
||||||
|
const QHostAddress& serverAddress() const
|
||||||
|
{ return rpcChannel->serverAddress(); }
|
||||||
|
quint16 serverPort() const
|
||||||
|
{ return rpcChannel->serverPort(); }
|
||||||
|
QAbstractSocket::SocketState state() const {
|
||||||
|
if (compat == kIncompatible)
|
||||||
|
return QAbstractSocket::SocketState(-1);
|
||||||
|
return rpcChannel->state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processVersionCompatibility(PbRpcController *controller);
|
||||||
|
void processPortIdList(PbRpcController *controller);
|
||||||
|
void processPortConfigList(PbRpcController *controller);
|
||||||
|
|
||||||
|
void processAddStreamAck(PbRpcController *controller);
|
||||||
|
void processDeleteStreamAck(PbRpcController *controller);
|
||||||
|
void processModifyStreamAck(int portIndex, PbRpcController *controller);
|
||||||
|
|
||||||
|
void modifyPort(int portId, OstProto::Port portConfig);
|
||||||
|
void processModifyPortAck(PbRpcController *controller);
|
||||||
|
void processUpdatedPortConfig(PbRpcController *controller);
|
||||||
|
|
||||||
|
void getStreamIdList();
|
||||||
|
void processStreamIdList(int portIndex, PbRpcController *controller);
|
||||||
|
void getStreamConfigList();
|
||||||
|
void processStreamConfigList(int portIndex, PbRpcController *controller);
|
||||||
|
|
||||||
|
void processModifyStreamAck(OstProto::Ack *ack);
|
||||||
|
|
||||||
|
void startTx(QList<uint> *portList = NULL);
|
||||||
|
void processStartTxAck(PbRpcController *controller);
|
||||||
|
void stopTx(QList<uint> *portList = NULL);
|
||||||
|
void processStopTxAck(PbRpcController *controller);
|
||||||
|
|
||||||
|
void startCapture(QList<uint> *portList = NULL);
|
||||||
|
void processStartCaptureAck(PbRpcController *controller);
|
||||||
|
void stopCapture(QList<uint> *portList = NULL);
|
||||||
|
void processStopCaptureAck(PbRpcController *controller);
|
||||||
|
void viewCapture(QList<uint> *portList = NULL);
|
||||||
|
void processViewCaptureAck(PbRpcController *controller);
|
||||||
|
|
||||||
|
void getPortStats();
|
||||||
|
void processPortStatsList();
|
||||||
|
void clearPortStats(QList<uint> *portList = NULL);
|
||||||
|
void processClearStatsAck(PbRpcController *controller);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void portGroupDataChanged(int portGroupId, int portId = 0xFFFF);
|
||||||
|
void portListAboutToBeChanged(quint32 portGroupId);
|
||||||
|
void portListChanged(quint32 portGroupId);
|
||||||
|
void statsChanged(quint32 portGroupId);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_reconnectTimer_timeout();
|
||||||
|
void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state);
|
||||||
|
void on_rpcChannel_connected();
|
||||||
|
void on_rpcChannel_disconnected();
|
||||||
|
void on_rpcChannel_error(QAbstractSocket::SocketError socketError);
|
||||||
|
|
||||||
|
void when_portListChanged(quint32 portGroupId);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void when_configApply(int portIndex);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
133
client/portgrouplist.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
// TODO(LOW): Remove
|
||||||
|
#include <modeltest.h>
|
||||||
|
|
||||||
|
PortGroupList::PortGroupList()
|
||||||
|
: mPortGroupListModel(this),
|
||||||
|
mStreamListModel(this),
|
||||||
|
mPortStatsModel(this, this)
|
||||||
|
{
|
||||||
|
PortGroup *pg;
|
||||||
|
|
||||||
|
#ifdef QT_NO_DEBUG
|
||||||
|
streamModelTester_ = NULL;
|
||||||
|
portModelTester_ = NULL;
|
||||||
|
portStatsModelTester_ = NULL;
|
||||||
|
#else
|
||||||
|
streamModelTester_ = new ModelTest(getStreamModel());
|
||||||
|
portModelTester_ = new ModelTest(getPortModel());
|
||||||
|
portStatsModelTester_ = new ModelTest(getPortStatsModel());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Add the "Local" Port Group
|
||||||
|
pg = new PortGroup;
|
||||||
|
addPortGroup(*pg);
|
||||||
|
}
|
||||||
|
|
||||||
|
PortGroupList::~PortGroupList()
|
||||||
|
{
|
||||||
|
delete portStatsModelTester_;
|
||||||
|
delete portModelTester_;
|
||||||
|
delete streamModelTester_;
|
||||||
|
|
||||||
|
while (!mPortGroups.isEmpty())
|
||||||
|
delete mPortGroups.takeFirst();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PortGroupList::isPortGroup(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
return mPortGroupListModel.isPortGroup(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PortGroupList::isPort(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
return mPortGroupListModel.isPort(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
Q_ASSERT(mPortGroupListModel.isPortGroup(index));
|
||||||
|
|
||||||
|
return *(mPortGroups[index.row()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Port& PortGroupList::port(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
Q_ASSERT(mPortGroupListModel.isPort(index));
|
||||||
|
return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroupList::addPortGroup(PortGroup &portGroup)
|
||||||
|
{
|
||||||
|
mPortGroupListModel.portGroupAboutToBeAppended();
|
||||||
|
|
||||||
|
connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)),
|
||||||
|
&mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int)));
|
||||||
|
#if 0
|
||||||
|
connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)),
|
||||||
|
&mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged()));
|
||||||
|
connect(&portGroup, SIGNAL(portListChanged(quint32)),
|
||||||
|
&mPortGroupListModel, SLOT(triggerLayoutChanged()));
|
||||||
|
#endif
|
||||||
|
connect(&portGroup, SIGNAL(portListChanged(quint32)),
|
||||||
|
&mPortGroupListModel, SLOT(when_portListChanged()));
|
||||||
|
|
||||||
|
connect(&portGroup, SIGNAL(portListChanged(quint32)),
|
||||||
|
&mPortStatsModel, SLOT(when_portListChanged()));
|
||||||
|
|
||||||
|
connect(&portGroup, SIGNAL(statsChanged(quint32)),
|
||||||
|
&mPortStatsModel, SLOT(when_portGroup_stats_update(quint32)));
|
||||||
|
|
||||||
|
mPortGroups.append(&portGroup);
|
||||||
|
portGroup.connectToHost();
|
||||||
|
|
||||||
|
mPortGroupListModel.portGroupAppended();
|
||||||
|
|
||||||
|
mPortStatsModel.when_portListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortGroupList::removePortGroup(PortGroup &portGroup)
|
||||||
|
{
|
||||||
|
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
|
||||||
|
|
||||||
|
PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup));
|
||||||
|
qDebug("after takeAt()");
|
||||||
|
mPortGroupListModel.portGroupRemoved();
|
||||||
|
|
||||||
|
delete pg;
|
||||||
|
|
||||||
|
mPortStatsModel.when_portListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//....................
|
||||||
|
// Private Methods
|
||||||
|
//....................
|
||||||
|
int PortGroupList::indexOfPortGroup(quint32 portGroupId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mPortGroups.size(); i++) {
|
||||||
|
if (mPortGroups.value(i)->id() == portGroupId)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
75
client/portgrouplist.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_GROUP_LIST_H
|
||||||
|
#define _PORT_GROUP_LIST_H
|
||||||
|
|
||||||
|
#include "portgroup.h"
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QItemSelection>
|
||||||
|
#include "portmodel.h"
|
||||||
|
#include "streammodel.h"
|
||||||
|
#include "portstatsmodel.h"
|
||||||
|
|
||||||
|
class PortModel;
|
||||||
|
class StreamModel;
|
||||||
|
|
||||||
|
class PortGroupList : public QObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class PortModel;
|
||||||
|
friend class StreamModel;
|
||||||
|
friend class PortStatsModel;
|
||||||
|
|
||||||
|
QList<PortGroup*> mPortGroups;
|
||||||
|
PortModel mPortGroupListModel;
|
||||||
|
StreamModel mStreamListModel;
|
||||||
|
PortStatsModel mPortStatsModel;
|
||||||
|
|
||||||
|
QObject *streamModelTester_;
|
||||||
|
QObject *portModelTester_;
|
||||||
|
QObject *portStatsModelTester_;
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
public:
|
||||||
|
PortGroupList();
|
||||||
|
~PortGroupList();
|
||||||
|
|
||||||
|
PortModel* getPortModel() { return &mPortGroupListModel; }
|
||||||
|
PortStatsModel* getPortStatsModel() { return &mPortStatsModel; }
|
||||||
|
StreamModel* getStreamModel() { return &mStreamListModel; }
|
||||||
|
|
||||||
|
bool isPortGroup(const QModelIndex& index);
|
||||||
|
bool isPort(const QModelIndex& index);
|
||||||
|
PortGroup& portGroup(const QModelIndex& index);
|
||||||
|
Port& port(const QModelIndex& index);
|
||||||
|
|
||||||
|
int numPortGroups() { return mPortGroups.size(); }
|
||||||
|
PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); }
|
||||||
|
|
||||||
|
void addPortGroup(PortGroup &portGroup);
|
||||||
|
void removePortGroup(PortGroup &portGroup);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int indexOfPortGroup(quint32 portGroupId);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
343
client/portmodel.cpp
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portmodel.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define DBG0(x) qDebug(x)
|
||||||
|
#define DBG1(x, p1) qDebug(x, (p1))
|
||||||
|
#else
|
||||||
|
#define DBG0(x) {}
|
||||||
|
#define DBG1(x, p1) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PortModel::PortModel(PortGroupList *p, QObject *parent)
|
||||||
|
: QAbstractItemModel(parent)
|
||||||
|
{
|
||||||
|
pgl = p;
|
||||||
|
|
||||||
|
portIconFactory[OstProto::LinkStateUnknown][false] =
|
||||||
|
QIcon(":/icons/bullet_white.png");
|
||||||
|
portIconFactory[OstProto::LinkStateDown][false] =
|
||||||
|
QIcon(":/icons/bullet_red.png");
|
||||||
|
portIconFactory[OstProto::LinkStateUp][false] =
|
||||||
|
QIcon(":/icons/bullet_green.png");
|
||||||
|
|
||||||
|
for (int linkState = 0; linkState < kLinkStatesCount; linkState++)
|
||||||
|
{
|
||||||
|
QPixmap pixmap(":/icons/deco_exclusive.png");
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
QIcon icon = portIconFactory[linkState][false];
|
||||||
|
|
||||||
|
painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32)));
|
||||||
|
portIconFactory[linkState][true] = QIcon(pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PortModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
// qDebug("RowCount Enter\n");
|
||||||
|
if (!parent.isValid())
|
||||||
|
{
|
||||||
|
// Top Level Item
|
||||||
|
//qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size());
|
||||||
|
return pgl->mPortGroups.size();
|
||||||
|
}
|
||||||
|
// qDebug("RowCount non top %d, %d, %llx\n",
|
||||||
|
// parent.row(), parent.column(), parent.internalId());
|
||||||
|
|
||||||
|
quint16 pg = (parent.internalId() >> 16) & 0xFFFF;
|
||||||
|
quint16 p = parent.internalId() & 0xFFFF;
|
||||||
|
if (p == 0xFFFF)
|
||||||
|
{
|
||||||
|
#if 0 // wrong code?
|
||||||
|
int count = 0;
|
||||||
|
foreach(PortGroup *pg, pgl->mPortGroups)
|
||||||
|
{
|
||||||
|
count += pg->numPorts();
|
||||||
|
}
|
||||||
|
//qDebug("RowCount (Mid) Exit: %d\n", count);
|
||||||
|
return count;
|
||||||
|
#endif
|
||||||
|
if (parent.column() == 0)
|
||||||
|
return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Leaf Item
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PortModel::columnCount(const QModelIndex &/*parent*/) const
|
||||||
|
{
|
||||||
|
return 1; // FIXME: hardcoding
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags PortModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
return QAbstractItemModel::flags(index); // FIXME: no need for this func
|
||||||
|
}
|
||||||
|
QVariant PortModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
|
||||||
|
DBG0("Enter PortModel data\n");
|
||||||
|
|
||||||
|
// Check for a valid index
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
DBG1("PortModel::data(index).row = %d", index.row());
|
||||||
|
DBG1("PortModel::data(index).column = %0d", index.column());
|
||||||
|
DBG1("PortModel::data(index).internalId = %08llx", index.internalId());
|
||||||
|
|
||||||
|
QModelIndex parent = index.parent();
|
||||||
|
|
||||||
|
if (!parent.isValid())
|
||||||
|
{
|
||||||
|
// Top Level Item - PortGroup
|
||||||
|
if ((role == Qt::DisplayRole))
|
||||||
|
{
|
||||||
|
DBG0("Exit PortModel data 1\n");
|
||||||
|
return QString("Port Group %1: %2 [%3:%4] (%5)").
|
||||||
|
arg(pgl->mPortGroups.at(index.row())->id()).
|
||||||
|
arg(pgl->mPortGroups.at(index.row())->userAlias()).
|
||||||
|
arg(pgl->mPortGroups.at(index.row())->serverAddress().toString()).
|
||||||
|
arg(pgl->mPortGroups.at(index.row())->serverPort()).
|
||||||
|
arg(pgl->mPortGroups.value(index.row())->numPorts());
|
||||||
|
}
|
||||||
|
else if ((role == Qt::DecorationRole))
|
||||||
|
{
|
||||||
|
DBG0("Exit PortModel data 2\n");
|
||||||
|
switch(pgl->mPortGroups.at(index.row())->state())
|
||||||
|
{
|
||||||
|
case QAbstractSocket::UnconnectedState:
|
||||||
|
return QIcon(":/icons/bullet_red.png");
|
||||||
|
|
||||||
|
case QAbstractSocket::HostLookupState:
|
||||||
|
return QIcon(":/icons/bullet_yellow.png");
|
||||||
|
|
||||||
|
case QAbstractSocket::ConnectingState:
|
||||||
|
case QAbstractSocket::ClosingState:
|
||||||
|
return QIcon(":/icons/bullet_orange.png");
|
||||||
|
|
||||||
|
case QAbstractSocket::ConnectedState:
|
||||||
|
return QIcon(":/icons/bullet_green.png");
|
||||||
|
|
||||||
|
|
||||||
|
case QAbstractSocket::BoundState:
|
||||||
|
case QAbstractSocket::ListeningState:
|
||||||
|
default:
|
||||||
|
return QIcon(":/icons/bullet_error.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG0("Exit PortModel data 3\n");
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pgl->mPortGroups.at(parent.row())->numPorts() == 0)
|
||||||
|
{
|
||||||
|
DBG0("Exit PortModel data 4\n");
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()];
|
||||||
|
|
||||||
|
// Non Top Level - Port
|
||||||
|
if ((role == Qt::DisplayRole))
|
||||||
|
{
|
||||||
|
QString rsvdBy;
|
||||||
|
|
||||||
|
if (!port->userName().isEmpty())
|
||||||
|
rsvdBy = "["+port->userName()+"] ";
|
||||||
|
|
||||||
|
return QString("Port %1: %2 %3(%4)")
|
||||||
|
.arg(port->id())
|
||||||
|
.arg(port->name())
|
||||||
|
.arg(rsvdBy)
|
||||||
|
.arg(port->description());
|
||||||
|
}
|
||||||
|
else if ((role == Qt::DecorationRole))
|
||||||
|
{
|
||||||
|
return portIconFactory[port->linkState()][port->hasExclusiveControl()];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG0("Exit PortModel data 6\n");
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
return QVariant();
|
||||||
|
else
|
||||||
|
return QString("Name");
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PortModel::index (int row, int col,
|
||||||
|
const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (!hasIndex(row, col, parent))
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
//qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n",
|
||||||
|
// row, col, parent.row(), parent.column(), parent.internalId());
|
||||||
|
|
||||||
|
if (!parent.isValid())
|
||||||
|
{
|
||||||
|
// Top Level Item
|
||||||
|
quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF;
|
||||||
|
quint32 id = (pg << 16) | p;
|
||||||
|
//qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id);
|
||||||
|
|
||||||
|
return createIndex(row, col, id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
quint16 pg = parent.internalId() >> 16;
|
||||||
|
quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id();
|
||||||
|
quint32 id = (pg << 16) | p;
|
||||||
|
//qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id);
|
||||||
|
|
||||||
|
return createIndex(row, col, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex PortModel::parent(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
//qDebug("parent: R=%d, C=%d ID=%llx\n",
|
||||||
|
// index.row(), index.column(), index.internalId());
|
||||||
|
|
||||||
|
quint16 pg = index.internalId() >> 16;
|
||||||
|
quint16 p = index.internalId() & 0x0000FFFF;
|
||||||
|
|
||||||
|
//qDebug("parent dbg: PG=%d, P=%d\n", pg, p);
|
||||||
|
|
||||||
|
if (p == 0xFFFF)
|
||||||
|
{
|
||||||
|
//qDebug("parent ret: NULL\n");
|
||||||
|
// Top Level Item - PG
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 id = (pg << 16) | 0xFFFF;
|
||||||
|
//qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id);
|
||||||
|
|
||||||
|
return createIndex(pgl->indexOfPortGroup(pg), 0, id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PortModel::isPortGroup(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PortModel::isPort(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 PortModel::portGroupId(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
return (index.internalId()) >> 16 & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 PortModel::portId(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
return (index.internalId()) & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// Slots
|
||||||
|
// ----------------------------------------------
|
||||||
|
void PortModel::when_portGroupDataChanged(int portGroupId, int portId)
|
||||||
|
{
|
||||||
|
QModelIndex index;
|
||||||
|
int row;
|
||||||
|
|
||||||
|
qDebug("portGroupId = %d, portId = %d", portGroupId, portId);
|
||||||
|
if (portId == 0xFFFF)
|
||||||
|
row = pgl->indexOfPortGroup(portGroupId);
|
||||||
|
else
|
||||||
|
row = portId;
|
||||||
|
|
||||||
|
index = createIndex(row, 0, (portGroupId << 16) | portId);
|
||||||
|
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortModel::portGroupAboutToBeAppended()
|
||||||
|
{
|
||||||
|
int row;
|
||||||
|
|
||||||
|
row = pgl->mPortGroups.size();
|
||||||
|
beginInsertRows(QModelIndex(), row, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortModel::portGroupAppended()
|
||||||
|
{
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup)
|
||||||
|
{
|
||||||
|
int row;
|
||||||
|
|
||||||
|
row = pgl->mPortGroups.indexOf(portGroup);
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortModel::portGroupRemoved()
|
||||||
|
{
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortModel::when_portListChanged()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
75
client/portmodel.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_MODEL_H
|
||||||
|
#define _PORT_MODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
class PortGroupList;
|
||||||
|
class PortGroup;
|
||||||
|
|
||||||
|
class PortModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class PortGroupList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortModel(PortGroupList *p, QObject *parent = 0);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation,
|
||||||
|
int role = Qt::DisplayRole) const;
|
||||||
|
QModelIndex index (int row, int col,
|
||||||
|
const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
QModelIndex parent(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
bool isPortGroup(const QModelIndex& index);
|
||||||
|
bool isPort(const QModelIndex& index);
|
||||||
|
quint32 portGroupId(const QModelIndex& index);
|
||||||
|
quint32 portId(const QModelIndex& index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortGroupList *pgl;
|
||||||
|
static const int kLinkStatesCount = 3;
|
||||||
|
static const int kExclusiveStatesCount = 2;
|
||||||
|
QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount];
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void when_portGroupDataChanged(int portGroupId, int portId);
|
||||||
|
|
||||||
|
void portGroupAboutToBeAppended();
|
||||||
|
void portGroupAppended();
|
||||||
|
void portGroupAboutToBeRemoved(PortGroup *portGroup);
|
||||||
|
void portGroupRemoved();
|
||||||
|
|
||||||
|
void when_portListChanged();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void triggerLayoutAboutToBeChanged();
|
||||||
|
void triggerLayoutChanged();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
170
client/portstatsfilter.ui
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>PortStatsFilterDialog</class>
|
||||||
|
<widget class="QDialog" name="PortStatsFilterDialog" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>319</width>
|
||||||
|
<height>193</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Select Ports</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portstats_filter.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="lvUnselected" >
|
||||||
|
<property name="acceptDrops" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragEnabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode" >
|
||||||
|
<enum>QAbstractItemView::NoDragDrop</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode" >
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="movement" >
|
||||||
|
<enum>QListView::Static</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbSelectIn" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>></string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/arrow_right.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbSelectOut" >
|
||||||
|
<property name="text" >
|
||||||
|
<string><</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/arrow_left.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="lvSelected" >
|
||||||
|
<property name="acceptDrops" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragEnabled" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropOverwriteMode" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode" >
|
||||||
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode" >
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="movement" >
|
||||||
|
<enum>QListView::Free</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons" >
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>lvUnselected</tabstop>
|
||||||
|
<tabstop>tbSelectIn</tabstop>
|
||||||
|
<tabstop>tbSelectOut</tabstop>
|
||||||
|
<tabstop>lvSelected</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>PortStatsFilterDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>PortStatsFilterDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
134
client/portstatsfilterdialog.cpp
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portstatsfilterdialog.h"
|
||||||
|
|
||||||
|
PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
mUnselected.setSortRole(kLogicalIndex);
|
||||||
|
mSelected.setSortRole(kVisualIndex);
|
||||||
|
|
||||||
|
lvUnselected->setModel(&mUnselected);
|
||||||
|
lvSelected->setModel(&mSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<uint> PortStatsFilterDialog::getItemList(bool* ok,
|
||||||
|
QAbstractItemModel *model, Qt::Orientation orientation,
|
||||||
|
QList<uint> initial)
|
||||||
|
{
|
||||||
|
QList<uint> ret;
|
||||||
|
|
||||||
|
uint count = (orientation == Qt::Vertical) ?
|
||||||
|
model->rowCount() : model->columnCount();
|
||||||
|
|
||||||
|
*ok = false;
|
||||||
|
|
||||||
|
mUnselected.clear();
|
||||||
|
mSelected.clear();
|
||||||
|
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
QStandardItem *item;
|
||||||
|
|
||||||
|
item = new QStandardItem(model->headerData(i, orientation).toString());
|
||||||
|
item->setData(i, kLogicalIndex);
|
||||||
|
item->setData(initial.indexOf(i), kVisualIndex);
|
||||||
|
item->setFlags(Qt::ItemIsSelectable
|
||||||
|
| Qt::ItemIsDragEnabled
|
||||||
|
//| Qt::ItemIsDropEnabled
|
||||||
|
| Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
if (initial.contains(i))
|
||||||
|
mSelected.appendRow(item);
|
||||||
|
else
|
||||||
|
mUnselected.appendRow(item);
|
||||||
|
}
|
||||||
|
mSelected.sort(0);
|
||||||
|
|
||||||
|
// No need to sort right now 'coz we have inserted items in order
|
||||||
|
|
||||||
|
if (exec() == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
uint count = mSelected.rowCount();
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
QModelIndex index = mSelected.index(i, 0, QModelIndex());
|
||||||
|
QStandardItem *item = mSelected.itemFromIndex(index);
|
||||||
|
ret.append(item->data(kLogicalIndex).toInt());
|
||||||
|
}
|
||||||
|
*ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsFilterDialog::on_tbSelectIn_clicked()
|
||||||
|
{
|
||||||
|
QList<int> rows;
|
||||||
|
|
||||||
|
foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes())
|
||||||
|
rows.append(idx.row());
|
||||||
|
qSort(rows.begin(), rows.end(), qGreater<int>());
|
||||||
|
|
||||||
|
QModelIndex idx = lvSelected->selectionModel()->currentIndex();
|
||||||
|
int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount();
|
||||||
|
|
||||||
|
foreach(int row, rows)
|
||||||
|
{
|
||||||
|
QList<QStandardItem*> items = mUnselected.takeRow(row);
|
||||||
|
mSelected.insertRow(insertAt, items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsFilterDialog::on_tbSelectOut_clicked()
|
||||||
|
{
|
||||||
|
QList<int> rows;
|
||||||
|
|
||||||
|
foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes())
|
||||||
|
rows.append(idx.row());
|
||||||
|
qSort(rows.begin(), rows.end(), qGreater<int>());
|
||||||
|
|
||||||
|
foreach(int row, rows)
|
||||||
|
{
|
||||||
|
QList<QStandardItem*> items = mSelected.takeRow(row);
|
||||||
|
mUnselected.appendRow(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
mUnselected.sort(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
QList<QStandardItem*> items = mUnselected.takeRow(index.row());
|
||||||
|
QModelIndex idx = lvSelected->selectionModel()->currentIndex();
|
||||||
|
int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount();
|
||||||
|
|
||||||
|
mSelected.insertRow(insertAt, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
QList<QStandardItem*> items = mSelected.takeRow(index.row());
|
||||||
|
mUnselected.appendRow(items);
|
||||||
|
mUnselected.sort(0);
|
||||||
|
}
|
||||||
|
|
55
client/portstatsfilterdialog.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_STATS_FILTER_DIALOG_H
|
||||||
|
#define _PORT_STATS_FILTER_DIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include "ui_portstatsfilter.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortStatsFilterDialog(QWidget *parent = 0);
|
||||||
|
QList<uint> getItemList(bool* ok, QAbstractItemModel *model,
|
||||||
|
Qt::Orientation orientation = Qt::Vertical,
|
||||||
|
QList<uint> initial = QList<uint>());
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum ItemRole {
|
||||||
|
kLogicalIndex = Qt::UserRole + 1,
|
||||||
|
kVisualIndex
|
||||||
|
};
|
||||||
|
QStandardItemModel mUnselected;
|
||||||
|
QStandardItemModel mSelected;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_tbSelectIn_clicked();
|
||||||
|
void on_tbSelectOut_clicked();
|
||||||
|
void on_lvUnselected_doubleClicked(const QModelIndex &index);
|
||||||
|
void on_lvSelected_doubleClicked(const QModelIndex &index);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
326
client/portstatsmodel.cpp
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portstatsmodel.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
{
|
||||||
|
pgl = p;
|
||||||
|
|
||||||
|
timer = new QTimer();
|
||||||
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateStats()));
|
||||||
|
timer->start(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
PortStatsModel::~PortStatsModel()
|
||||||
|
{
|
||||||
|
timer->stop();
|
||||||
|
delete timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PortStatsModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (numPorts.isEmpty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (numPorts.last() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (int) e_STAT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PortStatsModel::columnCount(const QModelIndex &parent ) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
if (numPorts.isEmpty())
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return numPorts.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsModel::getDomainIndexes(const QModelIndex &index,
|
||||||
|
uint &portGroupIdx, uint &portIdx) const
|
||||||
|
{
|
||||||
|
int portNum;
|
||||||
|
|
||||||
|
// TODO(LOW): Optimize using binary search: see qLowerBound()
|
||||||
|
portNum = index.column() + 1;
|
||||||
|
for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++)
|
||||||
|
if (portNum <= numPorts.at(portGroupIdx))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (portGroupIdx)
|
||||||
|
{
|
||||||
|
if (numPorts.at(portGroupIdx -1))
|
||||||
|
portIdx = (portNum - 1) - numPorts.at(portGroupIdx - 1);
|
||||||
|
else
|
||||||
|
portIdx = portNum - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
portIdx = portNum - 1;
|
||||||
|
|
||||||
|
//qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
uint pgidx, pidx;
|
||||||
|
int row;
|
||||||
|
|
||||||
|
// Check for a valid index
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
// Check for row/column limits
|
||||||
|
row = index.row();
|
||||||
|
if (row >= e_STAT_MAX)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (numPorts.isEmpty())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (index.column() >= (numPorts.last()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
getDomainIndexes(index, pgidx, pidx);
|
||||||
|
|
||||||
|
// Check role
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
|
OstProto::PortStats stats;
|
||||||
|
|
||||||
|
stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats();
|
||||||
|
|
||||||
|
switch(row)
|
||||||
|
{
|
||||||
|
// States
|
||||||
|
case e_LINK_STATE:
|
||||||
|
return LinkStateName.at(stats.state().link_state());
|
||||||
|
|
||||||
|
case e_TRANSMIT_STATE:
|
||||||
|
return BoolStateName.at(stats.state().is_transmit_on());
|
||||||
|
|
||||||
|
case e_CAPTURE_STATE:
|
||||||
|
return BoolStateName.at(stats.state().is_capture_on());
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
case e_STAT_FRAMES_RCVD:
|
||||||
|
return quint64(stats.rx_pkts());
|
||||||
|
|
||||||
|
case e_STAT_FRAMES_SENT:
|
||||||
|
return quint64(stats.tx_pkts());
|
||||||
|
|
||||||
|
case e_STAT_FRAME_SEND_RATE:
|
||||||
|
return quint64(stats.tx_pps());
|
||||||
|
|
||||||
|
case e_STAT_FRAME_RECV_RATE:
|
||||||
|
return quint64(stats.rx_pps());
|
||||||
|
|
||||||
|
case e_STAT_BYTES_RCVD:
|
||||||
|
return quint64(stats.rx_bytes());
|
||||||
|
|
||||||
|
case e_STAT_BYTES_SENT:
|
||||||
|
return quint64(stats.tx_bytes());
|
||||||
|
|
||||||
|
case e_STAT_BYTE_SEND_RATE:
|
||||||
|
return quint64(stats.tx_bps());
|
||||||
|
|
||||||
|
case e_STAT_BYTE_RECV_RATE:
|
||||||
|
return quint64(stats.rx_bps());
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
case e_STAT_FRAMES_RCVD_NIC:
|
||||||
|
return stats.rx_pkts_nic();
|
||||||
|
|
||||||
|
case e_STAT_FRAMES_SENT_NIC:
|
||||||
|
return stats.tx_pkts_nic();
|
||||||
|
|
||||||
|
case e_STAT_BYTES_RCVD_NIC:
|
||||||
|
return stats.rx_bytes_nic();
|
||||||
|
|
||||||
|
case e_STAT_BYTES_SENT_NIC:
|
||||||
|
return stats.tx_bytes_nic();
|
||||||
|
#endif
|
||||||
|
case e_STAT_RX_DROPS : return quint64(stats.rx_drops());
|
||||||
|
case e_STAT_RX_ERRORS: return quint64(stats.rx_errors());
|
||||||
|
case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors());
|
||||||
|
case e_STAT_RX_FRAME_ERRORS: return quint64(stats.rx_frame_errors());
|
||||||
|
|
||||||
|
default:
|
||||||
|
qWarning("%s: Unhandled stats id %d\n", __FUNCTION__,
|
||||||
|
index.row());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (role == Qt::TextAlignmentRole)
|
||||||
|
{
|
||||||
|
if (row >= e_STATE_START && row <= e_STATE_END)
|
||||||
|
return Qt::AlignHCenter;
|
||||||
|
else if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
|
||||||
|
return Qt::AlignRight;
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role == Qt::ToolTipRole)
|
||||||
|
{
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
QString notes;
|
||||||
|
uint portGroupIdx, portIdx;
|
||||||
|
|
||||||
|
if (numPorts.isEmpty() || section >= numPorts.last())
|
||||||
|
return QVariant();
|
||||||
|
getDomainIndexes(index(0, section), portGroupIdx, portIdx);
|
||||||
|
notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes();
|
||||||
|
if (!notes.isEmpty())
|
||||||
|
return notes;
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
uint portGroupIdx, portIdx;
|
||||||
|
QString portName;
|
||||||
|
|
||||||
|
if (numPorts.isEmpty() || section >= numPorts.last())
|
||||||
|
return QVariant();
|
||||||
|
getDomainIndexes(index(0, section), portGroupIdx, portIdx);
|
||||||
|
portName = QString("Port %1-%2")
|
||||||
|
.arg(pgl->mPortGroups.at(portGroupIdx)->id())
|
||||||
|
.arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id());
|
||||||
|
if (portGroupIdx < (uint) pgl->mPortGroups.size()
|
||||||
|
&& portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size())
|
||||||
|
{
|
||||||
|
if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes()
|
||||||
|
.isEmpty())
|
||||||
|
portName += " *";
|
||||||
|
}
|
||||||
|
return portName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return PortStatName.at(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsModel::portListFromIndex(QModelIndexList indices,
|
||||||
|
QList<PortGroupAndPortList> &portList)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
QModelIndexList selectedCols(indices);
|
||||||
|
|
||||||
|
portList.clear();
|
||||||
|
|
||||||
|
//selectedCols = indices.selectedColumns();
|
||||||
|
for (i = 0; i < selectedCols.size(); i++)
|
||||||
|
{
|
||||||
|
uint portGroupIdx, portIdx;
|
||||||
|
|
||||||
|
getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx);
|
||||||
|
for (j = 0; j < portList.size(); j++)
|
||||||
|
{
|
||||||
|
if (portList[j].portGroupId == portGroupIdx)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j >= portList.size())
|
||||||
|
{
|
||||||
|
// PortGroup Not found
|
||||||
|
PortGroupAndPortList p;
|
||||||
|
|
||||||
|
p.portGroupId = portGroupIdx;
|
||||||
|
p.portList.append(portIdx);
|
||||||
|
|
||||||
|
portList.append(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// PortGroup found
|
||||||
|
|
||||||
|
portList[j].portList.append(portIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Slots
|
||||||
|
//
|
||||||
|
void PortStatsModel::when_portListChanged()
|
||||||
|
{
|
||||||
|
int i, count = 0;
|
||||||
|
|
||||||
|
// recalc numPorts
|
||||||
|
while (numPorts.size())
|
||||||
|
numPorts.removeFirst();
|
||||||
|
|
||||||
|
for (i = 0; i < pgl->mPortGroups.size(); i++)
|
||||||
|
{
|
||||||
|
count += pgl->mPortGroups.at(i)->numPorts();
|
||||||
|
numPorts.append(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/)
|
||||||
|
{
|
||||||
|
QModelIndex topLeft = index(port, 0, QModelIndex());
|
||||||
|
QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex());
|
||||||
|
|
||||||
|
emit dataChanged(topLeft, bottomRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsModel::updateStats()
|
||||||
|
{
|
||||||
|
// Request each portgroup to fetch updated stats - the port group
|
||||||
|
// raises a signal once updated stats are available
|
||||||
|
for (int i = 0; i < pgl->mPortGroups.size(); i++)
|
||||||
|
pgl->mPortGroups[i]->getPortStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/)
|
||||||
|
{
|
||||||
|
// FIXME(MED): update only the changed ports, not all
|
||||||
|
|
||||||
|
QModelIndex topLeft = index(0, 0, QModelIndex());
|
||||||
|
QModelIndex bottomRight = index(rowCount(), columnCount(), QModelIndex());
|
||||||
|
|
||||||
|
emit dataChanged(topLeft, bottomRight);
|
||||||
|
}
|
151
client/portstatsmodel.h
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_STATS_MODEL_H
|
||||||
|
#define _PORT_STATS_MODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// State
|
||||||
|
e_STATE_START = 0,
|
||||||
|
|
||||||
|
e_LINK_STATE = e_STATE_START,
|
||||||
|
e_TRANSMIT_STATE,
|
||||||
|
e_CAPTURE_STATE,
|
||||||
|
|
||||||
|
e_STATE_END = e_CAPTURE_STATE,
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
e_STATISTICS_START,
|
||||||
|
|
||||||
|
e_STAT_FRAMES_RCVD = e_STATISTICS_START,
|
||||||
|
e_STAT_FRAMES_SENT,
|
||||||
|
e_STAT_FRAME_SEND_RATE,
|
||||||
|
e_STAT_FRAME_RECV_RATE,
|
||||||
|
e_STAT_BYTES_RCVD,
|
||||||
|
e_STAT_BYTES_SENT,
|
||||||
|
e_STAT_BYTE_SEND_RATE,
|
||||||
|
e_STAT_BYTE_RECV_RATE,
|
||||||
|
#if 0
|
||||||
|
e_STAT_FRAMES_RCVD_NIC,
|
||||||
|
e_STAT_FRAMES_SENT_NIC,
|
||||||
|
e_STAT_BYTES_RCVD_NIC,
|
||||||
|
e_STAT_BYTES_SENT_NIC,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Rx Errors
|
||||||
|
e_STAT_RX_DROPS,
|
||||||
|
e_STAT_RX_ERRORS,
|
||||||
|
e_STAT_RX_FIFO_ERRORS,
|
||||||
|
e_STAT_RX_FRAME_ERRORS,
|
||||||
|
|
||||||
|
e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS,
|
||||||
|
|
||||||
|
e_STAT_MAX
|
||||||
|
} PortStat;
|
||||||
|
|
||||||
|
static QStringList PortStatName = (QStringList()
|
||||||
|
<< "Link State"
|
||||||
|
<< "Transmit State"
|
||||||
|
<< "Capture State"
|
||||||
|
|
||||||
|
<< "Frames Received"
|
||||||
|
<< "Frames Sent"
|
||||||
|
<< "Frame Send Rate (fps)"
|
||||||
|
<< "Frame Receive Rate (fps)"
|
||||||
|
<< "Bytes Received"
|
||||||
|
<< "Bytes Sent"
|
||||||
|
<< "Byte Send Rate (Bps)"
|
||||||
|
<< "Byte Receive Rate (Bps)"
|
||||||
|
#if 0
|
||||||
|
<< "Frames Received (NIC)"
|
||||||
|
<< "Frames Sent (NIC)"
|
||||||
|
<< "Bytes Received (NIC)"
|
||||||
|
<< "Bytes Sent (NIC)"
|
||||||
|
#endif
|
||||||
|
<< "Receive Drops"
|
||||||
|
<< "Receive Errors"
|
||||||
|
<< "Receive Fifo Errors"
|
||||||
|
<< "Receive Frame Errors"
|
||||||
|
);
|
||||||
|
|
||||||
|
static QStringList LinkStateName = (QStringList()
|
||||||
|
<< "Unknown"
|
||||||
|
<< "Down"
|
||||||
|
<< "Up"
|
||||||
|
);
|
||||||
|
|
||||||
|
static QStringList BoolStateName = (QStringList()
|
||||||
|
<< "Off"
|
||||||
|
<< "On"
|
||||||
|
);
|
||||||
|
|
||||||
|
class PortGroupList;
|
||||||
|
|
||||||
|
class PortStatsModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
PortStatsModel(PortGroupList *p, QObject *parent = 0);
|
||||||
|
~PortStatsModel();
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation,
|
||||||
|
int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
class PortGroupAndPortList {
|
||||||
|
public:
|
||||||
|
uint portGroupId;
|
||||||
|
QList<uint> portList;
|
||||||
|
};
|
||||||
|
void portListFromIndex(QModelIndexList indices,
|
||||||
|
QList<PortGroupAndPortList> &portList);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void when_portListChanged();
|
||||||
|
void on_portStatsUpdate(int port, void*stats);
|
||||||
|
void when_portGroup_stats_update(quint32 portGroupId);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateStats();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortGroupList *pgl;
|
||||||
|
|
||||||
|
// numPorts stores the num of ports per portgroup
|
||||||
|
// in the same order as the portgroups are index in the pgl
|
||||||
|
// Also it stores them as cumulative totals
|
||||||
|
QList<quint16> numPorts;
|
||||||
|
|
||||||
|
QTimer *timer;
|
||||||
|
|
||||||
|
void getDomainIndexes(const QModelIndex &index,
|
||||||
|
uint &portGroupIdx, uint &portIdx) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
186
client/portstatswindow.cpp
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "portstatswindow.h"
|
||||||
|
#include "portstatsmodel.h"
|
||||||
|
#include "portstatsfilterdialog.h"
|
||||||
|
|
||||||
|
#include "QHeaderView"
|
||||||
|
|
||||||
|
PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
this->pgl = pgl;
|
||||||
|
model = pgl->getPortStatsModel();
|
||||||
|
tvPortStats->setModel(model);
|
||||||
|
|
||||||
|
tvPortStats->verticalHeader()->setHighlightSections(false);
|
||||||
|
tvPortStats->verticalHeader()->setDefaultSectionSize(
|
||||||
|
tvPortStats->verticalHeader()->minimumSectionSize());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PortStatsWindow::~PortStatsWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- SLOTS -------------- */
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbStartTransmit_clicked()
|
||||||
|
{
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
pgpl);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(pgpl.at(i).portGroupId).
|
||||||
|
startTx(&pgpl[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbStopTransmit_clicked()
|
||||||
|
{
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
pgpl);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(pgpl.at(i).portGroupId).
|
||||||
|
stopTx(&pgpl[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbStartCapture_clicked()
|
||||||
|
{
|
||||||
|
// TODO(MED)
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
pgpl);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(pgpl.at(i).portGroupId).
|
||||||
|
startCapture(&pgpl[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbStopCapture_clicked()
|
||||||
|
{
|
||||||
|
// TODO(MED)
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
pgpl);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(pgpl.at(i).portGroupId).
|
||||||
|
stopCapture(&pgpl[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbViewCapture_clicked()
|
||||||
|
{
|
||||||
|
// TODO(MED)
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
pgpl);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < pgpl.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(pgpl.at(i).portGroupId).
|
||||||
|
viewCapture(&pgpl[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbClear_clicked()
|
||||||
|
{
|
||||||
|
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||||
|
|
||||||
|
// Get selected ports
|
||||||
|
model->portListFromIndex(tvPortStats->selectionModel()->selectedColumns(),
|
||||||
|
portList);
|
||||||
|
|
||||||
|
// Clear selected ports, portgroup by portgroup
|
||||||
|
for (int i = 0; i < portList.size(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(portList.at(i).portGroupId).
|
||||||
|
clearPortStats(&portList[i].portList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbClearAll_clicked()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < pgl->numPortGroups(); i++)
|
||||||
|
{
|
||||||
|
pgl->portGroupByIndex(i).clearPortStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortStatsWindow::on_tbFilter_clicked()
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QList<uint> currentColumns, newColumns;
|
||||||
|
PortStatsFilterDialog dialog;
|
||||||
|
|
||||||
|
// create the input list for the filter dialog -
|
||||||
|
// list of logical-indexes ordered by their current visual indexes
|
||||||
|
for(int vi = 0; vi < model->columnCount(); vi++) {
|
||||||
|
int li = tvPortStats->horizontalHeader()->logicalIndex(vi);
|
||||||
|
if (!tvPortStats->isColumnHidden(li)) {
|
||||||
|
currentColumns.append(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return list from the filter dialog -
|
||||||
|
// list of logical-indexes ordered by their new visual indexes
|
||||||
|
newColumns = dialog.getItemList(&ok, model, Qt::Horizontal, currentColumns);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
QHeaderView *hv = tvPortStats->horizontalHeader();
|
||||||
|
|
||||||
|
// hide/show sections first ...
|
||||||
|
for(int li = 0; li < model->columnCount(); li++)
|
||||||
|
tvPortStats->setColumnHidden(li, !newColumns.contains(li));
|
||||||
|
|
||||||
|
// ... then for the 'shown' columns, set the visual index
|
||||||
|
for(int vi = 0; vi < newColumns.size(); vi++)
|
||||||
|
hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi);
|
||||||
|
}
|
||||||
|
}
|
56
client/portstatswindow.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORT_STATS_WINDOW_H
|
||||||
|
#define _PORT_STATS_WINDOW_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include "ui_portstatswindow.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "portstatsmodel.h"
|
||||||
|
|
||||||
|
class PortStatsWindow : public QWidget, public Ui::PortStatsWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0);
|
||||||
|
~PortStatsWindow();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PortGroupList *pgl;
|
||||||
|
PortStatsModel *model;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_tbStartTransmit_clicked();
|
||||||
|
void on_tbStopTransmit_clicked();
|
||||||
|
|
||||||
|
void on_tbStartCapture_clicked();
|
||||||
|
void on_tbStopCapture_clicked();
|
||||||
|
void on_tbViewCapture_clicked();
|
||||||
|
|
||||||
|
void on_tbClear_clicked();
|
||||||
|
void on_tbClearAll_clicked();
|
||||||
|
|
||||||
|
void on_tbFilter_clicked();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
182
client/portstatswindow.ui
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>PortStatsWindow</class>
|
||||||
|
<widget class="QWidget" name="PortStatsWindow" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>502</width>
|
||||||
|
<height>415</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame" >
|
||||||
|
<property name="frameShape" >
|
||||||
|
<enum>QFrame::Panel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow" >
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbStartTransmit" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Start Tx</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Starts transmit on selected port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Start Transmit</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/control_play.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbStopTransmit" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Stop Tx</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Stops transmit on selected port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Stop Trasmit</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/control_stop.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbClear" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Clear Selected Port Stats</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Clears statistics of the selected port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Clear</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portstats_clear.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbClearAll" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Clear All Ports Stats</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Clears statistics of all ports</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Clear All</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portstats_clear_all.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbStartCapture" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Start Capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Captures packets on the selected port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Start Capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/sound_none.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbStopCapture" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Stop Capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>End capture on selecteed port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Stop Capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/sound_mute.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbViewCapture" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>View Capture Buffer</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>View captured packets on selected port(s)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>View Capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/magnifier.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbFilter" >
|
||||||
|
<property name="statusTip" >
|
||||||
|
<string>Select which ports to view</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Filter</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portstats_filter.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tvPortStats" />
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
692
client/portswindow.cpp
Normal file
@ -0,0 +1,692 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portswindow.h"
|
||||||
|
|
||||||
|
#include "abstractfileformat.h"
|
||||||
|
#include "portconfigdialog.h"
|
||||||
|
#include "streamconfigdialog.h"
|
||||||
|
#include "streamlistdelegate.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QItemSelectionModel>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
QAction *sep;
|
||||||
|
|
||||||
|
delegate = new StreamListDelegate;
|
||||||
|
//slm = new StreamListModel();
|
||||||
|
//plm = new PortGroupList();
|
||||||
|
plm = pgl;
|
||||||
|
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
tvPortList->header()->hide();
|
||||||
|
|
||||||
|
tvStreamList->setItemDelegate(delegate);
|
||||||
|
|
||||||
|
tvStreamList->verticalHeader()->setDefaultSectionSize(
|
||||||
|
tvStreamList->verticalHeader()->minimumSectionSize());
|
||||||
|
|
||||||
|
// Populate PortList Context Menu Actions
|
||||||
|
tvPortList->addAction(actionNew_Port_Group);
|
||||||
|
tvPortList->addAction(actionDelete_Port_Group);
|
||||||
|
tvPortList->addAction(actionConnect_Port_Group);
|
||||||
|
tvPortList->addAction(actionDisconnect_Port_Group);
|
||||||
|
|
||||||
|
tvPortList->addAction(actionExclusive_Control);
|
||||||
|
tvPortList->addAction(actionPort_Configuration);
|
||||||
|
|
||||||
|
// Populate StramList Context Menu Actions
|
||||||
|
tvStreamList->addAction(actionNew_Stream);
|
||||||
|
tvStreamList->addAction(actionEdit_Stream);
|
||||||
|
tvStreamList->addAction(actionDuplicate_Stream);
|
||||||
|
tvStreamList->addAction(actionDelete_Stream);
|
||||||
|
|
||||||
|
sep = new QAction(this);
|
||||||
|
sep->setSeparator(true);
|
||||||
|
tvStreamList->addAction(sep);
|
||||||
|
|
||||||
|
tvStreamList->addAction(actionOpen_Streams);
|
||||||
|
tvStreamList->addAction(actionSave_Streams);
|
||||||
|
|
||||||
|
// PortList and StreamList actions combined make this window's actions
|
||||||
|
addActions(tvPortList->actions());
|
||||||
|
sep = new QAction(this);
|
||||||
|
sep->setSeparator(true);
|
||||||
|
addAction(sep);
|
||||||
|
addActions(tvStreamList->actions());
|
||||||
|
|
||||||
|
tvStreamList->setModel(plm->getStreamModel());
|
||||||
|
tvPortList->setModel(plm->getPortModel());
|
||||||
|
|
||||||
|
connect( plm->getPortModel(),
|
||||||
|
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(when_portModel_dataChanged(const QModelIndex&,
|
||||||
|
const QModelIndex&)));
|
||||||
|
|
||||||
|
connect(plm->getPortModel(), SIGNAL(modelReset()),
|
||||||
|
SLOT(when_portModel_reset()));
|
||||||
|
|
||||||
|
connect( tvPortList->selectionModel(),
|
||||||
|
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(when_portView_currentChanged(const QModelIndex&,
|
||||||
|
const QModelIndex&)));
|
||||||
|
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
|
||||||
|
connect(tvStreamList->selectionModel(),
|
||||||
|
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
connect(tvStreamList->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
SLOT(updateStreamViewActions()));
|
||||||
|
|
||||||
|
tvStreamList->resizeColumnToContents(StreamModel::StreamIcon);
|
||||||
|
tvStreamList->resizeColumnToContents(StreamModel::StreamStatus);
|
||||||
|
|
||||||
|
// Initially we don't have any ports/streams - so send signal triggers
|
||||||
|
when_portView_currentChanged(QModelIndex(), QModelIndex());
|
||||||
|
updateStreamViewActions();
|
||||||
|
|
||||||
|
connect(plm->getStreamModel(),
|
||||||
|
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(streamModelDataChanged()));
|
||||||
|
connect(plm->getStreamModel(),
|
||||||
|
SIGNAL(modelReset()),
|
||||||
|
this, SLOT(streamModelDataChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::streamModelDataChanged()
|
||||||
|
{
|
||||||
|
if (plm->isPort(tvPortList->currentIndex()))
|
||||||
|
plm->port(tvPortList->currentIndex()).recalculateAverageRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
PortsWindow::~PortsWindow()
|
||||||
|
{
|
||||||
|
delete delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
|
||||||
|
{
|
||||||
|
StreamConfigDialog *scd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
{
|
||||||
|
qDebug("%s: invalid index", __FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scd = new StreamConfigDialog(plm->port(tvPortList->currentIndex()),
|
||||||
|
index.row(), this);
|
||||||
|
qDebug("stream list activated\n");
|
||||||
|
ret = scd->exec();
|
||||||
|
|
||||||
|
if (ret == QDialog::Accepted)
|
||||||
|
plm->port(tvPortList->currentIndex()).recalculateAverageRates();
|
||||||
|
|
||||||
|
delete scd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::when_portView_currentChanged(const QModelIndex& current,
|
||||||
|
const QModelIndex& previous)
|
||||||
|
{
|
||||||
|
plm->getStreamModel()->setCurrentPortIndex(current);
|
||||||
|
updatePortViewActions(current);
|
||||||
|
updateStreamViewActions();
|
||||||
|
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
|
||||||
|
if (previous.isValid() && plm->isPort(previous))
|
||||||
|
{
|
||||||
|
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
|
||||||
|
this, SLOT(updatePortRates()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.isValid())
|
||||||
|
{
|
||||||
|
qDebug("setting stacked widget to blank page");
|
||||||
|
swDetail->setCurrentIndex(2); // blank page
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (plm->isPortGroup(current))
|
||||||
|
{
|
||||||
|
swDetail->setCurrentIndex(1); // portGroup detail page
|
||||||
|
}
|
||||||
|
else if (plm->isPort(current))
|
||||||
|
{
|
||||||
|
swDetail->setCurrentIndex(0); // port detail page
|
||||||
|
updatePortRates();
|
||||||
|
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
|
||||||
|
SLOT(updatePortRates()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft,
|
||||||
|
const QModelIndex& bottomRight)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex
|
||||||
|
if ((tvPortList->currentIndex() >= topLeft) &&
|
||||||
|
(tvPortList->currentIndex() <= bottomRight))
|
||||||
|
#endif
|
||||||
|
if (((topLeft < tvPortList->currentIndex()) ||
|
||||||
|
(topLeft == tvPortList->currentIndex())) &&
|
||||||
|
(((tvPortList->currentIndex() < bottomRight)) ||
|
||||||
|
(tvPortList->currentIndex() == bottomRight)))
|
||||||
|
{
|
||||||
|
// Update UI to reflect potential change in exclusive mode,
|
||||||
|
// transmit mode et al
|
||||||
|
when_portView_currentChanged(tvPortList->currentIndex(),
|
||||||
|
tvPortList->currentIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::when_portModel_reset()
|
||||||
|
{
|
||||||
|
when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_averagePacketsPerSec_editingFinished()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->currentIndex();
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(current));
|
||||||
|
|
||||||
|
bool isOk;
|
||||||
|
double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk);
|
||||||
|
|
||||||
|
plm->port(current).setAveragePacketRate(pps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_averageBitsPerSec_editingFinished()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->currentIndex();
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(current));
|
||||||
|
|
||||||
|
bool isOk;
|
||||||
|
double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk);
|
||||||
|
|
||||||
|
plm->port(current).setAverageBitRate(bps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::updatePortRates()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->currentIndex();
|
||||||
|
|
||||||
|
if (!current.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!plm->isPort(current))
|
||||||
|
return;
|
||||||
|
|
||||||
|
averagePacketsPerSec->setText(QString("%L1")
|
||||||
|
.arg(plm->port(current).averagePacketRate(), 0, 'f', 4));
|
||||||
|
averageBitsPerSec->setText(QString("%L1")
|
||||||
|
.arg(plm->port(current).averageBitRate(), 0, 'f', 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::updateStreamViewActions()
|
||||||
|
{
|
||||||
|
// For some reason hasSelection() returns true even if selection size is 0
|
||||||
|
// so additional check for size introduced
|
||||||
|
if (tvStreamList->selectionModel()->hasSelection() &&
|
||||||
|
(tvStreamList->selectionModel()->selection().size() > 0))
|
||||||
|
{
|
||||||
|
qDebug("Has selection %d",
|
||||||
|
tvStreamList->selectionModel()->selection().size());
|
||||||
|
|
||||||
|
// If more than one non-contiguous ranges selected,
|
||||||
|
// disable "New" and "Edit"
|
||||||
|
if (tvStreamList->selectionModel()->selection().size() > 1)
|
||||||
|
{
|
||||||
|
actionNew_Stream->setDisabled(true);
|
||||||
|
actionEdit_Stream->setDisabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actionNew_Stream->setEnabled(true);
|
||||||
|
|
||||||
|
// Enable "Edit" only if the single range has a single row
|
||||||
|
if (tvStreamList->selectionModel()->selection().at(0).height() > 1)
|
||||||
|
actionEdit_Stream->setDisabled(true);
|
||||||
|
else
|
||||||
|
actionEdit_Stream->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate/Delete are always enabled as long as we have a selection
|
||||||
|
actionDuplicate_Stream->setEnabled(true);
|
||||||
|
actionDelete_Stream->setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug("No selection");
|
||||||
|
if (plm->isPort(tvPortList->currentIndex()))
|
||||||
|
actionNew_Stream->setEnabled(true);
|
||||||
|
else
|
||||||
|
actionNew_Stream->setDisabled(true);
|
||||||
|
actionEdit_Stream->setDisabled(true);
|
||||||
|
actionDuplicate_Stream->setDisabled(true);
|
||||||
|
actionDelete_Stream->setDisabled(true);
|
||||||
|
}
|
||||||
|
actionOpen_Streams->setEnabled(plm->isPort(
|
||||||
|
tvPortList->selectionModel()->currentIndex()));
|
||||||
|
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::updatePortViewActions(const QModelIndex& current)
|
||||||
|
{
|
||||||
|
if (!current.isValid())
|
||||||
|
{
|
||||||
|
qDebug("current is now invalid");
|
||||||
|
actionDelete_Port_Group->setDisabled(true);
|
||||||
|
actionConnect_Port_Group->setDisabled(true);
|
||||||
|
actionDisconnect_Port_Group->setDisabled(true);
|
||||||
|
|
||||||
|
actionExclusive_Control->setDisabled(true);
|
||||||
|
actionPort_Configuration->setDisabled(true);
|
||||||
|
|
||||||
|
goto _EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("currentChanged %llx", current.internalId());
|
||||||
|
|
||||||
|
if (plm->isPortGroup(current))
|
||||||
|
{
|
||||||
|
actionDelete_Port_Group->setEnabled(true);
|
||||||
|
|
||||||
|
actionExclusive_Control->setDisabled(true);
|
||||||
|
actionPort_Configuration->setDisabled(true);
|
||||||
|
|
||||||
|
switch(plm->portGroup(current).state())
|
||||||
|
{
|
||||||
|
case QAbstractSocket::UnconnectedState:
|
||||||
|
case QAbstractSocket::ClosingState:
|
||||||
|
qDebug("state = unconnected|closing");
|
||||||
|
actionConnect_Port_Group->setEnabled(true);
|
||||||
|
actionDisconnect_Port_Group->setDisabled(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QAbstractSocket::HostLookupState:
|
||||||
|
case QAbstractSocket::ConnectingState:
|
||||||
|
case QAbstractSocket::ConnectedState:
|
||||||
|
qDebug("state = lookup|connecting|connected");
|
||||||
|
actionConnect_Port_Group->setDisabled(true);
|
||||||
|
actionDisconnect_Port_Group->setEnabled(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case QAbstractSocket::BoundState:
|
||||||
|
case QAbstractSocket::ListeningState:
|
||||||
|
default:
|
||||||
|
// FIXME(LOW): indicate error
|
||||||
|
qDebug("unexpected state");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (plm->isPort(current))
|
||||||
|
{
|
||||||
|
actionDelete_Port_Group->setDisabled(true);
|
||||||
|
actionConnect_Port_Group->setDisabled(true);
|
||||||
|
actionDisconnect_Port_Group->setDisabled(true);
|
||||||
|
|
||||||
|
actionExclusive_Control->setEnabled(true);
|
||||||
|
if (plm->port(current).hasExclusiveControl())
|
||||||
|
actionExclusive_Control->setChecked(true);
|
||||||
|
else
|
||||||
|
actionExclusive_Control->setChecked(false);
|
||||||
|
actionPort_Configuration->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_EXIT:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_pbApply_clicked()
|
||||||
|
{
|
||||||
|
QModelIndex curPort;
|
||||||
|
QModelIndex curPortGroup;
|
||||||
|
|
||||||
|
curPort = tvPortList->selectionModel()->currentIndex();
|
||||||
|
if (!curPort.isValid())
|
||||||
|
{
|
||||||
|
qDebug("%s: curPort is invalid", __FUNCTION__);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plm->isPort(curPort))
|
||||||
|
{
|
||||||
|
qDebug("%s: curPort is not a port", __FUNCTION__);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plm->port(curPort).getStats().state().is_transmit_on())
|
||||||
|
{
|
||||||
|
QMessageBox::information(0, "Configuration Change",
|
||||||
|
"Please stop transmit on the port before applying any changes");
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
curPortGroup = plm->getPortModel()->parent(curPort);
|
||||||
|
if (!curPortGroup.isValid())
|
||||||
|
{
|
||||||
|
qDebug("%s: curPortGroup is invalid", __FUNCTION__);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
if (!plm->isPortGroup(curPortGroup))
|
||||||
|
{
|
||||||
|
qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__);
|
||||||
|
goto _exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(HI): shd this be a signal?
|
||||||
|
//portGroup.when_configApply(port);
|
||||||
|
// FIXME(MED): mixing port id and index!!!
|
||||||
|
plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id());
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO (LOW): This block is for testing only
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (current.isValid())
|
||||||
|
qDebug("current = %llx", current.internalId());
|
||||||
|
else
|
||||||
|
qDebug("current is invalid");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionNew_Port_Group_triggered()
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString text = QInputDialog::getText(this,
|
||||||
|
"Add Port Group", "Port Group Address (IP[:Port])",
|
||||||
|
QLineEdit::Normal, lastNewPortGroup, &ok);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
QStringList addr = text.split(":");
|
||||||
|
if (addr.size() == 1) // Port unspecified
|
||||||
|
addr.append(QString().setNum(DEFAULT_SERVER_PORT));
|
||||||
|
PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort());
|
||||||
|
plm->addPortGroup(*pg);
|
||||||
|
lastNewPortGroup = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionDelete_Port_Group_triggered()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (current.isValid())
|
||||||
|
plm->removePortGroup(plm->portGroup(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionConnect_Port_Group_triggered()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (current.isValid())
|
||||||
|
plm->portGroup(current).connectToHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionDisconnect_Port_Group_triggered()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (current.isValid())
|
||||||
|
plm->portGroup(current).disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionExclusive_Control_triggered(bool checked)
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (plm->isPort(current))
|
||||||
|
{
|
||||||
|
OstProto::Port config;
|
||||||
|
|
||||||
|
config.set_is_exclusive_control(checked);
|
||||||
|
plm->portGroup(current.parent()).modifyPort(current.row(), config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionPort_Configuration_triggered()
|
||||||
|
{
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
|
||||||
|
if (!plm->isPort(current))
|
||||||
|
return;
|
||||||
|
|
||||||
|
OstProto::Port config;
|
||||||
|
config.set_transmit_mode(plm->port(current).transmitMode());
|
||||||
|
config.set_is_exclusive_control(plm->port(current).hasExclusiveControl());
|
||||||
|
config.set_user_name(plm->port(current).userName().toStdString());
|
||||||
|
|
||||||
|
PortConfigDialog dialog(config, this);
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
plm->portGroup(current.parent()).modifyPort(current.row(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionNew_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("New Stream Action");
|
||||||
|
|
||||||
|
// In case nothing is selected, insert 1 row at the top
|
||||||
|
int row = 0, count = 1;
|
||||||
|
|
||||||
|
// In case we have a single range selected; insert as many rows as
|
||||||
|
// in the singe selected range before the top of the selected range
|
||||||
|
if (tvStreamList->selectionModel()->selection().size() == 1)
|
||||||
|
{
|
||||||
|
row = tvStreamList->selectionModel()->selection().at(0).top();
|
||||||
|
count = tvStreamList->selectionModel()->selection().at(0).height();
|
||||||
|
}
|
||||||
|
|
||||||
|
plm->getStreamModel()->insertRows(row, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionEdit_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Edit Stream Action");
|
||||||
|
|
||||||
|
// Ensure we have only one range selected which contains only one row
|
||||||
|
if ((tvStreamList->selectionModel()->selection().size() == 1) &&
|
||||||
|
(tvStreamList->selectionModel()->selection().at(0).height() == 1))
|
||||||
|
{
|
||||||
|
on_tvStreamList_activated(tvStreamList->selectionModel()->
|
||||||
|
selection().at(0).topLeft());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionDuplicate_Stream_triggered()
|
||||||
|
{
|
||||||
|
QItemSelectionModel* model = tvStreamList->selectionModel();
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
qDebug("Duplicate Stream Action");
|
||||||
|
|
||||||
|
if (model->hasSelection())
|
||||||
|
{
|
||||||
|
bool isOk;
|
||||||
|
int count = QInputDialog::getInteger(this, "Duplicate Streams",
|
||||||
|
"Count", 1, 1, 9999, 1, &isOk);
|
||||||
|
|
||||||
|
if (!isOk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QList<int> list;
|
||||||
|
foreach(QModelIndex index, model->selectedRows())
|
||||||
|
list.append(index.row());
|
||||||
|
plm->port(current).duplicateStreams(list, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug("No selection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionDelete_Stream_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Delete Stream Action");
|
||||||
|
|
||||||
|
QModelIndex index;
|
||||||
|
|
||||||
|
if (tvStreamList->selectionModel()->hasSelection())
|
||||||
|
{
|
||||||
|
qDebug("SelectedIndexes %d",
|
||||||
|
tvStreamList->selectionModel()->selectedRows().size());
|
||||||
|
while(tvStreamList->selectionModel()->selectedRows().size())
|
||||||
|
{
|
||||||
|
index = tvStreamList->selectionModel()->selectedRows().at(0);
|
||||||
|
plm->getStreamModel()->removeRows(index.row(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug("No selection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionOpen_Streams_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Open Streams Action");
|
||||||
|
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
static QString dirName;
|
||||||
|
QString fileName;
|
||||||
|
QString errorStr;
|
||||||
|
bool append = true;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(current));
|
||||||
|
|
||||||
|
fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName);
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
if (tvStreamList->model()->rowCount())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(),
|
||||||
|
tr("Append to existing streams? Or overwrite?"),
|
||||||
|
QMessageBox::NoButton, this);
|
||||||
|
QPushButton *appendBtn = msgBox.addButton(tr("Append"),
|
||||||
|
QMessageBox::ActionRole);
|
||||||
|
QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"),
|
||||||
|
QMessageBox::ActionRole);
|
||||||
|
QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
if (msgBox.clickedButton() == cancelBtn)
|
||||||
|
goto _exit;
|
||||||
|
else if (msgBox.clickedButton() == appendBtn)
|
||||||
|
append = true;
|
||||||
|
else if (msgBox.clickedButton() == overwriteBtn)
|
||||||
|
append = false;
|
||||||
|
else
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = plm->port(current).openStreams(fileName, append, errorStr);
|
||||||
|
if (!ret || !errorStr.isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(this);
|
||||||
|
QStringList str = errorStr.split("\n\n\n\n");
|
||||||
|
|
||||||
|
msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical);
|
||||||
|
msgBox.setWindowTitle(qApp->applicationName());
|
||||||
|
msgBox.setText(str.at(0));
|
||||||
|
if (str.size() > 1)
|
||||||
|
msgBox.setDetailedText(str.at(1));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
dirName = QFileInfo(fileName).absolutePath();
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortsWindow::on_actionSave_Streams_triggered()
|
||||||
|
{
|
||||||
|
qDebug("Save Streams Action");
|
||||||
|
|
||||||
|
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||||
|
static QString fileName;
|
||||||
|
QStringList fileTypes = AbstractFileFormat::supportedFileTypes();
|
||||||
|
QString fileType;
|
||||||
|
QString errorStr;
|
||||||
|
QFileDialog::Options options;
|
||||||
|
|
||||||
|
// On Mac OS with Native Dialog, getSaveFileName() ignores fileType
|
||||||
|
// which we need.On some Linux distros the native dialog can't
|
||||||
|
// distinguish between Ostinato(*) and PCAP(*)
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_UNIX)
|
||||||
|
options |= QFileDialog::DontUseNativeDialog;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fileTypes.size())
|
||||||
|
fileType = fileTypes.at(0);
|
||||||
|
|
||||||
|
Q_ASSERT(plm->isPort(current));
|
||||||
|
|
||||||
|
_retry:
|
||||||
|
fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"),
|
||||||
|
fileName, fileTypes.join(";;"), &fileType, options);
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
goto _exit;
|
||||||
|
|
||||||
|
fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed();
|
||||||
|
if (!fileType.startsWith("Ostinato")
|
||||||
|
&& !fileType.startsWith("Python"))
|
||||||
|
{
|
||||||
|
if (QMessageBox::warning(this, tr("Ostinato"),
|
||||||
|
QString("You have chosen to save in %1 format. All stream "
|
||||||
|
"attributes may not be saved in this format.\n\n"
|
||||||
|
"It is recommended to save in native Ostinato format.\n\n"
|
||||||
|
"Continue to save in %2 format?").arg(fileType).arg(fileType),
|
||||||
|
QMessageBox::Yes|QMessageBox::No,
|
||||||
|
QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
goto _retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: all or selected?
|
||||||
|
|
||||||
|
if (!plm->port(current).saveStreams(fileName, fileType, errorStr))
|
||||||
|
QMessageBox::critical(this, qApp->applicationName(), errorStr);
|
||||||
|
else if (!errorStr.isEmpty())
|
||||||
|
QMessageBox::warning(this, qApp->applicationName(), errorStr);
|
||||||
|
|
||||||
|
fileName = QFileInfo(fileName).absolutePath();
|
||||||
|
_exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
87
client/portswindow.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PORTS_WINDOW_H
|
||||||
|
#define _PORTS_WINDOW_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include "ui_portswindow.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
HIGH
|
||||||
|
MED
|
||||||
|
LOW
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QAbstractItemDelegate;
|
||||||
|
|
||||||
|
class PortsWindow : public QWidget, private Ui::PortsWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
//QAbstractItemModel *slm; // stream list model
|
||||||
|
PortGroupList *plm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PortsWindow(PortGroupList *pgl, QWidget *parent = 0);
|
||||||
|
~PortsWindow();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString lastNewPortGroup;
|
||||||
|
QAbstractItemDelegate *delegate;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updatePortViewActions(const QModelIndex& current);
|
||||||
|
void updateStreamViewActions();
|
||||||
|
|
||||||
|
void on_averagePacketsPerSec_editingFinished();
|
||||||
|
void on_averageBitsPerSec_editingFinished();
|
||||||
|
void updatePortRates();
|
||||||
|
void on_tvStreamList_activated(const QModelIndex & index);
|
||||||
|
void when_portView_currentChanged(const QModelIndex& current,
|
||||||
|
const QModelIndex& previous);
|
||||||
|
void when_portModel_dataChanged(const QModelIndex& topLeft,
|
||||||
|
const QModelIndex& bottomRight);
|
||||||
|
void when_portModel_reset();
|
||||||
|
|
||||||
|
void on_pbApply_clicked();
|
||||||
|
|
||||||
|
void on_actionNew_Port_Group_triggered();
|
||||||
|
void on_actionDelete_Port_Group_triggered();
|
||||||
|
void on_actionConnect_Port_Group_triggered();
|
||||||
|
void on_actionDisconnect_Port_Group_triggered();
|
||||||
|
|
||||||
|
void on_actionExclusive_Control_triggered(bool checked);
|
||||||
|
void on_actionPort_Configuration_triggered();
|
||||||
|
|
||||||
|
void on_actionNew_Stream_triggered();
|
||||||
|
void on_actionEdit_Stream_triggered();
|
||||||
|
void on_actionDuplicate_Stream_triggered();
|
||||||
|
void on_actionDelete_Stream_triggered();
|
||||||
|
|
||||||
|
void on_actionOpen_Streams_triggered();
|
||||||
|
void on_actionSave_Streams_triggered();
|
||||||
|
|
||||||
|
void streamModelDataChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
307
client/portswindow.ui
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>PortsWindow</class>
|
||||||
|
<widget class="QWidget" name="PortsWindow" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>710</width>
|
||||||
|
<height>352</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" >
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<widget class="QSplitter" name="splitter" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="childrenCollapsible" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QTreeView" name="tvPortList" >
|
||||||
|
<property name="contextMenuPolicy" >
|
||||||
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode" >
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStackedWidget" name="swDetail" >
|
||||||
|
<property name="currentIndex" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="portDetail" >
|
||||||
|
<layout class="QGridLayout" >
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frAggregate" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape" >
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow" >
|
||||||
|
<enum>QFrame::Sunken</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" >
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<widget class="QRadioButton" name="radioButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Avg pps</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" >
|
||||||
|
<widget class="QLineEdit" name="averagePacketsPerSec" />
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" >
|
||||||
|
<widget class="QRadioButton" name="radioButton_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Avg bps</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" >
|
||||||
|
<widget class="QLineEdit" name="averageBitsPerSec" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2" >
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pbApply" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Apply</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" >
|
||||||
|
<widget class="QTableView" name="tvStreamList" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="contextMenuPolicy" >
|
||||||
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape" >
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth" >
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode" >
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior" >
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="portGroupDetail" >
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Select a port to configure streams</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="blankPage" />
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
<action name="actionNew_Port_Group" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portgroup_add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>New Port Group</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDelete_Port_Group" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portgroup_delete.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Delete Port Group</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionConnect_Port_Group" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portgroup_connect.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Connect Port Group</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDisconnect_Port_Group" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/portgroup_disconnect.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Disconnect Port Group</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionNew_Stream" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/stream_add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>New Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDelete_Stream" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/stream_delete.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Delete Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionEdit_Stream" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/stream_edit.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Edit Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionExclusive_Control" >
|
||||||
|
<property name="checkable" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Exclusive Port Control (EXPERIMENTAL)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionOpen_Streams" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Open Streams ...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSave_Streams" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Save Streams ...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionPort_Configuration" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Port Configuration ...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDuplicate_Stream" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/stream_duplicate.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Duplicate Stream</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>radioButton</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>averagePacketsPerSec</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>313</x>
|
||||||
|
<y>28</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>380</x>
|
||||||
|
<y>28</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>radioButton_2</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>averageBitsPerSec</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>333</x>
|
||||||
|
<y>55</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>395</x>
|
||||||
|
<y>56</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
137
client/preferences.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "preferences.h"
|
||||||
|
|
||||||
|
#include "../common/ostprotolib.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
QString kUserDefaultValue;
|
||||||
|
|
||||||
|
Preferences::Preferences()
|
||||||
|
{
|
||||||
|
Q_ASSERT(appSettings);
|
||||||
|
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey,
|
||||||
|
kWiresharkPathDefaultValue).toString());
|
||||||
|
tsharkPathEdit->setText(appSettings->value(kTsharkPathKey,
|
||||||
|
kTsharkPathDefaultValue).toString());
|
||||||
|
gzipPathEdit->setText(appSettings->value(kGzipPathKey,
|
||||||
|
kGzipPathDefaultValue).toString());
|
||||||
|
diffPathEdit->setText(appSettings->value(kDiffPathKey,
|
||||||
|
kDiffPathDefaultValue).toString());
|
||||||
|
awkPathEdit->setText(appSettings->value(kAwkPathKey,
|
||||||
|
kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
// TODO(only if required): kUserKey
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences::~Preferences()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::initDefaults()
|
||||||
|
{
|
||||||
|
Q_ASSERT(appSettings);
|
||||||
|
|
||||||
|
// Read username from the environment
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
kUserDefaultValue = QString(qgetenv("USERNAME").constData());
|
||||||
|
#else
|
||||||
|
kUserDefaultValue = QString(qgetenv("USER").constData());
|
||||||
|
#endif
|
||||||
|
qDebug("current user <%s>", qPrintable(kUserDefaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::accept()
|
||||||
|
{
|
||||||
|
appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text());
|
||||||
|
appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text());
|
||||||
|
appSettings->setValue(kGzipPathKey, gzipPathEdit->text());
|
||||||
|
appSettings->setValue(kDiffPathKey, diffPathEdit->text());
|
||||||
|
appSettings->setValue(kAwkPathKey, awkPathEdit->text());
|
||||||
|
|
||||||
|
OstProtoLib::setExternalApplicationPaths(
|
||||||
|
appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||||
|
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||||
|
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::on_wiresharkPathButton_clicked()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
path = QFileDialog::getOpenFileName(0, "Locate Wireshark",
|
||||||
|
wiresharkPathEdit->text());
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
wiresharkPathEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::on_tsharkPathButton_clicked()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
path = QFileDialog::getOpenFileName(0, "Locate tshark",
|
||||||
|
tsharkPathEdit->text());
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
tsharkPathEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::on_gzipPathButton_clicked()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
path = QFileDialog::getOpenFileName(0, "Locate gzip",
|
||||||
|
gzipPathEdit->text());
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
gzipPathEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::on_diffPathButton_clicked()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
path = QFileDialog::getOpenFileName(0, "Locate diff",
|
||||||
|
diffPathEdit->text());
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
diffPathEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::on_awkPathButton_clicked()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
path = QFileDialog::getOpenFileName(0, "Locate awk",
|
||||||
|
awkPathEdit->text());
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
awkPathEdit->setText(path);
|
||||||
|
}
|
47
client/preferences.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PREFERENCES_H
|
||||||
|
#define _PREFERENCES_H
|
||||||
|
|
||||||
|
#include "ui_preferences.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class Preferences : public QDialog, private Ui::Preferences
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Preferences();
|
||||||
|
~Preferences();
|
||||||
|
|
||||||
|
static void initDefaults();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void accept();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_wiresharkPathButton_clicked();
|
||||||
|
void on_tsharkPathButton_clicked();
|
||||||
|
void on_gzipPathButton_clicked();
|
||||||
|
void on_diffPathButton_clicked();
|
||||||
|
void on_awkPathButton_clicked();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
226
client/preferences.ui
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>Preferences</class>
|
||||||
|
<widget class="QDialog" name="Preferences" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>220</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Preferences</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon" >
|
||||||
|
<iconset resource="ostinato.qrc" >:/icons/preferences.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame" >
|
||||||
|
<property name="frameShape" >
|
||||||
|
<enum>QFrame::Box</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow" >
|
||||||
|
<enum>QFrame::Sunken</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" >
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<widget class="QLabel" name="label" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>'wireshark' Path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>wiresharkPathEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" >
|
||||||
|
<widget class="QLineEdit" name="wiresharkPathEdit" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2" >
|
||||||
|
<widget class="QToolButton" name="wiresharkPathButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" >
|
||||||
|
<widget class="QLabel" name="label_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>'tshark' Path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>tsharkPathEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" >
|
||||||
|
<widget class="QLineEdit" name="tsharkPathEdit" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2" >
|
||||||
|
<widget class="QToolButton" name="tsharkPathButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" >
|
||||||
|
<widget class="QLabel" name="label_5" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>'gzip' Path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>diffPathEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" >
|
||||||
|
<widget class="QLineEdit" name="gzipPathEdit" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2" >
|
||||||
|
<widget class="QToolButton" name="gzipPathButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" >
|
||||||
|
<widget class="QLabel" name="label_3" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>'diff' Path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>diffPathEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1" >
|
||||||
|
<widget class="QLineEdit" name="diffPathEdit" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2" >
|
||||||
|
<widget class="QToolButton" name="diffPathButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" >
|
||||||
|
<widget class="QLabel" name="label_4" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>'awk' Path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>awkPathEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1" >
|
||||||
|
<widget class="QLineEdit" name="awkPathEdit" >
|
||||||
|
<property name="enabled" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2" >
|
||||||
|
<widget class="QToolButton" name="awkPathButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1" >
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>21</width>
|
||||||
|
<height>61</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons" >
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>wiresharkPathEdit</tabstop>
|
||||||
|
<tabstop>wiresharkPathButton</tabstop>
|
||||||
|
<tabstop>tsharkPathEdit</tabstop>
|
||||||
|
<tabstop>tsharkPathButton</tabstop>
|
||||||
|
<tabstop>gzipPathEdit</tabstop>
|
||||||
|
<tabstop>gzipPathButton</tabstop>
|
||||||
|
<tabstop>diffPathEdit</tabstop>
|
||||||
|
<tabstop>diffPathButton</tabstop>
|
||||||
|
<tabstop>awkPathEdit</tabstop>
|
||||||
|
<tabstop>awkPathButton</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources>
|
||||||
|
<include location="ostinato.qrc" />
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Preferences</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Preferences</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
88
client/settings.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SETTINGS_H
|
||||||
|
#define _SETTINGS_H
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
extern QSettings *appSettings;
|
||||||
|
|
||||||
|
const QString kWiresharkPathKey("WiresharkPath");
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
const QString kWiresharkPathDefaultValue(
|
||||||
|
"C:/Program Files/Wireshark/wireshark.exe");
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
const QString kWiresharkPathDefaultValue(
|
||||||
|
"/Applications/Wireshark.app/Contents/Resources/bin/wireshark");
|
||||||
|
#else
|
||||||
|
const QString kWiresharkPathDefaultValue("/usr/bin/wireshark");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString kTsharkPathKey("TsharkPath");
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
const QString kTsharkPathDefaultValue(
|
||||||
|
"C:/Program Files/Wireshark/tshark.exe");
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
const QString kTsharkPathDefaultValue(
|
||||||
|
"/Applications/Wireshark.app/Contents/Resources/bin/tshark");
|
||||||
|
#else
|
||||||
|
const QString kTsharkPathDefaultValue("/usr/bin/tshark");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString kGzipPathKey("GzipPath");
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
extern QString kGzipPathDefaultValue;
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
const QString kGzipPathDefaultValue("/usr/bin/gzip");
|
||||||
|
#else
|
||||||
|
const QString kGzipPathDefaultValue("/usr/bin/gzip");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString kDiffPathKey("DiffPath");
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
extern QString kDiffPathDefaultValue;
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
const QString kDiffPathDefaultValue("/usr/bin/diff");
|
||||||
|
#else
|
||||||
|
const QString kDiffPathDefaultValue("/usr/bin/diff");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString kAwkPathKey("AwkPath");
|
||||||
|
#if defined(Q_OS_WIN32)
|
||||||
|
extern QString kAwkPathDefaultValue;
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
const QString kAwkPathDefaultValue("/usr/bin/awk");
|
||||||
|
#else
|
||||||
|
const QString kAwkPathDefaultValue("/usr/bin/awk");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QString kUserKey("User");
|
||||||
|
extern QString kUserDefaultValue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// LastUse Section Keys
|
||||||
|
//
|
||||||
|
const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry");
|
||||||
|
const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
51
client/stream.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \todo Remove this class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <qendian.h>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include "../common/protocollistiterator.h"
|
||||||
|
#include "../common/abstractprotocol.h"
|
||||||
|
|
||||||
|
Stream::Stream()
|
||||||
|
{
|
||||||
|
//mId = 0xFFFFFFFF;
|
||||||
|
setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream::~Stream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::loadProtocolWidgets()
|
||||||
|
{
|
||||||
|
qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::storeProtocolWidgets()
|
||||||
|
{
|
||||||
|
qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
42
client/stream.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STREAM_H
|
||||||
|
#define _STREAM_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include "../common/protocol.pb.h"
|
||||||
|
#include "../common/streambase.h"
|
||||||
|
|
||||||
|
class Stream : public StreamBase {
|
||||||
|
|
||||||
|
//quint32 mId;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Stream();
|
||||||
|
~Stream();
|
||||||
|
|
||||||
|
void loadProtocolWidgets();
|
||||||
|
void storeProtocolWidgets();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
1212
client/streamconfigdialog.cpp
Normal file
150
client/streamconfigdialog.h
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STREAM_CONFIG_DIALOG_H
|
||||||
|
#define _STREAM_CONFIG_DIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "ui_streamconfigdialog.h"
|
||||||
|
#include "port.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "packetmodel.h"
|
||||||
|
#include "modeltest.h"
|
||||||
|
|
||||||
|
#define MAX_MAC_ITER_COUNT 256
|
||||||
|
#define MIN_PKT_LEN 64
|
||||||
|
#define MAX_PKT_LEN 16384
|
||||||
|
|
||||||
|
/*
|
||||||
|
** TODO
|
||||||
|
** \todo Improve HexStr handling
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AbstractProtocolConfigForm;
|
||||||
|
|
||||||
|
class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0);
|
||||||
|
~StreamConfigDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum ButtonId
|
||||||
|
{
|
||||||
|
ButtonIdNone = 0,
|
||||||
|
ButtonIdOther = -2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ProtoButtonGroup
|
||||||
|
{
|
||||||
|
ProtoMin,
|
||||||
|
ProtoL1 = 0,
|
||||||
|
ProtoVlan = 1,
|
||||||
|
ProtoL2 = 2,
|
||||||
|
ProtoL3 = 3,
|
||||||
|
ProtoL4 = 4,
|
||||||
|
ProtoL5 = 5,
|
||||||
|
ProtoPayload = 6,
|
||||||
|
ProtoMax
|
||||||
|
};
|
||||||
|
|
||||||
|
QButtonGroup *bgProto[ProtoMax];
|
||||||
|
|
||||||
|
QStringListModel *mpAvailableProtocolsModel;
|
||||||
|
QStringListModel *mpSelectedProtocolsModel;
|
||||||
|
|
||||||
|
Port& mPort;
|
||||||
|
uint mCurrentStreamIndex;
|
||||||
|
|
||||||
|
Stream *mpStream;
|
||||||
|
ProtocolListIterator *_iter;
|
||||||
|
QHash<AbstractProtocol*, AbstractProtocolConfigForm*> _protocolWidgets;
|
||||||
|
|
||||||
|
bool isUpdateInProgress;
|
||||||
|
|
||||||
|
PacketModel *mpPacketModel;
|
||||||
|
ModelTest *mpPacketModelTester;
|
||||||
|
|
||||||
|
// The following static variables are used to track the "selected" tab
|
||||||
|
// for the various tab widgets so that it can be restored when the dialog
|
||||||
|
// is opened the next time. We also track the last Dialog geometry.
|
||||||
|
static QRect lastGeometry;
|
||||||
|
static int lastTopLevelTabIndex;
|
||||||
|
static int lastProtocolDataIndex;
|
||||||
|
|
||||||
|
void setupUiExtra();
|
||||||
|
void LoadCurrentStream();
|
||||||
|
void StoreCurrentStream();
|
||||||
|
void loadProtocolWidgets();
|
||||||
|
void storeProtocolWidgets();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_cmbPktLenMode_currentIndexChanged(QString mode);
|
||||||
|
void update_NumPacketsAndNumBursts();
|
||||||
|
|
||||||
|
void on_twTopLevel_currentChanged(int index);
|
||||||
|
void on_tbSelectProtocols_currentChanged(int index);
|
||||||
|
|
||||||
|
// "Simple" Protocol Selection related
|
||||||
|
bool skipProtocols(int layer);
|
||||||
|
|
||||||
|
void disableProtocols(QButtonGroup *protocolGroup, bool checked);
|
||||||
|
void forceProtocolNone(bool checked);
|
||||||
|
|
||||||
|
void updateProtocol(int newId);
|
||||||
|
void __updateProtocol(int level, int newId);
|
||||||
|
|
||||||
|
void updateSelectProtocolsSimpleWidget();
|
||||||
|
|
||||||
|
// "Advanced" Protocol Selection related
|
||||||
|
void when_lvAllProtocols_selectionChanged(
|
||||||
|
const QItemSelection &selected, const QItemSelection &deselected);
|
||||||
|
void when_lvSelectedProtocols_currentChanged(
|
||||||
|
const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
|
|
||||||
|
void on_tbAdd_clicked();
|
||||||
|
void on_tbDelete_clicked();
|
||||||
|
void on_tbUp_clicked();
|
||||||
|
void on_tbDown_clicked();
|
||||||
|
|
||||||
|
void updateSelectProtocolsAdvancedWidget();
|
||||||
|
|
||||||
|
// "Protocol Data" related
|
||||||
|
void on_tbProtocolData_currentChanged(int index);
|
||||||
|
|
||||||
|
// "Stream Control" related
|
||||||
|
void on_rbPacketsPerSec_toggled(bool checked);
|
||||||
|
void on_rbBurstsPerSec_toggled(bool checked);
|
||||||
|
|
||||||
|
void on_lePacketsPerBurst_textChanged(const QString &text);
|
||||||
|
void on_lePacketsPerSec_textChanged(const QString &text);
|
||||||
|
void on_leBurstsPerSec_textChanged(const QString &text);
|
||||||
|
void on_leBitsPerSec_textEdited(const QString &text);
|
||||||
|
|
||||||
|
void on_pbPrev_clicked();
|
||||||
|
void on_pbNext_clicked();
|
||||||
|
|
||||||
|
void on_pbOk_clicked();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
1481
client/streamconfigdialog.ui
Normal file
194
client/streamlistdelegate.cpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#include "streammodel.h"
|
||||||
|
#include "streamlistdelegate.h"
|
||||||
|
|
||||||
|
StreamListDelegate::StreamListDelegate(QObject *parent)
|
||||||
|
: QItemDelegate(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QWidget *StreamListDelegate::createEditor(QWidget *parent,
|
||||||
|
const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QWidget *editor = NULL;
|
||||||
|
|
||||||
|
switch ((StreamModel::StreamFields) index.column())
|
||||||
|
{
|
||||||
|
case StreamModel::StreamStatus:
|
||||||
|
{
|
||||||
|
editor = new QCheckBox(parent);
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
case StreamModel::StreamNextWhat:
|
||||||
|
{
|
||||||
|
editor = new QComboBox(parent);
|
||||||
|
static_cast<QComboBox*>(editor)->insertItems(0,
|
||||||
|
StreamModel::nextWhatOptionList());
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
case StreamModel::StreamIcon:
|
||||||
|
case StreamModel::StreamName:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor = QItemDelegate::createEditor(parent, option, index);
|
||||||
|
|
||||||
|
_handled:
|
||||||
|
return editor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StreamListDelegate::setEditorData(QWidget *editor,
|
||||||
|
const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
switch ((StreamModel::StreamFields) index.column())
|
||||||
|
{
|
||||||
|
case StreamModel::StreamStatus:
|
||||||
|
{
|
||||||
|
QCheckBox *cb = static_cast<QCheckBox*>(editor);
|
||||||
|
cb->setChecked(
|
||||||
|
index.model()->data(index, Qt::EditRole).toBool());
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
case StreamModel::StreamNextWhat:
|
||||||
|
{
|
||||||
|
QComboBox *cb = static_cast<QComboBox*>(editor);
|
||||||
|
cb->setCurrentIndex(
|
||||||
|
index.model()->data(index, Qt::EditRole).toInt());
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
case StreamModel::StreamIcon:
|
||||||
|
case StreamModel::StreamName:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QItemDelegate::setEditorData(editor, index);
|
||||||
|
|
||||||
|
_handled:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StreamListDelegate::setModelData(QWidget *editor,
|
||||||
|
QAbstractItemModel *model, const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
switch ((StreamModel::StreamFields) index.column())
|
||||||
|
{
|
||||||
|
case StreamModel::StreamStatus:
|
||||||
|
{
|
||||||
|
QCheckBox *cb = static_cast<QCheckBox*>(editor);
|
||||||
|
model->setData(index, cb->isChecked(), Qt::EditRole);
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
case StreamModel::StreamNextWhat:
|
||||||
|
{
|
||||||
|
QComboBox *cb = static_cast<QComboBox*>(editor);
|
||||||
|
model->setData(index, cb->currentIndex(), Qt::EditRole);
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
case StreamModel::StreamIcon:
|
||||||
|
case StreamModel::StreamName:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QItemDelegate::setModelData(editor, model, index);
|
||||||
|
|
||||||
|
_handled:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StreamListDelegate::updateEditorGeometry(QWidget *editor,
|
||||||
|
const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
switch ((StreamModel::StreamFields) index.column())
|
||||||
|
{
|
||||||
|
case StreamModel::StreamStatus:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* extra 'coz QItemDelegate does it - otherwise the editor
|
||||||
|
* placement is incorrect
|
||||||
|
*/
|
||||||
|
int extra = 2 * (qApp->style()->pixelMetric(
|
||||||
|
QStyle::PM_FocusFrameHMargin, 0) + 1);
|
||||||
|
|
||||||
|
editor->setGeometry(option.rect.translated(extra, 0));
|
||||||
|
goto _handled;
|
||||||
|
}
|
||||||
|
case StreamModel::StreamIcon:
|
||||||
|
case StreamModel::StreamName:
|
||||||
|
case StreamModel::StreamNextWhat:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QItemDelegate::updateEditorGeometry(editor, option, index);
|
||||||
|
|
||||||
|
_handled:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||||
|
const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Special Handling so that user can use the "Stream status" checkbox
|
||||||
|
* without double clicking first. Copied from QItemDelegate::editorEvent()
|
||||||
|
* and modified suitably
|
||||||
|
*/
|
||||||
|
if ((StreamModel::StreamFields)index.column() ==
|
||||||
|
StreamModel::StreamStatus)
|
||||||
|
{
|
||||||
|
// make sure that we have the right event type
|
||||||
|
if ((event->type() == QEvent::MouseButtonRelease)
|
||||||
|
|| (event->type() == QEvent::MouseButtonDblClick))
|
||||||
|
{
|
||||||
|
QRect checkRect = check(option, option.rect, Qt::Checked);
|
||||||
|
QRect emptyRect;
|
||||||
|
doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
|
||||||
|
if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Qt::CheckState state = (static_cast<Qt::CheckState>(index.data(
|
||||||
|
Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
|
||||||
|
return model->setData(index, state, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QItemDelegate::editorEvent(event, model, option, index);
|
||||||
|
}
|
||||||
|
|
48
client/streamlistdelegate.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STREAM_LIST_DELEGATE_H
|
||||||
|
#define STREAM_LIST_DELEGATE_H
|
||||||
|
|
||||||
|
#include <QItemDelegate>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
class StreamListDelegate : public QItemDelegate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
StreamListDelegate(QObject *parent = 0);
|
||||||
|
|
||||||
|
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const;
|
||||||
|
|
||||||
|
void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||||
|
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||||
|
const QModelIndex &index) const;
|
||||||
|
|
||||||
|
void updateEditorGeometry(QWidget *editor,
|
||||||
|
const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
|
|
||||||
|
bool editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||||
|
const QStyleOptionViewItem &option, const QModelIndex &index);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
288
client/streammodel.cpp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2010 Srivats P.
|
||||||
|
|
||||||
|
This file is part of "Ostinato"
|
||||||
|
|
||||||
|
This is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stream.h"
|
||||||
|
#include "streammodel.h"
|
||||||
|
#include "portgrouplist.h"
|
||||||
|
#include "qicon.h"
|
||||||
|
|
||||||
|
StreamModel::StreamModel(PortGroupList *p, QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
{
|
||||||
|
pgl = p;
|
||||||
|
mCurrentPort = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StreamModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (mCurrentPort)
|
||||||
|
return mCurrentPort->numStreams();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StreamModel::columnCount(const QModelIndex &/*parent*/) const
|
||||||
|
{
|
||||||
|
int count = StreamMaxFields;
|
||||||
|
if (mCurrentPort &&
|
||||||
|
(mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit))
|
||||||
|
count--;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
|
||||||
|
|
||||||
|
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case StreamIcon:
|
||||||
|
break;
|
||||||
|
case StreamName:
|
||||||
|
flags |= Qt::ItemIsEditable;
|
||||||
|
break;
|
||||||
|
case StreamStatus:
|
||||||
|
flags |= Qt::ItemIsUserCheckable;
|
||||||
|
break;
|
||||||
|
case StreamNextWhat:
|
||||||
|
flags |= Qt::ItemIsEditable;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//qFatal("Missed case in switch!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StreamModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
// Check for a valid index
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
// Check for row/column limits
|
||||||
|
if (index.row() >= mCurrentPort->numStreams())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (index.column() >= StreamMaxFields)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (mCurrentPort == NULL)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
// Return data based on field and role
|
||||||
|
switch(index.column())
|
||||||
|
{
|
||||||
|
case StreamIcon:
|
||||||
|
{
|
||||||
|
if (role == Qt::DecorationRole)
|
||||||
|
return QIcon(":/icons/stream_edit.png");
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StreamName:
|
||||||
|
{
|
||||||
|
if ((role == Qt::DisplayRole) || (role == Qt::EditRole))
|
||||||
|
return mCurrentPort->streamByIndex(index.row())->name();
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StreamStatus:
|
||||||
|
{
|
||||||
|
if ((role == Qt::CheckStateRole))
|
||||||
|
{
|
||||||
|
if (mCurrentPort->streamByIndex(index.row())->isEnabled())
|
||||||
|
return Qt::Checked;
|
||||||
|
else
|
||||||
|
return Qt::Unchecked;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StreamNextWhat:
|
||||||
|
{
|
||||||
|
int val = mCurrentPort->streamByIndex(index.row())->nextWhat();
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return nextWhatOptionList().at(val);
|
||||||
|
else if (role == Qt::EditRole)
|
||||||
|
return val;
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qFatal("-------------UNHANDLED STREAM FIELD----------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (mCurrentPort == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (index.isValid())
|
||||||
|
{
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
// Edit Supported Fields
|
||||||
|
case StreamName:
|
||||||
|
mCurrentPort->streamByIndex(index.row())->setName(value.toString());
|
||||||
|
emit(dataChanged(index, index));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case StreamStatus:
|
||||||
|
mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool());
|
||||||
|
emit(dataChanged(index, index));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case StreamNextWhat:
|
||||||
|
if (role == Qt::EditRole)
|
||||||
|
{
|
||||||
|
mCurrentPort->streamByIndex(index.row())->setNextWhat(
|
||||||
|
(Stream::NextWhat)value.toInt());
|
||||||
|
emit(dataChanged(index, index));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Edit Not Supported Fields
|
||||||
|
case StreamIcon:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Unhandled Stream Field
|
||||||
|
default:
|
||||||
|
qDebug("-------------UNHANDLED STREAM FIELD----------------");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
switch(section)
|
||||||
|
{
|
||||||
|
case StreamIcon:
|
||||||
|
return QString("");
|
||||||
|
break;
|
||||||
|
case StreamName:
|
||||||
|
return QString("Name");
|
||||||
|
break;
|
||||||
|
case StreamStatus:
|
||||||
|
return QString("");
|
||||||
|
break;
|
||||||
|
case StreamNextWhat:
|
||||||
|
return QString("Goto");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug("-------------UNHANDLED STREAM FIELD----------------");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QString("%1").arg(section+1);
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
|
||||||
|
{
|
||||||
|
qDebug("insertRows() row = %d", row);
|
||||||
|
qDebug("insertRows() count = %d", count);
|
||||||
|
beginInsertRows(QModelIndex(), row, row+count-1);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
mCurrentPort->newStreamAt(row);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/)
|
||||||
|
{
|
||||||
|
qDebug("removeRows() row = %d", row);
|
||||||
|
qDebug("removeRows() count = %d", count);
|
||||||
|
beginRemoveRows(QModelIndex(), row, row+count-1);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mCurrentPort->deleteStreamAt(row);
|
||||||
|
}
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- SLOTS ------------------------
|
||||||
|
|
||||||
|
void StreamModel::setCurrentPortIndex(const QModelIndex ¤t)
|
||||||
|
{
|
||||||
|
if (!current.isValid() || !pgl->isPort(current))
|
||||||
|
{
|
||||||
|
qDebug("current is either invalid or not a port");
|
||||||
|
mCurrentPort = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug("change to valid port");
|
||||||
|
// Disconnect any existing connection to avoid duplication
|
||||||
|
// Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible
|
||||||
|
// with earlier Qt versions
|
||||||
|
if (mCurrentPort)
|
||||||
|
{
|
||||||
|
disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
|
||||||
|
this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
|
||||||
|
}
|
||||||
|
quint16 pg = current.internalId() >> 16;
|
||||||
|
mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()];
|
||||||
|
connect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
|
||||||
|
this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId,
|
||||||
|
int portId)
|
||||||
|
{
|
||||||
|
qDebug("In %s", __FUNCTION__);
|
||||||
|
if (mCurrentPort)
|
||||||
|
{
|
||||||
|
if ((quint32(portGroupId) == mCurrentPort->portGroupId())
|
||||||
|
&& (quint32(portId) == mCurrentPort->id()))
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|