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();
|
||||
}
|
||||
}
|