Merge branch 'master' into ci-dev
This commit is contained in:
commit
4fbf4446e3
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,12 +11,14 @@ ostinato
|
||||
|
||||
# Qt generated files
|
||||
ui_*.h
|
||||
moc_*.h
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
|
||||
# QMake generated files
|
||||
Makefile*
|
||||
*\object_script.*
|
||||
.qmake.stash
|
||||
|
||||
# protobuf generated files
|
||||
*.pb.h
|
||||
|
18
.travis.yml
18
.travis.yml
@ -16,25 +16,27 @@ matrix:
|
||||
|
||||
before_install:
|
||||
- "if [ $TRAVIS_OS_NAME = 'osx' ]; then \
|
||||
brew update && \
|
||||
brew tap cartr/qt4 && \
|
||||
brew tap-pin cartr/qt4 && \
|
||||
brew install qt@4 && \
|
||||
brew install qt5 && \
|
||||
brew link qt5 --force && \
|
||||
brew install protobuf && \
|
||||
ls -lR /usr/local/include; \
|
||||
ls -lR /usr/local/include/google/protobuf; \
|
||||
which clang++; \
|
||||
clang++ -E -x c++ - -v < /dev/null; \
|
||||
export CPLUS_INCLUDE_PATH=/usr/local/include; \
|
||||
export LIBRARY_PATH=/usr/local/lib; \
|
||||
fi"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libqt4-dev
|
||||
- qt4-qmake
|
||||
- qtbase5-dev
|
||||
- qtscript5-dev
|
||||
- libpcap-dev
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
|
||||
script:
|
||||
- qmake
|
||||
- QT_SELECT=qt5 qmake
|
||||
- make
|
||||
|
||||
notifications:
|
||||
|
@ -2,11 +2,8 @@
|
||||
|
||||
[![Build Status](https://travis-ci.org/pstavirs/ostinato.svg?branch=master)](https://travis-ci.org/pstavirs/ostinato)
|
||||
|
||||
Ostinato is an open-source, cross-platform network packet crafter/traffic generator and analyzer with a friendly GUI and powerful python API. Craft and send packets of several streams with different protocols at different rates.
|
||||
This is the code repository for the Ostinato network packet crafter and traffic generator
|
||||
|
||||
Ostinato aims to be "Wireshark in Reverse" and become complementary to Wireshark.
|
||||
Visit https://ostinato.org for demo video and details
|
||||
|
||||
License: GPLv3+ (see [COPYING](https://raw.githubusercontent.com/pstavirs/ostinato/master/COPYING))
|
||||
|
||||
For more information visit http://ostinato.org.
|
||||
|
||||
|
@ -1,674 +0,0 @@
|
||||
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>.
|
@ -1,9 +0,0 @@
|
||||
===============
|
||||
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
|
@ -1,28 +0,0 @@
|
||||
# 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__)
|
@ -1,4 +0,0 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += pkg_info
|
||||
|
||||
include(../version.pri)
|
@ -1,69 +0,0 @@
|
||||
# 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
|
||||
import protocols.emulproto_pb2 as emul
|
||||
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.client_name = 'python-ostinato'
|
||||
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()
|
||||
|
@ -1,188 +0,0 @@
|
||||
#! /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)
|
@ -1,17 +0,0 @@
|
||||
# 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/>
|
||||
|
169
binding/rpc.py
169
binding/rpc.py
@ -1,169 +0,0 @@
|
||||
# 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__)
|
||||
if not request.IsInitialized():
|
||||
raise RpcError('missing required fields in request')
|
||||
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)
|
||||
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
#!/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},
|
||||
)
|
||||
|
@ -94,7 +94,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="CopyrightLabel" >
|
||||
<property name="text" >
|
||||
<string>Copyright (c) 2007-2016 Srivats P.</string>
|
||||
<string>Copyright (c) 2007-2018 Srivats P.</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignCenter</set>
|
||||
|
@ -138,14 +138,17 @@ QVariant ArpStatusModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
void ArpStatusModel::setDeviceIndex(Port *port, int deviceIndex)
|
||||
{
|
||||
beginResetModel();
|
||||
port_ = port;
|
||||
deviceIndex_ = deviceIndex;
|
||||
if (port_)
|
||||
neighbors_ = port_->deviceNeighbors(deviceIndex);
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void ArpStatusModel::updateArpStatus()
|
||||
{
|
||||
reset();
|
||||
// FIXME: why needed?
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ DeviceGroupDialog::DeviceGroupDialog(
|
||||
vlans->setItemDelegateForColumn(i, spd);
|
||||
}
|
||||
|
||||
vlans->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
|
||||
vlans->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
vlans->resizeRowsToContents();
|
||||
|
||||
// Set vlan tag count *after* adding all items, so connected slots
|
||||
|
@ -257,8 +257,9 @@ bool DeviceGroupModel::removeRows(
|
||||
|
||||
void DeviceGroupModel::setPort(Port *port)
|
||||
{
|
||||
beginResetModel();
|
||||
port_ = port;
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -243,10 +243,11 @@ QVariant DeviceModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
void DeviceModel::setPort(Port *port)
|
||||
{
|
||||
beginResetModel();
|
||||
port_ = port;
|
||||
if (port_)
|
||||
connect(port_, SIGNAL(deviceInfoChanged()), SLOT(updateDeviceList()));
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QAbstractItemModel* DeviceModel::detailModel(const QModelIndex &index)
|
||||
@ -268,7 +269,8 @@ QAbstractItemModel* DeviceModel::detailModel(const QModelIndex &index)
|
||||
|
||||
void DeviceModel::updateDeviceList()
|
||||
{
|
||||
reset();
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
// Style roles for drillable fields
|
||||
|
@ -170,13 +170,13 @@ void DevicesWidget::on_deviceInfo_toggled(bool checked)
|
||||
|
||||
void DevicesWidget::on_actionNewDeviceGroup_triggered()
|
||||
{
|
||||
// In case nothing is selected, insert 1 row at the top
|
||||
int row = 0, count = 1;
|
||||
QItemSelection selection = deviceGroupList->selectionModel()->selection();
|
||||
|
||||
if (!portGroups_)
|
||||
return;
|
||||
|
||||
// In case nothing is selected, insert 1 row at the end
|
||||
int row = portGroups_->getDeviceGroupModel()->rowCount(), count = 1;
|
||||
QItemSelection selection = deviceGroupList->selectionModel()->selection();
|
||||
|
||||
// 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 (selection.size() == 1) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DevicesWidget</class>
|
||||
<widget class="QWidget" name="DevicesWidget">
|
||||
@ -13,16 +14,7 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="leftMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@ -49,7 +41,7 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>131</width>
|
||||
<height>23</height>
|
||||
@ -69,17 +61,25 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/refresh.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/refresh.png</normaloff>:/icons/refresh.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="deviceGroupList" >
|
||||
<widget class="XTableView" name="deviceGroupList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This is the device group list for the selected port
|
||||
|
||||
A device group is a set of one or more devices/hosts which will be emulated by Ostinato
|
||||
|
||||
Right-click to create/edit a device group</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
@ -95,20 +95,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="deviceList" >
|
||||
<widget class="XTableView" name="deviceList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>No devices being emulated
|
||||
|
||||
To emulate a device, click on Configuration and create a device group</string>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="deviceDetail" >
|
||||
<widget class="XTableView" name="deviceDetail">
|
||||
<property name="whatsThis">
|
||||
<string>IP neighbor cache is empty</string>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
@ -117,7 +125,8 @@
|
||||
</layout>
|
||||
<action name="actionNewDeviceGroup">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_add.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/devicegroup_add.png</normaloff>:/icons/devicegroup_add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Device Group</string>
|
||||
@ -125,7 +134,8 @@
|
||||
</action>
|
||||
<action name="actionDeleteDeviceGroup">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_delete.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/devicegroup_delete.png</normaloff>:/icons/devicegroup_delete.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Device Group</string>
|
||||
@ -133,13 +143,21 @@
|
||||
</action>
|
||||
<action name="actionEditDeviceGroup">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/devicegroup_edit.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/devicegroup_edit.png</normaloff>:/icons/devicegroup_edit.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Device Group</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>XTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>xtableview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="ostinato.qrc"/>
|
||||
</resources>
|
||||
|
@ -19,6 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "dumpview.h"
|
||||
|
||||
#include <QScrollBar>
|
||||
#include <QStylePainter>
|
||||
|
||||
//! \todo Enable Scrollbars
|
||||
|
||||
DumpView::DumpView(QWidget *parent)
|
||||
|
@ -17,7 +17,7 @@ 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
|
||||
#include <QAbstractItemView>
|
||||
|
||||
|
||||
class DumpView: public QAbstractItemView
|
||||
@ -48,7 +48,7 @@ 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; }
|
||||
{if ((c >= 32) && (c <= 126)) return true; else return false; }
|
||||
|
||||
private:
|
||||
QRect mOffsetPaneTopRect;
|
||||
|
@ -64,10 +64,10 @@ void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/)
|
||||
bool isOk;
|
||||
ulong num;
|
||||
|
||||
qDebug("before = %s\n", text().toAscii().data());
|
||||
qDebug("before = %s\n", qPrintable(text()));
|
||||
num = text().remove(QChar(' ')).toULong(&isOk, 16);
|
||||
setText(uintToHexStr(num, 4));
|
||||
qDebug("after = %s\n", text().toAscii().data());
|
||||
qDebug("after = %s\n", qPrintable(text()));
|
||||
#undef uintToHexStr
|
||||
#endif
|
||||
}
|
||||
|
BIN
client/icons/add.png
Normal file
BIN
client/icons/add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 733 B |
BIN
client/icons/stream_stats.png
Normal file
BIN
client/icons/stream_stats.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 714 B |
38
client/jumpurl.h
Normal file
38
client/jumpurl.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright (C) 2017 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 _JUMP_URL_H
|
||||
#define _JUMP_URL_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
inline QString jumpUrl(
|
||||
QString keyword,
|
||||
QString source="app",
|
||||
QString medium="hint",
|
||||
QString name="help")
|
||||
{
|
||||
return QString("http://jump.ostinato.org/" + keyword + "?"
|
||||
+ "utm_source=" + source + "&"
|
||||
+ "utm_medium=" + medium + "&"
|
||||
+ "utm_campaign=" + name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "dbgthread.h"
|
||||
#endif
|
||||
|
||||
#include "jumpurl.h"
|
||||
#include "params.h"
|
||||
#include "portgrouplist.h"
|
||||
#include "portstatswindow.h"
|
||||
@ -41,8 +42,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QProgressDialog>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <ntstatus.h>
|
||||
#endif
|
||||
|
||||
extern const char* version;
|
||||
extern const char* revision;
|
||||
|
||||
@ -68,8 +77,13 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
qDebug("staring local server - %s", qPrintable(serverApp));
|
||||
localServer_ = new QProcess(this);
|
||||
connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||
connect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||
SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
localServer_->start(serverApp, QStringList());
|
||||
QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor()));
|
||||
}
|
||||
else
|
||||
localServer_ = NULL;
|
||||
@ -82,7 +96,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
portsDock->setObjectName("portsDock");
|
||||
portsDock->setFeatures(
|
||||
portsDock->features() & ~QDockWidget::DockWidgetClosable);
|
||||
statsDock = new QDockWidget(tr("Statistics"), this);
|
||||
statsDock = new QDockWidget(tr("Port Statistics"), this);
|
||||
statsDock->setObjectName("statsDock");
|
||||
statsDock->setFeatures(
|
||||
statsDock->features() & ~QDockWidget::DockWidgetClosable);
|
||||
@ -140,6 +154,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
stopLocalServerMonitor();
|
||||
if (localServer_) {
|
||||
#ifdef Q_OS_WIN32
|
||||
//! \todo - find a way to terminate cleanly
|
||||
@ -151,6 +166,13 @@ MainWindow::~MainWindow()
|
||||
|
||||
delete pgl;
|
||||
|
||||
// We don't want to save state for Stream Stats Docks - so delete them
|
||||
QList<QDockWidget*> streamStatsDocks
|
||||
= findChildren<QDockWidget*>("streamStatsDock");
|
||||
foreach(QDockWidget *dock, streamStatsDocks)
|
||||
delete dock;
|
||||
Q_ASSERT(findChildren<QDockWidget*>("streamStatsDock").size() == 0);
|
||||
|
||||
QByteArray layout = saveState(0);
|
||||
appSettings->setValue(kApplicationWindowLayout, layout);
|
||||
appSettings->setValue(kApplicationWindowGeometryKey, geometry());
|
||||
@ -294,12 +316,22 @@ void MainWindow::on_actionViewRestoreDefaults_triggered()
|
||||
setGeometry(defaultGeometry_);
|
||||
restoreState(defaultLayout_, 0);
|
||||
|
||||
// Add streamStats as tabs
|
||||
QList<QDockWidget*> streamStatsDocks
|
||||
= findChildren<QDockWidget*>("streamStatsDock");
|
||||
foreach(QDockWidget *dock, streamStatsDocks) {
|
||||
dock->setFloating(false);
|
||||
tabifyDockWidget(statsDock, dock);
|
||||
}
|
||||
statsDock->show();
|
||||
statsDock->raise();
|
||||
|
||||
actionViewShowMyReservedPortsOnly->setChecked(false);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionHelpOnline_triggered()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("http://ostinato.org/docs"));
|
||||
QDesktopServices::openUrl(QUrl(jumpUrl("help", "app", "menu")));
|
||||
}
|
||||
|
||||
void MainWindow::on_actionHelpAbout_triggered()
|
||||
@ -316,10 +348,76 @@ void MainWindow::on_actionHelpAbout_triggered()
|
||||
delete aboutDialog;
|
||||
}
|
||||
|
||||
void MainWindow::stopLocalServerMonitor()
|
||||
{
|
||||
// We are only interested in startup errors
|
||||
disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||
}
|
||||
|
||||
void MainWindow::onLocalServerFinished(int exitCode,
|
||||
QProcess::ExitStatus /*exitStatus*/)
|
||||
{
|
||||
if (exitCode)
|
||||
reportLocalServerError();
|
||||
}
|
||||
|
||||
void MainWindow::onLocalServerError(QProcess::ProcessError /*error*/)
|
||||
{
|
||||
reportLocalServerError();
|
||||
}
|
||||
|
||||
void MainWindow::reportLocalServerError()
|
||||
{
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5"); // mouse copy
|
||||
QString errorStr = tr("<p>Failed to start the local drone agent - "
|
||||
"error 0x%1, exit status 0x%2 exit code 0x%3.</p>")
|
||||
.arg(localServer_->error(), 0, 16)
|
||||
.arg(localServer_->exitStatus(), 0, 16)
|
||||
.arg(localServer_->exitCode(), 0, 16);
|
||||
if (localServer_->error() == QProcess::FailedToStart)
|
||||
errorStr.append(tr("<p>The drone program does not exist at %1 or you "
|
||||
"don't have sufficient permissions to execute it."
|
||||
"</p>")
|
||||
.arg(QCoreApplication::applicationDirPath()));
|
||||
if (localServer_->exitCode() == 1)
|
||||
errorStr.append(tr("<p>The drone program was not able to bind to "
|
||||
"TCP port 7878 - maybe a drone process is already "
|
||||
"running?</p>"));
|
||||
#ifdef Q_OS_WIN32
|
||||
if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND)
|
||||
errorStr.append(tr("<p>This is most likely because Packet.dll "
|
||||
"was not found - make sure you have "
|
||||
"<a href='%1'>WinPcap"
|
||||
"</a> installed.</p>")
|
||||
.arg(jumpUrl("winpcap")));
|
||||
#endif
|
||||
msgBox.setText(errorStr);
|
||||
msgBox.setInformativeText(tr("Try running drone directly."));
|
||||
msgBox.exec();
|
||||
|
||||
QMessageBox::information(this, QString(),
|
||||
tr("<p>If you have remote drone agents running, you can still add "
|
||||
"and connect to them.</p>"
|
||||
"<p>If you don't want to start the local drone agent at startup, "
|
||||
"provide the <b>-c</b> option to Ostinato on the command line.</p>"
|
||||
"<p>Learn about Ostinato's <a href='%1'>Controller-Agent "
|
||||
"architecture</a></p>").arg(jumpUrl("arch")));
|
||||
}
|
||||
|
||||
void MainWindow::onNewVersion(QString newVersion)
|
||||
{
|
||||
statusBar()->showMessage(QString("New Ostinato version %1 available. "
|
||||
"Visit http://ostinato.org to download").arg(newVersion));
|
||||
QLabel *msg = new QLabel(tr("New Ostinato version %1 available. Visit "
|
||||
"<a href='%2'>ostinato.org</a> to download")
|
||||
.arg(newVersion)
|
||||
.arg(jumpUrl("download", "app", "status", "update")));
|
||||
msg->setOpenExternalLinks(true);
|
||||
statusBar()->addPermanentWidget(msg);
|
||||
}
|
||||
|
||||
//! Returns true on success (or user cancel) and false on failure
|
||||
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
|
||||
class PortsWindow;
|
||||
class PortStatsWindow;
|
||||
@ -59,6 +60,10 @@ public slots:
|
||||
void on_actionHelpAbout_triggered();
|
||||
|
||||
private slots:
|
||||
void stopLocalServerMonitor();
|
||||
void onLocalServerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onLocalServerError(QProcess::ProcessError error);
|
||||
void reportLocalServerError();
|
||||
void onNewVersion(QString version);
|
||||
};
|
||||
|
||||
|
@ -1,542 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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)));
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
@ -1,4 +0,0 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
DEPENDPATH += $$PWD
|
||||
SOURCES += $$PWD/modeltest.cpp
|
||||
HEADERS += $$PWD/modeltest.h
|
@ -142,14 +142,17 @@ QVariant NdpStatusModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
void NdpStatusModel::setDeviceIndex(Port *port, int deviceIndex)
|
||||
{
|
||||
beginResetModel();
|
||||
port_ = port;
|
||||
deviceIndex_ = deviceIndex;
|
||||
if (port_)
|
||||
neighbors_ = port_->deviceNeighbors(deviceIndex);
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void NdpStatusModel::updateNdpStatus()
|
||||
{
|
||||
reset();
|
||||
// FIXME: why needed?
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ CONFIG += qt ver_info
|
||||
macx: TARGET = Ostinato
|
||||
win32:RC_FILE = ostinato.rc
|
||||
macx:ICON = icons/logo.icns
|
||||
QT += network script xml
|
||||
QT += widgets network script xml
|
||||
INCLUDEPATH += "../rpc/" "../common/"
|
||||
win32 {
|
||||
QMAKE_LFLAGS += -static
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L"../common/debug" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/debug" -lpbrpc
|
||||
@ -58,6 +59,9 @@ HEADERS += \
|
||||
streamconfigdialog.h \
|
||||
streamlistdelegate.h \
|
||||
streammodel.h \
|
||||
streamstatsfiltermodel.h \
|
||||
streamstatsmodel.h \
|
||||
streamstatswindow.h \
|
||||
variablefieldswidget.h
|
||||
|
||||
FORMS += \
|
||||
@ -71,6 +75,7 @@ FORMS += \
|
||||
portswindow.ui \
|
||||
preferences.ui \
|
||||
streamconfigdialog.ui \
|
||||
streamstatswindow.ui \
|
||||
variablefieldswidget.ui
|
||||
|
||||
SOURCES += \
|
||||
@ -100,6 +105,8 @@ SOURCES += \
|
||||
streamconfigdialog.cpp \
|
||||
streamlistdelegate.cpp \
|
||||
streammodel.cpp \
|
||||
streamstatsmodel.cpp \
|
||||
streamstatswindow.cpp \
|
||||
variablefieldswidget.cpp
|
||||
|
||||
|
||||
@ -107,6 +114,10 @@ QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../install.pri)
|
||||
include(../version.pri)
|
||||
include(../options.pri)
|
||||
|
||||
# TODO(LOW): Test only
|
||||
CONFIG(debug, debug|release):include(modeltest.pri)
|
||||
INCLUDEPATH += "../extra/modeltest"
|
||||
greaterThan(QT_MINOR_VERSION, 6) {
|
||||
CONFIG(debug, debug|release): LIBS += -L"../extra/modeltest/$(OBJECTS_DIR)/" -lmodeltest
|
||||
CONFIG(debug, debug|release): QT += testlib
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/" >
|
||||
<file>icons/about.png</file>
|
||||
<file>icons/add.png</file>
|
||||
<file>icons/arrow_down.png</file>
|
||||
<file>icons/arrow_left.png</file>
|
||||
<file>icons/arrow_right.png</file>
|
||||
@ -42,5 +43,6 @@
|
||||
<file>icons/stream_delete.png</file>
|
||||
<file>icons/stream_duplicate.png</file>
|
||||
<file>icons/stream_edit.png</file>
|
||||
<file>icons/stream_stats.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -38,8 +38,9 @@ void PacketModel::setSelectedProtocols(ProtocolListIterator &iter)
|
||||
|
||||
if (mSelectedProtocols != currentProtocols)
|
||||
{
|
||||
beginResetModel();
|
||||
mSelectedProtocols = currentProtocols;
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -31,10 +31,10 @@ int Params::parseCommandLine(int argc, char* argv[])
|
||||
int c, n = 0;
|
||||
|
||||
opterr = 0;
|
||||
while ((c = getopt (argc, argv, "s")) != -1) {
|
||||
while ((c = getopt (argc, argv, "c")) != -1) {
|
||||
switch (c)
|
||||
{
|
||||
case 's':
|
||||
case 'c':
|
||||
localDrone_ = false;
|
||||
break;
|
||||
default:
|
||||
|
@ -57,6 +57,7 @@ Port::Port(quint32 id, quint32 portGroupId)
|
||||
stats.mutable_port_id()->set_id(id);
|
||||
mPortGroupId = portGroupId;
|
||||
capFile_ = NULL;
|
||||
dirty_ = false;
|
||||
}
|
||||
|
||||
Port::~Port()
|
||||
@ -100,6 +101,15 @@ void Port::reorderStreamsByOrdinals()
|
||||
qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan);
|
||||
}
|
||||
|
||||
void Port::setDirty(bool dirty)
|
||||
{
|
||||
if (dirty == dirty_)
|
||||
return;
|
||||
|
||||
dirty_ = dirty;
|
||||
emit localConfigChanged(mPortGroupId, mPortId, dirty_);
|
||||
}
|
||||
|
||||
void Port::recalculateAverageRates()
|
||||
{
|
||||
double pps = 0;
|
||||
@ -209,6 +219,7 @@ void Port::setAveragePacketRate(double packetsPerSec)
|
||||
Q_ASSERT(false); // Unreachable!!
|
||||
}
|
||||
numActiveStreams_ = n;
|
||||
setDirty(true);
|
||||
}
|
||||
else
|
||||
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||
@ -282,6 +293,7 @@ void Port::setAverageBitRate(double bitsPerSec)
|
||||
Q_ASSERT(false); // Unreachable!!
|
||||
}
|
||||
numActiveStreams_ = n;
|
||||
setDirty(true);
|
||||
}
|
||||
else
|
||||
avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0;
|
||||
@ -305,6 +317,7 @@ bool Port::newStreamAt(int index, OstProto::Stream const *stream)
|
||||
mStreams.insert(index, s);
|
||||
updateStreamOrdinalsFromIndex();
|
||||
recalculateAverageRates();
|
||||
setDirty(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -317,6 +330,7 @@ bool Port::deleteStreamAt(int index)
|
||||
delete mStreams.takeAt(index);
|
||||
updateStreamOrdinalsFromIndex();
|
||||
recalculateAverageRates();
|
||||
setDirty(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -506,6 +520,8 @@ void Port::when_syncComplete()
|
||||
deviceGroups_.at(i)->device_group_id().id());
|
||||
}
|
||||
modifiedDeviceGroupList_.clear();
|
||||
|
||||
setDirty(false);
|
||||
}
|
||||
|
||||
void Port::updateStats(OstProto::PortStats *portStats)
|
||||
@ -543,6 +559,7 @@ void Port::duplicateStreams(const QList<int> &list, int count)
|
||||
insertAt++;
|
||||
}
|
||||
}
|
||||
setDirty(true);
|
||||
|
||||
emit streamListChanged(mPortGroupId, mPortId);
|
||||
}
|
||||
@ -625,6 +642,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
||||
if (i % 32 == 0)
|
||||
qApp->processEvents();
|
||||
}
|
||||
setDirty(true);
|
||||
|
||||
_user_cancel:
|
||||
emit streamListChanged(mPortGroupId, mPortId);
|
||||
@ -743,11 +761,12 @@ OstProto::DeviceGroup* Port::mutableDeviceGroupByIndex(int index)
|
||||
|
||||
// Caller can modify DeviceGroup - assume she will
|
||||
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
||||
setDirty(true);
|
||||
|
||||
return devGrp;
|
||||
}
|
||||
|
||||
OstProto::DeviceGroup* Port::deviceGroupById(uint deviceGroupId)
|
||||
const OstProto::DeviceGroup* Port::deviceGroupById(uint deviceGroupId) const
|
||||
{
|
||||
for (int i = 0; i < deviceGroups_.size(); i++) {
|
||||
OstProto::DeviceGroup *devGrp = deviceGroups_.at(i);
|
||||
@ -776,6 +795,7 @@ bool Port::newDeviceGroupAt(int index, const OstProto::DeviceGroup *deviceGroup)
|
||||
devGrp->mutable_device_group_id()->set_id(newDeviceGroupId());
|
||||
deviceGroups_.insert(index, devGrp);
|
||||
modifiedDeviceGroupList_.insert(devGrp->device_group_id().id());
|
||||
setDirty(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -788,6 +808,7 @@ bool Port::deleteDeviceGroupAt(int index)
|
||||
OstProto::DeviceGroup *devGrp = deviceGroups_.takeAt(index);
|
||||
modifiedDeviceGroupList_.remove(devGrp->device_group_id().id());
|
||||
delete devGrp;
|
||||
setDirty(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -818,7 +839,12 @@ bool Port::updateDeviceGroup(
|
||||
uint deviceGroupId,
|
||||
OstProto::DeviceGroup *deviceGroup)
|
||||
{
|
||||
OstProto::DeviceGroup *devGrp = deviceGroupById(deviceGroupId);
|
||||
using OstProto::DeviceGroup;
|
||||
|
||||
// XXX: We should not call mutableDeviceGroupById() because that will
|
||||
// implicitly set the port as dirty, so we use a const_cast hack instead
|
||||
DeviceGroup *devGrp = const_cast<DeviceGroup*>(
|
||||
deviceGroupById(deviceGroupId));
|
||||
|
||||
if (!devGrp) {
|
||||
qDebug("%s: deviceGroup id %u does not exist", __FUNCTION__,
|
||||
|
@ -50,6 +50,7 @@ class Port : public QObject {
|
||||
quint32 mPortId;
|
||||
quint32 mPortGroupId;
|
||||
QString mUserAlias; // user defined
|
||||
bool dirty_;
|
||||
|
||||
double avgPacketsPerSec_;
|
||||
double avgBitsPerSec_;
|
||||
@ -70,6 +71,7 @@ class Port : public QObject {
|
||||
void updateStreamOrdinalsFromIndex();
|
||||
void reorderStreamsByOrdinals();
|
||||
|
||||
void setDirty(bool dirty);
|
||||
|
||||
public:
|
||||
enum AdminStatus { AdminDisable, AdminEnable };
|
||||
@ -92,15 +94,17 @@ public:
|
||||
{ return QString().fromStdString(d.notes()); }
|
||||
const QString userName() const
|
||||
{ return QString().fromStdString(d.user_name()); }
|
||||
AdminStatus adminStatus()
|
||||
AdminStatus adminStatus() const
|
||||
{ return (d.is_enabled()?AdminEnable:AdminDisable); }
|
||||
bool hasExclusiveControl()
|
||||
bool hasExclusiveControl() const
|
||||
{ return d.is_exclusive_control(); }
|
||||
OstProto::TransmitMode transmitMode()
|
||||
OstProto::TransmitMode transmitMode() const
|
||||
{ return d.transmit_mode(); }
|
||||
double averagePacketRate()
|
||||
bool trackStreamStats() const
|
||||
{ return d.is_tracking_stream_stats(); }
|
||||
double averagePacketRate() const
|
||||
{ return avgPacketsPerSec_; }
|
||||
double averageBitRate()
|
||||
double averageBitRate() const
|
||||
{ return avgBitsPerSec_; }
|
||||
|
||||
//void setAdminEnable(AdminStatus status) { mAdminStatus = status; }
|
||||
@ -108,11 +112,22 @@ public:
|
||||
//void setExclusive(bool flag);
|
||||
|
||||
int numStreams() { return mStreams.size(); }
|
||||
Stream* streamByIndex(int index)
|
||||
const Stream* streamByIndex(int index) const
|
||||
{
|
||||
Q_ASSERT(index < mStreams.size());
|
||||
return mStreams[index];
|
||||
}
|
||||
Stream* mutableStreamByIndex(int index, bool assumeChange = true)
|
||||
{
|
||||
Q_ASSERT(index < mStreams.size());
|
||||
if (assumeChange)
|
||||
setDirty(true);
|
||||
return mStreams[index];
|
||||
}
|
||||
void setLocalConfigChanged(bool changed)
|
||||
{
|
||||
setDirty(changed);
|
||||
}
|
||||
OstProto::LinkState linkState()
|
||||
{ return stats.state().link_state(); }
|
||||
|
||||
@ -129,6 +144,7 @@ public:
|
||||
|
||||
void protoDataCopyInto(OstProto::Port *data);
|
||||
|
||||
//! Used when config received from server
|
||||
// FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal
|
||||
void updatePortConfig(OstProto::Port *port);
|
||||
|
||||
@ -144,6 +160,7 @@ public:
|
||||
bool updateStream(uint streamId, OstProto::Stream *stream);
|
||||
//@}
|
||||
|
||||
bool isDirty() { return dirty_; }
|
||||
void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||
void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList);
|
||||
void getModifiedStreamsSinceLastSync(
|
||||
@ -178,7 +195,7 @@ public:
|
||||
int numDeviceGroups() const;
|
||||
const OstProto::DeviceGroup* deviceGroupByIndex(int index) const;
|
||||
OstProto::DeviceGroup* mutableDeviceGroupByIndex(int index);
|
||||
OstProto::DeviceGroup* deviceGroupById(uint deviceGroupId);
|
||||
const OstProto::DeviceGroup* deviceGroupById(uint deviceGroupId) const;
|
||||
|
||||
//! Used by StreamModel
|
||||
//@{
|
||||
@ -216,11 +233,20 @@ public:
|
||||
void deviceInfoRefreshed();
|
||||
|
||||
signals:
|
||||
//! Used when local config changed and when config received from server
|
||||
void portRateChanged(int portGroupId, int portId);
|
||||
void portDataChanged(int portGroupId, int portId);
|
||||
void streamListChanged(int portGroupId, int portId);
|
||||
void deviceInfoChanged();
|
||||
|
||||
//! Used by MyService::Stub to update from config received from server
|
||||
//@{
|
||||
void portDataChanged(int portGroupId, int portId);
|
||||
void deviceInfoChanged();
|
||||
//@}
|
||||
|
||||
//! Used when local config changed
|
||||
//@{
|
||||
void streamListChanged(int portGroupId, int portId);
|
||||
void localConfigChanged(int portGroupId, int portId, bool changed);
|
||||
//@}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -20,7 +20,10 @@ 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)
|
||||
PortConfigDialog::PortConfigDialog(
|
||||
OstProto::Port &portConfig,
|
||||
const OstProto::PortState &portState,
|
||||
QWidget *parent)
|
||||
: QDialog(parent), portConfig_(portConfig)
|
||||
{
|
||||
QString currentUser(portConfig_.user_name().c_str());
|
||||
@ -64,6 +67,13 @@ PortConfigDialog::PortConfigDialog(OstProto::Port &portConfig, QWidget *parent)
|
||||
qDebug("reservedBy_ = %d", reservedBy_);
|
||||
|
||||
exclusiveControlButton->setChecked(portConfig_.is_exclusive_control());
|
||||
streamStatsButton->setChecked(portConfig_.is_tracking_stream_stats());
|
||||
|
||||
// Disable UI elements based on portState
|
||||
if (portState.is_transmit_on()) {
|
||||
transmitModeBox->setDisabled(true);
|
||||
streamStatsButton->setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void PortConfigDialog::accept()
|
||||
@ -95,6 +105,7 @@ void PortConfigDialog::accept()
|
||||
}
|
||||
|
||||
pc.set_is_exclusive_control(exclusiveControlButton->isChecked());
|
||||
pc.set_is_tracking_stream_stats(streamStatsButton->isChecked());
|
||||
|
||||
// Update fields that have changed, clear the rest
|
||||
if (pc.transmit_mode() != portConfig_.transmit_mode())
|
||||
@ -112,5 +123,10 @@ void PortConfigDialog::accept()
|
||||
else
|
||||
portConfig_.clear_is_exclusive_control();
|
||||
|
||||
if (pc.is_tracking_stream_stats() != portConfig_.is_tracking_stream_stats())
|
||||
portConfig_.set_is_tracking_stream_stats(pc.is_tracking_stream_stats());
|
||||
else
|
||||
portConfig_.clear_is_tracking_stream_stats();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
class PortConfigDialog : public QDialog, public Ui::PortConfigDialog
|
||||
{
|
||||
public:
|
||||
PortConfigDialog(OstProto::Port &portConfig, QWidget *parent);
|
||||
PortConfigDialog(OstProto::Port &portConfig,
|
||||
const OstProto::PortState& portState,
|
||||
QWidget *parent);
|
||||
|
||||
private:
|
||||
virtual void accept();
|
||||
|
@ -6,7 +6,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>244</width>
|
||||
<height>233</height>
|
||||
<height>257</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -69,6 +69,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="streamStatsButton" >
|
||||
<property name="text" >
|
||||
<string>Stream Statistics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "portgroup.h"
|
||||
|
||||
#include "jumpurl.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "emulproto.pb.h"
|
||||
@ -200,6 +201,19 @@ void PortGroup::processVersionCompatibility(PbRpcController *controller)
|
||||
qPrintable(QString::fromStdString(verCompat->notes())));
|
||||
compat = kIncompatible;
|
||||
emit portGroupDataChanged(mPortGroupId);
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5");
|
||||
msgBox.setText(tr("The Drone agent at %1:%2 is incompatible with this "
|
||||
"Ostinato version - %3")
|
||||
.arg(serverName())
|
||||
.arg(int(serverPort()))
|
||||
.arg(version));
|
||||
msgBox.setInformativeText(QString::fromStdString(verCompat->notes()));
|
||||
msgBox.exec();
|
||||
|
||||
goto _error_exit;
|
||||
}
|
||||
|
||||
@ -302,23 +316,22 @@ void PortGroup::on_rpcChannel_notification(int notifType,
|
||||
|
||||
void PortGroup::when_portListChanged(quint32 /*portGroupId*/)
|
||||
{
|
||||
QString faq("http://ostinato.org/docs/faq#q-port-group-has-no-interfaces");
|
||||
if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0)
|
||||
{
|
||||
if (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 help see the Ostinato FAQ (%3)")
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setStyleSheet("messagebox-text-interaction-flags: 5");
|
||||
QString msg = tr("<p>The portgroup %1:%2 does not contain any ports!<p>"
|
||||
"<p>Packet Transmit/Capture requires special privileges. "
|
||||
"Please ensure that you are running 'drone' - the agent "
|
||||
"component of Ostinato with required privileges.<p>")
|
||||
.arg(serverName())
|
||||
.arg(int(serverPort()))
|
||||
.arg(faq.remove(QRegExp("#.*$"))),
|
||||
QMessageBox::Ok | QMessageBox::Help,
|
||||
QMessageBox::Ok) == QMessageBox::Help)
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(faq));
|
||||
}
|
||||
.arg(int(serverPort()));
|
||||
msgBox.setText(msg);
|
||||
msgBox.setInformativeText(tr("See the <a href='%1'>Ostinato FAQ</a> "
|
||||
"for instructions to fix this problem").arg(jumpUrl("noports")));
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,6 +360,8 @@ void PortGroup::processPortIdList(PbRpcController *controller)
|
||||
p = new Port(portIdList->port_id(i).id(), mPortGroupId);
|
||||
connect(p, SIGNAL(portDataChanged(int, int)),
|
||||
this, SIGNAL(portGroupDataChanged(int, int)));
|
||||
connect(p, SIGNAL(localConfigChanged(int, int, bool)),
|
||||
this, SIGNAL(portGroupDataChanged(int, int)));
|
||||
qDebug("before port append\n");
|
||||
mPorts.append(p);
|
||||
atConnectPortConfig_.append(NULL); // will be filled later
|
||||
@ -1397,7 +1412,7 @@ void PortGroup::viewCapture(QList<uint> *portList)
|
||||
portId->set_id(portList->at(i));
|
||||
|
||||
capFile->open(QIODevice::ReadWrite|QIODevice::Truncate);
|
||||
qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData());
|
||||
qDebug("Temp CapFile = %s", qPrintable(capFile->fileName()));
|
||||
controller->setBinaryBlob(capFile);
|
||||
|
||||
serviceStub->getCaptureBuffer(controller, portId, buf,
|
||||
@ -1578,13 +1593,14 @@ void PortGroup::clearPortStats(QList<uint> *portList)
|
||||
}
|
||||
|
||||
serviceStub->clearStats(controller, portIdList, ack,
|
||||
NewCallback(this, &PortGroup::processClearStatsAck, controller));
|
||||
NewCallback(this, &PortGroup::processClearPortStatsAck,
|
||||
controller));
|
||||
}
|
||||
_exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void PortGroup::processClearStatsAck(PbRpcController *controller)
|
||||
void PortGroup::processClearPortStatsAck(PbRpcController *controller)
|
||||
{
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
|
||||
@ -1594,3 +1610,78 @@ void PortGroup::processClearStatsAck(PbRpcController *controller)
|
||||
delete controller;
|
||||
}
|
||||
|
||||
bool PortGroup::clearStreamStats(QList<uint> *portList)
|
||||
{
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
|
||||
if (state() != QAbstractSocket::ConnectedState)
|
||||
return false;
|
||||
|
||||
OstProto::StreamGuidList *guidList = new OstProto::StreamGuidList;
|
||||
OstProto::Ack *ack = new OstProto::Ack;
|
||||
PbRpcController *controller = new PbRpcController(guidList, ack);
|
||||
|
||||
if (portList == NULL)
|
||||
guidList->mutable_port_id_list()->CopyFrom(*portIdList_);
|
||||
else
|
||||
for (int i = 0; i < portList->size(); i++)
|
||||
guidList->mutable_port_id_list()->add_port_id()
|
||||
->set_id(portList->at(i));
|
||||
|
||||
serviceStub->clearStreamStats(controller, guidList, ack,
|
||||
NewCallback(this, &PortGroup::processClearStreamStatsAck,
|
||||
controller));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PortGroup::processClearStreamStatsAck(PbRpcController *controller)
|
||||
{
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
bool PortGroup::getStreamStats(QList<uint> *portList)
|
||||
{
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
|
||||
if (state() != QAbstractSocket::ConnectedState)
|
||||
return false;
|
||||
|
||||
OstProto::StreamGuidList *guidList = new OstProto::StreamGuidList;
|
||||
OstProto::StreamStatsList *statsList = new OstProto::StreamStatsList;
|
||||
PbRpcController *controller = new PbRpcController(guidList, statsList);
|
||||
|
||||
if (portList == NULL)
|
||||
guidList->mutable_port_id_list()->CopyFrom(*portIdList_);
|
||||
else
|
||||
for (int i = 0; i < portList->size(); i++)
|
||||
guidList->mutable_port_id_list()->add_port_id()
|
||||
->set_id(portList->at(i));
|
||||
|
||||
serviceStub->getStreamStats(controller, guidList, statsList,
|
||||
NewCallback(this, &PortGroup::processStreamStatsList, controller));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PortGroup::processStreamStatsList(PbRpcController *controller)
|
||||
{
|
||||
using OstProto::StreamStatsList;
|
||||
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
|
||||
StreamStatsList *streamStatsList =
|
||||
static_cast<StreamStatsList*>(controller->response());
|
||||
|
||||
// XXX: It is required to emit the signal even if the returned
|
||||
// streamStatsList contains no records since the recipient
|
||||
// StreamStatsModel slot needs to disconnect this signal-slot
|
||||
// connection to prevent future stream stats for this portgroup
|
||||
// to be sent to it
|
||||
emit streamStatsReceived(mPortGroupId, streamStatsList);
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ LOW
|
||||
namespace OstProto {
|
||||
class PortContent;
|
||||
class PortGroupContent;
|
||||
class StreamStatsList;
|
||||
}
|
||||
|
||||
class QFile;
|
||||
@ -163,13 +164,19 @@ public:
|
||||
void getPortStats();
|
||||
void processPortStatsList();
|
||||
void clearPortStats(QList<uint> *portList = NULL);
|
||||
void processClearStatsAck(PbRpcController *controller);
|
||||
void processClearPortStatsAck(PbRpcController *controller);
|
||||
bool clearStreamStats(QList<uint> *portList = NULL);
|
||||
void processClearStreamStatsAck(PbRpcController *controller);
|
||||
bool getStreamStats(QList<uint> *portList = NULL);
|
||||
void processStreamStatsList(PbRpcController *controller);
|
||||
|
||||
signals:
|
||||
void portGroupDataChanged(int portGroupId, int portId = 0xFFFF);
|
||||
void portListAboutToBeChanged(quint32 portGroupId);
|
||||
void portListChanged(quint32 portGroupId);
|
||||
void statsChanged(quint32 portGroupId);
|
||||
void streamStatsReceived(quint32 portGroupId,
|
||||
const OstProto::StreamStatsList *stats);
|
||||
|
||||
private slots:
|
||||
void on_reconnectTimer_timeout();
|
||||
|
@ -21,8 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "params.h"
|
||||
|
||||
// TODO(LOW): Remove
|
||||
#include <modeltest.h>
|
||||
#include "modeltest.h"
|
||||
|
||||
PortGroupList::PortGroupList()
|
||||
: mPortGroupListModel(this),
|
||||
@ -31,7 +30,7 @@ PortGroupList::PortGroupList()
|
||||
mDeviceGroupModel(this),
|
||||
mDeviceModel(this)
|
||||
{
|
||||
#ifdef QT_NO_DEBUG
|
||||
#if defined(QT_NO_DEBUG) || QT_VERSION < 0x050700
|
||||
streamModelTester_ = NULL;
|
||||
portModelTester_ = NULL;
|
||||
portStatsModelTester_ = NULL;
|
||||
|
@ -187,6 +187,10 @@ QVariant PortModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
return portIconFactory[port->linkState()][port->hasExclusiveControl()];
|
||||
}
|
||||
else if ((role == Qt::ForegroundRole))
|
||||
{
|
||||
return port->isDirty() ? QBrush(Qt::red) : QVariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG0("Exit PortModel data 6\n");
|
||||
@ -339,5 +343,7 @@ void PortModel::portGroupRemoved()
|
||||
|
||||
void PortModel::when_portListChanged()
|
||||
{
|
||||
reset();
|
||||
// FIXME: why needed?
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
@ -22,6 +22,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
enum {
|
||||
// XXX: The byte stats don't include FCS so include it in the overhead
|
||||
kPerPacketByteOverhead = 24 // 1(SFD)+7(Preamble)+12(IPG)+4(FCS)
|
||||
};
|
||||
|
||||
PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
@ -134,28 +139,38 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
// Statistics
|
||||
case e_STAT_FRAMES_RCVD:
|
||||
return quint64(stats.rx_pkts());
|
||||
return QString("%L1").arg(quint64(stats.rx_pkts()));
|
||||
|
||||
case e_STAT_FRAMES_SENT:
|
||||
return quint64(stats.tx_pkts());
|
||||
return QString("%L1").arg(quint64(stats.tx_pkts()));
|
||||
|
||||
case e_STAT_FRAME_SEND_RATE:
|
||||
return quint64(stats.tx_pps());
|
||||
return QString("%L1").arg(quint64(stats.tx_pps()));
|
||||
|
||||
case e_STAT_FRAME_RECV_RATE:
|
||||
return quint64(stats.rx_pps());
|
||||
return QString("%L1").arg(quint64(stats.rx_pps()));
|
||||
|
||||
case e_STAT_BYTES_RCVD:
|
||||
return quint64(stats.rx_bytes());
|
||||
return QString("%L1").arg(quint64(stats.rx_bytes()));
|
||||
|
||||
case e_STAT_BYTES_SENT:
|
||||
return quint64(stats.tx_bytes());
|
||||
return QString("%L1").arg(quint64(stats.tx_bytes()));
|
||||
|
||||
case e_STAT_BYTE_SEND_RATE:
|
||||
return quint64(stats.tx_bps());
|
||||
return QString("%L1").arg(quint64(stats.tx_bps()));
|
||||
|
||||
case e_STAT_BYTE_RECV_RATE:
|
||||
return quint64(stats.rx_bps());
|
||||
return QString("%L1").arg(quint64(stats.rx_bps()));
|
||||
|
||||
case e_STAT_BIT_SEND_RATE:
|
||||
return QString("%L1").arg(quint64(
|
||||
stats.tx_bps()
|
||||
+ stats.tx_pps()*kPerPacketByteOverhead)*8);
|
||||
|
||||
case e_STAT_BIT_RECV_RATE:
|
||||
return QString("%L1").arg(quint64(
|
||||
stats.rx_bps()
|
||||
+ stats.rx_pps()*kPerPacketByteOverhead)*8);
|
||||
|
||||
#if 0
|
||||
case e_STAT_FRAMES_RCVD_NIC:
|
||||
@ -170,10 +185,15 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||
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());
|
||||
|
||||
case e_STAT_RX_DROPS:
|
||||
return QString("%L1").arg(quint64(stats.rx_drops()));
|
||||
case e_STAT_RX_ERRORS:
|
||||
return QString("%L1").arg(quint64(stats.rx_errors()));
|
||||
case e_STAT_RX_FIFO_ERRORS:
|
||||
return QString("%L1").arg(quint64(stats.rx_fifo_errors()));
|
||||
case e_STAT_RX_FRAME_ERRORS:
|
||||
return QString("%L1").arg(quint64(stats.rx_frame_errors()));
|
||||
|
||||
default:
|
||||
qWarning("%s: Unhandled stats id %d\n", __FUNCTION__,
|
||||
@ -288,6 +308,8 @@ void PortStatsModel::when_portListChanged()
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
beginResetModel();
|
||||
|
||||
// recalc numPorts
|
||||
while (numPorts.size())
|
||||
numPorts.removeFirst();
|
||||
@ -298,7 +320,7 @@ void PortStatsModel::when_portListChanged()
|
||||
numPorts.append(count);
|
||||
}
|
||||
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
// FIXME: unused? if used, the index calculation row/column needs to be swapped
|
||||
|
@ -53,6 +53,8 @@ typedef enum {
|
||||
e_STAT_BYTES_SENT,
|
||||
e_STAT_BYTE_SEND_RATE,
|
||||
e_STAT_BYTE_RECV_RATE,
|
||||
e_STAT_BIT_SEND_RATE,
|
||||
e_STAT_BIT_RECV_RATE,
|
||||
#if 0
|
||||
e_STAT_FRAMES_RCVD_NIC,
|
||||
e_STAT_FRAMES_SENT_NIC,
|
||||
@ -66,6 +68,7 @@ typedef enum {
|
||||
e_STAT_RX_FIFO_ERRORS,
|
||||
e_STAT_RX_FRAME_ERRORS,
|
||||
|
||||
|
||||
e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS,
|
||||
|
||||
e_STAT_MAX
|
||||
@ -86,12 +89,15 @@ static QStringList PortStatName = (QStringList()
|
||||
<< "Bytes Sent"
|
||||
<< "Byte Send Rate (Bps)"
|
||||
<< "Byte Receive Rate (Bps)"
|
||||
<< "Bit Send Rate (bps)"
|
||||
<< "Bit 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"
|
||||
|
@ -23,9 +23,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "portstatsfilterdialog.h"
|
||||
#include "portstatsmodel.h"
|
||||
#include "portstatsproxymodel.h"
|
||||
#include "streamstatsmodel.h"
|
||||
#include "streamstatswindow.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QMainWindow>
|
||||
|
||||
extern QMainWindow *mainWindow;
|
||||
|
||||
PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
: QWidget(parent), proxyStatsModel(NULL)
|
||||
@ -43,10 +49,17 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
else
|
||||
tvPortStats->setModel(model);
|
||||
|
||||
tvPortStats->setAlternatingRowColors(true);
|
||||
tvPortStats->verticalHeader()->setHighlightSections(false);
|
||||
tvPortStats->verticalHeader()->setDefaultSectionSize(
|
||||
tvPortStats->verticalHeader()->minimumSectionSize());
|
||||
|
||||
connect(tvPortStats->selectionModel(),
|
||||
SIGNAL(selectionChanged(
|
||||
const QItemSelection&, const QItemSelection&)),
|
||||
SLOT(when_tvPortStats_selectionChanged(
|
||||
const QItemSelection&, const QItemSelection&)));
|
||||
when_tvPortStats_selectionChanged(QItemSelection(), QItemSelection());
|
||||
}
|
||||
|
||||
PortStatsWindow::~PortStatsWindow()
|
||||
@ -70,12 +83,44 @@ void PortStatsWindow::showMyReservedPortsOnly(bool enabled)
|
||||
}
|
||||
|
||||
/* ------------- SLOTS (private) -------------- */
|
||||
|
||||
void PortStatsWindow::when_tvPortStats_selectionChanged(
|
||||
const QItemSelection& /*selected*/,
|
||||
const QItemSelection& /*deselected*/)
|
||||
{
|
||||
QModelIndexList indexList =
|
||||
tvPortStats->selectionModel()->selectedColumns();
|
||||
|
||||
if (proxyStatsModel) {
|
||||
selectedColumns.clear();
|
||||
foreach(QModelIndex index, indexList)
|
||||
selectedColumns.append(proxyStatsModel->mapToSource(index));
|
||||
}
|
||||
else
|
||||
selectedColumns = indexList;
|
||||
|
||||
bool isEmpty = selectedColumns.isEmpty();
|
||||
|
||||
tbStartTransmit->setDisabled(isEmpty);
|
||||
tbStopTransmit->setDisabled(isEmpty);
|
||||
|
||||
tbStartCapture->setDisabled(isEmpty);
|
||||
tbStopCapture->setDisabled(isEmpty);
|
||||
tbViewCapture->setDisabled(isEmpty);
|
||||
|
||||
tbClear->setDisabled(isEmpty);
|
||||
tbGetStreamStats->setDisabled(isEmpty);
|
||||
|
||||
tbResolveNeighbors->setDisabled(isEmpty);
|
||||
tbClearNeighbors->setDisabled(isEmpty);
|
||||
}
|
||||
|
||||
void PortStatsWindow::on_tbStartTransmit_clicked()
|
||||
{
|
||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), pgpl);
|
||||
model->portListFromIndex(selectedColumns, pgpl);
|
||||
|
||||
// Clear selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < pgpl.size(); i++)
|
||||
@ -90,7 +135,7 @@ void PortStatsWindow::on_tbStopTransmit_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), pgpl);
|
||||
model->portListFromIndex(selectedColumns, pgpl);
|
||||
|
||||
// Clear selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < pgpl.size(); i++)
|
||||
@ -106,7 +151,7 @@ void PortStatsWindow::on_tbStartCapture_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), pgpl);
|
||||
model->portListFromIndex(selectedColumns, pgpl);
|
||||
|
||||
// Clear selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < pgpl.size(); i++)
|
||||
@ -122,7 +167,7 @@ void PortStatsWindow::on_tbStopCapture_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), pgpl);
|
||||
model->portListFromIndex(selectedColumns, pgpl);
|
||||
|
||||
// Clear selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < pgpl.size(); i++)
|
||||
@ -138,7 +183,7 @@ void PortStatsWindow::on_tbViewCapture_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> pgpl;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), pgpl);
|
||||
model->portListFromIndex(selectedColumns, pgpl);
|
||||
|
||||
// Clear selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < pgpl.size(); i++)
|
||||
@ -153,7 +198,7 @@ void PortStatsWindow::on_tbResolveNeighbors_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), portList);
|
||||
model->portListFromIndex(selectedColumns, portList);
|
||||
|
||||
// Resolve ARP/ND for selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < portList.size(); i++)
|
||||
@ -168,7 +213,7 @@ void PortStatsWindow::on_tbClearNeighbors_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), portList);
|
||||
model->portListFromIndex(selectedColumns, portList);
|
||||
|
||||
// Clear ARP/ND for ports, portgroup by portgroup
|
||||
for (int i = 0; i < portList.size(); i++)
|
||||
@ -183,13 +228,15 @@ void PortStatsWindow::on_tbClear_clicked()
|
||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns(), portList);
|
||||
model->portListFromIndex(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);
|
||||
pgl->portGroupByIndex(portList.at(i).portGroupId).
|
||||
clearStreamStats(&portList[i].portList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +263,44 @@ void PortStatsWindow::on_tbClearAll_clicked()
|
||||
{
|
||||
pgl->portGroupByIndex(portList.at(i).portGroupId)
|
||||
.clearPortStats(&portList[i].portList);
|
||||
pgl->portGroupByIndex(portList.at(i).portGroupId)
|
||||
.clearStreamStats(&portList[i].portList);
|
||||
}
|
||||
}
|
||||
|
||||
void PortStatsWindow::on_tbGetStreamStats_clicked()
|
||||
{
|
||||
QList<PortStatsModel::PortGroupAndPortList> portList;
|
||||
StreamStatsModel *streamStatsModel;
|
||||
|
||||
// Get selected ports
|
||||
model->portListFromIndex(selectedColumns, portList);
|
||||
|
||||
if (portList.size()) {
|
||||
QDockWidget *dock = new QDockWidget(mainWindow);
|
||||
streamStatsModel = new StreamStatsModel(dock);
|
||||
dock->setWidget(new StreamStatsWindow(streamStatsModel, dock));
|
||||
dock->setWindowTitle(dock->widget()->windowTitle());
|
||||
dock->setObjectName("streamStatsDock");
|
||||
dock->setAttribute(Qt::WA_DeleteOnClose);
|
||||
QDockWidget *statsDock = mainWindow->findChild<QDockWidget*>(
|
||||
"statsDock");
|
||||
mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock);
|
||||
mainWindow->tabifyDockWidget(statsDock, dock);
|
||||
dock->show();
|
||||
dock->raise();
|
||||
}
|
||||
|
||||
// Get stream stats for selected ports, portgroup by portgroup
|
||||
for (int i = 0; i < portList.size(); i++)
|
||||
{
|
||||
PortGroup &pg = pgl->portGroupByIndex(portList.at(i).portGroupId);
|
||||
if (pg.getStreamStats(&portList[i].portList)) {
|
||||
connect(&pg,SIGNAL(streamStatsReceived(
|
||||
quint32, const OstProto::StreamStatsList*)),
|
||||
streamStatsModel, SLOT(appendStreamStatsList(
|
||||
quint32, const OstProto::StreamStatsList*)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,20 +337,3 @@ void PortStatsWindow::on_tbFilter_clicked()
|
||||
hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------ Private Methods -------------- */
|
||||
|
||||
QModelIndexList PortStatsWindow::selectedColumns()
|
||||
{
|
||||
QModelIndexList indexList =
|
||||
tvPortStats->selectionModel()->selectedColumns();
|
||||
QModelIndexList sourceIndexList;
|
||||
|
||||
if (!proxyStatsModel)
|
||||
return indexList;
|
||||
|
||||
foreach(QModelIndex index, indexList)
|
||||
sourceIndexList.append(proxyStatsModel->mapToSource(index));
|
||||
|
||||
return sourceIndexList;
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ public slots:
|
||||
void showMyReservedPortsOnly(bool enabled);
|
||||
|
||||
private slots:
|
||||
void when_tvPortStats_selectionChanged(const QItemSelection &selected,
|
||||
const QItemSelection &deselected);
|
||||
|
||||
void on_tbStartTransmit_clicked();
|
||||
void on_tbStopTransmit_clicked();
|
||||
|
||||
@ -49,6 +52,7 @@ private slots:
|
||||
|
||||
void on_tbClear_clicked();
|
||||
void on_tbClearAll_clicked();
|
||||
void on_tbGetStreamStats_clicked();
|
||||
|
||||
void on_tbResolveNeighbors_clicked();
|
||||
void on_tbClearNeighbors_clicked();
|
||||
@ -56,11 +60,10 @@ private slots:
|
||||
void on_tbFilter_clicked();
|
||||
|
||||
private:
|
||||
QModelIndexList selectedColumns();
|
||||
|
||||
PortGroupList *pgl;
|
||||
PortStatsModel *model;
|
||||
QSortFilterProxyModel *proxyStatsModel;
|
||||
QModelIndexList selectedColumns;
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PortStatsWindow</class>
|
||||
<widget class="QWidget" name="PortStatsWindow">
|
||||
@ -22,6 +23,13 @@
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Transmit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="tbStartTransmit">
|
||||
<property name="toolTip">
|
||||
@ -31,10 +39,11 @@
|
||||
<string>Starts transmit on selected port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Transmit</string>
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/control_play.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/control_play.png</normaloff>:/icons/control_play.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -47,10 +56,41 @@
|
||||
<string>Stops transmit on selected port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop Trasmit</string>
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/control_stop.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/control_stop.png</normaloff>:/icons/control_stop.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Stats</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="tbGetStreamStats" >
|
||||
<property name="toolTip" >
|
||||
<string>Fetch Selected Port Stream Stats</string>
|
||||
</property>
|
||||
<property name="statusTip" >
|
||||
<string>Fetches stream statistics from the selected port(s)</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Fetch Stream Stats</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_stats.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -66,7 +106,8 @@
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portstats_clear.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portstats_clear.png</normaloff>:/icons/portstats_clear.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -82,7 +123,22 @@
|
||||
<string>Clear All</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portstats_clear_all.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portstats_clear_all.png</normaloff>:/icons/portstats_clear_all.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Capture</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -95,10 +151,11 @@
|
||||
<string>Captures packets on the selected port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Capture</string>
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/sound_none.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/sound_none.png</normaloff>:/icons/sound_none.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -111,10 +168,11 @@
|
||||
<string>End capture on selecteed port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop Capture</string>
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/sound_mute.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/sound_mute.png</normaloff>:/icons/sound_mute.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -127,10 +185,11 @@
|
||||
<string>View captured packets on selected port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Capture</string>
|
||||
<string>View</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/magnifier.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/magnifier.png</normaloff>:/icons/magnifier.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -141,6 +200,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>ARP/ND</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="tbResolveNeighbors">
|
||||
<property name="toolTip">
|
||||
@ -153,7 +219,8 @@
|
||||
<string>Resolve Neighbors</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/neighbor_resolve.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/neighbor_resolve.png</normaloff>:/icons/neighbor_resolve.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -169,7 +236,8 @@
|
||||
<string>Clear Neighbors</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/neighbor_clear.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/neighbor_clear.png</normaloff>:/icons/neighbor_clear.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -185,7 +253,7 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
@ -202,7 +270,8 @@
|
||||
<string>Filter</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portstats_filter.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portstats_filter.png</normaloff>:/icons/portstats_filter.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -210,7 +279,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="tvPortStats" />
|
||||
<widget class="QTableView" name="tvPortStats">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectColumns</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -302,26 +302,26 @@ void PortsWindow::showMyReservedPortsOnly(bool enabled)
|
||||
|
||||
void PortsWindow::on_tvStreamList_activated(const QModelIndex & index)
|
||||
{
|
||||
QModelIndex currentPort = tvPortList->currentIndex();
|
||||
StreamConfigDialog *scd;
|
||||
int ret;
|
||||
|
||||
if (proxyPortModel)
|
||||
currentPort = proxyPortModel->mapToSource(currentPort);
|
||||
|
||||
if (!index.isValid())
|
||||
{
|
||||
qDebug("%s: invalid index", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
scd = new StreamConfigDialog(plm->port(currentPort), index.row(), this);
|
||||
|
||||
qDebug("stream list activated\n");
|
||||
ret = scd->exec();
|
||||
|
||||
if (ret == QDialog::Accepted)
|
||||
plm->port(currentPort).recalculateAverageRates();
|
||||
Port &curPort = plm->port(proxyPortModel ?
|
||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||
tvPortList->currentIndex());
|
||||
|
||||
delete scd;
|
||||
QList<Stream*> streams;
|
||||
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||
|
||||
StreamConfigDialog scd(streams, curPort, this);
|
||||
if (scd.exec() == QDialog::Accepted) {
|
||||
curPort.recalculateAverageRates();
|
||||
curPort.setLocalConfigChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||
@ -345,12 +345,16 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||
{
|
||||
disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)),
|
||||
this, SLOT(updatePortRates()));
|
||||
disconnect(&(plm->port(previous)),
|
||||
SIGNAL(localConfigChanged(int, int, bool)),
|
||||
this,
|
||||
SLOT(updateApplyHint(int, int, bool)));
|
||||
}
|
||||
|
||||
if (!current.isValid())
|
||||
{
|
||||
qDebug("setting stacked widget to blank page");
|
||||
swDetail->setCurrentIndex(2); // blank page
|
||||
qDebug("setting stacked widget to welcome page");
|
||||
swDetail->setCurrentIndex(0); // welcome page
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -360,10 +364,21 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||
}
|
||||
else if (plm->isPort(current))
|
||||
{
|
||||
swDetail->setCurrentIndex(0); // port detail page
|
||||
swDetail->setCurrentIndex(2); // port detail page
|
||||
updatePortRates();
|
||||
connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)),
|
||||
SLOT(updatePortRates()));
|
||||
connect(&(plm->port(current)),
|
||||
SIGNAL(localConfigChanged(int, int, bool)),
|
||||
SLOT(updateApplyHint(int, int, bool)));
|
||||
if (plm->port(current).isDirty())
|
||||
updateApplyHint(plm->port(current).portGroupId(),
|
||||
plm->port(current).id(), true);
|
||||
else if (plm->port(current).numStreams())
|
||||
applyHint->setText("Use the Statistics window to transmit "
|
||||
"packets");
|
||||
else
|
||||
applyHint->setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +388,22 @@ void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex,
|
||||
void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight)
|
||||
{
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
qDebug("In %s %d:(%d, %d) - %d:(%d, %d)", __FUNCTION__,
|
||||
topLeft.parent().isValid(), topLeft.row(), topLeft.column(),
|
||||
bottomRight.parent().isValid(), bottomRight.row(), bottomRight.column());
|
||||
|
||||
if (!topLeft.isValid() || !bottomRight.isValid())
|
||||
return;
|
||||
|
||||
if (topLeft.parent() != bottomRight.parent())
|
||||
return;
|
||||
|
||||
// If a port has changed, expand the port group
|
||||
if (topLeft.parent().isValid())
|
||||
tvPortList->expand(proxyPortModel ?
|
||||
proxyPortModel->mapFromSource(topLeft.parent()) :
|
||||
topLeft.parent());
|
||||
|
||||
#if 0 // not sure why the >= <= operators are not overloaded in QModelIndex
|
||||
if ((tvPortList->currentIndex() >= topLeft) &&
|
||||
(tvPortList->currentIndex() <= bottomRight))
|
||||
@ -469,11 +499,6 @@ void PortsWindow::updateStreamViewActions()
|
||||
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);
|
||||
}
|
||||
|
||||
@ -496,6 +521,18 @@ void PortsWindow::updateStreamViewActions()
|
||||
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||
}
|
||||
|
||||
void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/,
|
||||
bool configChanged)
|
||||
{
|
||||
if (configChanged)
|
||||
applyHint->setText("Configuration has changed - "
|
||||
"<font color='red'><b>click Apply</b></font> "
|
||||
"to activate the changes");
|
||||
else
|
||||
applyHint->setText("Configuration activated. Use the Statistics "
|
||||
"window to transmit packets");
|
||||
}
|
||||
|
||||
void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex)
|
||||
{
|
||||
QModelIndex current = currentIndex;
|
||||
@ -516,7 +553,7 @@ void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex)
|
||||
goto _EXIT;
|
||||
}
|
||||
|
||||
qDebug("currentChanged %llx", current.internalId());
|
||||
qDebug("currentChanged %p", (void*)current.internalId());
|
||||
|
||||
if (plm->isPortGroup(current))
|
||||
{
|
||||
@ -718,12 +755,19 @@ void PortsWindow::on_actionPort_Configuration_triggered()
|
||||
if (!plm->isPort(current))
|
||||
return;
|
||||
|
||||
Port &port = plm->port(current);
|
||||
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());
|
||||
// XXX: we don't call Port::protoDataCopyInto() to get config b'coz
|
||||
// we want only the modifiable fields populated to send to Drone
|
||||
// TODO: extend Port::protoDataCopyInto() to accept an optional param
|
||||
// which says copy only modifiable fields
|
||||
//plm->port(current).protoDataCopyInto(&config);
|
||||
config.set_transmit_mode(port.transmitMode());
|
||||
config.set_is_tracking_stream_stats(port.trackStreamStats());
|
||||
config.set_is_exclusive_control(port.hasExclusiveControl());
|
||||
config.set_user_name(port.userName().toStdString());
|
||||
|
||||
PortConfigDialog dialog(config, this);
|
||||
PortConfigDialog dialog(config, port.getStats().state(), this);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
plm->portGroup(current.parent()).modifyPort(current.row(), config);
|
||||
@ -733,30 +777,59 @@ 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;
|
||||
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||
if (selectionModel->selection().size() > 1) {
|
||||
qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__,
|
||||
selectionModel->selection().size());
|
||||
return;
|
||||
}
|
||||
|
||||
// In case nothing is selected, insert 1 row at the end
|
||||
StreamModel *streamModel = plm->getStreamModel();
|
||||
int row = streamModel->rowCount(), 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)
|
||||
if (selectionModel->selection().size() == 1)
|
||||
{
|
||||
row = tvStreamList->selectionModel()->selection().at(0).top();
|
||||
count = tvStreamList->selectionModel()->selection().at(0).height();
|
||||
row = selectionModel->selection().at(0).top();
|
||||
count = selectionModel->selection().at(0).height();
|
||||
}
|
||||
|
||||
plm->getStreamModel()->insertRows(row, count);
|
||||
Port &curPort = plm->port(proxyPortModel ?
|
||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||
tvPortList->currentIndex());
|
||||
|
||||
QList<Stream*> streams;
|
||||
for (int i = 0; i < count; i++)
|
||||
streams.append(new Stream);
|
||||
|
||||
StreamConfigDialog scd(streams, curPort, this);
|
||||
scd.setWindowTitle(tr("Add Stream"));
|
||||
if (scd.exec() == QDialog::Accepted)
|
||||
streamModel->insert(row, streams);
|
||||
}
|
||||
|
||||
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());
|
||||
QItemSelectionModel* streamModel = tvStreamList->selectionModel();
|
||||
if (!streamModel->hasSelection())
|
||||
return;
|
||||
|
||||
Port &curPort = plm->port(proxyPortModel ?
|
||||
proxyPortModel->mapToSource(tvPortList->currentIndex()) :
|
||||
tvPortList->currentIndex());
|
||||
|
||||
QList<Stream*> streams;
|
||||
foreach(QModelIndex index, streamModel->selectedRows())
|
||||
streams.append(curPort.mutableStreamByIndex(index.row(), false));
|
||||
|
||||
StreamConfigDialog scd(streams, curPort, this);
|
||||
if (scd.exec() == QDialog::Accepted) {
|
||||
curPort.recalculateAverageRates();
|
||||
curPort.setLocalConfigChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,7 +846,7 @@ void PortsWindow::on_actionDuplicate_Stream_triggered()
|
||||
if (model->hasSelection())
|
||||
{
|
||||
bool isOk;
|
||||
int count = QInputDialog::getInteger(this, "Duplicate Streams",
|
||||
int count = QInputDialog::getInt(this, "Duplicate Streams",
|
||||
"Count", 1, 1, 9999, 1, &isOk);
|
||||
|
||||
if (!isOk)
|
||||
|
@ -66,6 +66,7 @@ public slots:
|
||||
void showMyReservedPortsOnly(bool enabled);
|
||||
|
||||
private slots:
|
||||
void updateApplyHint(int portGroupId, int portId, bool configChanged);
|
||||
void updatePortViewActions(const QModelIndex& currentIndex);
|
||||
void updateStreamViewActions();
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PortsWindow</class>
|
||||
<widget class="QWidget" name="PortsWindow">
|
||||
@ -21,9 +22,9 @@
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QTreeView" name="tvPortList" >
|
||||
<widget class="XTreeView" name="tvPortList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -37,26 +38,95 @@
|
||||
</widget>
|
||||
<widget class="QStackedWidget" name="swDetail">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="blankPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><p><b>Welcome to Ostinato</b></p>
|
||||
<p>The port list on the left contains all the ports on which you can transmit packets.</p>
|
||||
<p>Ports belong to a port group. Make sure the Port Group has a <img src=":/icons/bullet_green.png"/> next to it, then double click the port group to show or hide the ports in the port group.</p>
|
||||
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
|
||||
<p>To create a stream, select the port on which you want to send packets.</p>
|
||||
<hr/>
|
||||
<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="portGroupDetail">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><p>You have selected a port group in the port list on the left.</p>
|
||||
<p>You can transmit packets on any of the ports within the port group.</p>
|
||||
<p>Make sure the port group has a <img src=":/icons/bullet_green.png"/> next to it and then double click the port group to show or hide the ports in the port group.</p>
|
||||
<p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p>
|
||||
<p>To create a stream, select the port on which you want to send packets. </p>
|
||||
<hr/>
|
||||
<p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>177</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="portDetail">
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="leftMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@ -68,16 +138,7 @@
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="leftMargin" >
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
@ -85,7 +146,27 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="applyHint">
|
||||
<property name="text">
|
||||
<string>Apply Hint</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
@ -147,7 +228,7 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
@ -158,9 +239,9 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="tvStreamList" >
|
||||
<widget class="XTableView" name="tvStreamList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
@ -168,6 +249,13 @@
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This is the stream list for the selected port
|
||||
|
||||
A stream is a sequence of one or more packets
|
||||
|
||||
Right-click to create a stream</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
@ -190,7 +278,7 @@
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="DevicesWidget" native="1" name="devicesWidget" />
|
||||
<widget class="DevicesWidget" name="devicesWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -198,28 +286,14 @@
|
||||
</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>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portgroup_add.png</normaloff>:/icons/portgroup_add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Port Group</string>
|
||||
@ -227,7 +301,8 @@
|
||||
</action>
|
||||
<action name="actionDelete_Port_Group">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_delete.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portgroup_delete.png</normaloff>:/icons/portgroup_delete.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Port Group</string>
|
||||
@ -235,7 +310,8 @@
|
||||
</action>
|
||||
<action name="actionConnect_Port_Group">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_connect.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portgroup_connect.png</normaloff>:/icons/portgroup_connect.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connect Port Group</string>
|
||||
@ -243,7 +319,8 @@
|
||||
</action>
|
||||
<action name="actionDisconnect_Port_Group">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/portgroup_disconnect.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/portgroup_disconnect.png</normaloff>:/icons/portgroup_disconnect.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disconnect Port Group</string>
|
||||
@ -251,7 +328,8 @@
|
||||
</action>
|
||||
<action name="actionNew_Stream">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_add.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/stream_add.png</normaloff>:/icons/stream_add.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Stream</string>
|
||||
@ -259,7 +337,8 @@
|
||||
</action>
|
||||
<action name="actionDelete_Stream">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_delete.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/stream_delete.png</normaloff>:/icons/stream_delete.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Stream</string>
|
||||
@ -267,7 +346,8 @@
|
||||
</action>
|
||||
<action name="actionEdit_Stream">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_edit.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Stream</string>
|
||||
@ -298,7 +378,8 @@
|
||||
</action>
|
||||
<action name="actionDuplicate_Stream">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_duplicate.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/stream_duplicate.png</normaloff>:/icons/stream_duplicate.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Duplicate Stream</string>
|
||||
@ -312,6 +393,16 @@
|
||||
<header>deviceswidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>XTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>xtreeview.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>XTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>xtableview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="ostinato.qrc"/>
|
||||
|
@ -30,6 +30,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "../common/protocolmanager.h"
|
||||
#include "../common/protocolwidgetfactory.h"
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QMessageBox>
|
||||
|
||||
extern ProtocolManager *OstProtocolManager;
|
||||
extern ProtocolWidgetFactory *OstProtocolWidgetFactory;
|
||||
|
||||
@ -39,20 +42,36 @@ int StreamConfigDialog::lastProtocolDataIndex = 0;
|
||||
|
||||
static const uint kEthFrameOverHead = 20;
|
||||
|
||||
StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
||||
QWidget *parent) : QDialog (parent), mPort(port)
|
||||
StreamConfigDialog::StreamConfigDialog(
|
||||
QList<Stream*> &streamList,
|
||||
const Port &port,
|
||||
QWidget *parent)
|
||||
: QDialog (parent), _userStreamList(streamList), mPort(port)
|
||||
{
|
||||
mCurrentStreamIndex = 0;
|
||||
|
||||
Q_ASSERT(_userStreamList.size() > 0);
|
||||
|
||||
// Create a copy of the user provided stream list
|
||||
// We need a copy because user may edit multiple streams and then
|
||||
// discard the edit - in this case the user provided stream list
|
||||
// should not be modified on return
|
||||
foreach(Stream* stm, _userStreamList) {
|
||||
OstProto::Stream s;
|
||||
mCurrentStreamIndex = streamIndex;
|
||||
mpStream = new Stream;
|
||||
mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s);
|
||||
mpStream->protoDataCopyFrom(s);
|
||||
stm->protoDataCopyInto(s);
|
||||
_streamList.append(new Stream());
|
||||
_streamList.last()->protoDataCopyFrom(s);
|
||||
}
|
||||
|
||||
mpStream = _streamList.at(mCurrentStreamIndex);
|
||||
_iter = mpStream->createProtocolListIterator();
|
||||
isUpdateInProgress = false;
|
||||
|
||||
setupUi(this);
|
||||
setupUiExtra();
|
||||
|
||||
_windowTitle = windowTitle();
|
||||
|
||||
for (int i = ProtoMin; i < ProtoMax; i++)
|
||||
{
|
||||
bgProto[i]->setProperty("ProtocolLevel", i);
|
||||
@ -69,13 +88,14 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
||||
|
||||
// Time to play match the signals and slots!
|
||||
|
||||
// If L1/L2(FT)/L3 = None, force subsequent protocol level(s) also to None
|
||||
// If L1/L2(FT)/L3/L4 = None,
|
||||
// force subsequent protocol level(s) also to None
|
||||
connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool)));
|
||||
connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool)));
|
||||
connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool)));
|
||||
connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool)));
|
||||
|
||||
// If L1/L2(FT)/L3/L4 = Other, force subsequent protocol to Other and
|
||||
// If L1/L2(FT)/L3/L4/L5 = Other, force subsequent protocol to Other and
|
||||
// disable the subsequent protocol group as well
|
||||
connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool)));
|
||||
connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool)));
|
||||
@ -83,8 +103,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
||||
connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool)));
|
||||
connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool)));
|
||||
connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool)));
|
||||
connect(rbL4Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool)));
|
||||
connect(rbL4Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool)));
|
||||
connect(rbL4Other, SIGNAL(toggled(bool)), rbL5Other, SLOT(setChecked(bool)));
|
||||
connect(rbL4Other, SIGNAL(toggled(bool)), gbL5Proto, SLOT(setDisabled(bool)));
|
||||
connect(rbL5Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool)));
|
||||
connect(rbL5Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool)));
|
||||
|
||||
// Setup valid subsequent protocols for L2 to L4 protocols
|
||||
for (int i = ProtoL2; i <= ProtoL4; i++)
|
||||
@ -151,12 +173,10 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
||||
this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&,
|
||||
const QModelIndex&)));
|
||||
|
||||
variableFieldsWidget->setStream(mpStream);
|
||||
|
||||
LoadCurrentStream();
|
||||
mpPacketModel = new PacketModel(this);
|
||||
tvPacketTree->setModel(mpPacketModel);
|
||||
#ifdef QT_NO_DEBUG
|
||||
#if defined(QT_NO_DEBUG) || QT_VERSION < 0x050700
|
||||
mpPacketModelTester = NULL;
|
||||
#else
|
||||
mpPacketModelTester = new ModelTest(mpPacketModel);
|
||||
@ -165,10 +185,9 @@ StreamConfigDialog::StreamConfigDialog(Port &port, uint streamIndex,
|
||||
vwPacketDump->setModel(mpPacketModel);
|
||||
vwPacketDump->setSelectionModel(tvPacketTree->selectionModel());
|
||||
|
||||
// TODO(MED):
|
||||
//! \todo Enable navigation of streams
|
||||
pbPrev->setHidden(true);
|
||||
pbNext->setHidden(true);
|
||||
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||
|
||||
//! \todo Support Goto Stream Id
|
||||
leStreamId->setHidden(true);
|
||||
disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool)));
|
||||
@ -281,6 +300,17 @@ void StreamConfigDialog::setupUiExtra()
|
||||
bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber);
|
||||
bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther);
|
||||
#endif
|
||||
|
||||
// Special
|
||||
bgProto[ProtoSign] = new QButtonGroup();
|
||||
bgProto[ProtoSign]->addButton(rbSpecialNone, ButtonIdNone);
|
||||
bgProto[ProtoSign]->addButton(rbSignature,
|
||||
OstProto::Protocol::kSignFieldNumber);
|
||||
// Trailer
|
||||
bgProto[ProtoTrailer] = new QButtonGroup();
|
||||
bgProto[ProtoTrailer]->addButton(rbTrailerNone, ButtonIdNone);
|
||||
bgProto[ProtoTrailer]->addButton(rbTrailerOther, ButtonIdOther);
|
||||
|
||||
/*
|
||||
** Setup Validators
|
||||
*/
|
||||
@ -318,7 +348,14 @@ StreamConfigDialog::~StreamConfigDialog()
|
||||
}
|
||||
|
||||
delete _iter;
|
||||
delete mpStream;
|
||||
while (!_streamList.isEmpty())
|
||||
delete _streamList.takeFirst();
|
||||
}
|
||||
|
||||
void StreamConfigDialog::setWindowTitle(const QString &title)
|
||||
{
|
||||
_windowTitle = title;
|
||||
QDialog::setWindowTitle(title);
|
||||
}
|
||||
|
||||
void StreamConfigDialog::loadProtocolWidgets()
|
||||
@ -395,34 +432,10 @@ void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode)
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data());
|
||||
qWarning("Unhandled/Unknown PktLenMode = %s", qPrintable(mode));
|
||||
}
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbPrev_clicked()
|
||||
{
|
||||
#if 0
|
||||
StoreCurrentStream(currStreamIdx);
|
||||
currStreamIdx--;
|
||||
LoadCurrentStream(currStreamIdx);
|
||||
|
||||
pbPrev->setDisabled((currStreamIdx == 0));
|
||||
pbNext->setDisabled((currStreamIdx == 2));
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbNext_clicked()
|
||||
{
|
||||
#if 0
|
||||
StoreCurrentStream(currStreamIdx);
|
||||
currStreamIdx++;
|
||||
LoadCurrentStream(currStreamIdx);
|
||||
|
||||
pbPrev->setDisabled((currStreamIdx == 0));
|
||||
pbNext->setDisabled((currStreamIdx == 2));
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index)
|
||||
{
|
||||
qDebug("%s, index = %d", __FUNCTION__, index);
|
||||
@ -700,9 +713,9 @@ void StreamConfigDialog::on_lePattern_editingFinished()
|
||||
QString str;
|
||||
|
||||
num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16);
|
||||
qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num);
|
||||
qDebug("editfinished (%s | %x)\n", qPrintable(lePattern->text()));
|
||||
lePattern->setText(uintToHexStr(num, str, 4));
|
||||
qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num);
|
||||
qDebug("editfinished (%s | %x)\n", qPrintable(lePattern->text()));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -792,6 +805,8 @@ void StreamConfigDialog::forceProtocolNone(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
// Button 'newId' has been clicked
|
||||
// - update the protocol list correspondingly
|
||||
void StreamConfigDialog::updateProtocol(int newId)
|
||||
{
|
||||
int level;
|
||||
@ -806,6 +821,8 @@ void StreamConfigDialog::updateProtocol(int newId)
|
||||
__updateProtocol(level, newId);
|
||||
}
|
||||
|
||||
// Button 'newId' belonging to layer 'level' has been clicked
|
||||
// - update the protocol list correspondingly
|
||||
void StreamConfigDialog::__updateProtocol(int level, int newId)
|
||||
{
|
||||
int oldId;
|
||||
@ -853,7 +870,7 @@ void StreamConfigDialog::__updateProtocol(int level, int newId)
|
||||
// Free both protocol and associated widget
|
||||
delete _protocolWidgets.take(p);
|
||||
delete p;
|
||||
if (level == ProtoPayload)
|
||||
if (level == ProtoTrailer)
|
||||
{
|
||||
while (_iter->hasNext())
|
||||
{
|
||||
@ -872,6 +889,7 @@ void StreamConfigDialog::__updateProtocol(int level, int newId)
|
||||
return;
|
||||
}
|
||||
|
||||
//! Traverse the ProtocolList and update the SelectProtocols (Simple) widget
|
||||
void StreamConfigDialog::updateSelectProtocolsSimpleWidget()
|
||||
{
|
||||
int i;
|
||||
@ -897,7 +915,7 @@ void StreamConfigDialog::updateSelectProtocolsSimpleWidget()
|
||||
id = _iter->next()->protocolNumber();
|
||||
btn = bgProto[i]->button(id);
|
||||
|
||||
if (btn)
|
||||
if (btn) // we have a button for this protocol
|
||||
{
|
||||
if (btn->isEnabled())
|
||||
btn->click();
|
||||
@ -907,23 +925,32 @@ void StreamConfigDialog::updateSelectProtocolsSimpleWidget()
|
||||
__updateProtocol(i, id);
|
||||
}
|
||||
}
|
||||
else
|
||||
else // we don't have a button for this protocol
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case ProtoVlan:
|
||||
// No vlan - proto may belong to next layer
|
||||
_iter->previous();
|
||||
break;
|
||||
|
||||
case ProtoSign:
|
||||
// No sign - but we have a trailer
|
||||
_iter->previous();
|
||||
break;
|
||||
|
||||
case ProtoPayload:
|
||||
goto _other;
|
||||
|
||||
default:
|
||||
default: // viz. L1, L2, L3, L4, L5, Trailer
|
||||
// Is this a Payload layer protocol?
|
||||
// (maybe intermediate layers are not present)
|
||||
btn = bgProto[ProtoPayload]->button(id);
|
||||
if (btn && btn->isEnabled())
|
||||
{
|
||||
btn->click();
|
||||
break;
|
||||
i = ProtoPayload;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
goto _other;
|
||||
@ -931,20 +958,22 @@ void StreamConfigDialog::updateSelectProtocolsSimpleWidget()
|
||||
}
|
||||
}
|
||||
|
||||
// If more protocol(s) beyond payload ...
|
||||
// If more protocol(s) beyond trailer ...
|
||||
if (_iter->hasNext())
|
||||
{
|
||||
i = ProtoPayload;
|
||||
i = ProtoTrailer;
|
||||
goto _other;
|
||||
}
|
||||
|
||||
Q_ASSERT(!_iter->hasNext()); // At end of the ProtocolList
|
||||
goto _done;
|
||||
|
||||
_other:
|
||||
// Set remaining protocols as 'Other'
|
||||
for (int j = i; j < ProtoMax; j++)
|
||||
{
|
||||
// VLAN doesn't have a "Other" button
|
||||
if (j == ProtoVlan)
|
||||
// VLAN/Sign doesn't have a "Other" button
|
||||
if ((j == ProtoVlan) || (j == ProtoSign))
|
||||
continue;
|
||||
|
||||
bgProto[j]->button(ButtonIdOther)->setChecked(true);
|
||||
@ -960,9 +989,16 @@ void StreamConfigDialog::LoadCurrentStream()
|
||||
QString str;
|
||||
|
||||
qDebug("loading mpStream %p", mpStream);
|
||||
variableFieldsWidget->setStream(mpStream);
|
||||
|
||||
QDialog::setWindowTitle(QString("%1 [%2]").arg(_windowTitle)
|
||||
.arg(mpStream->name().isEmpty() ?
|
||||
tr("<unnamed>") : mpStream->name()));
|
||||
|
||||
// Meta Data
|
||||
{
|
||||
name->setText(mpStream->name());
|
||||
enabled->setChecked(mpStream->isEnabled());
|
||||
cmbPktLenMode->setCurrentIndex(mpStream->lenMode());
|
||||
lePktLen->setText(str.setNum(mpStream->frameLen()));
|
||||
lePktLenMin->setText(str.setNum(mpStream->frameLenMin()));
|
||||
@ -979,6 +1015,7 @@ void StreamConfigDialog::LoadCurrentStream()
|
||||
|
||||
// Variable Fields
|
||||
{
|
||||
variableFieldsWidget->clear();
|
||||
variableFieldsWidget->load();
|
||||
}
|
||||
|
||||
@ -1047,6 +1084,8 @@ void StreamConfigDialog::StoreCurrentStream()
|
||||
qDebug("storing pStream %p", pStream);
|
||||
|
||||
// Meta Data
|
||||
pStream->setName(name->text());
|
||||
pStream->setEnabled(enabled->isChecked());
|
||||
pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex());
|
||||
pStream->setFrameLen(lePktLen->text().toULong(&isOk));
|
||||
pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk));
|
||||
@ -1150,7 +1189,7 @@ void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text)
|
||||
uint burstSize = lePacketsPerBurst->text().toULong(&isOk);
|
||||
uint frameLen;
|
||||
|
||||
qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData());
|
||||
qDebug("start of %s(%s)", __FUNCTION__, qPrintable(text));
|
||||
if (pStream->lenMode() == Stream::e_fl_fixed)
|
||||
frameLen = pStream->frameLen();
|
||||
else
|
||||
@ -1195,34 +1234,99 @@ void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text)
|
||||
}
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbOk_clicked()
|
||||
bool StreamConfigDialog::isCurrentStreamValid()
|
||||
{
|
||||
QString log;
|
||||
OstProto::Stream s;
|
||||
|
||||
// Store dialog contents into stream
|
||||
StoreCurrentStream();
|
||||
QStringList log;
|
||||
|
||||
if ((mPort.transmitMode() == OstProto::kInterleavedTransmit)
|
||||
&& (mpStream->isFrameVariable()))
|
||||
{
|
||||
log += "In 'Interleaved Streams' transmit mode, the count for "
|
||||
"varying fields at transmit time may not be same as configured\n";
|
||||
log << tr("In 'Interleaved Streams' transmit mode, the count for "
|
||||
"varying fields at transmit time may not be same as configured");
|
||||
}
|
||||
|
||||
if (!mPort.trackStreamStats()
|
||||
&& mpStream->hasProtocol(OstProto::Protocol::kSignFieldNumber))
|
||||
{
|
||||
log << tr("Stream contains special signature, but per stream statistics "
|
||||
"will not be available till it is enabled on the port");
|
||||
}
|
||||
|
||||
mpStream->preflightCheck(log);
|
||||
|
||||
if (log.length())
|
||||
if (log.size())
|
||||
{
|
||||
if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?",
|
||||
if (QMessageBox::warning(this, "Preflight Check",
|
||||
tr("<p>We found possible problems with this stream -</p>")
|
||||
+ "<ul>"
|
||||
+ log.replaceInStrings(QRegExp("(.*)"), "<li>\\1</li>")
|
||||
.join("\n")
|
||||
+ "</ul>"
|
||||
+ tr("<p>Ignore?</p>"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
== QMessageBox::No)
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the data from the "local working copy of stream" to "actual stream"
|
||||
mpStream->protoDataCopyInto(s);
|
||||
mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbPrev_clicked()
|
||||
{
|
||||
Q_ASSERT(mCurrentStreamIndex > 0);
|
||||
|
||||
StoreCurrentStream();
|
||||
|
||||
if (!isCurrentStreamValid())
|
||||
return;
|
||||
|
||||
delete _iter;
|
||||
mpStream = _streamList.at(--mCurrentStreamIndex);
|
||||
_iter = mpStream->createProtocolListIterator();
|
||||
|
||||
LoadCurrentStream();
|
||||
on_twTopLevel_currentChanged(twTopLevel->currentIndex());
|
||||
|
||||
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbNext_clicked()
|
||||
{
|
||||
Q_ASSERT(int(mCurrentStreamIndex) < (_streamList.size()-1));
|
||||
|
||||
StoreCurrentStream();
|
||||
|
||||
if (!isCurrentStreamValid())
|
||||
return;
|
||||
|
||||
delete _iter;
|
||||
mpStream = _streamList.at(++mCurrentStreamIndex);
|
||||
_iter = mpStream->createProtocolListIterator();
|
||||
|
||||
LoadCurrentStream();
|
||||
on_twTopLevel_currentChanged(twTopLevel->currentIndex());
|
||||
|
||||
pbPrev->setDisabled(mCurrentStreamIndex == 0);
|
||||
pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1));
|
||||
|
||||
}
|
||||
|
||||
void StreamConfigDialog::on_pbOk_clicked()
|
||||
{
|
||||
// Store dialog contents into current stream
|
||||
StoreCurrentStream();
|
||||
|
||||
if (!isCurrentStreamValid())
|
||||
return;
|
||||
|
||||
// Copy the working copy of streams to user provided streams
|
||||
Q_ASSERT(_userStreamList.size() == _streamList.size());
|
||||
for (int i = 0; i < _streamList.size(); i++) {
|
||||
OstProto::Stream s;
|
||||
_streamList.at(i)->protoDataCopyInto(s);
|
||||
_userStreamList[i]->protoDataCopyFrom(s);
|
||||
}
|
||||
|
||||
qDebug("stream stored");
|
||||
|
||||
|
@ -25,7 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "port.h"
|
||||
#include "stream.h"
|
||||
#include "packetmodel.h"
|
||||
#include "modeltest.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QProgressDialog>
|
||||
#include <QStringListModel>
|
||||
|
||||
#define MAX_MAC_ITER_COUNT 256
|
||||
#define MIN_PKT_LEN 64
|
||||
@ -38,14 +41,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
class AbstractProtocolConfigForm;
|
||||
class ModelTest;
|
||||
|
||||
class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StreamConfigDialog(Port &port, uint streamIndex, QWidget *parent = 0);
|
||||
StreamConfigDialog(QList<Stream*> &streamList, const Port &port,
|
||||
QWidget *parent = 0);
|
||||
~StreamConfigDialog();
|
||||
|
||||
void setWindowTitle(const QString &title);
|
||||
|
||||
private:
|
||||
|
||||
enum ButtonId
|
||||
@ -64,6 +71,8 @@ private:
|
||||
ProtoL4 = 4,
|
||||
ProtoL5 = 5,
|
||||
ProtoPayload = 6,
|
||||
ProtoSign = 7,
|
||||
ProtoTrailer = 8,
|
||||
ProtoMax
|
||||
};
|
||||
|
||||
@ -72,7 +81,10 @@ private:
|
||||
QStringListModel *mpAvailableProtocolsModel;
|
||||
QStringListModel *mpSelectedProtocolsModel;
|
||||
|
||||
Port& mPort;
|
||||
QList<Stream*> _userStreamList;
|
||||
QList<Stream*> _streamList;
|
||||
const Port& mPort;
|
||||
QString _windowTitle;
|
||||
uint mCurrentStreamIndex;
|
||||
|
||||
Stream *mpStream;
|
||||
@ -92,6 +104,7 @@ private:
|
||||
static int lastProtocolDataIndex;
|
||||
|
||||
void setupUiExtra();
|
||||
bool isCurrentStreamValid();
|
||||
void LoadCurrentStream();
|
||||
void StoreCurrentStream();
|
||||
void loadProtocolWidgets();
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StreamConfigDialog</class>
|
||||
<widget class="QDialog" name="StreamConfigDialog">
|
||||
@ -8,12 +9,12 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>634</width>
|
||||
<height>507</height>
|
||||
<width>647</width>
|
||||
<height>549</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -22,14 +23,15 @@
|
||||
<string>Edit Stream</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/stream_edit.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/stream_edit.png</normaloff>:/icons/stream_edit.png</iconset>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string>QLineEdit:enabled[inputMask = "HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH HH HH; "], 
|
||||
QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } 
|
||||
<string>QLineEdit:enabled[inputMask = "HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH HH HH; "],
|
||||
QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff }
|
||||
</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
@ -49,20 +51,36 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<string>Protocol Selection</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Basics</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout2">
|
||||
<item row="0" column="0">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>241</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
<property name="buddy">
|
||||
<cstring>name</cstring>
|
||||
</property>
|
||||
</spacer>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="name"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="enabled">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QGroupBox" name="gbFrameLength">
|
||||
<property name="title">
|
||||
<string>Frame Length (including FCS)</string>
|
||||
@ -136,7 +154,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2" >
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QToolBox" name="tbSelectProtocols">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
@ -146,8 +164,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>592</width>
|
||||
<height>269</height>
|
||||
<width>605</width>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@ -193,7 +211,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item rowspan="2" row="0" column="1" >
|
||||
<item rowspan="3" row="0" column="1" >
|
||||
<widget class="QGroupBox" name="gbFrameType" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
@ -376,49 +394,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3" >
|
||||
<widget class="QGroupBox" name="gbL5Proto" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>L5</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5None" >
|
||||
<property name="text" >
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5Text" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5Other" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Other</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<item rowspan="2" row="1" column="0" >
|
||||
<widget class="QGroupBox" name="gbVlan" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
@ -542,7 +518,49 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3" >
|
||||
<item row="2" column="2" >
|
||||
<widget class="QGroupBox" name="gbL5Proto" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>L5</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5None" >
|
||||
<property name="text" >
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5Text" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbL5Other" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Other</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3" >
|
||||
<widget class="QGroupBox" name="gbPayloadProto" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
@ -591,6 +609,67 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3" >
|
||||
<widget class="QGroupBox" name="gbSpecial" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Special</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSpecialNone" >
|
||||
<property name="text" >
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbSignature" >
|
||||
<property name="text" >
|
||||
<string>Signature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3" >
|
||||
<widget class="QGroupBox" name="gbTrailer" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Trailer</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbTrailerNone" >
|
||||
<property name="text" >
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbTrailerOther" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Other</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
@ -637,7 +716,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
@ -651,10 +730,11 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
<string>></string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/arrow_right.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/arrow_right.png</normaloff>:/icons/arrow_right.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -663,7 +743,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
@ -693,7 +773,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/arrow_up.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/arrow_up.png</normaloff>:/icons/arrow_up.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -706,7 +787,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<string>v</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/arrow_down.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/arrow_down.png</normaloff>:/icons/arrow_down.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -719,7 +801,8 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<string>-</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc" >:/icons/delete.png</iconset>
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/delete.png</normaloff>:/icons/delete.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -728,7 +811,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
@ -773,7 +856,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="VariableFieldsWidget" native="1" name="variableFieldsWidget" />
|
||||
<widget class="VariableFieldsWidget" name="variableFieldsWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -808,7 +891,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item rowspan="2" row="0" column="1" >
|
||||
<item row="0" column="1" rowspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_13">
|
||||
<property name="title">
|
||||
<string>Numbers</string>
|
||||
@ -880,7 +963,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item rowspan="2" row="0" column="2" >
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_14">
|
||||
<property name="title">
|
||||
<string>Rate</string>
|
||||
@ -949,7 +1032,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item rowspan="2" row="0" column="3" >
|
||||
<item row="0" column="3" rowspan="2">
|
||||
<widget class="QGroupBox" name="nextWhat">
|
||||
<property name="title">
|
||||
<string>After this stream</string>
|
||||
@ -992,12 +1075,12 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item rowspan="2" row="0" column="4" >
|
||||
<item row="0" column="4" rowspan="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>41</height>
|
||||
@ -1124,7 +1207,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>153</width>
|
||||
<height>21</height>
|
||||
@ -1155,7 +1238,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="DumpView" native="1" name="vwPacketDump" />
|
||||
<widget class="DumpView" name="vwPacketDump" native="true"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -1183,7 +1266,7 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>191</width>
|
||||
<height>20</height>
|
||||
|
@ -177,7 +177,7 @@ bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||
if ((event->type() == QEvent::MouseButtonRelease)
|
||||
|| (event->type() == QEvent::MouseButtonDblClick))
|
||||
{
|
||||
QRect checkRect = check(option, option.rect, Qt::Checked);
|
||||
QRect checkRect = doCheck(option, option.rect, Qt::Checked);
|
||||
QRect emptyRect;
|
||||
doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
|
||||
if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
|
||||
|
@ -155,19 +155,21 @@ bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int r
|
||||
{
|
||||
// Edit Supported Fields
|
||||
case StreamName:
|
||||
mCurrentPort->streamByIndex(index.row())->setName(value.toString());
|
||||
mCurrentPort->mutableStreamByIndex(index.row())
|
||||
->setName(value.toString());
|
||||
emit(dataChanged(index, index));
|
||||
return true;
|
||||
|
||||
case StreamStatus:
|
||||
mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool());
|
||||
mCurrentPort->mutableStreamByIndex(index.row())
|
||||
->setEnabled(value.toBool());
|
||||
emit(dataChanged(index, index));
|
||||
return true;
|
||||
|
||||
case StreamNextWhat:
|
||||
if (role == Qt::EditRole)
|
||||
{
|
||||
mCurrentPort->streamByIndex(index.row())->setNextWhat(
|
||||
mCurrentPort->mutableStreamByIndex(index.row())->setNextWhat(
|
||||
(Stream::NextWhat)value.toInt());
|
||||
emit(dataChanged(index, index));
|
||||
return true;
|
||||
@ -221,6 +223,30 @@ QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int r
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Inserts streams before the given row
|
||||
*
|
||||
* StreamModel takes ownership of the passed streams; caller should
|
||||
* not try to access them after calling this function
|
||||
*/
|
||||
bool StreamModel::insert(int row, QList<Stream*> &streams)
|
||||
{
|
||||
int count = streams.size();
|
||||
qDebug("insert row = %d", row);
|
||||
qDebug("insert count = %d", count);
|
||||
beginInsertRows(QModelIndex(), row, row+count-1);
|
||||
for (int i = 0; i < count; i++) {
|
||||
OstProto::Stream s;
|
||||
streams.at(i)->protoDataCopyInto(s);
|
||||
mCurrentPort->newStreamAt(row+i, &s);
|
||||
delete streams.at(i);
|
||||
}
|
||||
streams.clear();
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
|
||||
{
|
||||
qDebug("insertRows() row = %d", row);
|
||||
@ -251,6 +277,7 @@ bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/)
|
||||
|
||||
void StreamModel::setCurrentPortIndex(const QModelIndex ¤t)
|
||||
{
|
||||
beginResetModel();
|
||||
if (!current.isValid() || !pgl->isPort(current))
|
||||
{
|
||||
qDebug("current is either invalid or not a port");
|
||||
@ -272,7 +299,7 @@ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t)
|
||||
connect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
|
||||
this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
|
||||
}
|
||||
reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId,
|
||||
@ -283,6 +310,9 @@ void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId,
|
||||
{
|
||||
if ((quint32(portGroupId) == mCurrentPort->portGroupId())
|
||||
&& (quint32(portId) == mCurrentPort->id()))
|
||||
reset();
|
||||
{
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "port.h"
|
||||
|
||||
class PortGroupList;
|
||||
class Stream;
|
||||
|
||||
class StreamModel : public QAbstractTableModel
|
||||
{
|
||||
@ -44,6 +45,7 @@ class StreamModel : public QAbstractTableModel
|
||||
int role = Qt::EditRole);
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const;
|
||||
bool insert(int row, QList<Stream*> &streams);
|
||||
bool insertRows (int row, int count,
|
||||
const QModelIndex & parent = QModelIndex());
|
||||
bool removeRows (int row, int count,
|
||||
|
50
client/streamstatsfiltermodel.h
Normal file
50
client/streamstatsfiltermodel.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2017 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_STATS_FILTER_MODEL_H
|
||||
#define _STREAM_STATS_FILTER_MODEL_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class StreamStatsFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StreamStatsFilterModel(QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool filterAcceptsColumn(int sourceColumn,
|
||||
const QModelIndex &/*sourceParent*/) const
|
||||
{
|
||||
QString title = sourceModel()->headerData(sourceColumn, Qt::Horizontal)
|
||||
.toString();
|
||||
return filterRegExp().exactMatch(title) ? true : false;
|
||||
}
|
||||
bool filterAcceptsRow(int /*sourceRow*/,
|
||||
const QModelIndex &/*sourceParent*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
234
client/streamstatsmodel.cpp
Normal file
234
client/streamstatsmodel.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright (C) 2016 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 "streamstatsmodel.h"
|
||||
|
||||
#include "protocol.pb.h"
|
||||
|
||||
#include <QBrush>
|
||||
|
||||
// XXX: Keep the enum in sync with it's string
|
||||
enum {
|
||||
kTxPkts,
|
||||
kRxPkts,
|
||||
kTxBytes,
|
||||
kRxBytes,
|
||||
kMaxStreamStats
|
||||
};
|
||||
static QStringList statTitles = QStringList()
|
||||
<< "Tx Pkts"
|
||||
<< "Rx Pkts"
|
||||
<< "Tx Bytes"
|
||||
<< "Rx Bytes";
|
||||
|
||||
// XXX: Keep the enum in sync with it's string
|
||||
enum {
|
||||
kAggrTxPkts,
|
||||
kAggrRxPkts,
|
||||
kAggrPktLoss,
|
||||
kMaxAggrStreamStats
|
||||
};
|
||||
static QStringList aggrStatTitles = QStringList()
|
||||
<< "Total\nTx Pkts"
|
||||
<< "Total\nRx Pkts"
|
||||
<< "Total\nPkt Loss";
|
||||
|
||||
static const uint kAggrGuid = 0xffffffff;
|
||||
|
||||
StreamStatsModel::StreamStatsModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
clearStats();
|
||||
}
|
||||
|
||||
int StreamStatsModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return guidList_.size();
|
||||
}
|
||||
|
||||
int StreamStatsModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
if (!portList_.size())
|
||||
return 0;
|
||||
|
||||
return kMaxAggrStreamStats + portList_.size() * kMaxStreamStats;
|
||||
}
|
||||
|
||||
QVariant StreamStatsModel::headerData(
|
||||
int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
switch (orientation) {
|
||||
case Qt::Horizontal: // Column Header
|
||||
if (section < kMaxAggrStreamStats)
|
||||
return aggrStatTitles.at(section % kMaxAggrStreamStats);
|
||||
|
||||
section -= kMaxAggrStreamStats;
|
||||
return QString("Port %1-%2\n%3")
|
||||
.arg(portList_.at(section/kMaxStreamStats).first)
|
||||
.arg(portList_.at(section/kMaxStreamStats).second)
|
||||
.arg(statTitles.at(section % kMaxStreamStats));
|
||||
case Qt::Vertical: // Row Header
|
||||
if (section == (guidList_.size() - 1))
|
||||
return QString("GUID Total");
|
||||
return QString("Stream GUID %1")
|
||||
.arg(guidList_.at(section));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == Qt::TextAlignmentRole)
|
||||
return Qt::AlignRight;
|
||||
|
||||
int portColumn = index.column() - kMaxAggrStreamStats;
|
||||
if (role == Qt::BackgroundRole) {
|
||||
if (portColumn < 0)
|
||||
return QBrush(QColor("lavender")); // Aggregate Column
|
||||
if (index.row() == (guidList_.size() - 1))
|
||||
return QBrush(QColor("burlywood")); // Aggregate Row
|
||||
else if ((portColumn/kMaxStreamStats) & 1)
|
||||
return QBrush(QColor("beige")); // Color alternate Ports
|
||||
}
|
||||
|
||||
Guid guid = guidList_.at(index.row());
|
||||
if (role == Qt::ForegroundRole) {
|
||||
if ((index.column() == kAggrPktLoss)
|
||||
&& aggrGuidStats_.value(guid).pktLoss)
|
||||
return QBrush(QColor("firebrick"));
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
if (index.column() < kMaxAggrStreamStats) {
|
||||
int stat = index.column() % kMaxAggrStreamStats;
|
||||
switch (stat) {
|
||||
case kAggrRxPkts:
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).rxPkts);
|
||||
case kAggrTxPkts:
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
|
||||
case kAggrPktLoss:
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
PortGroupPort pgp = portList_.at(portColumn/kMaxStreamStats);
|
||||
int stat = portColumn % kMaxStreamStats;
|
||||
|
||||
switch (stat) {
|
||||
case kRxPkts:
|
||||
return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxPkts);
|
||||
case kTxPkts:
|
||||
return QString("%L1").arg(streamStats_.value(guid).value(pgp).txPkts);
|
||||
case kRxBytes:
|
||||
return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxBytes);
|
||||
case kTxBytes:
|
||||
return QString("%L1").arg(streamStats_.value(guid).value(pgp).txBytes);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// --------------------------------------------- //
|
||||
// Slots
|
||||
// --------------------------------------------- //
|
||||
void StreamStatsModel::clearStats()
|
||||
{
|
||||
#if QT_VERSION >= 0x040600
|
||||
beginResetModel();
|
||||
#endif
|
||||
|
||||
guidList_.clear();
|
||||
portList_.clear();
|
||||
streamStats_.clear();
|
||||
aggrGuidStats_.clear();
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
endResetModel();
|
||||
#else
|
||||
reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamStatsModel::appendStreamStatsList(
|
||||
quint32 portGroupId,
|
||||
const OstProto::StreamStatsList *stats)
|
||||
{
|
||||
int n = stats->stream_stats_size();
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
beginResetModel();
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const OstProto::StreamStats &s = stats->stream_stats(i);
|
||||
PortGroupPort pgp = PortGroupPort(portGroupId, s.port_id().id());
|
||||
Guid guid = s.stream_guid().id();
|
||||
StreamStats &ss = streamStats_[guid][pgp];
|
||||
StreamStats &aggrPort = streamStats_[kAggrGuid][pgp];
|
||||
AggrGuidStats &aggrGuid = aggrGuidStats_[guid];
|
||||
AggrGuidStats &aggrAggr = aggrGuidStats_[kAggrGuid];
|
||||
|
||||
ss.rxPkts = s.rx_pkts();
|
||||
ss.txPkts = s.tx_pkts();
|
||||
ss.rxBytes = s.rx_bytes();
|
||||
ss.txBytes = s.tx_bytes();
|
||||
|
||||
aggrPort.rxPkts += ss.rxPkts;
|
||||
aggrPort.txPkts += ss.txPkts;
|
||||
aggrPort.rxBytes += ss.rxBytes;
|
||||
aggrPort.txBytes += ss.txBytes;
|
||||
|
||||
aggrGuid.rxPkts += ss.rxPkts;
|
||||
aggrGuid.txPkts += ss.txPkts;
|
||||
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
|
||||
|
||||
aggrAggr.rxPkts += ss.rxPkts;
|
||||
aggrAggr.txPkts += ss.txPkts;
|
||||
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
|
||||
|
||||
if (!portList_.contains(pgp))
|
||||
portList_.append(pgp);
|
||||
if (!guidList_.contains(guid))
|
||||
guidList_.append(guid);
|
||||
}
|
||||
|
||||
if (guidList_.size())
|
||||
guidList_.append(kAggrGuid);
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
endResetModel();
|
||||
#else
|
||||
reset();
|
||||
#endif
|
||||
|
||||
// Prevent receiving any future updates from this sender
|
||||
disconnect(sender(), 0, this, 0);
|
||||
}
|
70
client/streamstatsmodel.h
Normal file
70
client/streamstatsmodel.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (C) 2016 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_STATS_MODEL_H
|
||||
#define _STREAM_STATS_MODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
|
||||
namespace OstProto {
|
||||
class StreamStatsList;
|
||||
}
|
||||
|
||||
class StreamStatsModel: public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StreamStatsModel(QObject *parent = 0);
|
||||
|
||||
int rowCount(const QModelIndex &parent=QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent=QModelIndex()) const;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
|
||||
public slots:
|
||||
void clearStats();
|
||||
void appendStreamStatsList(quint32 portGroupId,
|
||||
const OstProto::StreamStatsList *stats);
|
||||
private:
|
||||
typedef QPair<uint, uint> PortGroupPort; // Pair = (PortGroupId, PortId)
|
||||
typedef uint Guid;
|
||||
struct StreamStats {
|
||||
quint64 rxPkts;
|
||||
quint64 txPkts;
|
||||
quint64 rxBytes;
|
||||
quint64 txBytes;
|
||||
};
|
||||
struct AggrGuidStats {
|
||||
quint64 rxPkts;
|
||||
quint64 txPkts;
|
||||
qint64 pktLoss;
|
||||
};
|
||||
QList<Guid> guidList_;
|
||||
QList<PortGroupPort> portList_;
|
||||
QHash<Guid, QHash<PortGroupPort, StreamStats> > streamStats_;
|
||||
QHash<Guid, AggrGuidStats> aggrGuidStats_;
|
||||
};
|
||||
#endif
|
||||
|
65
client/streamstatswindow.cpp
Normal file
65
client/streamstatswindow.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright (C) 2016 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 "streamstatswindow.h"
|
||||
|
||||
#include "streamstatsfiltermodel.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QHeaderView>
|
||||
|
||||
static int id;
|
||||
static int count;
|
||||
|
||||
StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
streamStats->addAction(actionShowByteCounters);
|
||||
|
||||
if (id)
|
||||
setWindowTitle(windowTitle() + QString("(%1)").arg(id));
|
||||
id++;
|
||||
count++;
|
||||
|
||||
filterModel_ = new StreamStatsFilterModel(this);
|
||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
||||
filterModel_->setSourceModel(model);
|
||||
streamStats->setModel(filterModel_);
|
||||
|
||||
streamStats->verticalHeader()->setHighlightSections(false);
|
||||
streamStats->verticalHeader()->setDefaultSectionSize(
|
||||
streamStats->verticalHeader()->minimumSectionSize());
|
||||
}
|
||||
|
||||
StreamStatsWindow::~StreamStatsWindow()
|
||||
{
|
||||
delete filterModel_;
|
||||
count--;
|
||||
if (count == 0)
|
||||
id = 0;
|
||||
}
|
||||
|
||||
void StreamStatsWindow::on_actionShowByteCounters_triggered(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
filterModel_->setFilterRegExp(QRegExp(".*"));
|
||||
else
|
||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
||||
}
|
43
client/streamstatswindow.h
Normal file
43
client/streamstatswindow.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (C) 2016 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_STATS_WINDOW_H
|
||||
#define _STREAM_STATS_WINDOW_H
|
||||
|
||||
#include "ui_streamstatswindow.h"
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
class StreamStatsWindow: public QWidget, private Ui::StreamStatsWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
StreamStatsWindow(QAbstractItemModel *model, QWidget *parent = 0);
|
||||
~StreamStatsWindow();
|
||||
|
||||
private slots:
|
||||
void on_actionShowByteCounters_triggered(bool checked);
|
||||
|
||||
private:
|
||||
QSortFilterProxyModel *filterModel_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
48
client/streamstatswindow.ui
Normal file
48
client/streamstatswindow.ui
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StreamStatsWindow</class>
|
||||
<widget class="QWidget" name="StreamStatsWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>551</width>
|
||||
<height>452</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Stream Statistics</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="XTableView" name="streamStats">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Oops! We don't seem to have any stream statistics for the requested port(s)
|
||||
|
||||
Wait a little bit to see if they appear, otherwise verify your stream stats configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionShowByteCounters">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Byte Counters</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>XTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>xtableview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -63,7 +63,6 @@ VariableFieldsWidget::VariableFieldsWidget(QWidget *parent)
|
||||
stream_ = NULL;
|
||||
isProgLoad_ = false;
|
||||
lastSelectedProtocolIndex_ = 0;
|
||||
lastSelectedVariableFieldIndex_ = 0;
|
||||
|
||||
setupUi(this);
|
||||
attribGroup->setHidden(true);
|
||||
@ -109,8 +108,11 @@ void VariableFieldsWidget::load()
|
||||
AbstractProtocol *proto = iter->next();
|
||||
QListWidgetItem *protoItem = new QListWidgetItem;
|
||||
|
||||
protoItem->setData(Qt::UserRole, QVariant::fromValue(proto));
|
||||
protoItem->setData(kProtocolPtrRole, QVariant::fromValue(proto));
|
||||
protoItem->setData(kCurrentVarFieldRole,
|
||||
QVariant::fromValue(proto->variableFieldCount() ? 0 : -1));
|
||||
protoItem->setText(proto->shortName());
|
||||
decorateProtocolItem(protoItem);
|
||||
|
||||
protocolList->addItem(protoItem);
|
||||
}
|
||||
@ -121,9 +123,6 @@ void VariableFieldsWidget::load()
|
||||
|
||||
// XXX: protocolList->setCurrentRow() above will emit currentItemChanged
|
||||
// which will load variableFieldsList - no need to load it explicitly
|
||||
|
||||
if (lastSelectedVariableFieldIndex_ < variableFieldList->count())
|
||||
variableFieldList->setCurrentRow(lastSelectedVariableFieldIndex_);
|
||||
}
|
||||
|
||||
void VariableFieldsWidget::store()
|
||||
@ -148,7 +147,7 @@ void VariableFieldsWidget::on_protocolList_currentItemChanged(
|
||||
if (current == NULL)
|
||||
goto _exit;
|
||||
|
||||
proto = current->data(Qt::UserRole).value<AbstractProtocol*>();
|
||||
proto = current->data(kProtocolPtrRole).value<AbstractProtocol*>();
|
||||
loadProtocolFields(proto);
|
||||
|
||||
variableFieldList->clear();
|
||||
@ -167,6 +166,9 @@ void VariableFieldsWidget::on_protocolList_currentItemChanged(
|
||||
field->setCurrentIndex(-1);
|
||||
type->setCurrentIndex(-1);
|
||||
|
||||
variableFieldList->setCurrentRow(
|
||||
current->data(kCurrentVarFieldRole).value<int>());
|
||||
|
||||
lastSelectedProtocolIndex_ = protocolList->currentRow();
|
||||
|
||||
_exit:
|
||||
@ -185,7 +187,7 @@ void VariableFieldsWidget::on_variableFieldList_currentItemChanged(
|
||||
if (current == NULL)
|
||||
goto _exit;
|
||||
|
||||
vf = current->data(Qt::UserRole).value<OstProto::VariableField>();
|
||||
vf = current->data(kVarFieldRole).value<OstProto::VariableField>();
|
||||
|
||||
isProgLoad_ = true;
|
||||
|
||||
@ -200,8 +202,9 @@ void VariableFieldsWidget::on_variableFieldList_currentItemChanged(
|
||||
|
||||
isProgLoad_ = false;
|
||||
|
||||
lastSelectedVariableFieldIndex_ = variableFieldList->currentRow();
|
||||
|
||||
protocolList->currentItem()->setData(
|
||||
kCurrentVarFieldRole,
|
||||
QVariant::fromValue(variableFieldList->currentRow()));
|
||||
_exit:
|
||||
attribGroup->setHidden(current == NULL);
|
||||
deleteButton->setEnabled(current != NULL);
|
||||
@ -214,7 +217,7 @@ void VariableFieldsWidget::on_addButton_clicked()
|
||||
if (!protoItem)
|
||||
return;
|
||||
|
||||
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
|
||||
AbstractProtocol *proto = protoItem->data(kProtocolPtrRole)
|
||||
.value<AbstractProtocol*>();
|
||||
OstProto::VariableField vf;
|
||||
QListWidgetItem *vfItem = new QListWidgetItem;
|
||||
@ -222,6 +225,9 @@ void VariableFieldsWidget::on_addButton_clicked()
|
||||
proto->appendVariableField(vf);
|
||||
setVariableFieldItem(vfItem, proto, vf);
|
||||
variableFieldList->addItem(vfItem);
|
||||
variableFieldList->setCurrentItem(vfItem);
|
||||
|
||||
decorateProtocolItem(protoItem);
|
||||
}
|
||||
|
||||
void VariableFieldsWidget::on_deleteButton_clicked()
|
||||
@ -232,10 +238,21 @@ void VariableFieldsWidget::on_deleteButton_clicked()
|
||||
if (!protoItem || (vfIdx < 0))
|
||||
return;
|
||||
|
||||
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
|
||||
AbstractProtocol *proto = protoItem->data(kProtocolPtrRole)
|
||||
.value<AbstractProtocol*>();
|
||||
proto->removeVariableField(vfIdx);
|
||||
delete variableFieldList->takeItem(vfIdx);
|
||||
|
||||
// XXX: takeItem() above triggers a currentChanged, but the signal
|
||||
// is emitted after the "current" is changed to an item after
|
||||
// or before the item(s) to be deleted but before the item(s)
|
||||
// are actually deleted - so the current inside that slot is not
|
||||
// correct and we need to re-save it again
|
||||
protocolList->currentItem()->setData(
|
||||
kCurrentVarFieldRole,
|
||||
QVariant::fromValue(variableFieldList->currentRow()));
|
||||
|
||||
decorateProtocolItem(protoItem);
|
||||
}
|
||||
|
||||
void VariableFieldsWidget::on_field_currentIndexChanged(int index)
|
||||
@ -268,7 +285,8 @@ void VariableFieldsWidget::on_type_currentIndexChanged(int index)
|
||||
if ((index < 0) || !protocolList->currentItem())
|
||||
return;
|
||||
|
||||
AbstractProtocol *proto = protocolList->currentItem()->data(Qt::UserRole)
|
||||
AbstractProtocol *proto = protocolList->currentItem()
|
||||
->data(kProtocolPtrRole)
|
||||
.value<AbstractProtocol*>();
|
||||
int protoSize = proto->protocolFrameSize();
|
||||
|
||||
@ -295,8 +313,8 @@ void VariableFieldsWidget::on_type_currentIndexChanged(int index)
|
||||
bitmask->setInputMask("HHHHHHHH");
|
||||
bitmask->setText("FFFFFFFF");
|
||||
valueRange_->setRange(0, 0xFFFFFFFF);
|
||||
count->setRange(0, 0xFFFF);
|
||||
step->setRange(0, 0xFFFF);
|
||||
count->setRange(0, 0x7FFFFFFF);
|
||||
step->setRange(0, 0x7FFFFFFF);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false); // unreachable
|
||||
@ -326,12 +344,21 @@ void VariableFieldsWidget::updateCurrentVariableField()
|
||||
vf.set_step(step->value());
|
||||
|
||||
QListWidgetItem *protoItem = protocolList->currentItem();
|
||||
AbstractProtocol *proto = protoItem->data(Qt::UserRole)
|
||||
AbstractProtocol *proto = protoItem->data(kProtocolPtrRole)
|
||||
.value<AbstractProtocol*>();
|
||||
proto->mutableVariableField(variableFieldList->currentRow())->CopyFrom(vf);
|
||||
setVariableFieldItem(variableFieldList->currentItem(), proto, vf);
|
||||
}
|
||||
|
||||
void VariableFieldsWidget::decorateProtocolItem(QListWidgetItem *item)
|
||||
{
|
||||
AbstractProtocol *proto = item->data(kProtocolPtrRole)
|
||||
.value<AbstractProtocol*>();
|
||||
QFont font = item->font();
|
||||
font.setBold(proto->variableFieldCount() > 0);
|
||||
item->setFont(font);
|
||||
}
|
||||
|
||||
void VariableFieldsWidget::loadProtocolFields(
|
||||
const AbstractProtocol *protocol)
|
||||
{
|
||||
@ -419,7 +446,7 @@ void VariableFieldsWidget::setVariableFieldItem(
|
||||
else
|
||||
to = (vf.value() + (vf.count()-1)*vf.step()) & vf.mask();
|
||||
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(vf));
|
||||
item->setData(kVarFieldRole, QVariant::fromValue(vf));
|
||||
itemText = QString("%1 %2 %3 from %4 to %5")
|
||||
.arg(protocol->shortName())
|
||||
.arg(fieldName)
|
||||
|
@ -54,6 +54,7 @@ private slots:
|
||||
void on_type_currentIndexChanged(int index);
|
||||
void updateCurrentVariableField();
|
||||
private:
|
||||
void decorateProtocolItem(QListWidgetItem *item);
|
||||
void loadProtocolFields(const AbstractProtocol *protocol);
|
||||
int typeSize(OstProto::VariableField::Type type);
|
||||
int fieldIndex(const OstProto::VariableField &vf);
|
||||
@ -62,6 +63,17 @@ private:
|
||||
const AbstractProtocol *protocol,
|
||||
const OstProto::VariableField &vf);
|
||||
|
||||
// Custom roles for protocol items
|
||||
enum {
|
||||
kProtocolPtrRole = Qt::UserRole,
|
||||
kCurrentVarFieldRole,
|
||||
};
|
||||
|
||||
// Custom roles for variable field items
|
||||
enum {
|
||||
kVarFieldRole = Qt::UserRole
|
||||
};
|
||||
|
||||
Stream *stream_;
|
||||
QIntValidator *valueRange_;
|
||||
bool isProgLoad_;
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0" >
|
||||
<class>VariableFieldsWidget</class>
|
||||
<widget class="QWidget" name="VariableFieldsWidget" >
|
||||
@ -12,80 +13,58 @@
|
||||
<property name="windowTitle" >
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="spacing" >
|
||||
<number>0</number>
|
||||
<property name="autoFillBackground" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QListWidget" name="protocolList" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding" >
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="variableFieldList" >
|
||||
<widget class="QWidget" name="widget" native="true" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred" >
|
||||
<horstretch>6</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Sunken</enum>
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<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>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="addButton" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Add variable field</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>+</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="ostinato.qrc" >
|
||||
<normaloff>:/icons/add.png</normaloff>:/icons/add.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -93,20 +72,27 @@
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Remove variable field</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string> - </string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="ostinato.qrc" >
|
||||
<normaloff>:/icons/delete.png</normaloff>:/icons/delete.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<spacer name="horizontalSpacer" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
@ -114,12 +100,23 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="XListWidget" name="variableFieldList" >
|
||||
<property name="whatsThis" >
|
||||
<string>A variable field is a protocol field which you want to vary across packets
|
||||
|
||||
To create one, select the protocol on the left and click '+' above</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="attribGroup" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -138,7 +135,7 @@
|
||||
<item row="0" column="1" >
|
||||
<widget class="QComboBox" name="field" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed" >
|
||||
<horstretch>3</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -158,7 +155,7 @@
|
||||
<item row="0" column="3" >
|
||||
<widget class="QComboBox" name="type" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed" >
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -178,7 +175,7 @@
|
||||
<item row="0" column="5" >
|
||||
<widget class="QSpinBox" name="offset" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed" >
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -201,7 +198,7 @@
|
||||
<item row="0" column="7" >
|
||||
<widget class="QLineEdit" name="bitmask" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed" >
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -263,7 +260,7 @@
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>561</width>
|
||||
<height>41</height>
|
||||
@ -273,10 +270,16 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>XListWidget</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header>xlistwidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>protocolList</tabstop>
|
||||
<tabstop>variableFieldList</tabstop>
|
||||
<tabstop>addButton</tabstop>
|
||||
<tabstop>deleteButton</tabstop>
|
||||
<tabstop>field</tabstop>
|
||||
<tabstop>type</tabstop>
|
||||
@ -287,6 +290,8 @@
|
||||
<tabstop>count</tabstop>
|
||||
<tabstop>step</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="ostinato.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
48
client/xlistwidget.h
Normal file
48
client/xlistwidget.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (C) 2018 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 _X_LIST_WIDGET_H
|
||||
#define _X_LIST_WIDGET_H
|
||||
|
||||
#include <QListWidget>
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
class XListWidget : public QListWidget
|
||||
{
|
||||
public:
|
||||
XListWidget(QWidget *parent) : QListWidget(parent) {}
|
||||
virtual ~XListWidget() {}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if (!model()->hasChildren()) {
|
||||
QPainter painter(viewport());
|
||||
style()->drawItemText(&painter, viewport()->rect(),
|
||||
layoutDirection() | Qt::AlignCenter, palette(),
|
||||
true, whatsThis(), QPalette::WindowText);
|
||||
}
|
||||
else
|
||||
QListWidget::paintEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
48
client/xtableview.h
Normal file
48
client/xtableview.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (C) 2017 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 _X_TABLE_VIEW_H
|
||||
#define _X_TABLE_VIEW_H
|
||||
|
||||
#include <QTableView>
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
class XTableView : public QTableView
|
||||
{
|
||||
public:
|
||||
XTableView(QWidget *parent) : QTableView(parent) {}
|
||||
virtual ~XTableView() {}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if (!model()->hasChildren()) {
|
||||
QPainter painter(viewport());
|
||||
style()->drawItemText(&painter, viewport()->rect(),
|
||||
layoutDirection() | Qt::AlignCenter, palette(),
|
||||
true, whatsThis(), QPalette::WindowText);
|
||||
}
|
||||
else
|
||||
QTableView::paintEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
54
client/xtreeview.h
Normal file
54
client/xtreeview.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (C) 2017 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 _X_TREE_VIEW_H
|
||||
#define _X_TREE_VIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
#if 0 // FIXME: Test and remove
|
||||
#if QT_VERSION >= 0x050000
|
||||
#error "Do we even need this anymore?"
|
||||
#endif
|
||||
|
||||
class XTreeView : public QTreeView
|
||||
{
|
||||
public:
|
||||
XTreeView(QWidget *parent) : QTreeView(parent) {}
|
||||
virtual ~XTreeView() {}
|
||||
|
||||
private:
|
||||
virtual void mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QModelIndex item = indexAt(event->pos());
|
||||
|
||||
if (!item.isValid())
|
||||
setCurrentIndex(QModelIndex());
|
||||
|
||||
QTreeView::mousePressEvent(event);
|
||||
}
|
||||
};
|
||||
#else
|
||||
typedef QTreeView XTreeView;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "protocollistiterator.h"
|
||||
#include "streambase.h"
|
||||
|
||||
#include "bswap.h"
|
||||
|
||||
#include <qendian.h>
|
||||
|
||||
/*!
|
||||
@ -618,9 +620,9 @@ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum)
|
||||
else
|
||||
field = fieldData(i, FieldFrameValue, streamIndex).toByteArray();
|
||||
qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos,
|
||||
QString(proto.toHex()).toAscii().constData());
|
||||
qPrintable(QString(proto.toHex())));
|
||||
qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(),
|
||||
QString(field.toHex()).toAscii().constData());
|
||||
qPrintable(QString(field.toHex())));
|
||||
|
||||
if (bits == (uint) field.size() * 8)
|
||||
{
|
||||
@ -972,18 +974,29 @@ out:
|
||||
quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex,
|
||||
CksumType cksumType, CksumScope cksumScope) const
|
||||
{
|
||||
quint32 sum = 0;
|
||||
quint32 sum;
|
||||
quint16 cksum;
|
||||
AbstractProtocol *p = next;
|
||||
|
||||
Q_ASSERT(cksumType == CksumIp);
|
||||
|
||||
while (p)
|
||||
{
|
||||
if (!p)
|
||||
return 0xFFFF;
|
||||
|
||||
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
||||
sum += (quint16) ~cksum;
|
||||
sum = (quint16) ~cksum;
|
||||
if (cksumScope == CksumScopeAdjacentProtocol)
|
||||
goto out;
|
||||
|
||||
p = p->next;
|
||||
while (p)
|
||||
{
|
||||
// when combining cksums, a non-first protocol starting at odd offset
|
||||
// needs a byte swap (see RFC 1071 section(s) 2A, 2B)
|
||||
cksum = p->protocolFrameCksum(streamIndex, cksumType);
|
||||
if (p->protocolFrameOffset(streamIndex) & 0x1)
|
||||
cksum = swap16(cksum);
|
||||
sum += (quint16) ~cksum;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
@ -998,7 +1011,9 @@ out:
|
||||
while(sum>>16)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
|
||||
return (quint16) ~sum;
|
||||
cksum = (quint16) ~sum;
|
||||
qDebug("%s: cksum = %u", __FUNCTION__, cksum);
|
||||
return cksum;
|
||||
}
|
||||
|
||||
// Stein's binary GCD algo - from wikipedia
|
||||
|
39
common/bswap.h
Normal file
39
common/bswap.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2010-2016 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 _B_SWAP_H
|
||||
#define _B_SWAP_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
static inline quint32 swap32(quint32 val)
|
||||
{
|
||||
return (((val >> 24) & 0x000000FF) |
|
||||
((val >> 16) & 0x0000FF00) |
|
||||
((val << 16) & 0x00FF0000) |
|
||||
((val << 24) & 0xFF000000));
|
||||
}
|
||||
|
||||
static inline quint16 swap16(quint16 val)
|
||||
{
|
||||
return (((val >> 8) & 0x00FF) |
|
||||
((val << 8) & 0xFF00));
|
||||
}
|
||||
|
||||
#endif
|
@ -69,10 +69,10 @@ public:
|
||||
dynamic_cast<ComboProtocol<protoNumber, ProtoA, ProtoB>*>(proto);
|
||||
|
||||
Q_ASSERT_X(comboProto != NULL,
|
||||
QString("ComboProtocolConfigForm{%1}::loadWidget()")
|
||||
.arg(protoNumber).toAscii().constData(),
|
||||
QString("Protocol{%1} is not a instance of ComboProtocol")
|
||||
.arg(proto->protocolNumber()).toAscii().constData());
|
||||
qPrintable(tr("ComboProtocolConfigForm{%1}::loadWidget()")
|
||||
.arg(protoNumber)),
|
||||
qPrintable(tr("Protocol{%1} is not a instance of ComboProtocol")
|
||||
.arg(proto->protocolNumber())));
|
||||
|
||||
formA->loadWidget(comboProto->protoA);
|
||||
formB->loadWidget(comboProto->protoB);
|
||||
@ -83,10 +83,10 @@ public:
|
||||
dynamic_cast<ComboProtocol<protoNumber, ProtoA, ProtoB>*>(proto);
|
||||
|
||||
Q_ASSERT_X(comboProto != NULL,
|
||||
QString("ComboProtocolConfigForm{%1}::loadWidget()")
|
||||
.arg(protoNumber).toAscii().constData(),
|
||||
QString("Protocol{%1} is not a instance of ComboProtocol")
|
||||
.arg(proto->protocolNumber()).toAscii().constData());
|
||||
qPrintable(tr("ComboProtocolConfigForm{%1}::loadWidget()")
|
||||
.arg(protoNumber)),
|
||||
qPrintable(tr("Protocol{%1} is not a instance of ComboProtocol")
|
||||
.arg(proto->protocolNumber())));
|
||||
|
||||
formA->storeWidget(comboProto->protoA);
|
||||
formB->storeWidget(comboProto->protoB);
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// (802.2 LLC)
|
||||
/*
|
||||
Copyright (C) 2010 Srivats P.
|
||||
|
||||
@ -18,12 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import "protocol.proto";
|
||||
import "dot3.proto";
|
||||
import "llc.proto";
|
||||
|
||||
package OstProto;
|
||||
|
||||
// 802.2 LLC
|
||||
message Dot2Llc {
|
||||
// Empty since this is a 'combo' protocol
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// (802.2 SNAP)
|
||||
/*
|
||||
Copyright (C) 2010 Srivats P.
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// (802.3)
|
||||
/*
|
||||
Copyright (C) 2010 Srivats P.
|
||||
|
||||
|
@ -88,7 +88,7 @@ message File {
|
||||
// FieldNumber 1 is reserved and MUST not be used!
|
||||
required bytes magic_value = 2;
|
||||
required FileMetaData meta_data = 3;
|
||||
optional FileContent content_matter = 9;
|
||||
optional FileContentMatter content_matter = 9;
|
||||
required fixed32 checksum_value = 15;
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,9 @@ 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 "protocol.proto";
|
||||
|
||||
package OstProto;
|
||||
|
||||
// Group Management Protocol (i.e. IGMP and MLD)
|
||||
/// Group Management Protocol (i.e. IGMP and MLD)
|
||||
message Gmp {
|
||||
//
|
||||
// Common fields for both ASM and SSM messages
|
||||
|
@ -200,7 +200,10 @@ int HexDumpProtocol::protocolFrameSize(int streamIndex) const
|
||||
if (data.pad_until_end())
|
||||
{
|
||||
int pad = mpStream->frameLen(streamIndex)
|
||||
- (protocolFrameOffset(streamIndex) + len + kFcsSize);
|
||||
- (protocolFrameOffset(streamIndex)
|
||||
+ len
|
||||
+ protocolFramePayloadSize(streamIndex)
|
||||
+ kFcsSize);
|
||||
if (pad < 0)
|
||||
pad = 0;
|
||||
len += pad;
|
||||
|
@ -94,7 +94,7 @@ void PdmlIcmp6Protocol::unknownFieldHandler(QString name,
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("unexpected field %s", name.toAscii().constData());
|
||||
qDebug("unexpected field %s", qPrintable(name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,22 +50,8 @@ Length (x4)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_22" >
|
||||
<property name="text" >
|
||||
<string>TOS/DSCP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="leIpTos" >
|
||||
<property name="inputMask" >
|
||||
<string>>HH; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<widget class="TosDscpWidget" name="tosDscp" native="true" />
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QCheckBox" name="cbIpLengthOverride" >
|
||||
@ -91,7 +77,7 @@ Length (x4)</string>
|
||||
<item row="4" column="1" >
|
||||
<widget class="QLineEdit" name="leIpId" >
|
||||
<property name="inputMask" >
|
||||
<string>>HH HH; </string>
|
||||
<string>>HH HH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -143,7 +129,7 @@ Length (x4)</string>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>>HH; </string>
|
||||
<string>>HH</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
@ -163,7 +149,7 @@ Length (x4)</string>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>>HH HH; </string>
|
||||
<string>>HH HH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -395,12 +381,20 @@ Length (x4)</string>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TosDscpWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>tosdscp.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>cbIpVersionOverride</tabstop>
|
||||
<tabstop>leIpVersion</tabstop>
|
||||
<tabstop>cbIpHdrLenOverride</tabstop>
|
||||
<tabstop>leIpHdrLen</tabstop>
|
||||
<tabstop>leIpTos</tabstop>
|
||||
<tabstop>tosDscp</tabstop>
|
||||
<tabstop>cbIpLengthOverride</tabstop>
|
||||
<tabstop>leIpLength</tabstop>
|
||||
<tabstop>leIpId</tabstop>
|
||||
|
@ -71,11 +71,11 @@ void Ip4ConfigForm::loadWidget(AbstractProtocol *proto)
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
|
||||
leIpTos->setText(uintToHexStr(
|
||||
tosDscp->setValue(
|
||||
proto->fieldData(
|
||||
Ip4Protocol::ip4_tos,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt(), 1));
|
||||
).toUInt());
|
||||
|
||||
cbIpLengthOverride->setChecked(
|
||||
proto->fieldData(
|
||||
@ -205,7 +205,7 @@ void Ip4ConfigForm::storeWidget(AbstractProtocol *proto)
|
||||
|
||||
proto->setFieldData(
|
||||
Ip4Protocol::ip4_tos,
|
||||
hexStrToUInt(leIpTos->text()));
|
||||
tosDscp->value());
|
||||
|
||||
proto->setFieldData(
|
||||
Ip4Protocol::ip4_totLen,
|
||||
|
@ -26,7 +26,6 @@ PdmlIp4Protocol::PdmlIp4Protocol()
|
||||
{
|
||||
ostProtoId_ = OstProto::Protocol::kIp4FieldNumber;
|
||||
|
||||
fieldMap_.insert("ip.version", OstProto::Ip4::kVerHdrlenFieldNumber);
|
||||
fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber);
|
||||
fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber);
|
||||
fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber);
|
||||
@ -50,7 +49,18 @@ void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/,
|
||||
{
|
||||
bool isOk;
|
||||
|
||||
if ((name == "ip.options") ||
|
||||
if (name == "ip.version")
|
||||
{
|
||||
OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4);
|
||||
|
||||
if (!attributes.value("unmaskedvalue").isEmpty())
|
||||
ip4->set_ver_hdrlen(attributes.value("unmaskedvalue")
|
||||
.toString().toUInt(&isOk, kBaseHex));
|
||||
else
|
||||
ip4->set_ver_hdrlen(attributes.value("value")
|
||||
.toString().toUInt(&isOk, kBaseHex));
|
||||
}
|
||||
else if ((name == "ip.options") ||
|
||||
attributes.value("show").toString().startsWith("Options"))
|
||||
{
|
||||
options_ = QByteArray::fromHex(
|
||||
|
@ -56,25 +56,8 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_22" >
|
||||
<property name="text" >
|
||||
<string>Traffic Class</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>trafficClass</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="trafficClass" >
|
||||
<property name="inputMask" >
|
||||
<string>>HH; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="TosDscpWidget" name="tosDscp" native="true" />
|
||||
</item>
|
||||
<item row="1" column="3" >
|
||||
<widget class="QCheckBox" name="isNextHeaderOverride" >
|
||||
@ -394,10 +377,18 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TosDscpWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>tosdscp.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>isVersionOverride</tabstop>
|
||||
<tabstop>version</tabstop>
|
||||
<tabstop>trafficClass</tabstop>
|
||||
<tabstop>tosDscp</tabstop>
|
||||
<tabstop>flowLabel</tabstop>
|
||||
<tabstop>isPayloadLengthOverride</tabstop>
|
||||
<tabstop>payloadLength</tabstop>
|
||||
|
@ -84,11 +84,11 @@ void Ip6ConfigForm::loadWidget(AbstractProtocol *ip6Proto)
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
|
||||
trafficClass->setText(uintToHexStr(
|
||||
tosDscp->setValue(
|
||||
ip6Proto->fieldData(
|
||||
Ip6Protocol::ip6_trafficClass,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt(), 1));
|
||||
).toUInt());
|
||||
|
||||
flowLabel->setText(QString("%1").arg(
|
||||
ip6Proto->fieldData(
|
||||
@ -180,7 +180,7 @@ void Ip6ConfigForm::storeWidget(AbstractProtocol *ip6Proto)
|
||||
|
||||
ip6Proto->setFieldData(
|
||||
Ip6Protocol::ip6_trafficClass,
|
||||
trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX));
|
||||
tosDscp->value());
|
||||
|
||||
ip6Proto->setFieldData(
|
||||
Ip6Protocol::ip6_flowLabel,
|
||||
|
@ -26,7 +26,9 @@ PdmlIp6Protocol::PdmlIp6Protocol()
|
||||
ostProtoId_ = OstProto::Protocol::kIp6FieldNumber;
|
||||
|
||||
fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber);
|
||||
// Tshark 1.x uses .class while 2.x uses .tclass - we'll use either
|
||||
fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber);
|
||||
fieldMap_.insert("ipv6.tclass", OstProto::Ip6::kTrafficClassFieldNumber);
|
||||
fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber);
|
||||
fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber);
|
||||
fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber);
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
QValidator::State state;
|
||||
QHostAddress addr(input);
|
||||
|
||||
//qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos);
|
||||
//qDebug("%s: %s (%d)", __FUNCTION__, qPrintable(input), pos);
|
||||
|
||||
if (addr.protocol() == QAbstractSocket::IPv6Protocol)
|
||||
state = Acceptable;
|
||||
@ -48,7 +48,7 @@ public:
|
||||
else
|
||||
state = Invalid;
|
||||
//qDebug("%s(%d): %s (%d), ", __FUNCTION__, state,
|
||||
//input.toAscii().constData(), pos);
|
||||
//qPrintable(input), pos);
|
||||
return state;
|
||||
}
|
||||
virtual void fixup(QString &input) const
|
||||
@ -57,7 +57,7 @@ public:
|
||||
QHostAddress addr(input);
|
||||
int len = input.size();
|
||||
|
||||
//qDebug("%s: %s", __FUNCTION__, input.toAscii().constData());
|
||||
//qDebug("%s: %s", __FUNCTION__, qPrintable(input));
|
||||
|
||||
while (addr.protocol() != QAbstractSocket::IPv6Protocol)
|
||||
{
|
||||
|
@ -143,7 +143,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib,
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("Desination");
|
||||
return QString("Destination");
|
||||
case FieldValue:
|
||||
return dstMac;
|
||||
case FieldTextValue:
|
||||
@ -278,15 +278,13 @@ bool MacProtocol::setFieldData(int index, const QVariant &value,
|
||||
{
|
||||
case mac_dstAddr:
|
||||
{
|
||||
quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX);
|
||||
if (isOk)
|
||||
quint64 mac = value.toULongLong();
|
||||
data.set_dst_mac(mac);
|
||||
break;
|
||||
}
|
||||
case mac_srcAddr:
|
||||
{
|
||||
quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX);
|
||||
if (isOk)
|
||||
quint64 mac = value.toULongLong();
|
||||
data.set_src_mac(mac);
|
||||
break;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>mac</class>
|
||||
<widget class="QWidget" name="mac">
|
||||
@ -49,19 +50,13 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leDstMac" >
|
||||
<widget class="MacEdit" name="leDstMac">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>>HH HH HH HH HH HH; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string> </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
@ -89,29 +84,17 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLineEdit" name="leDstMacCount" >
|
||||
<widget class="IntEdit" name="leDstMacCount">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="cursorPosition" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLineEdit" name="leDstMacStep" >
|
||||
<widget class="IntEdit" name="leDstMacStep">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="cursorPosition" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
@ -122,14 +105,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="leSrcMac" >
|
||||
<property name="inputMask" >
|
||||
<string>>HH HH HH HH HH HH; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string> </string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="MacEdit" name="leSrcMac"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QComboBox" name="cmbSrcMacMode">
|
||||
@ -156,26 +132,17 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLineEdit" name="leSrcMacCount" >
|
||||
<widget class="IntEdit" name="leSrcMacCount">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QLineEdit" name="leSrcMacStep" >
|
||||
<widget class="IntEdit" name="leSrcMacStep">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="cursorPosition" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="5">
|
||||
@ -193,7 +160,7 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
@ -203,6 +170,18 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>IntEdit</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>intedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MacEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>macedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -20,12 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "macconfig.h"
|
||||
#include "mac.h"
|
||||
|
||||
#define MAX_MAC_ITER_COUNT 256
|
||||
|
||||
MacConfigForm::MacConfigForm(QWidget *parent)
|
||||
: AbstractProtocolConfigForm(parent)
|
||||
{
|
||||
QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}");
|
||||
|
||||
setupUi(this);
|
||||
resolveInfo->hide();
|
||||
@ -34,10 +31,10 @@ MacConfigForm::MacConfigForm(QWidget *parent)
|
||||
resolveInfo->setPixmap(resolveInfo->style()->standardIcon(
|
||||
QStyle::SP_MessageBoxInformation).pixmap(128));
|
||||
#endif
|
||||
leDstMac->setValidator(new QRegExpValidator(reMac, this));
|
||||
leSrcMac->setValidator(new QRegExpValidator(reMac, this));
|
||||
leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this));
|
||||
leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this));
|
||||
leDstMacCount->setMinimum(1);
|
||||
leSrcMacCount->setMinimum(1);
|
||||
leDstMacStep->setMinimum(0);
|
||||
leSrcMacStep->setMinimum(0);
|
||||
}
|
||||
|
||||
MacConfigForm::~MacConfigForm()
|
||||
@ -100,75 +97,75 @@ void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index)
|
||||
|
||||
void MacConfigForm::loadWidget(AbstractProtocol *proto)
|
||||
{
|
||||
leDstMac->setText(
|
||||
leDstMac->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_dstAddr,
|
||||
AbstractProtocol::FieldTextValue
|
||||
).toString());
|
||||
AbstractProtocol::FieldValue
|
||||
).toULongLong());
|
||||
cmbDstMacMode->setCurrentIndex(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_dstMacMode,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt());
|
||||
leDstMacCount->setText(
|
||||
leDstMacCount->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_dstMacCount,
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
leDstMacStep->setText(
|
||||
).toUInt());
|
||||
leDstMacStep->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_dstMacStep,
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
).toUInt());
|
||||
|
||||
leSrcMac->setText(
|
||||
leSrcMac->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_srcAddr,
|
||||
AbstractProtocol::FieldTextValue
|
||||
).toString());
|
||||
AbstractProtocol::FieldValue
|
||||
).toULongLong());
|
||||
cmbSrcMacMode->setCurrentIndex(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_srcMacMode,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt());
|
||||
leSrcMacCount->setText(
|
||||
leSrcMacCount->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_srcMacCount,
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
leSrcMacStep->setText(
|
||||
).toUInt());
|
||||
leSrcMacStep->setValue(
|
||||
proto->fieldData(
|
||||
MacProtocol::mac_srcMacStep,
|
||||
AbstractProtocol::FieldValue
|
||||
).toString());
|
||||
).toUInt());
|
||||
}
|
||||
|
||||
void MacConfigForm::storeWidget(AbstractProtocol *proto)
|
||||
{
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_dstAddr,
|
||||
leDstMac->text().remove(QChar(' ')));
|
||||
leDstMac->value());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_dstMacMode,
|
||||
cmbDstMacMode->currentIndex());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_dstMacCount,
|
||||
leDstMacCount->text());
|
||||
leDstMacCount->value());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_dstMacStep,
|
||||
leDstMacStep->text());
|
||||
leDstMacStep->value());
|
||||
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_srcAddr,
|
||||
leSrcMac->text().remove(QChar(' ')));
|
||||
leSrcMac->value());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_srcMacMode,
|
||||
cmbSrcMacMode->currentIndex());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_srcMacCount,
|
||||
leSrcMacCount->text());
|
||||
leSrcMacCount->value());
|
||||
proto->setFieldData(
|
||||
MacProtocol::mac_srcMacStep,
|
||||
leSrcMacStep->text());
|
||||
leSrcMacStep->value());
|
||||
}
|
||||
|
||||
|
@ -29,19 +29,32 @@ public:
|
||||
|
||||
quint64 value();
|
||||
void setValue(quint64 val);
|
||||
|
||||
protected:
|
||||
virtual void focusOutEvent(QFocusEvent *e);
|
||||
};
|
||||
|
||||
inline MacEdit::MacEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}");
|
||||
// Allow : or - as separator
|
||||
QRegExp reMac("([0-9,a-f,A-F]{0,2}[:-]){5,5}[0-9,a-f,A-F]{0,2}");
|
||||
|
||||
setValidator(new QRegExpValidator(reMac, this));
|
||||
}
|
||||
|
||||
inline quint64 MacEdit::value()
|
||||
{
|
||||
return text().remove(QChar(':')).toULongLong(NULL, 16);
|
||||
QStringList bytes = text().split(QRegExp("[:-]"));
|
||||
quint64 mac = 0;
|
||||
|
||||
while (bytes.count() > 6)
|
||||
bytes.removeLast();
|
||||
|
||||
for (int i = 0; i < bytes.count(); i++)
|
||||
mac |= (bytes.at(i).toULongLong(NULL, 16) & 0xff) << (5-i)*8;
|
||||
|
||||
return mac;
|
||||
}
|
||||
|
||||
inline void MacEdit::setValue(quint64 val)
|
||||
@ -50,5 +63,12 @@ inline void MacEdit::setValue(quint64 val)
|
||||
.replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper());
|
||||
}
|
||||
|
||||
inline void MacEdit::focusOutEvent(QFocusEvent *e)
|
||||
{
|
||||
// be helpful and show a well-formatted value on focus out
|
||||
setValue(value());
|
||||
QLineEdit::focusOutEvent(e);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -103,7 +103,7 @@ bool NativeFileFormat::open(
|
||||
qDebug("%s: size = %d", __FUNCTION__, size);
|
||||
|
||||
//qDebug("Read %d bytes", buf.size());
|
||||
//qDebug("%s", QString(buf.toHex()).toAscii().constData());
|
||||
//qDebug("%s", qPrintable(QString(buf.toHex())));
|
||||
|
||||
// Parse and verify magic
|
||||
if (!magic.ParseFromArray(
|
||||
@ -148,7 +148,7 @@ bool NativeFileFormat::open(
|
||||
}
|
||||
|
||||
qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__,
|
||||
QString().fromStdString(meta.DebugString()).toAscii().constData());
|
||||
meta.DebugString().c_str());
|
||||
qDebug("%s: END MetaData", __FUNCTION__);
|
||||
|
||||
// MetaData Validation(s)
|
||||
@ -193,11 +193,8 @@ bool NativeFileFormat::open(
|
||||
|
||||
_content_parse_fail:
|
||||
error = QString(tr("Failed parsing %1 contents")).arg(fileName);
|
||||
qDebug("Error: %s", QString().fromStdString(
|
||||
content.InitializationErrorString())
|
||||
.toAscii().constData());
|
||||
qDebug("Debug: %s", QString().fromStdString(
|
||||
content.DebugString()).toAscii().constData());
|
||||
qDebug("Error: %s", content.InitializationErrorString().c_str());
|
||||
qDebug("Debug: %s", content.DebugString().c_str());
|
||||
goto _fail;
|
||||
_incompatible_file_version:
|
||||
error = QString(tr("%1 is in an incompatible format version - %2.%3.%4"
|
||||
@ -217,9 +214,7 @@ _unexpected_file_type:
|
||||
goto _fail;
|
||||
_metadata_parse_fail:
|
||||
error = QString(tr("Failed parsing %1 meta data")).arg(fileName);
|
||||
qDebug("Error: %s", QString().fromStdString(
|
||||
meta.data().InitializationErrorString())
|
||||
.toAscii().constData());
|
||||
qDebug("Error: %s", meta.data().InitializationErrorString().c_str());
|
||||
goto _fail;
|
||||
_cksum_verify_fail:
|
||||
error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3"))
|
||||
@ -236,18 +231,14 @@ _zero_cksum_serialize_fail:
|
||||
goto _fail;
|
||||
_cksum_parse_fail:
|
||||
error = QString(tr("Failed parsing %1 checksum")).arg(fileName);
|
||||
qDebug("Error: %s", QString().fromStdString(
|
||||
cksum.InitializationErrorString())
|
||||
.toAscii().constData());
|
||||
qDebug("Error: %s", cksum.InitializationErrorString().c_str());
|
||||
goto _fail;
|
||||
_magic_match_fail:
|
||||
error = QString(tr("%1 is not an Ostinato file")).arg(fileName);
|
||||
goto _fail;
|
||||
_magic_parse_fail:
|
||||
error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName);
|
||||
qDebug("Error: %s", QString().fromStdString(
|
||||
magic.InitializationErrorString())
|
||||
.toAscii().constData());
|
||||
qDebug("Error: %s", magic.InitializationErrorString().c_str());
|
||||
goto _fail;
|
||||
_read_fail:
|
||||
error = QString(tr("Error reading from %1")).arg(fileName);
|
||||
@ -263,7 +254,7 @@ _open_fail:
|
||||
error = QString(tr("Error opening %1")).arg(fileName);
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -344,7 +335,7 @@ bool NativeFileFormat::save(
|
||||
}
|
||||
|
||||
qDebug("Writing %d bytes", buf.size());
|
||||
//qDebug("%s", QString(buf.toHex()).toAscii().constData());
|
||||
//qDebug("%s", qPrintable(QString(buf.toHex())));
|
||||
|
||||
// TODO: emit status("Writing to disk...");
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
@ -402,7 +393,7 @@ _content_not_init:
|
||||
.arg(QString().fromStdString(content.DebugString()));
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -431,7 +422,7 @@ bool NativeFileFormat::isNativeFileFormat(
|
||||
if (!meta.ParseFromArray(
|
||||
(void*)(buf.constData() + kFileMetaDataOffset), metaSize)) {
|
||||
qDebug("%s: File MetaData\n%s", __FUNCTION__,
|
||||
QString().fromStdString(meta.DebugString()).toAscii().constData());
|
||||
meta.DebugString().c_str());
|
||||
goto _close_exit;
|
||||
}
|
||||
if (meta.data().file_type() == fileType)
|
||||
|
@ -50,7 +50,7 @@ _missing_session:
|
||||
error = QString(tr("%1 does not contain a session")).arg(fileName);
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
_exit:
|
||||
return false;
|
||||
}
|
||||
@ -76,7 +76,7 @@ _session_not_init:
|
||||
.arg(QString().fromStdString(session.DebugString()));
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ _missing_streams:
|
||||
error = QString(tr("%1 does not contain any streams")).arg(fileName);
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ _stream_not_init:
|
||||
.arg(QString().fromStdString(streams.DebugString()));
|
||||
goto _fail;
|
||||
_fail:
|
||||
qDebug("%s", error.toAscii().constData());
|
||||
qDebug("%s", qPrintable(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,8 @@ PROTOS += \
|
||||
textproto.proto \
|
||||
userscript.proto \
|
||||
hexdump.proto \
|
||||
sample.proto
|
||||
sample.proto \
|
||||
sign.proto
|
||||
|
||||
HEADERS = \
|
||||
abstractprotocol.h \
|
||||
@ -77,6 +78,7 @@ HEADERS += \
|
||||
hexdump.h \
|
||||
payload.h \
|
||||
sample.h \
|
||||
sign.h \
|
||||
userscript.h
|
||||
|
||||
SOURCES = \
|
||||
@ -110,6 +112,7 @@ SOURCES += \
|
||||
hexdump.cpp \
|
||||
payload.cpp \
|
||||
sample.cpp \
|
||||
sign.cpp \
|
||||
userscript.cpp
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
@ -118,4 +121,5 @@ QMAKE_DISTCLEAN += object_script.*
|
||||
#QMAKE_EXTRA_TARGETS += binding
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += network xml script
|
||||
QT += widgets network xml script
|
||||
INCLUDEPATH += "../extra/qhexedit2/src"
|
||||
LIBS += \
|
||||
-lprotobuf
|
||||
@ -24,9 +24,11 @@ FORMS += \
|
||||
tcp.ui \
|
||||
udp.ui \
|
||||
textproto.ui \
|
||||
tosdscp.ui \
|
||||
hexdump.ui \
|
||||
payload.ui \
|
||||
sample.ui \
|
||||
sign.ui \
|
||||
userscript.ui
|
||||
|
||||
PROTOS = \
|
||||
@ -50,6 +52,9 @@ HEADERS = \
|
||||
streamfileformat.h \
|
||||
spinboxdelegate.h
|
||||
|
||||
HEADERS += \
|
||||
tosdscp.h
|
||||
|
||||
HEADERS += \
|
||||
abstractprotocolconfig.h \
|
||||
comboprotocolconfig.h \
|
||||
@ -79,6 +84,7 @@ HEADERS += \
|
||||
hexdumpconfig.h \
|
||||
payloadconfig.h \
|
||||
sampleconfig.h \
|
||||
signconfig.h \
|
||||
userscriptconfig.h
|
||||
|
||||
SOURCES += \
|
||||
@ -96,6 +102,9 @@ SOURCES += \
|
||||
streamfileformat.cpp \
|
||||
spinboxdelegate.cpp
|
||||
|
||||
SOURCES += \
|
||||
tosdscp.cpp
|
||||
|
||||
SOURCES += \
|
||||
protocolwidgetfactory.cpp \
|
||||
macconfig.cpp \
|
||||
@ -118,6 +127,7 @@ SOURCES += \
|
||||
hexdumpconfig.cpp \
|
||||
payloadconfig.cpp \
|
||||
sampleconfig.cpp \
|
||||
signconfig.cpp \
|
||||
userscriptconfig.cpp
|
||||
|
||||
SOURCES += \
|
||||
@ -141,3 +151,4 @@ SOURCES += \
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
||||
|
@ -68,7 +68,7 @@ int PayloadProtocol::protocolFrameSize(int streamIndex) const
|
||||
int len;
|
||||
|
||||
len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex)
|
||||
- kFcsSize;
|
||||
- protocolFramePayloadSize(streamIndex) - kFcsSize;
|
||||
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
@ -215,7 +215,7 @@ bool PayloadProtocol::setFieldData(int index, const QVariant &value,
|
||||
|
||||
bool PayloadProtocol::isProtocolFrameValueVariable() const
|
||||
{
|
||||
return (AbstractProtocol::isProtocolFrameSizeVariable()
|
||||
return (AbstractProtocol::isProtocolFrameValueVariable()
|
||||
|| isProtocolFrameSizeVariable());
|
||||
}
|
||||
|
||||
|
@ -31,20 +31,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include <QTemporaryFile>
|
||||
#include <QtGlobal>
|
||||
|
||||
static inline quint32 swap32(quint32 val)
|
||||
{
|
||||
return (((val >> 24) && 0x000000FF) |
|
||||
((val >> 16) && 0x0000FF00) |
|
||||
((val << 16) && 0x00FF0000) |
|
||||
((val << 24) && 0xFF000000));
|
||||
}
|
||||
|
||||
static inline quint16 swap16(quint16 val)
|
||||
{
|
||||
return (((val >> 8) && 0x00FF) |
|
||||
((val << 8) && 0xFF00));
|
||||
}
|
||||
|
||||
const quint32 kPcapFileMagic = 0xa1b2c3d4;
|
||||
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
|
||||
const quint16 kPcapFileVersionMajor = 2;
|
||||
@ -129,7 +115,7 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
goto _err_unzip_fail;
|
||||
}
|
||||
|
||||
qDebug("decompressing to %s", file2.fileName().toAscii().constData());
|
||||
qDebug("decompressing to %s", qPrintable(file2.fileName()));
|
||||
|
||||
gzip.setStandardOutputFile(file2.fileName());
|
||||
gzip.start(OstProtoLib::gzipPath(),
|
||||
@ -209,7 +195,7 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
goto _non_pdml;
|
||||
}
|
||||
|
||||
qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData());
|
||||
qDebug("generating PDML %s", qPrintable(pdmlFile.fileName()));
|
||||
emit status("Generating PDML...");
|
||||
emit target(0);
|
||||
|
||||
@ -279,7 +265,7 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
goto _diff_fail;
|
||||
}
|
||||
qDebug("generating text file (original) %s",
|
||||
originalTextFile.fileName().toAscii().constData());
|
||||
qPrintable(originalTextFile.fileName()));
|
||||
|
||||
emit status("Preparing original PCAP for diff...");
|
||||
emit target(0);
|
||||
@ -339,7 +325,7 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
goto _diff_fail;
|
||||
}
|
||||
qDebug("generating text file (imported) %s",
|
||||
importedTextFile.fileName().toAscii().constData());
|
||||
qPrintable(importedTextFile.fileName()));
|
||||
|
||||
emit status("Preparing imported PCAP for diff...");
|
||||
emit target(0);
|
||||
@ -384,9 +370,9 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
goto _diff_fail;
|
||||
}
|
||||
qDebug("diffing %s and %s > %s",
|
||||
originalTextFile.fileName().toAscii().constData(),
|
||||
importedTextFile.fileName().toAscii().constData(),
|
||||
diffFile.fileName().toAscii().constData());
|
||||
qPrintable(originalTextFile.fileName()),
|
||||
qPrintable(importedTextFile.fileName()),
|
||||
qPrintable(diffFile.fileName()));
|
||||
|
||||
emit status("Taking diff...");
|
||||
emit target(0);
|
||||
|
@ -91,7 +91,7 @@ bool PdmlFileFormat::save(const OstProto::StreamConfigList streams,
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData());
|
||||
qDebug("intermediate PCAP %s", qPrintable(pcapFile.fileName()));
|
||||
|
||||
connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int)));
|
||||
connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int)));
|
||||
@ -99,7 +99,7 @@ bool PdmlFileFormat::save(const OstProto::StreamConfigList streams,
|
||||
emit status("Writing intermediate PCAP file...");
|
||||
isOk = fmt->save(streams, pcapFile.fileName(), error);
|
||||
|
||||
qDebug("generating PDML %s", fileName.toAscii().constData());
|
||||
qDebug("generating PDML %s", qPrintable(fileName));
|
||||
emit status("Converting PCAP to PDML...");
|
||||
emit target(0);
|
||||
|
||||
|
@ -162,8 +162,8 @@ void PdmlProtocol::fieldHandler(QString name,
|
||||
QString valueHexStr = attributes.value("value").toString();
|
||||
|
||||
qDebug("\t(KNOWN) fieldName:%s, value:%s",
|
||||
name.toAscii().constData(),
|
||||
valueHexStr.toAscii().constData());
|
||||
qPrintable(name),
|
||||
qPrintable(valueHexStr));
|
||||
|
||||
knownFieldHandler(name, valueHexStr, pbProto);
|
||||
}
|
||||
@ -178,7 +178,7 @@ void PdmlProtocol::fieldHandler(QString name,
|
||||
size = attributes.value("size").toString().toInt();
|
||||
|
||||
qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d",
|
||||
name.toAscii().constData(), pos, size);
|
||||
qPrintable(name), pos, size);
|
||||
|
||||
unknownFieldHandler(name, pos, size, attributes, pbProto, stream);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user