osr-plastic firmware legacy

This commit is contained in:
lovebird 2026-03-07 19:54:13 +01:00
parent da06170c9d
commit 69be47bc2b
21898 changed files with 7812159 additions and 0 deletions

12
firmware/legacy/.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
*.pdf filter=lfs diff=lfs merge=lfs -text
*.igs filter=lfs diff=lfs merge=lfs -text
*.iges filter=lfs diff=lfs merge=lfs -text
*.step filter=lfs diff=lfs merge=lfs -text
SLDASM filter=lfs diff=lfs merge=lfs -text
STEP filter=lfs diff=lfs merge=lfs -text
jpg filter=lfs diff=lfs merge=lfs -text
*.SLDASM filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.SLDPRT filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

85
firmware/legacy/.gitignore vendored Normal file
View File

@ -0,0 +1,85 @@
# Logs
documents/isos
logs
archives
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
academy
~$*.SLDDRW
~$*.SLDPRT
~$*.SLDASM
# Runtime data
pids
*.pid
*.seed
*.pid.lock
_site
.jekyll-cache
.jekyll-metadata
*.tar
*.avi
*.mp4
*.suo
*.obj
*.db
*.ipch
*.lock
*.vsidx
*.o
*.d
*.lib
*.a
*.cache
ref
_algolia_api_key
docs.plastic-hub.com
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next

0
firmware/legacy/.gitmodules vendored Normal file
View File

232
firmware/legacy/LICENSE Normal file
View File

@ -0,0 +1,232 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright © 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>.

BIN
firmware/legacy/arduino-libraries/Arduino-CmdMessenger.zip (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
*.nupkg

View File

@ -0,0 +1,24 @@
language: c
before_install:
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16"
- sleep 3
- export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-1.8.1-linux64.tar.xz
- tar xf arduino-1.8.1-linux64.tar.xz
- sudo mv arduino-1.8.1 /usr/local/share/arduino
- sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino
install:
- ln -s $PWD /usr/local/share/arduino/libraries/CmdMessenger
script:
- arduino --verify --board arduino:avr:uno $PWD/examples/ArduinoController/ArduinoController.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/CmdMessengerTest/CmdMessengerTest.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/ConsoleShell/ConsoleShell.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/DataLogging/DataLogging.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/Receive/Receive.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/SendAndReceive/SendAndReceive.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/SendAndReceiveArguments/SendAndReceiveArguments.ino
- arduino --verify --board arduino:avr:uno $PWD/examples/SendAndReceiveBinaryArguments/SendAndReceiveBinaryArguments.ino
notifications:
email:
on_success: change
on_failure: change

View File

@ -0,0 +1,683 @@
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Initial Messenger Library - Thomas Ouellet Fredericks.
CmdMessenger Version 1 - Neil Dudman.
CmdMessenger Version 2 - Dreamcat4.
CmdMessenger Version 3 - Thijs Elenbaas.
3.6 - Fixes
- Better compatibility between platforms
- Unit tests
3.5 - Fixes, speed improvements for Teensy
3.4 - Internal update
3.3 - Fixed warnings
- Some code optimization
3.2 - Small fixes and sending long argument support
3.1 - Added examples
3.0 - Bugfixes on 2.2
- Wait for acknowlegde
- Sending of common type arguments (float, int, char)
- Multi-argument commands
- Escaping of special characters
- Sending of binary data of any type (uses escaping)
*/
extern "C" {
#include <stdlib.h>
#include <stdarg.h>
}
#include <stdio.h>
#include <CmdMessenger.h>
#define _CMDMESSENGER_VERSION 3_6 // software version of this library
// **** Initialization ****
/**
* CmdMessenger constructor
*/
CmdMessenger::CmdMessenger(Stream &ccomms, const char fld_separator, const char cmd_separator, const char esc_character)
{
init(ccomms, fld_separator, cmd_separator, esc_character);
}
/**
* Enables printing newline after a sent command
*/
void CmdMessenger::init(Stream &ccomms, const char fld_separator, const char cmd_separator, const char esc_character)
{
startCommand = false;
default_callback = NULL;
comms = &ccomms;
print_newlines = false;
field_separator = fld_separator;
command_separator = cmd_separator;
escape_character = esc_character;
bufferLength = MESSENGERBUFFERSIZE;
bufferLastIndex = MESSENGERBUFFERSIZE - 1;
reset();
default_callback = NULL;
for (int i = 0; i < MAXCALLBACKS; i++)
callbackList[i] = NULL;
pauseProcessing = false;
}
/**
* Resets the command buffer and message state
*/
void CmdMessenger::reset()
{
bufferIndex = 0;
current = NULL;
last = NULL;
dumped = true;
}
/**
* Enables printing newline after a sent command
*/
void CmdMessenger::printLfCr(bool addNewLine)
{
print_newlines = addNewLine;
}
/**
* Attaches an default function for commands that are not explicitly attached
*/
void CmdMessenger::attach(messengerCallbackFunction newFunction)
{
default_callback = newFunction;
}
/**
* Attaches a function to a command ID
*/
void CmdMessenger::attach(byte msgId, messengerCallbackFunction newFunction)
{
if (msgId >= 0 && msgId < MAXCALLBACKS)
callbackList[msgId] = newFunction;
}
// **** Command processing ****
/**
* Feeds serial data in CmdMessenger
*/
void CmdMessenger::feedinSerialData()
{
while (!pauseProcessing && comms->available())
{
// The Stream class has a readBytes() function that reads many bytes at once. On Teensy 2.0 and 3.0, readBytes() is optimized.
// Benchmarks about the incredible difference it makes: http://www.pjrc.com/teensy/benchmark_usb_serial_receive.html
size_t bytesAvailable = min(comms->available(), MAXSTREAMBUFFERSIZE);
comms->readBytes(streamBuffer, bytesAvailable);
// Process the bytes in the stream buffer, and handles dispatches callbacks, if commands are received
for (size_t byteNo = 0; byteNo < bytesAvailable; byteNo++)
{
int messageState = processLine(streamBuffer[byteNo]);
// If waiting for acknowledge command
if (messageState == kEndOfMessage)
{
handleMessage();
}
}
}
}
/**
* Processes bytes and determines message state
*/
uint8_t CmdMessenger::processLine(char serialChar)
{
messageState = kProccesingMessage;
//char serialChar = (char)serialByte;
bool escaped = isEscaped(&serialChar, escape_character, &CmdlastChar);
if ((serialChar == command_separator) && !escaped) {
commandBuffer[bufferIndex] = 0;
if (bufferIndex > 0) {
messageState = kEndOfMessage;
current = commandBuffer;
CmdlastChar = '\0';
}
reset();
}
else {
commandBuffer[bufferIndex] = serialChar;
bufferIndex++;
if (bufferIndex >= bufferLastIndex) reset();
}
return messageState;
}
/**
* Dispatches attached callbacks based on command
*/
void CmdMessenger::handleMessage()
{
lastCommandId = readInt16Arg();
// if command attached, we will call it
if (lastCommandId >= 0 && lastCommandId < MAXCALLBACKS && ArgOk && callbackList[lastCommandId] != NULL)
(*callbackList[lastCommandId])();
else // If command not attached, call default callback (if attached)
if (default_callback != NULL) (*default_callback)();
}
/**
* Waits for reply from sender or timeout before continuing
*/
bool CmdMessenger::blockedTillReply(unsigned int timeout, byte ackCmdId)
{
unsigned long time = millis();
unsigned long start = time;
bool receivedAck = false;
while ((time - start) < timeout && !receivedAck) {
time = millis();
receivedAck = checkForAck(ackCmdId);
}
return receivedAck;
}
/**
* Loops as long data is available to determine if acknowledge has come in
*/
bool CmdMessenger::checkForAck(byte ackCommand)
{
while (comms->available()) {
//Processes a byte and determines if an acknowlegde has come in
int messageState = processLine(comms->read());
if (messageState == kEndOfMessage) {
int id = readInt16Arg();
if (ackCommand == id && ArgOk) {
return true;
}
else {
return false;
}
}
return false;
}
return false;
}
/**
* Gets next argument. Returns true if an argument is available
*/
bool CmdMessenger::next()
{
char * temppointer = NULL;
// Currently, cmd messenger only supports 1 char for the field seperator
switch (messageState) {
case kProccesingMessage:
return false;
case kEndOfMessage:
temppointer = commandBuffer;
messageState = kProcessingArguments;
default:
if (dumped)
current = split_r(temppointer, field_separator, &last);
if (current != NULL) {
dumped = true;
return true;
}
}
return false;
}
/**
* Returns if an argument is available. Alias for next()
*/
bool CmdMessenger::available()
{
return next();
}
/**
* Returns if the latest argument is well formed.
*/
bool CmdMessenger::isArgOk()
{
return ArgOk;
}
/**
* Returns the commandID of the current command
*/
uint8_t CmdMessenger::commandID()
{
return lastCommandId;
}
// **** Command sending ****
/**
* Send start of command. This makes it easy to send multiple arguments per command
*/
void CmdMessenger::sendCmdStart(byte cmdId)
{
if (!startCommand) {
startCommand = true;
pauseProcessing = true;
comms->print(cmdId);
}
}
/**
* Send an escaped command argument
*/
void CmdMessenger::sendCmdEscArg(char* arg)
{
if (startCommand) {
comms->print(field_separator);
printEsc(arg);
}
}
/**
* Send formatted argument.
* Note that floating points are not supported and resulting string is limited to 128 chars
*/
void CmdMessenger::sendCmdfArg(char *fmt, ...)
{
const int maxMessageSize = 128;
if (startCommand) {
char msg[maxMessageSize];
va_list args;
va_start(args, fmt);
vsnprintf(msg, maxMessageSize, fmt, args);
va_end(args);
comms->print(field_separator);
comms->print(msg);
}
}
/**
* Send double argument in scientific format.
* This will overcome the boundary of normal float sending which is limited to abs(f) <= MAXLONG
*/
void CmdMessenger::sendCmdSciArg(double arg, unsigned int n)
{
if (startCommand)
{
comms->print(field_separator);
printSci(arg, n);
}
}
/**
* Send end of command
*/
bool CmdMessenger::sendCmdEnd(bool reqAc, byte ackCmdId, unsigned int timeout)
{
bool ackReply = false;
if (startCommand) {
comms->print(command_separator);
if (print_newlines)
comms->println(); // should append BOTH \r\n
if (reqAc) {
ackReply = blockedTillReply(timeout, ackCmdId);
}
}
pauseProcessing = false;
startCommand = false;
return ackReply;
}
/**
* Send a command without arguments, with acknowledge
*/
bool CmdMessenger::sendCmd(byte cmdId, bool reqAc, byte ackCmdId)
{
if (!startCommand) {
sendCmdStart(cmdId);
return sendCmdEnd(reqAc, ackCmdId, DEFAULT_TIMEOUT);
}
return false;
}
/**
* Send a command without arguments, without acknowledge
*/
bool CmdMessenger::sendCmd(byte cmdId)
{
if (!startCommand) {
sendCmdStart(cmdId);
return sendCmdEnd(false, 1, DEFAULT_TIMEOUT);
}
return false;
}
// **** Command receiving ****
/**
* Find next argument in command
*/
int CmdMessenger::findNext(char *str, char delim)
{
int pos = 0;
bool escaped = false;
bool EOL = false;
ArglastChar = '\0';
while (true) {
escaped = isEscaped(str, escape_character, &ArglastChar);
EOL = (*str == '\0' && !escaped);
if (EOL) {
return pos;
}
if (*str == field_separator && !escaped) {
return pos;
}
else {
str++;
pos++;
}
}
return pos;
}
/**
* Read the next argument as int
*/
int16_t CmdMessenger::readInt16Arg()
{
if (next()) {
dumped = true;
ArgOk = true;
return atoi(current);
}
ArgOk = false;
return 0;
}
/**
* Read the next argument as int
*/
int32_t CmdMessenger::readInt32Arg()
{
if (next()) {
dumped = true;
ArgOk = true;
return atol(current);
}
ArgOk = false;
return 0L;
}
/**
* Read the next argument as bool
*/
bool CmdMessenger::readBoolArg()
{
return (readInt16Arg() != 0) ? true : false;
}
/**
* Read the next argument as char
*/
char CmdMessenger::readCharArg()
{
if (next()) {
dumped = true;
ArgOk = true;
return current[0];
}
ArgOk = false;
return 0;
}
/**
* Read the next argument as float
*/
float CmdMessenger::readFloatArg()
{
if (next()) {
dumped = true;
ArgOk = true;
//return atof(current);
return strtod(current, NULL);
}
ArgOk = false;
return 0;
}
/**
* Read the next argument as double
*/
double CmdMessenger::readDoubleArg()
{
if (next()) {
dumped = true;
ArgOk = true;
return strtod(current, NULL);
}
ArgOk = false;
return 0;
}
/**
* Read next argument as string.
* Note that the String is valid until the current command is replaced
*/
const char* CmdMessenger::readStringArg()
{
if (next()) {
dumped = true;
ArgOk = true;
unescape(current);
return current;
}
ArgOk = false;
return "\0";
}
/**
* Return next argument as a new string
* Note that this is useful if the string needs to be persisted
*/
void CmdMessenger::copyStringArg(char *string, uint8_t size)
{
if (next()) {
dumped = true;
ArgOk = true;
unescape(current);
strlcpy(string, current, size);
}
else {
ArgOk = false;
if (size) string[0] = '\0';
}
}
/**
* Compare the next argument with a string
*/
uint8_t CmdMessenger::compareStringArg(char *string)
{
if (next()) {
unescape(current);
if (strcmp(string, current) == 0) {
dumped = true;
ArgOk = true;
return 1;
}
else {
ArgOk = false;
return 0;
}
}
return 0;
}
// **** Escaping tools ****
/**
* Unescapes a string
* Note that this is done inline
*/
void CmdMessenger::unescape(char *fromChar)
{
// Move unescaped characters right
char *toChar = fromChar;
while (*fromChar != '\0') {
if (*fromChar == escape_character) {
fromChar++;
}
*toChar++ = *fromChar++;
}
// Pad string with \0 if string was shortened
for (; toChar < fromChar; toChar++) {
*toChar = '\0';
}
}
/**
* Split string in different tokens, based on delimiter
* Note that this is basically strtok_r, but with support for an escape character
*/
char* CmdMessenger::split_r(char *str, const char delim, char **nextp)
{
char *ret;
// if input null, this is not the first call, use the nextp pointer instead
if (str == NULL) {
str = *nextp;
}
// Strip leading delimiters
while (findNext(str, delim) == 0 && *str) {
str++;
}
// If this is a \0 char, return null
if (*str == '\0') {
return NULL;
}
// Set start of return pointer to this position
ret = str;
// Find next delimiter
LastArgLength = findNext(str, delim);
str += LastArgLength;
// and exchange this for a a \0 char. This will terminate the char
if (*str) {
*str++ = '\0';
}
// Set the next pointer to this char
*nextp = str;
// return current pointer
return ret;
}
/**
* Indicates if the current character is escaped
*/
bool CmdMessenger::isEscaped(char *currChar, const char escapeChar, char *lastChar)
{
bool escaped;
escaped = (*lastChar == escapeChar);
*lastChar = *currChar;
// special case: the escape char has been escaped:
if (*lastChar == escape_character && escaped) {
*lastChar = '\0';
}
return escaped;
}
/**
* Escape and print a string
*/
void CmdMessenger::printEsc(char *str)
{
while (*str != '\0') {
printEsc(*str++);
}
}
/**
* Escape and print a character
*/
void CmdMessenger::printEsc(char str)
{
if (str == field_separator || str == command_separator || str == escape_character || str == '\0') {
comms->print(escape_character);
}
comms->print(str);
}
/**
* Print float and double in scientific format
*/
void CmdMessenger::printSci(double f, unsigned int digits)
{
// handle sign
if (f < 0.0)
{
comms->print('-');
f = -f;
}
// handle infinite values
if (isinf(f))
{
comms->print("INF");
return;
}
// handle Not a Number
if (isnan(f))
{
comms->print("NaN");
return;
}
// max digits
if (digits > 6) digits = 6;
long multiplier = pow(10, digits); // fix int => long
int exponent;
if (abs(f) < 10.0) {
exponent = 0;
}
else {
exponent = int(log10(f));
}
float g = f / pow(10, exponent);
if ((g < 1.0) && (g != 0.0))
{
g *= 10;
exponent--;
}
long whole = long(g); // single digit
long part = long((g - whole)*multiplier + 0.5); // # digits
// Check for rounding above .99:
if (part == 100) {
whole++;
part = 0;
}
char format[16];
sprintf(format, "%%ld.%%0%dldE%%+d", digits);
char output[16];
sprintf(output, format, whole, part, exponent);
comms->print(output);
}

View File

@ -0,0 +1,306 @@
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef CmdMessenger_h
#define CmdMessenger_h
#include <inttypes.h>
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
//#include "Stream.h"
extern "C"
{
// callback functions always follow the signature: void cmd(void);
typedef void(*messengerCallbackFunction) (void);
}
#define MAXCALLBACKS 50 // The maximum number of commands (default: 50)
#define MESSENGERBUFFERSIZE 64 // The length of the commandbuffer (default: 64)
#define MAXSTREAMBUFFERSIZE 512 // The length of the streambuffer (default: 64)
#define DEFAULT_TIMEOUT 5000 // Time out on unanswered messages. (default: 5s)
// Message States
enum
{
kProccesingMessage, // Message is being received, not reached command separator
kEndOfMessage, // Message is fully received, reached command separator
kProcessingArguments, // Message is received, arguments are being read parsed
};
#define white_space(c) ((c) == ' ' || (c) == '\t')
#define valid_digit(c) ((c) >= '0' && (c) <= '9')
class CmdMessenger
{
private:
// **** Private variables ***
bool startCommand; // Indicates if sending of a command is underway
uint8_t lastCommandId; // ID of last received command
uint8_t bufferIndex; // Index where to write data in buffer
uint8_t bufferLength; // Is set to MESSENGERBUFFERSIZE
uint8_t bufferLastIndex; // The last index of the buffer
uint8_t LastArgLength; //The length if the last received argument
char ArglastChar; // Bookkeeping of argument escape char
char CmdlastChar; // Bookkeeping of command escape char
bool pauseProcessing; // pauses processing of new commands, during sending
bool print_newlines; // Indicates if \r\n should be added after send command
char commandBuffer[MESSENGERBUFFERSIZE]; // Buffer that holds the data
char streamBuffer[MAXSTREAMBUFFERSIZE]; // Buffer that holds the data
uint8_t messageState; // Current state of message processing
bool dumped; // Indicates if last argument has been externally read
bool ArgOk; // Indicated if last fetched argument could be read
char *current; // Pointer to current buffer position
char *last; // Pointer to previous buffer position
char prevChar; // Previous char (needed for unescaping)
Stream *comms; // Serial data stream
char command_separator; // Character indicating end of command (default: ';')
char field_separator; // Character indicating end of argument (default: ',')
char escape_character; // Character indicating escaping of special chars
messengerCallbackFunction default_callback; // default callback function
messengerCallbackFunction callbackList[MAXCALLBACKS]; // list of attached callback functions
// **** Initialize ****
void init(Stream & comms, const char fld_separator, const char cmd_separator, const char esc_character);
void reset();
// **** Command processing ****
inline uint8_t processLine(char serialChar) __attribute__((always_inline));
inline void handleMessage() __attribute__((always_inline));
inline bool blockedTillReply(unsigned int timeout = DEFAULT_TIMEOUT, byte ackCmdId = 1) __attribute__((always_inline));
inline bool checkForAck(byte AckCommand) __attribute__((always_inline));
// **** Command sending ****
/**
* Print variable of type T binary in binary format
*/
template < class T >
void writeBin(const T & value)
{
const byte *bytePointer = (const byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
printEsc(*bytePointer);
bytePointer++;
}
}
// **** Command receiving ****
int findNext(char *str, char delim);
/**
* Read a variable of any type in binary format
*/
template < class T >
T readBin(char *str)
{
T value;
unescape(str);
byte *bytePointer = (byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
*bytePointer = 0;
if( i < LastArgLength )
*bytePointer = str[i];
bytePointer++;
}
return value;
}
template < class T >
T empty()
{
T value;
byte *bytePointer = (byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
*bytePointer = '\0';
bytePointer++;
}
return value;
}
// **** Escaping tools ****
char *split_r(char *str, const char delim, char **nextp);
bool isEscaped(char *currChar, const char escapeChar, char *lastChar);
void printEsc(char *str);
void printEsc(char str);
public:
// ****** Public functions ******
// **** Initialization ****
CmdMessenger(Stream & comms, const char fld_separator = ',',
const char cmd_separator = ';',
const char esc_character = '/');
void printLfCr(bool addNewLine = true);
void attach(messengerCallbackFunction newFunction);
void attach(byte msgId, messengerCallbackFunction newFunction);
// **** Command processing ****
void feedinSerialData();
bool next();
bool available();
bool isArgOk();
uint8_t commandID();
// **** Command sending ****
/**
* Send a command with a single argument of any type
* Note that the argument is sent as string
*/
template < class T >
bool sendCmd(byte cmdId, T arg, bool reqAc = false, byte ackCmdId = 1,
unsigned int timeout = DEFAULT_TIMEOUT)
{
if (!startCommand) {
sendCmdStart(cmdId);
sendCmdArg(arg);
return sendCmdEnd(reqAc, ackCmdId, timeout);
}
return false;
}
/**
* Send a command with a single argument of any type
* Note that the argument is sent in binary format
*/
template < class T >
bool sendBinCmd(byte cmdId, T arg, bool reqAc = false, byte ackCmdId = 1,
unsigned int timeout = DEFAULT_TIMEOUT)
{
if (!startCommand) {
sendCmdStart(cmdId);
sendCmdBinArg(arg);
return sendCmdEnd(reqAc, ackCmdId, timeout);
}
return false;
}
bool sendCmd(byte cmdId);
bool sendCmd(byte cmdId, bool reqAc, byte ackCmdId);
// **** Command sending with multiple arguments ****
void sendCmdStart(byte cmdId);
void sendCmdEscArg(char *arg);
void sendCmdfArg(char *fmt, ...);
bool sendCmdEnd(bool reqAc = false, byte ackCmdId = 1, unsigned int timeout = DEFAULT_TIMEOUT);
/**
* Send a single argument as string
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdArg(T arg)
{
if (startCommand) {
comms->print(field_separator);
comms->print(arg);
}
}
/**
* Send a single argument as string with custom accuracy
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdArg(T arg, unsigned int n)
{
if (startCommand) {
comms->print(field_separator);
comms->print(arg, n);
}
}
/**
* Send double argument in scientific format.
* This will overcome the boundary of normal d sending which is limited to abs(f) <= MAXLONG
*/
void sendCmdSciArg(double arg, unsigned int n = 6);
/**
* Send a single argument in binary format
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdBinArg(T arg)
{
if (startCommand) {
comms->print(field_separator);
writeBin(arg);
}
}
// **** Command receiving ****
bool readBoolArg();
int16_t readInt16Arg();
int32_t readInt32Arg();
char readCharArg();
float readFloatArg();
double readDoubleArg();
const char *readStringArg();
void copyStringArg(char *string, uint8_t size);
uint8_t compareStringArg(char *string);
/**
* Read an argument of any type in binary format
*/
template < class T > T readBinArg()
{
if (next()) {
dumped = true;
ArgOk = true;
return readBin < T >(current);
}
else {
ArgOk = false;
return empty < T >();
}
}
// **** Escaping tools ****
void unescape(char *fromChar);
void printSci(double f, unsigned int digits);
};
#endif

View File

@ -0,0 +1,24 @@
# MIT License
--------------
Copyright (c) 2013, 2014, 2015, 2016, 2017
Thijs Elenbaas, Valeriy Kucherenko,
Dreamcat4, Neil Dudman, Thomas Ouellet Fredericks.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,291 @@
# CmdMessenger
[![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org)
A messaging library for the Arduino and .NET/Mono platform
C# Build status: [![C# Build status](https://ci.appveyor.com/api/projects/status/07nelgrs4wu8nh5r?svg=true)](https://ci.appveyor.com/project/ThijsElenbaas/arduino-cmdmessenger)
VB# Build status: [![VB Build status](https://ci.appveyor.com/api/projects/status/3qhrsm2nw7cqc4st?svg=true)](https://ci.appveyor.com/project/ThijsElenbaas/arduino-cmdmessenger-yaykh)
Arduino Build status: [![Build Status](https://api.travis-ci.org/thijse/Arduino-CmdMessenger.svg?branch=master&dfff)](https://travis-ci.org/thijse/Arduino-CmdMessenger)
## Introduction
CmdMessenger is a messaging library for the Arduino Platform (and .NET/Mono platform). It supports multiple transport layers: serial port over USB, Bluetooth, TCP/IP (under development)
The message format is:
```
Cmd Id, param 1, [...] , param N;
```
Although the field separator ',' and command separator ';' can be changed
The library can
* both send and receive of commands
* Both write and read multiple arguments
* Both write and read all primary data types
* Attach callback functions any received command
The library supports any primary data types, and zero to many multiple arguments. Arguments can either be sent in plain text (to be human readable) or in binary form (to be efficient).
With version 3.x also comes a full implementation of the toolkit in C#, which runs both in Mono (http://monodevelop.com/Download) and Visual Studio (http://www.microsoft.com/visualstudio/eng#downloads)
This allows for full 2-way communication between the arduino controller and the PC.
If you are looking for a Python client to communicate with, please have a look at [PyCmdMessenger](https://github.com/harmsm/PyCmdMessenger)
## Requirements
* [Arduino IDE Version 1.0.5 or later](http://www.arduino.cc/en/Main/Software)*
\* Earlier versions of the Arduino IDE will probably work but have not been tested.
## Downloading
This package can be downloaded in different manners
- The Arduino Library Manager: [see here how to use it](http://www.arduino.cc/en/guide/libraries#toc3).
- The PlatformIO Library Manager: [see here how to use it](http://docs.platformio.org/en/latest/ide/arduino.html).
- By directly loading fetching the Archive from GitHub:
1. Go to [https://github.com/thijse/Arduino-CmdMessenger](https://github.com/thijse/Arduino-CmdMessenger)
2. Click the DOWNLOAD ZIP button in the panel on the
3. Rename the uncompressed folder **Arduino-CmdMessenger-master** to **CmdMessenger**.
4. You may need to create the libraries subfolder if its your first library.
5. Place the **CmdMessenger** library folder in your **arduinosketchfolder/libraries/** folder.
5. Restart the IDE.
6. For more information, [read this extended manual](http://thijs.elenbaas.net/2012/07/installing-an-arduino-library/)
- If you want to have a package that includes all referenced libraries, use the pre-packaged library
1. Download the package as a zipfile [here](https://github.com/thijse/Zipballs/blob/master/CmdMessenger/CmdMessenger.zip?raw=true) or as a tarball [here ](https://github.com/thijse/Zipballs/blob/master/CmdMessenger/CmdMessenger.tar.gz?raw=true).
2. Copy the folders inside the **libraries** folder to you your **arduinosketchfolder/libraries/** folder.
3. Restart the IDE.
3. For more information, [read this extended manual](http://thijs.elenbaas.net/2012/07/installing-an-arduino-library/)
## Getting Started
Get to know the library, by trying the examples,from simple to complex:
### Receive
The 1st example will make the PC toggle the integrated led on the Arduino board.
* On the Arduino side, it demonstrates how to:
- Define commands
- Set up a serial connection
- Receive a command with a parameter from the PC
* On the PC side, it demonstrates how to:
- Define commands
- Set up a serial connection
- Send a command with a parameter to the Arduino
### SendandReceive
This example expands the previous Receive example. The Arduino will now send back a status.
On the Arduino side,
* it demonstrates how to:
- Handle received commands that do not have a function attache
- Send a command with a parameter to the PC
* On the PC side, it demonstrates how to:
- Handle received commands that do not have a function attached
- Receive a command with a parameter from the Arduino
### SendandReceiveArguments
This example expands the previous SendandReceive example. The Arduino will now receive multiple
and sent multiple float values.
* On the arduino side, it demonstrates how to:
- Return multiple types status
- Receive multiple parameters,
- Send multiple parameters
- Call a function periodically
* On the PC side, it demonstrates how to:
- Send multiple parameters, and wait for response
- Receive multiple parameters
- Add logging events on data that has been sent or received
### SendandReceiveBinaryArguments
This example expands the previous SendandReceiveArguments example. The Arduino will receive and send multiple
Binary values, demonstrating that this is more efficient way of communication.
* On the Arduino side, it demonstrates how to:
- Send binary parameters
- Receive binary parameters
* On the PC side, it demonstrates how to:
- Receive multiple binary parameters,
- Send multiple binary parameters
- How callback events can be handled while the main program waits
- How to calculate milliseconds, similar to Arduino function Millis()
### DataLogging
This example expands the previous SendandReceiveArguments example. The PC will now send a start command to the Arduino, and wait for a response from the Arduino. The Arduino will start sending analog data which the PC will plot in a chart
This example shows how to :
- Use CmdMessenger in combination with GUI applications
- Use CmdMessenger in combination with ZedGraph
- Use the StaleGeneralStrategy
### ArduinoController
This example expands the SendandReceiveArguments example. The PC will now sends commands to the Arduino when the trackbar is pulled. Every TrackBarChanged events will queue a message to the Arduino to set the blink speed of the internal / pin 13 LED
This example shows how to :
- use CmdMessenger in combination with GUI applications
- use CmdMessenger in combination with ZedGraph
- Send queued commandssds
- use the CollapseCommandStrategy
### SimpleWatchdog
This example shows the usage of the watchdog for communication over virtual serial port:
- Use auto scanning and connecting
- Use watchdog
### SimpleBluetooth
This example shows the usage of the watchdog for communication over Bluetooth, tested with the well known JY-MCU HC-05 and HC-06
On Arduino side, this uses the SimpleWatchdog.ino script as the previous example
- Use bluetooth connection
- Use auto scanning and connecting
- Use watchdog
### TemperatureControl
This example expands the previous ArduinoController example. The PC will now send a start command to the Arduino, and wait for a response from the Arduino. The Arduino will start sending temperature data and the heater steering value data which the PC will plot in a chart. With a slider we can set the goal temperature, which will make the PID software on the controller adjust the setting of the heater.
This example shows how to design a responsive performance UI that sends and receives commands
- Send queued commands
- Add queue strategies
### ConsoleShell
This example shows how to use CmdMessenger as a shell, and communicate with it using the Serial Console
This example is different from the others:
- there is no PC counterpart
- it will only receive commands, instead of sending
- commands it will use Serial.Print
Below is an example of interacting with the sample:
```
Available commands
0; - This command list
1,<led state>; - Set led. 0 = off, 1 = on
2,<led brightness>; - Set led brighness. 0 - 1000
3; - Show led state
Command> 3;
Led status: on
Led brightness: 500
Command> 2,1000;
Led status: on
Led brightness: 1000
Command> 1,0;
Led status: off
Led brightness: 1000
```
All samples are heavily documented and should be self explanatory.
1. Open the Example sketch in the Arduino IDE and compile and upload it to your board.
2. Open de CmdMessenger.sln solution in Visual Studio or Mono Develop/Xamarin Studio
3. Set example project with same name as the Arduino sketch as start-up project, and run
4. Enjoy!
## Trouble shooting
* If the PC and Arduino are not able to connect, chances are that either the selected port on the PC side is not correct or that the Arduino and PC are not at the same baud rate. Try it out by typing commands into the Arduino Serial Monitor, using the ConsoleShell
* Some boards (e.g. Sparkfun Pro Micro) need DtrEnable set to be true.
* If the port and baud rate are correct but callbacks are not being invoked, try looking at logging of sent and received data. See the SendandReceiveArguments project for an example.
* If you have a problem that is hard to pinpoint, use the CommandMessengerTests testsuite. This project runs unit tests on several parts on the mayor parts of the CmdMessenger library. Note that the primary function is not to serve as an example, so the code may be less documented and clean as the example projects.
## Notes
An example for use with Max5 / MaxMSP was included up until version 2. (it can still be found here https://github.com/dreamcat4/CmdMessenger).
Since we have not been able to check it wil Max/MaxMSP, the example was removed.
## Changelog
### CmdMessenger v4.0.0
* [Arduino] Additional autoConnect sample
* [.Net/.Mono] Full Threading redesign.
* [.Net/.Mono] AutoConnect & watchdog functionality.
* [.Net/.Mono] Tested Linux compatibility
* [.Net/.Mono] Visual Basic samples.
* [.Net/.Mono] Native Bluetooth support (windows only)
### CmdMessenger v3.6
* [Arduino] Bugfix: approx 1 in 1000 commands failed, when multiple binary parameters are sent over
* [Arduino] Bugfix: Binary sending of non-number would give compile time error
* [Arduino] feature: Posibility to send command without argument
* [Arduino] feature: Posibility to send floats with scientific notation, to get full float range
* [.Net/.Mono] Added Unit tests
* [.Net/.Mono] Consistent variables on .NET and Arduino side.
* [.Net/.Mono] Major performance improvement (for boards like Teensy 3), by combining queued commands
### CmdMessenger v3.5
* [Arduino] Added console shell sample
* [Arduino] Minor performance improvement
* [.Net/.Mono] Minor performance improvement
### CmdMessenger v3.4
* [Arduino] Bug-fix in receiving binary values
* [.Net/.Mono] Bug-fix that makes communication 100x (!) faster, while lowering system load
* [.Net/.Mono] Added function to run on single core
### CmdMessenger v3.3
* [Arduino] Speed improvements for Teensy
### CmdMessenger v3.2
* [All] Clean transport layer interface makes it easy to implement other transport modes
(Bluetooth, ZigBee, Web), even if they do not implement a virtual serial port
* [.Net/.Mono] Adaptive throttling to work with transport layers of any speed
* [.Net/.Mono] Smart queuing for smooth running applications and no hanging UI
* [Arduino] Small fixes and sending long argument support
### CmdMessenger v3.1
* Adds 2 GUI examples
### CmdMessenger v3.0
* Wait for acknowlegde commands
* Sending of common type arguments (float, int, char)
* Multi-argument commands
* Escaping of special characters in strings
* Sending of binary data of any type (uses escaping, no need for Base-64 Encoding)
* Bugfixes
* Added code documentation
* Added multiple samples
### CmdMessenger v2
* Updated to work with Arduino IDE 022
* Enable / disable newline (print and ignore)
* New generic example (works with all Arduinos)
* More reliable process() loop.
* User can set their own cmd and field seperator
(defaults to ';' and ',')
* Base-64 encoded data to avoid collisions with ^^
* Works with Arduino Serial Monitor for easy debugging
## Credit
* Initial Messenger Library - Thomas Ouellet Fredericks.
* CmdMessenger Version 1 - Neil Dudman.
* CmdMessenger Version 2 - Dreamcat4.
* CmdMessenger Version 3 - Thijs Elenbaas
* CmdMessenger Version 4 - Thijs Elenbaas & Valeriy kucherenko
## On using and modifying libraries
- [http://www.arduino.cc/en/Main/Libraries](http://www.arduino.cc/en/Main/Libraries)
- [http://www.arduino.cc/en/Reference/Libraries](http://www.arduino.cc/en/Reference/Libraries)
## Copyright
CmdMessenger is provided Copyright © 2013,2014,2015,2016, 2017 under MIT License.

View File

@ -0,0 +1,109 @@
// *** ArduinoController ***
// This example expands the previous Receive example. The Arduino will now send back a status.
// It adds a demonstration of how to:
// - Handle received commands that do not have a function attached
// - Send a command with a parameter to the PC
// - Shows how to invoke on the UI thread
#include <CmdMessenger.h> // CmdMessenger
// Blinking led variables
const int kBlinkLed = 13; // Pin of internal Led
bool ledState = 1; // Current state of Led
float ledFrequency = 1.0; // Current blink frequency of Led
unsigned long intervalOn;
unsigned long intervalOff;
unsigned long prevBlinkTime = 0;
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
kAcknowledge,
kError,
kSetLed, // Command to request led to be set in specific state
kSetLedFrequency,
};
// Callbacks define on which received commands we take action
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kSetLed, OnSetLed);
cmdMessenger.attach(kSetLedFrequency, OnSetLedFrequency);
}
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(kError,"Command without attached callback");
}
// Callback function that sets led on or off
void OnSetLed()
{
// Read led state argument, interpret string as boolean
ledState = cmdMessenger.readBoolArg();
cmdMessenger.sendCmd(kAcknowledge,ledState);
}
// Callback function that sets leds blinking frequency
void OnSetLedFrequency()
{
// Read led state argument, interpret string as boolean
ledFrequency = cmdMessenger.readFloatArg();
// Make sure the frequency is not zero (to prevent divide by zero)
if (ledFrequency < 0.001) { ledFrequency = 0.001; }
// translate frequency in on and off times in miliseconds
intervalOn = (500.0/ledFrequency);
intervalOff = (1000.0/ledFrequency);
cmdMessenger.sendCmd(kAcknowledge,ledFrequency);
}
// Setup function
void setup()
{
// Listen on serial connection for messages from the PC
Serial.begin(115200);
// Adds newline to every command
//cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Send the status to the PC that says the Arduino has booted
// Note that this is a good debug function: it will let you also know
// if your program had a bug and the arduino restarted
cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
delay(10);
blinkLed();
}
// Returns if it has been more than interval (in ms) ago. Used for periodic actions
void blinkLed() {
if ( millis() - prevBlinkTime > intervalOff ) {
// Turn led off during halfway interval
prevBlinkTime = millis();
digitalWrite(kBlinkLed, LOW);
} else if ( millis() - prevBlinkTime > intervalOn ) {
// Turn led on at end of interval (if led state is on)
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
}
}

View File

@ -0,0 +1,373 @@
// *** CommandMessengerTest ***
// This project runs unit tests on several parts on the mayor parts of the CmdMessenger library
// Note that the primary function is not to serve as an example, so the code may be less documented
// and clean than the example projects.
#include <CmdMessenger.h> // CmdMessenger
char field_separator = ',';
char command_separator = ';';
char escape_separator = '/';
// Blinking led variables
unsigned long previousToggleLed = 0; // Last time the led was toggled
bool ledState = 0; // Current state of Led
const int kBlinkLed = 13; // Pin of internal Led
int seriesLength;
int seriesLengthCount;
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial, field_separator, command_separator);
// ------------------ C M D L I S T I N G ( T X / R X ) ---------------------
// We can define up to a default of 50 cmds total, including both directions (send + receive)
// and including also the first 4 default command codes for the generic error handling.
// If you run out of message slots, then just increase the value of MAXCALLBACKS in CmdMessenger.h
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
//
// Note that commands work both directions:
// - All commands can be sent
// - Commands that have callbacks attached can be received
//
// This means that both sides should have an identical command list:
// both sides can either send it or receive it (or even both)
enum
{
// General commands
kCommError , // Command reports serial port comm error (only works for some comm errors)
kComment , // Command to sent comment in argument
// Setup connection test
kAcknowledge , // Command to acknowledge that cmd was received
kAreYouReady , // Command to ask if other side is ready
kError , // Command to report errors
// Acknowledge test
kAskUsIfReady , // Command to ask other side to ask if ready
kYouAreReady , // Command to acknowledge that other is ready
// Clear & Binary text data test
kValuePing , // Command to send value to other side
kValuePong , // Command to return value received with pong
// Multiple Arguments test
kMultiValuePing , // Command to send value to other side
kMultiValuePong , // Command to return value received with pong
// Benchmarks
kRequestReset , // Command Request reset
kRequestResetAcknowledge , // Command to acknowledge reset
kRequestSeries , // Command Request to send series in plain text
kReceiveSeries , // Command to send an item in plain text
kDoneReceiveSeries,
kPrepareSendSeries , // Command to tell other side to prepare for receiving a series of text float commands
kSendSeries , // Command to send a series of text float commands
kAckSendSeries , // Command to acknowledge the send series of text float commands
};
// Needed for ping-pong function
enum
{
kBool,
kInt16,
kInt32,
kFloat,
kFloatSci,
kDouble,
kDoubleSci,
kChar,
kString,
kBBool,
kBInt16,
kBInt32,
kBFloat,
kBDouble,
kBChar,
kEscString,
};
void attachCommandCallbacks()
{
// Attach callback methods
//cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kAreYouReady, OnArduinoReady);
cmdMessenger.attach(kAskUsIfReady, OnAskUsIfReady);
// Clear & Binary text data test
cmdMessenger.attach(kValuePing, OnValuePing);
cmdMessenger.attach(kMultiValuePing, OnMultiValuePing);
// Benchmarks
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kRequestReset, OnRequestReset);
cmdMessenger.attach(kRequestSeries, OnRequestSeries);
cmdMessenger.attach(kPrepareSendSeries, OnPrepareSendSeries);
cmdMessenger.attach(kSendSeries, OnSendSeries);
}
// ------------------ C A L L B A C K S -----------------------
void OnArduinoReady()
{
// In response to ping. We just send a throw-away Acknowledgment to say "i'm ready"
cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}
void OnUnknownCommand()
{
// Default response for unknown commands and corrupt messages
cmdMessenger.sendCmd(kError,"Unknown command");
cmdMessenger.sendCmdStart(kYouAreReady);
cmdMessenger.sendCmdArg("Command without attached callback");
cmdMessenger.sendCmdArg(cmdMessenger.commandID());
cmdMessenger.sendCmdEnd();
}
void OnAskUsIfReady()
{
// The other side asks us to send kAreYouReady command, wait for
//acknowledge
int isAck = cmdMessenger.sendCmd(kAreYouReady, "Asking PC if ready", true, kAcknowledge,1000 );
// Now we send back whether or not we got an acknowledgments
cmdMessenger.sendCmd(kYouAreReady,isAck?1:0);
}
void OnValuePing()
{
int dataType = cmdMessenger.readInt16Arg();
switch (dataType)
{
// Plain text
case kBool:
{
bool value = cmdMessenger.readBoolArg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kInt16:
{
int value = cmdMessenger.readInt16Arg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kInt32:
{
long value = cmdMessenger.readInt32Arg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kFloat:
{
float value = cmdMessenger.readFloatArg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kDouble:
{
double value = cmdMessenger.readDoubleArg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kChar:
{
char value = cmdMessenger.readCharArg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
case kString:
{
char * value = cmdMessenger.readStringArg();
cmdMessenger.sendCmd(kValuePong, value);
break;
}
// Binary values
case kBBool:
{
bool value = cmdMessenger.readBinArg<bool>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kBInt16:
{
int16_t value = cmdMessenger.readBinArg<int16_t>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kBInt32:
{
int32_t value = cmdMessenger.readBinArg<int32_t>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kBFloat:
{
float value = cmdMessenger.readBinArg<float>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kFloatSci:
{
float value = cmdMessenger.readFloatArg();
cmdMessenger.sendCmdStart(kValuePong);
cmdMessenger.sendCmdSciArg(value,2);
cmdMessenger.sendCmdEnd();
break;
}
case kBDouble:
{
double value = cmdMessenger.readBinArg<double>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kDoubleSci:
{
double value = cmdMessenger.readDoubleArg();
cmdMessenger.sendCmdStart(kValuePong);
cmdMessenger.sendCmdSciArg(value,4);
cmdMessenger.sendCmdEnd();
break;
}
case kBChar:
{
char value = cmdMessenger.readBinArg<char>();
cmdMessenger.sendBinCmd(kValuePong, value);
break;
}
case kEscString:
{
char * value = cmdMessenger.readStringArg();
cmdMessenger.unescape(value);
cmdMessenger.sendCmdStart(kValuePong);
cmdMessenger.sendCmdEscArg(value);
cmdMessenger.sendCmdEnd();
break;
}
default:
cmdMessenger.sendCmd(kError,"Unsupported type for valuePing!");
break;
}
}
void OnMultiValuePing()
{
int16_t valueInt16 = cmdMessenger.readBinArg<int16_t>();
int32_t valueInt32 = cmdMessenger.readBinArg<int32_t>();
double valueDouble = cmdMessenger.readBinArg<double>();
cmdMessenger.sendCmdStart(kMultiValuePong);
cmdMessenger.sendCmdBinArg(valueInt16);
cmdMessenger.sendCmdBinArg(valueInt32);
cmdMessenger.sendCmdBinArg(valueDouble);
cmdMessenger.sendCmdEnd();
}
//--------------- Benchmarks ----------------------
void OnRequestReset()
{
seriesLengthCount = 0;
cmdMessenger.sendCmd(kRequestResetAcknowledge,"");
}
// Callback function calculates the sum of the two received float values
void OnRequestSeries()
{
// Get series length from 1st parameter
int seriesLength = cmdMessenger.readInt16Arg();
float seriesBase = cmdMessenger.readFloatArg();
// Send back series of floats
for(int i=0;i< seriesLength;i++) {
cmdMessenger.sendCmdStart (kReceiveSeries);
cmdMessenger.sendCmdArg<float>(((float)i*(float)seriesBase),6);
cmdMessenger.sendCmdEnd ();
}
cmdMessenger.sendCmd(kDoneReceiveSeries,"");
}
void OnPrepareSendSeries()
{
seriesLength = cmdMessenger.readInt16Arg();
seriesLengthCount = 0;
}
void OnSendSeries()
{
seriesLengthCount++;
//float seriesBase = cmdMessenger.readFloatArg();
if (seriesLengthCount == seriesLength) {
cmdMessenger.sendCmd(kAckSendSeries,"");
}
}
// ------------------ M A I N ( ) ----------------------
void setup()
{
// Listen on serial connection for messages from the pc
// 115200 is the max speed on Arduino Uno, Mega, with AT8u2 USB
// SERIAL_8N1 is the default config, but we want to make certain
// that we have 8 bits to our disposal
Serial.begin(115200);
// Maximum speed of some boards: Arduino Duemilanove, FTDI Serial
//Serial.begin(57600);
// Many bluetooth breakout boards run on 9600 at default speed
// The Serial setting below should match this
//Serial.begin(9600);
// Makes output more readable whilst debugging in Arduino Serial Monitor,
// but uses more bytes
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Set command to PC to say we're ready
//OnArduinoReady();
cmdMessenger.sendCmd(kAcknowledge,"Arduino has resetted!");
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
}
bool hasExpired(unsigned long &prevTime, unsigned long interval) {
if ( millis() - prevTime > interval ) {
prevTime = millis();
return true;
} else
return false;
}
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
// toggle LED. If the LED does not toggle every timeoutInterval,
// this means the callbacks my the Messenger are taking a longer time than that
if (hasExpired(previousToggleLed,2000)) // Every 2 secs
{
toggleLed();
}
}
// Toggle led state
void toggleLed()
{
ledState = !ledState;
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
}

View File

@ -0,0 +1,186 @@
// *** ConsoleShell ***
// This example shows how to use CmdMessenger as a shell, and communicate with it using the Serial Console
// This example is different from all others:
// - there is no PC counterpart
// - it will only receive commands, instead of sending commands it will use Serial.Pring
//
// Below is an example of interacting with the sample:
//
// Available commands
// 0; - This command list
// 1,<led state>; - Set led. 0 = off, 1 = on
// 2,<led brightness>; - Set led brighness. 0 - 1000
// 3; - Show led state
//
// Command> 3;
//
// Led status: on
// Led brightness: 500
//
// Command> 2,1000;
//
// Led status: on
// Led brightness: 1000
//
// Command> 1,0;
//
// Led status: off
// Led brightness: 1000
#include <CmdMessenger.h> // CmdMessenger
// PWM timing variables
unsigned long intervalOn = 0;
unsigned long prevBlinkTime = 0;
const unsigned long PWMinterval = 1000;
// Blinking led variables
bool ledState = 1; // On/Off state of Led
int ledBrightness = prevBlinkTime /2 ; // 50 % Brightness
const int kBlinkLed = 13; // Pin of internal Led
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// This is the list of recognized commands.
// In order to receive, attach a callback function to these events
enum
{
kCommandList , // Command to request list of available commands
kSetLed , // Command to request led to be set in specific state
kSetLedBrightness , // Command to request led to be set in to specific brightness
kStatus , // Command to request led status
};
// Callbacks define on which received commands we take action
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kCommandList, OnCommandList);
cmdMessenger.attach(kSetLed, OnSetLed);
cmdMessenger.attach(kSetLedBrightness, OnSetLedBrightness);
cmdMessenger.attach(kStatus, OnStatus);
}
// Called when a received command has no attached function
void OnUnknownCommand()
{
Serial.println("This command is unknown!");
ShowCommands();
}
// Callback function that shows a list of commands
void OnCommandList()
{
ShowCommands();
}
// Callback function that sets led on or off
void OnSetLed()
{
// Read led state argument, expects 0 or 1 and interprets as false or true
ledState = cmdMessenger.readBoolArg();
ShowLedState();
}
// Callback function that sets led on or off
void OnSetLedBrightness()
{
// Read led brightness argument, expects value between 0 to 255
ledBrightness = cmdMessenger.readInt16Arg();
// Set led brightness
SetBrightness();
// Show Led state
ShowLedState();
}
// Callback function that shows led status
void OnStatus()
{
// Send back status that describes the led state
ShowLedState();
}
// Show available commands
void ShowCommands()
{
Serial.println("Available commands");
Serial.println(" 0; - This command list");
Serial.println(" 1,<led state>; - Set led. 0 = off, 1 = on");
Serial.print (" 2,<led brightness>; - Set led brighness. 0 - ");
Serial.println(PWMinterval);
Serial.println(" 3; - Show led state");
}
// Show led state
void ShowLedState()
{
Serial.print("Led status: ");
Serial.println(ledState?"on":"off");
Serial.print("Led brightness: ");
Serial.println(ledBrightness);
}
// Set led state
void SetLedState()
{
if (ledState) {
// If led is turned on, go to correct brightness using analog write
analogWrite(kBlinkLed, ledBrightness);
} else {
// If led is turned off, use digital write to disable PWM
digitalWrite(kBlinkLed, LOW);
}
}
// Set led brightness
void SetBrightness()
{
// clamp value intervalOn on 0 and PWMinterval
intervalOn = 1;// max(min(ledBrightness,PWMinterval),0);
}
// Pulse Width Modulation to vary Led intensity
// turn on until intervalOn, then turn off until PWMinterval
bool blinkLed() {
if ( micros() - prevBlinkTime > PWMinterval ) {
// Turn led on at end of interval (if led state is on)
prevBlinkTime = micros();
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
} else if ( micros() - prevBlinkTime > intervalOn ) {
// Turn led off at halfway interval
digitalWrite(kBlinkLed, LOW);
}
return true;
}
// Setup function
void setup()
{
// Listen on serial connection for messages from the PC
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
// Show command list
ShowCommands();
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
blinkLed();
}

View File

@ -0,0 +1,118 @@
// *** DataLogging ***
// This example expands the previous SendandReceiveArguments example.
// The Arduino will now wait for the StartLogging command, before sending analog data to the PC
// and sent multiple float values in scientific format.
#include <CmdMessenger.h> // CmdMessenger
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// Thermocouple pins
const int AnalogPin1 = 0;
const int AnalogPin2 = 1;
bool acquireData = false;
const unsigned long sampleInterval = 100; // 0.1 second interval, 10 Hz frequency
unsigned long previousSampleMillis = 0;
long startAcqMillis = 0;
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
// Commands
kAcknowledge , // Command to acknowledge that cmd was received
kError , // Command to report errors
kStartLogging , // Command to request logging start (typically PC -> Arduino)
kPlotDataPoint , // Command to request datapoint plotting (typically Arduino -> PC)
};
// Commands we send from the PC and want to receive on the Arduino.
// We must define a callback function in our Arduino program for each entry in the list below.
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kStartLogging, OnStartLogging);
}
// ------------------ C A L L B A C K S -----------------------
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(kError,"Command without attached callback");
}
// Callback function that responds that Arduino is ready (has booted up)
void OnArduinoReady()
{
cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}
// Callback function calculates the sum of the two received float values
void OnStartLogging()
{
// Start data acquisition
startAcqMillis = millis();
acquireData = true;
cmdMessenger.sendCmd(kAcknowledge,"Start Logging");
}
// ------------------ M A I N ----------------------
// Setup function
void setup()
{
// Listen on serial connection for messages from the pc
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Send the status to the PC that says the Arduino has booted
cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");
}
// Returns if it has been more than interval (in ms) ago. Used for periodic actions
bool hasExpired(unsigned long &prevTime, unsigned long interval) {
if ( millis() - prevTime > interval ) {
prevTime = millis();
return true;
} else
return false;
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
// Do measurement after certain sample interval
if (hasExpired(previousSampleMillis,sampleInterval))
{
if (acquireData) {
measure();
}
}
}
// simple readout of two Analog pins.
void measure() {
float seconds = (float) (millis()-startAcqMillis) /1000.0 ;
float Analog1 = analogRead(AnalogPin1);
float Analog2 = analogRead(AnalogPin2);
cmdMessenger.sendCmdStart(kPlotDataPoint);
cmdMessenger.sendCmdArg(seconds,4);
cmdMessenger.sendCmdSciArg(Analog1);
cmdMessenger.sendCmdSciArg(Analog2);
cmdMessenger.sendCmdEnd();
}

View File

@ -0,0 +1,78 @@
// *** Receive ***
// This 1st example will make the PC toggle the integrated led on the arduino board.
// It demonstrates how to:
// - Define commands
// - Set up a serial connection
// - Receive a command with a parameter from the PC
#include <CmdMessenger.h> // CmdMessenger
// Blinking led variables
bool ledState = 0; // Current state of Led
const int kBlinkLed = 13; // Pin of internal Led
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// We can define up to a default of 50 cmds total, including both directions (send + receive)
// and including also the first 4 default command codes for the generic error handling.
// If you run out of message slots, then just increase the value of MAXCALLBACKS in CmdMessenger.h
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
//
// Note that commands work both directions:
// - All commands can be sent
// - Commands that have callbacks attached can be received
//
// This means that both sides should have an identical command list:
// both sides can either send it or receive it (or even both)
// Commands
enum
{
kSetLed, // Command to request led to be set in specific state
};
// Callbacks define on which received commands we take action
void attachCommandCallbacks()
{
cmdMessenger.attach(kSetLed, OnSetLed);
}
// Callback function that sets led on or off
void OnSetLed()
{
// Read led state argument, interpret string as boolean
ledState = cmdMessenger.readBoolArg();
// Set led
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
}
// Setup function
void setup()
{
// Listen on serial connection for messages from the PC
// 115200 is the max speed on Arduino Uno, Mega, with AT8u2 USB
// Use 57600 for the Arduino Duemilanove and others with FTDI Serial
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
}

View File

@ -0,0 +1,78 @@
// *** SentandReceive ***
// This example expands the previous Receive example. The Arduino will now send back a status.
// It adds a demonstration of how to:
// - Handle received commands that do not have a function attached
// - Send a command with a parameter to the PC
#include <CmdMessenger.h> // CmdMessenger
// Blinking led variables
bool ledState = 0; // Current state of Led
const int kBlinkLed = 13; // Pin of internal Led
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
kSetLed , // Command to request led to be set in specific state
kStatus , // Command to report status
};
// Callbacks define on which received commands we take action
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kSetLed, OnSetLed);
}
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(kStatus,"Command without attached callback");
}
// Callback function that sets led on or off
void OnSetLed()
{
// Read led state argument, interpret string as boolean
ledState = cmdMessenger.readBoolArg();
// Set led
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
// Send back status that describes the led state
cmdMessenger.sendCmd(kStatus,(int)ledState);
}
// Setup function
void setup()
{
// Listen on serial connection for messages from the PC
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Send the status to the PC that says the Arduino has booted
// Note that this is a good debug function: it will let you also know
// if your program had a bug and the arduino restarted
cmdMessenger.sendCmd(kStatus,"Arduino has started!");
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
}

View File

@ -0,0 +1,122 @@
// *** SendandReceiveArguments ***
// This example expands the previous SendandReceive example. The Arduino will now receive multiple
// and sent multiple float values.
// It adds a demonstration of how to:
// - Return multiple types status; It can return an Acknowlegde and Error command
// - Receive multiple parameters,
// - Send multiple parameters
// - Call a function periodically
#include <CmdMessenger.h> // CmdMessenger
// Blinking led variables
unsigned long previousToggleLed = 0; // Last time the led was toggled
bool ledState = 0; // Current state of Led
const int kBlinkLed = 13; // Pin of internal Led
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
// Commands
kAcknowledge , // Command to acknowledge that cmd was received
kError , // Command to report errors
kFloatAddition , // Command to request add two floats
kFloatAdditionResult , // Command to report addition result
};
// Commands we send from the PC and want to receive on the Arduino.
// We must define a callback function in our Arduino program for each entry in the list below.
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kFloatAddition, OnFloatAddition);
}
// ------------------ C A L L B A C K S -----------------------
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(kError,"Command without attached callback");
}
// Callback function that responds that Arduino is ready (has booted up)
void OnArduinoReady()
{
cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}
// Callback function calculates the sum of the two received float values
void OnFloatAddition()
{
// Retreive first parameter as float
float a = cmdMessenger.readFloatArg();
// Retreive second parameter as float
float b = cmdMessenger.readFloatArg();
// Send back the result of the addition
//cmdMessenger.sendCmd(kFloatAdditionResult,a + b);
cmdMessenger.sendCmdStart(kFloatAdditionResult);
cmdMessenger.sendCmdArg(a+b);
cmdMessenger.sendCmdArg(a-b);
cmdMessenger.sendCmdEnd();
}
// ------------------ M A I N ----------------------
// Setup function
void setup()
{
// Listen on serial connection for messages from the pc
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Send the status to the PC that says the Arduino has booted
cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");
// set pin for blink LED
pinMode(kBlinkLed, OUTPUT);
}
// Returns if it has been more than interval (in ms) ago. Used for periodic actions
bool hasExpired(unsigned long &prevTime, unsigned long interval) {
if ( millis() - prevTime > interval ) {
prevTime = millis();
return true;
} else
return false;
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
// Toggle LED periodically. If the LED does not toggle every 2000 ms,
// this means that cmdMessenger are taking a longer time than this
if (hasExpired(previousToggleLed,2000)) // Toggle every 2 secs
{
toggleLed();
}
}
// Toggle led state
void toggleLed()
{
ledState = !ledState;
digitalWrite(kBlinkLed, ledState?HIGH:LOW);
}

View File

@ -0,0 +1,110 @@
// *** SendandReceiveBinaryArguments ***
// This example expands the previous SendandReceiveArguments example. The Arduino will
// receive and send multiple Binary values, demonstrating that this is more compact and faster.
// Since the output is not human readable any more, the logging is disabled and the NewLines
// are removed
//
// It adds a demonstration of how to:
// - Send binary parameters
// - Receive binary parameters,
#include <CmdMessenger.h> // CmdMessenger
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
kRequestPlainTextFloatSeries , // Command Request to send series in plain text
kReceivePlainTextFloatSeries , // Command to send an item in plain text
kRequestBinaryFloatSeries , // Command Request to send series in binary form
kReceiveBinaryFloatSeries , // Command to send an item in binary form
};
// Callbacks define on which received commands we take action
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kRequestPlainTextFloatSeries, OnRequestPlainTextFloatSeries);
cmdMessenger.attach(kRequestBinaryFloatSeries, OnRequestBinaryFloatSeries);
}
// ------------------ C A L L B A C K S -----------------------
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(0,"Command without attached callback");
}
// Callback function calculates the sum of the two received float values
void OnRequestPlainTextFloatSeries()
{
// Get series length from 1st parameter
int16_t seriesLength = cmdMessenger.readInt16Arg();
float seriesBase = cmdMessenger.readFloatArg();
// Send back series of floats
for(int i=0;i< seriesLength;i++) {
cmdMessenger.sendCmdStart (kReceivePlainTextFloatSeries);
cmdMessenger.sendCmdArg<uint16_t>((uint16_t)i);
cmdMessenger.sendCmdArg<float>(((float)i*(float)seriesBase),6);
cmdMessenger.sendCmdEnd ();
}
}
// Callback function calculates the sum of the two received float values
void OnRequestBinaryFloatSeries()
{
// Get series length from 1st parameter
int16_t seriesLength = cmdMessenger.readBinArg<uint16_t>();
float seriesBase = cmdMessenger.readBinArg<float>();
// Disable new lines, this saves another 2 chars per command
cmdMessenger.printLfCr(false);
// Send back series of floats
for(int i=0;i< seriesLength;i++) {
cmdMessenger.sendCmdStart (kReceiveBinaryFloatSeries);
cmdMessenger.sendCmdBinArg<uint16_t>((uint16_t)i);
cmdMessenger.sendCmdBinArg<float>(((float)i*(float)seriesBase));
cmdMessenger.sendCmdEnd ();
}
// Re-enable new lines, for human readability
cmdMessenger.printLfCr(true);
}
// ------------------ M A I N ----------------------
// Setup function
void setup()
{
// Listen on serial connection for messages from the pc
Serial.begin(115200);
// Adds newline to every command
cmdMessenger.printLfCr();
// Attach my application's user-defined callback methods
attachCommandCallbacks();
}
// Returns if it has been more than interval (in ms) ago. Used for periodic actions
bool hasExpired(unsigned long &prevTime, unsigned long interval) {
if ( millis() - prevTime > interval ) {
prevTime = millis();
return true;
} else
return false;
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
}

View File

@ -0,0 +1,92 @@
// *** SimpleWatchdog ***
// This example shows how to autoconnect between the PC and Aduino.
//
// It demonstrates how to
// - Respond to a connection request from the PC
// - Use a identifier to handshake
#include <CmdMessenger.h> // CmdMessenger
// Internal led
const int ledPin = 13;
// Listen on serial connection for messages from the pc
CmdMessenger messenger(Serial);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
kIdentify, // This command will be used both to identify exact device and for watchdog if multiple devices is connected to PC with serial communication
kTurnLedOn, // This command will be used to turn the internal led on after connection has been established
};
void attachCommandCallbacks()
{
// Attach callback methods
messenger.attach(onUnknownCommand);
messenger.attach(kIdentify , onIdentifyRequest);
messenger.attach(kTurnLedOn , onTurnLedOn);
}
// ------------------ C A L L B A C K S -----------------------
// Called when a received command has no attached function
void onUnknownCommand()
{
}
// Callback function to respond to indentify request. This is part of the
// Auto connection handshake.
void onIdentifyRequest()
{
// Here we will send back our communication identifier. Make sure it
// corresponds the Id in the C# code. Use F() macro to store ID in PROGMEM
// You can make a unique identifier per device
messenger.sendCmd(kIdentify, F("BFAF4176-766E-436A-ADF2-96133C02B03C"));
// You could also check for the first device that has the correct application+version running
//messenger.sendCmd(kIdentify, F("SimpleWatchdog__1_0_1"));
}
// Callback to perform some action
void onTurnLedOn()
{
// turn led on (this happens after connection)
digitalWrite(ledPin, HIGH);
}
// ------------------ M A I N ----------------------
// Setup function
void setup()
{
// Listen on serial connection for messages from the pc
// 115200 is typically the maximum speed for serial over USB
//Serial.begin(115200);
// Many bluetooth breakout boards (such as the HC-05/HC-06)
// run on 9600 at default speed
// The Serial setting below should match this
Serial.begin(9600);
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// initialize the digital pin as an output.
pinMode(ledPin, OUTPUT);
// Make sure led is turned off after start or reset
digitalWrite(ledPin, LOW);
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
messenger.feedinSerialData();
}

View File

@ -0,0 +1,295 @@
// *** TemperatureControl ***
// This example expands the previous ArduinoController example. The PC will now send a start command to the controller,
// the controller will now start measuring the temperature and controlling the heater. The controller will also start
// sending temperature and heater steer data which the PC will plot in a chart. With a slider we can set the goal
// temperature, which will make the PID library on the controller adjust the setting of the heater.
// To use this example without having a thermocouple or heating element, it comes with a simulated boiler
// In order to use the simulator, disable the #define REAL_HEATER
// NOTE: If you used a package manager to download CmdMessenger library,
// make sure have also fetched these libraries:
//
// * PID
// * Adafruit_MAX31855 (not necessary in simulated mode)
//
// A package that includes all referenced libraries can be found at:
// https://github.com/thijse/Zipballs/blob/master/CmdMessenger/CmdMessenger.zip?raw=true
#include <utility\HeaterSim.h>
//#define REAL_HEATER;
#ifdef REAL_HEATER
#include <Adafruit_MAX31855.h>
#else
#include <utility\HeaterSim.h>
#endif
#include <CmdMessenger.h>
#include <PID_v1.h>
#include <utility\\DoEvery.h>
// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger(Serial);
const int heaterPwmInterval = 300; // PWM cycle duration
const int measureInterval = 100; // Interval between measurements
DoEvery tempTimer(measureInterval);
DoEvery pidTimer(heaterPwmInterval);
// PID settings
double pidP = 1500;
double pidI = 25;
double pidD = 0;
// Thermocouple pins
int thermoDO = 3;
int thermoCS = 4;
int thermoCLK = 5;
// Solid State switch pin
const int switchPin = 4;
bool acquireData = false; // Logging start/stop flag
bool controlHeater = false; // Heater start/stop flag
long startAcqMillis = 0;
double CurrentTemperature = 20; // Measured temperature
double goalTemperature = 20; // Goal temperature
bool heaterOn = false; // Initial binary heater state
double heaterSteerValue = 0; // Initial normalized heater value
// Initialize thermocouple library
#ifdef REAL_HEATER
Adafruit_MAX31855 thermocouple(thermoCLK, thermoCS, thermoDO);
#else
HeaterSim heaterSim(20); // Heater is placed in ambient temperature of 20 degrees Celsius
#endif
// Initialize PID library
PID pid(&CurrentTemperature, &heaterSteerValue, &goalTemperature,pidP,pidI,pidD,DIRECT);
// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
// Commands
kWatchdog , // Command to request application ID
kAcknowledge , // Command to acknowledge a received command
kError , // Command to message that an error has occurred
kStartLogging , // Command to request logging start
kStopLogging , // Command to request logging stop
kPlotDataPoint , // Command to request data-point plotting
kSetGoalTemperature , // Command to set the goal temperature
KSetStartTime , // Command to set the new start time for the logger
};
// Commands we send from the PC and want to receive on the Arduino.
// We must define a callback function in our Arduino program for each entry in the list below.
void attachCommandCallbacks()
{
// Attach callback methods
cmdMessenger.attach(OnUnknownCommand);
cmdMessenger.attach(kWatchdog, OnWatchdogRequest);
cmdMessenger.attach(kStartLogging, OnStartLogging);
cmdMessenger.attach(kStopLogging, OnStopLogging);
cmdMessenger.attach(kSetGoalTemperature, OnSetGoalTemperature);
cmdMessenger.attach(KSetStartTime, OnSetStartTime);
}
// ------------------ C A L L B A C K S -----------------------
// Called when a received command has no attached function
void OnUnknownCommand()
{
cmdMessenger.sendCmd(kError,"Command without attached callback");
}
void OnWatchdogRequest()
{
// Will respond with same command ID and Unique device identifier.
cmdMessenger.sendCmd(kWatchdog, "77FAEDD5-FAC8-46BD-875E-5E9B6D44F85C");
}
// Callback function that responds that Arduino is ready (has booted up)
void OnArduinoReady()
{
cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}
// Start data acquisition
void OnStartLogging()
{
// Start data acquisition
acquireData = true;
cmdMessenger.sendCmd(kAcknowledge,"Start Logging");
}
// Stop data acquisition
void OnStopLogging()
{
acquireData = false;
cmdMessenger.sendCmd(kAcknowledge,"Stop Logging");
}
// Callback function that sets the goal temperature
void OnSetGoalTemperature()
{
// Read led state argument, interpret string as float
float newTemperature = cmdMessenger.readBinArg<float>();
// Make sure that the argument is valid before we change
// the goal temperature
if (cmdMessenger.isArgOk()) {
goalTemperature = newTemperature;
// Enable heater control (was disabled at initialization)
controlHeater = true;
// Send acknowledgment back to PC
cmdMessenger.sendBinCmd(kAcknowledge,goalTemperature);
} else {
// Error in received goal temperature! Send error back to PC
cmdMessenger.sendCmd(kError,"Error in received new goal temperature");
}
}
// Callback function that sets the start time
void OnSetStartTime()
{
// Read led state argument, interpret string as float
float startTime = cmdMessenger.readBinArg<float>();
// Make sure that the argument is valid before we change
if (cmdMessenger.isArgOk()) {
unsigned long milis = millis();
// translate time in seconds to time in milliseconds wrt to internal clock;
startAcqMillis = (unsigned long)((float)startTime*1000.0f);
if (startAcqMillis > milis) { startAcqMillis = milis; }
startAcqMillis = milis- startAcqMillis;
}
}
// ------------------ M A I N ----------------------
// Setup function
void setup()
{
// Listen on serial connection for messages from the pc
// 115200 is typically the maximum speed for serial over USB
Serial.begin(115200);
// Many bluetooth breakout boards run on 9600 at default speed
// The Serial setting below should match this
//Serial.begin(9600);
// Do not print newLine at end of command,
// in order to reduce data being sent
cmdMessenger.printLfCr(false);
//initialize timers
tempTimer.reset();
pidTimer.reset();
//initialize the PID variables
pid.SetOutputLimits(0,heaterPwmInterval);
// Read the current temperature
#ifdef REAL_HEATER
CurrentTemperature= thermocouple.readCelsius();
#else
CurrentTemperature = heaterSim.GetTemp();
#endif
//prepare PID port for writing
pinMode(switchPin, OUTPUT);
//turn the PID on and set to automatic
pid.SetMode(AUTOMATIC);
// Set pid sample time to the measure interval
pid.SetSampleTime(measureInterval);
// Attach my application's user-defined callback methods
attachCommandCallbacks();
// Send the status to the PC that says the Arduino has booted
cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");
}
// Loop function
void loop()
{
// Process incoming serial data, and perform callbacks
cmdMessenger.feedinSerialData();
// Every 100 ms, update the temperature
if(tempTimer.check()) measure();
// Update the PID timer.
pidTimer.check();
// Check if are controlling the heater
if (controlHeater) {
//compute new PID parameters
pid.Compute();
//Control the heater using Pulse Width Modulation
heaterPWM();
}
}
// Measure temperature in boiler
void measure() {
if (acquireData) {
// Calculate time
float seconds = (float) (millis()-startAcqMillis) /1000.0 ;
// Measure temperature
#ifdef REAL_HEATER
CurrentTemperature= thermocouple.readCelsius(); // measure with thermocouple
#else
CurrentTemperature = heaterSim.GetTemp(); // measure temperature in simulated boiler
#endif
// Send data to PC
cmdMessenger.sendCmdStart(kPlotDataPoint);
cmdMessenger.sendCmdBinArg((float)seconds); // Time
cmdMessenger.sendCmdBinArg((float)CurrentTemperature); // Measured temperature
cmdMessenger.sendCmdBinArg((float)goalTemperature); // Goal temperature
cmdMessenger.sendCmdBinArg((float)((double)heaterSteerValue/(double)heaterPwmInterval)); // normalized heater steer value
cmdMessenger.sendCmdBinArg((bool)heaterOn); // On / off state during PWM cycle
cmdMessenger.sendCmdEnd();
}
}
void SetHeaterState(bool heaterOn)
{
// Turn heater, connected to relay at pin switchPin
digitalWrite(switchPin,heaterOn?HIGH:LOW);
}
// Set binary heater state
void heaterPWM()
{
// Switch heater on or off, based on moment in the PWM cycle
heaterOn = pidTimer.before(heaterSteerValue);
#ifdef REAL_HEATER
SetHeaterState(heaterOn); // Turn on heater of boiler
#else
heaterSim.SetHeaterState(heaterOn); // Turn on heater of simulated boiler
#endif
}

View File

@ -0,0 +1,5 @@
[Bb]in/
[Oo]bj/
_ReSharper*/
*.suo
*.csproj.user

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F994C186-EB6F-4E71-B274-DBF46F3534BB}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ArduinoController</RootNamespace>
<AssemblyName>ArduinoController</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ControllerForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ControllerForm.Designer.cs">
<DependentUpon>ControllerForm.cs</DependentUpon>
</Compile>
<Compile Include="ArduinoController.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="ControllerForm.resx">
<DependentUpon>ControllerForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="app.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger.Transport.Serial\CommandMessenger.Transport.Serial.csproj">
<Project>{00D85F0B-00A5-41FA-8A99-428C0199C663}</Project>
<Name>CommandMessenger.Transport.Serial</Name>
</ProjectReference>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,156 @@
// *** ArduinoController ***
// This example expands the SendandReceiveArguments example. The PC will now sends commands to the Arduino when the trackbar
// is pulled. Every TrackBarChanged events will queue a message to the Arduino to set the blink speed of the
// internal / pin 13 LED
//
// This example shows how to :
// - use in combination with WinForms
// - use in combination with ZedGraph
// - send queued commands
// - Use the CollapseCommandStrategy
using System;
using CommandMessenger;
using CommandMessenger.Queue;
using CommandMessenger.Transport.Serial;
namespace ArduinoController
{
enum Command
{
Acknowledge, // Command to acknowledge a received command
Error, // Command to message that an error has occurred
SetLed, // Command to turn led ON or OFF
SetLedFrequency, // Command to set led blink frequency
};
public class ArduinoController
{
// This class (kind of) contains presentation logic, and domain model.
// ChartForm.cs contains the view components
private SerialTransport _serialTransport;
private CmdMessenger _cmdMessenger;
private ControllerForm _controllerForm;
// ------------------ MAIN ----------------------
// Setup function
public void Setup(ControllerForm controllerForm)
{
// storing the controller form for later reference
_controllerForm = controllerForm;
// Create Serial Port object
// Note that for some boards (e.g. Sparkfun Pro Micro) DtrEnable may need to be true.
_serialTransport = new SerialTransport
{
CurrentSerialSettings = { PortName = "COM6", BaudRate = 115200, DtrEnable = false } // object initializer
};
// Initialize the command messenger with the Serial Port transport layer
// Set if it is communicating with a 16- or 32-bit Arduino board
_cmdMessenger = new CmdMessenger(_serialTransport, BoardType.Bit16);
// Tell CmdMessenger to "Invoke" commands on the thread running the WinForms UI
_cmdMessenger.ControlToInvokeOn = _controllerForm;
// Attach the callbacks to the Command Messenger
AttachCommandCallBacks();
// Attach to NewLinesReceived for logging purposes
_cmdMessenger.NewLineReceived += NewLineReceived;
// Attach to NewLineSent for logging purposes
_cmdMessenger.NewLineSent += NewLineSent;
// Start listening
_cmdMessenger.Connect();
_controllerForm.SetLedState(true);
_controllerForm.SetFrequency(2);
}
// Exit function
public void Exit()
{
// Stop listening
_cmdMessenger.Disconnect();
// Dispose Command Messenger
_cmdMessenger.Dispose();
// Dispose Serial Port object
_serialTransport.Dispose();
}
/// Attach command call backs.
private void AttachCommandCallBacks()
{
_cmdMessenger.Attach(OnUnknownCommand);
_cmdMessenger.Attach((int)Command.Acknowledge, OnAcknowledge);
_cmdMessenger.Attach((int)Command.Error, OnError);
}
// ------------------ CALLBACKS ---------------------
// Called when a received command has no attached function.
// In a WinForm application, console output gets routed to the output panel of your IDE
void OnUnknownCommand(ReceivedCommand arguments)
{
Console.WriteLine(@"Command without attached callback received");
}
// Callback function that prints that the Arduino has acknowledged
void OnAcknowledge(ReceivedCommand arguments)
{
Console.WriteLine(@" Arduino is ready");
}
// Callback function that prints that the Arduino has experienced an error
void OnError(ReceivedCommand arguments)
{
Console.WriteLine(@"Arduino has experienced an error");
}
// Log received line to console
private void NewLineReceived(object sender, CommandEventArgs e)
{
Console.WriteLine(@"Received > " + e.Command.CommandString());
}
// Log sent line to console
private void NewLineSent(object sender, CommandEventArgs e)
{
Console.WriteLine(@"Sent > " + e.Command.CommandString());
}
// Sent command to change led blinking frequency
public void SetLedFrequency(double ledFrequency)
{
// Create command to start sending data
var command = new SendCommand((int)Command.SetLedFrequency,ledFrequency);
// Put the command on the queue and wrap it in a collapse command strategy
// This strategy will avoid duplicates of this certain command on the queue: if a SetLedFrequency command is
// already on the queue when a new one is added, it will be replaced at its current queue-position.
// Otherwise the command will be added to the back of the queue.
//
// This will make sure that when the slider raises a lot of events that each send a new blink frequency, the
// embedded controller will not start lagging.
_cmdMessenger.QueueCommand(new CollapseCommandStrategy(command));
}
// Sent command to change led on/of state
public void SetLedState(bool ledState)
{
// Create command to start sending data
var command = new SendCommand((int)Command.SetLed, ledState);
// Send command
_cmdMessenger.SendCommand(new SendCommand((int)Command.SetLed, ledState));
}
}
}

View File

@ -0,0 +1,96 @@
namespace ArduinoController
{
partial class ControllerForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.EnableLedCheckBox = new System.Windows.Forms.CheckBox();
this.LedFrequencyLabelTrackBar = new System.Windows.Forms.TrackBar();
this.LedFrequencyLabel = new System.Windows.Forms.Label();
this.LedFrequencyValue = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.LedFrequencyLabelTrackBar)).BeginInit();
this.SuspendLayout();
//
// EnableLedCheckBox
//
this.EnableLedCheckBox.AutoSize = true;
this.EnableLedCheckBox.Checked = true;
this.EnableLedCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
this.EnableLedCheckBox.Location = new System.Drawing.Point(30, 12);
this.EnableLedCheckBox.Name = "EnableLedCheckBox";
this.EnableLedCheckBox.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.EnableLedCheckBox.Size = new System.Drawing.Size(80, 17);
this.EnableLedCheckBox.TabIndex = 0;
this.EnableLedCheckBox.Text = "Enable Led";
this.EnableLedCheckBox.UseVisualStyleBackColor = true;
this.EnableLedCheckBox.CheckedChanged += new System.EventHandler(this.EnableLedCheckBoxCheckedChanged);
//
// LedFrequencyLabelTrackBar
//
this.LedFrequencyLabelTrackBar.Location = new System.Drawing.Point(90, 35);
this.LedFrequencyLabelTrackBar.Maximum = 240;
this.LedFrequencyLabelTrackBar.Name = "LedFrequencyLabelTrackBar";
this.LedFrequencyLabelTrackBar.Size = new System.Drawing.Size(208, 45);
this.LedFrequencyLabelTrackBar.TabIndex = 1;
this.LedFrequencyLabelTrackBar.Tag = "";
this.LedFrequencyLabelTrackBar.TickFrequency = 10;
this.LedFrequencyLabelTrackBar.Scroll += new System.EventHandler(this.LedFrequencyTrackBarScroll);
this.LedFrequencyLabelTrackBar.ValueChanged += new System.EventHandler(this.LedFrequencyLabelTrackBarValueChanged);
//
// LedFrequencyLabel
//
this.LedFrequencyLabel.AutoSize = true;
this.LedFrequencyLabel.Location = new System.Drawing.Point(14, 36);
this.LedFrequencyLabel.Name = "LedFrequencyLabel";
this.LedFrequencyLabel.Size = new System.Drawing.Size(78, 13);
this.LedFrequencyLabel.TabIndex = 2;
this.LedFrequencyLabel.Text = "Led Frequency";
//
// LedFrequencyValue
//
this.LedFrequencyValue.AutoSize = true;
this.LedFrequencyValue.Location = new System.Drawing.Point(304, 38);
this.LedFrequencyValue.Name = "LedFrequencyValue";
this.LedFrequencyValue.Size = new System.Drawing.Size(13, 13);
this.LedFrequencyValue.TabIndex = 3;
this.LedFrequencyValue.Text = "0";
//
// ControllerForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(344, 85);
this.Controls.Add(this.LedFrequencyValue);
this.Controls.Add(this.LedFrequencyLabel);
this.Controls.Add(this.LedFrequencyLabelTrackBar);
this.Controls.Add(this.EnableLedCheckBox);
this.Name = "ControllerForm";
this.Text = "Arduino Controller";
((System.ComponentModel.ISupportInitialize)(this.LedFrequencyLabelTrackBar)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox EnableLedCheckBox;
private System.Windows.Forms.TrackBar LedFrequencyLabelTrackBar;
private System.Windows.Forms.Label LedFrequencyLabel;
private System.Windows.Forms.Label LedFrequencyValue;
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ArduinoController
{
public partial class ControllerForm : Form
{
private readonly ArduinoController _arduinoController;
private double _ledFrequency;
public ControllerForm()
{
InitializeComponent();
_arduinoController = new ArduinoController();
_arduinoController.Setup(this);
}
// Update arduinoController on value checkbox checked/unchecked
private void EnableLedCheckBoxCheckedChanged(object sender, EventArgs e)
{
_arduinoController.SetLedState(EnableLedCheckBox.Checked);
}
// Update value label and arduinoController on value changed using slider
private void LedFrequencyTrackBarScroll(object sender, EventArgs e)
{
_ledFrequency = 0.4 + ((double)LedFrequencyLabelTrackBar.Value) / 25.0;
LedFrequencyValue.Text = _ledFrequency.ToString(CultureInfo.InvariantCulture);
_arduinoController.SetLedFrequency(_ledFrequency);
}
// Set ledState checkbox
public void SetLedState(bool ledState)
{
EnableLedCheckBox.Checked = ledState;
}
// Set frequency slider
public void SetFrequency(double ledFrequency)
{
LedFrequencyLabelTrackBar.Value = (int) ((ledFrequency - 0.4)*2.5);
}
// Update value label and arduinoController on value changed
private void LedFrequencyLabelTrackBarValueChanged(object sender, EventArgs e)
{
LedFrequencyTrackBarScroll(sender,e);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing )
{
_arduinoController.Exit();
if (components!=null)
components.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,19 @@
using System;
using System.Windows.Forms;
namespace ArduinoController
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ControllerForm());
}
}
}

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("6-ArduinoController")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a231b913-d766-4279-802d-33591474da50")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ArduinoController.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ArduinoController.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ArduinoController.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

View File

@ -0,0 +1,176 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessenger", "CommandMessenger\CommandMessenger.csproj", "{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "1-Receive", "Receive\1-Receive.csproj", "{C5545DBA-7364-4FD1-B061-9C5FA5C06141}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "2-SendAndReceive", "SendAndReceive\2-SendAndReceive.csproj", "{37286642-47EE-44D8-87D9-3CFAFC8D68BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "3-SendAndReceiveArguments", "SendAndReceiveArguments\3-SendAndReceiveArguments.csproj", "{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "4-SendAndReceiveBinaryArguments", "SendAndReceiveBinaryArguments\4-SendAndReceiveBinaryArguments.csproj", "{E8A22A1D-5EA8-4FF1-808A-503225720837}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "5-DataLogging", "DataLogging\5-DataLogging.csproj", "{8CBED77C-78BF-453F-8733-97798A95916F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "6-ArduinoController", "ArduinoController\6-ArduinoController.csproj", "{F994C186-EB6F-4E71-B274-DBF46F3534BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "9-TemperatureControl", "TemperatureControl\9-TemperatureControl.csproj", "{78810760-E45A-4ACB-8D94-0E359E2F73FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessengerTests", "CommandMessengerTests\CommandMessengerTests.csproj", "{B869057C-DAB3-4C19-958C-C89D3B1521EC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "7-SimpleWatchdog", "SimpleWatchdog\7-SimpleWatchdog.csproj", "{0D783DF1-9DEB-406D-8410-33A789B1FFD4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "8-SimpleBluetooth", "SimpleBluetooth\8-SimpleBluetooth.csproj", "{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommandMessenger.Transport", "CommandMessenger.Transport", "{36B3296D-342F-4F63-8BC3-ADA3DB458A88}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessenger.Transport.Serial", "CommandMessenger.Transport.Serial\CommandMessenger.Transport.Serial.csproj", "{00D85F0B-00A5-41FA-8A99-428C0199C663}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessenger.Transport.Network", "CommandMessenger.Transport.Network\CommandMessenger.Transport.Network.csproj", "{1C05AD41-693D-440B-A787-AF196B0F9576}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessenger.Transport.Bluetooth", "CommandMessenger.Transport.Bluetooth\CommandMessenger.Transport.Bluetooth.csproj", "{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandMessenger.Transport.IPC", "CommandMessenger.Transport.IPC\CommandMessenger.Transport.IPC.csproj", "{27DF28C4-F702-4AF5-8104-133833253AA6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Debug|x86.ActiveCfg = Release|x86
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Debug|x86.Build.0 = Release|x86
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Release|Any CPU.Build.0 = Release|Any CPU
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Release|x86.ActiveCfg = Release|x86
{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}.Release|x86.Build.0 = Release|x86
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Debug|x86.ActiveCfg = Release|x86
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Debug|x86.Build.0 = Release|x86
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Release|Any CPU.Build.0 = Release|Any CPU
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Release|x86.ActiveCfg = Release|x86
{C5545DBA-7364-4FD1-B061-9C5FA5C06141}.Release|x86.Build.0 = Release|x86
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Debug|x86.ActiveCfg = Release|x86
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Debug|x86.Build.0 = Release|x86
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Release|Any CPU.Build.0 = Release|Any CPU
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Release|x86.ActiveCfg = Release|x86
{37286642-47EE-44D8-87D9-3CFAFC8D68BD}.Release|x86.Build.0 = Release|x86
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Debug|x86.ActiveCfg = Release|x86
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Debug|x86.Build.0 = Release|x86
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Release|Any CPU.Build.0 = Release|Any CPU
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Release|x86.ActiveCfg = Release|x86
{A1DBBE25-AD1A-4AA0-9CB4-5BD47B495178}.Release|x86.Build.0 = Release|x86
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Debug|x86.ActiveCfg = Release|x86
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Debug|x86.Build.0 = Release|x86
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Release|Any CPU.Build.0 = Release|Any CPU
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Release|x86.ActiveCfg = Release|x86
{E8A22A1D-5EA8-4FF1-808A-503225720837}.Release|x86.Build.0 = Release|x86
{8CBED77C-78BF-453F-8733-97798A95916F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CBED77C-78BF-453F-8733-97798A95916F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CBED77C-78BF-453F-8733-97798A95916F}.Debug|x86.ActiveCfg = Release|x86
{8CBED77C-78BF-453F-8733-97798A95916F}.Debug|x86.Build.0 = Release|x86
{8CBED77C-78BF-453F-8733-97798A95916F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CBED77C-78BF-453F-8733-97798A95916F}.Release|Any CPU.Build.0 = Release|Any CPU
{8CBED77C-78BF-453F-8733-97798A95916F}.Release|x86.ActiveCfg = Release|x86
{8CBED77C-78BF-453F-8733-97798A95916F}.Release|x86.Build.0 = Release|x86
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Debug|x86.ActiveCfg = Release|x86
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Debug|x86.Build.0 = Release|x86
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Release|Any CPU.Build.0 = Release|Any CPU
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Release|x86.ActiveCfg = Release|x86
{F994C186-EB6F-4E71-B274-DBF46F3534BB}.Release|x86.Build.0 = Release|x86
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Debug|x86.ActiveCfg = Release|x86
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Debug|x86.Build.0 = Release|x86
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Release|Any CPU.Build.0 = Release|Any CPU
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Release|x86.ActiveCfg = Release|x86
{78810760-E45A-4ACB-8D94-0E359E2F73FB}.Release|x86.Build.0 = Release|x86
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Debug|x86.ActiveCfg = Release|x86
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Debug|x86.Build.0 = Release|x86
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Release|Any CPU.Build.0 = Release|Any CPU
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Release|x86.ActiveCfg = Release|x86
{B869057C-DAB3-4C19-958C-C89D3B1521EC}.Release|x86.Build.0 = Release|x86
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Debug|x86.ActiveCfg = Release|x86
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Debug|x86.Build.0 = Release|x86
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Release|Any CPU.Build.0 = Release|Any CPU
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Release|x86.ActiveCfg = Release|x86
{0D783DF1-9DEB-406D-8410-33A789B1FFD4}.Release|x86.Build.0 = Release|x86
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Debug|x86.ActiveCfg = Release|x86
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Debug|x86.Build.0 = Release|x86
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Release|Any CPU.Build.0 = Release|Any CPU
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Release|x86.ActiveCfg = Release|x86
{D1494302-6BD6-415F-A43A-4C3CD3EB7FD9}.Release|x86.Build.0 = Release|x86
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Debug|x86.ActiveCfg = Release|x86
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Debug|x86.Build.0 = Release|x86
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Release|Any CPU.Build.0 = Release|Any CPU
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Release|x86.ActiveCfg = Release|x86
{00D85F0B-00A5-41FA-8A99-428C0199C663}.Release|x86.Build.0 = Release|x86
{1C05AD41-693D-440B-A787-AF196B0F9576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C05AD41-693D-440B-A787-AF196B0F9576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C05AD41-693D-440B-A787-AF196B0F9576}.Debug|x86.ActiveCfg = Release|x86
{1C05AD41-693D-440B-A787-AF196B0F9576}.Debug|x86.Build.0 = Release|x86
{1C05AD41-693D-440B-A787-AF196B0F9576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C05AD41-693D-440B-A787-AF196B0F9576}.Release|Any CPU.Build.0 = Release|Any CPU
{1C05AD41-693D-440B-A787-AF196B0F9576}.Release|x86.ActiveCfg = Release|x86
{1C05AD41-693D-440B-A787-AF196B0F9576}.Release|x86.Build.0 = Release|x86
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Debug|x86.ActiveCfg = Release|x86
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Debug|x86.Build.0 = Release|x86
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Release|Any CPU.Build.0 = Release|Any CPU
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Release|x86.ActiveCfg = Release|x86
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}.Release|x86.Build.0 = Release|x86
{27DF28C4-F702-4AF5-8104-133833253AA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27DF28C4-F702-4AF5-8104-133833253AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27DF28C4-F702-4AF5-8104-133833253AA6}.Debug|x86.ActiveCfg = Debug|x86
{27DF28C4-F702-4AF5-8104-133833253AA6}.Debug|x86.Build.0 = Debug|x86
{27DF28C4-F702-4AF5-8104-133833253AA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27DF28C4-F702-4AF5-8104-133833253AA6}.Release|Any CPU.Build.0 = Release|Any CPU
{27DF28C4-F702-4AF5-8104-133833253AA6}.Release|x86.ActiveCfg = Release|x86
{27DF28C4-F702-4AF5-8104-133833253AA6}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{00D85F0B-00A5-41FA-8A99-428C0199C663} = {36B3296D-342F-4F63-8BC3-ADA3DB458A88}
{1C05AD41-693D-440B-A787-AF196B0F9576} = {36B3296D-342F-4F63-8BC3-ADA3DB458A88}
{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA} = {36B3296D-342F-4F63-8BC3-ADA3DB458A88}
{27DF28C4-F702-4AF5-8104-133833253AA6} = {36B3296D-342F-4F63-8BC3-ADA3DB458A88}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,29 @@
<Properties StartupItem="TemperatureControl\9-TemperatureControl.csproj">
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
<MonoDevelop.Ide.Workbench ActiveDocument="TemperatureControl\TemperatureControl.cs">
<Files>
<File FileName="ArduinoController\ArduinoController.cs" Line="1" Column="1" />
<File FileName="TemperatureControl\TemperatureControl.cs" Line="177" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\ConnectionManager.cs" Line="1" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\AsyncWorker.cs" Line="117" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\CmdMessenger.cs" Line="1" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\EventWaiter.cs" Line="1" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\Queue\CommandQueue.cs" Line="1" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\CommunicationManager.cs" Line="1" Column="1" />
<File FileName="CommandMessenger.Transport.Serial\SerialTransport.cs" Line="1" Column="1" />
<File FileName="CommandMessenger.Transport.Bluetooth\BluetoothTransport.cs" Line="1" Column="1" />
<File FileName="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger.Transport.Serial\SerialConnectionManager.cs" Line="1" Column="1" />
</Files>
</MonoDevelop.Ide.Workbench>
<MonoDevelop.Ide.DebuggingService.Breakpoints>
<BreakpointStore>
<Breakpoint file="D:\My Documents\Github\Arduino-Code-and-Libraries\Libraries\CmdMessenger\CSharp\CommandMessengerTests\Common.cs" line="266" column="1" />
<Breakpoint file="D:\My Documents\Github\Arduino-Code-and-Libraries\Libraries\CmdMessenger\CSharp\CommandMessengerTests\Common.cs" line="229" column="1" />
<Breakpoint file="D:\My Documents\Github\Arduino-Code-and-Libraries\Libraries\CmdMessenger\CSharp\CommandMessengerTests\Common.cs" line="235" column="1" />
<Breakpoint traceExpression="{this.Name}" file="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\AsyncWorker.cs" line="32" column="1" />
</BreakpointStore>
</MonoDevelop.Ide.DebuggingService.Breakpoints>
<MonoDevelop.Ide.DebuggingService.PinnedWatches>
<Watch file="d:\My Documents\Github\Arduino-Libraries-xprmt - Clean\Libraries\CmdMessenger\CSharp\CommandMessenger\AsyncWorker.cs" line="32" offsetX="340" offsetY="496" expression="this.Name" liveUpdate="True" />
</MonoDevelop.Ide.DebuggingService.PinnedWatches>
</Properties>

View File

@ -0,0 +1,422 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
// todo: User added common PINs and per-device PINs
namespace CommandMessenger.Transport.Bluetooth
{
/// <summary>
/// Class for storing last succesful connection
/// </summary>
[Serializable]
public class BluetoothConnectionManagerSettings
{
public BluetoothAddress BluetoothAddress { get; set; }
public Dictionary<BluetoothAddress, string> StoredDevicePins { get; set; }
public BluetoothConnectionManagerSettings()
{
StoredDevicePins = new Dictionary<BluetoothAddress, string>();
}
}
/// <summary>
/// Connection manager for Bluetooth devices
/// </summary>
public class BluetoothConnectionManager : ConnectionManager
{
private static readonly List<string> CommonDevicePins = new List<string>
{
"0000",
"1111",
"1234",
};
private enum ScanType { None, Quick, Thorough }
private BluetoothConnectionManagerSettings _bluetoothConnectionManagerSettings;
private readonly IBluetoothConnectionStorer _bluetoothConnectionStorer;
private readonly BluetoothTransport _bluetoothTransport;
private ScanType _scanType;
// The control to invoke the callback on
private readonly object _tryConnectionLock = new object();
private readonly List<BluetoothDeviceInfo> _deviceList;
private List<BluetoothDeviceInfo> _prevDeviceList;
/// <summary>
/// Lookup dictionary of Pincode per device
/// </summary>
public Dictionary<string, string> DevicePins { get; set; }
/// <summary>
/// List of Pincodes tried for unknown devices
/// </summary>
public List<string> GeneralPins { get; set; }
/// <summary>
/// Connection manager for Bluetooth devices
/// </summary>
public BluetoothConnectionManager(BluetoothTransport bluetoothTransport, CmdMessenger cmdMessenger, int watchdogCommandId = 0, string uniqueDeviceId = null, IBluetoothConnectionStorer bluetoothConnectionStorer = null) :
base(cmdMessenger, watchdogCommandId, uniqueDeviceId)
{
if (bluetoothTransport == null)
throw new ArgumentNullException("bluetoothTransport", "Transport is null.");
_bluetoothTransport = bluetoothTransport;
_bluetoothConnectionManagerSettings = new BluetoothConnectionManagerSettings();
_bluetoothConnectionStorer = bluetoothConnectionStorer;
PersistentSettings = (_bluetoothConnectionStorer != null);
ReadSettings();
_deviceList = new List<BluetoothDeviceInfo>();
_prevDeviceList = new List<BluetoothDeviceInfo>();
DevicePins = new Dictionary<string, string>();
GeneralPins = new List<string>();
}
//Try to connect using current connections settings and trigger event if succesful
protected override void DoWorkConnect()
{
const int timeOut = 1000;
var activeConnection = false;
try
{
activeConnection = TryConnection(timeOut);
}
catch
{
// Do nothing
}
if (activeConnection)
{
ConnectionFoundEvent();
}
}
// Perform scan to find connected systems
protected override void DoWorkScan()
{
if (Thread.CurrentThread.Name == null) Thread.CurrentThread.Name = "BluetoothConnectionManager";
var activeConnection = false;
// Starting scan
if (_scanType == ScanType.None)
{
_scanType = ScanType.Quick;
}
switch (_scanType)
{
case ScanType.Quick:
try { activeConnection = QuickScan(); } catch {
//Do nothing
}
_scanType = ScanType.Thorough;
break;
case ScanType.Thorough:
try { activeConnection = ThoroughScan(); } catch {
//Do nothing
}
_scanType = ScanType.Quick;
break;
}
// Trigger event when a connection was made
if (activeConnection)
{
ConnectionFoundEvent();
}
}
// Quick scan of available devices
private void QuickScanDevices()
{
// Fast
_prevDeviceList = _deviceList;
_deviceList.Clear();
_deviceList.AddRange(_bluetoothTransport.BluetoothClient.DiscoverDevices(255, true, true, false, false));
}
// Thorough scan of available devices
private void ThorougScanForDevices()
{
// Slow
_deviceList.Clear();
_deviceList.AddRange(_bluetoothTransport.BluetoothClient.DiscoverDevices(65536, true, true, true, true));
}
// Pair a Bluetooth device
private bool PairDevice(BluetoothDeviceInfo device)
{
if (device.Authenticated) return true;
Log(2, "Trying to pair device " + device.DeviceName + " (" + device.DeviceAddress + ") ");
// Check if PIN for this device has been injected in ConnectionManager
string adress = device.DeviceAddress.ToString();
var matchedDevicePin = FindPin(adress);
if (matchedDevicePin!=null)
{
Log(3, "Trying known key for device " + device.DeviceName);
if (BluetoothSecurity.PairRequest(device.DeviceAddress, matchedDevicePin))
{
Log(2, "Pairing device " + device.DeviceName + " succesful! ");
return true;
}
// When trying PINS, you really need to wait in between
Thread.Sleep(1000);
}
// Check if PIN has been previously found and stored
if (_bluetoothConnectionManagerSettings.StoredDevicePins.ContainsKey(device.DeviceAddress))
{
Log(3, "Trying stored key for device " + device.DeviceName );
if (BluetoothSecurity.PairRequest(device.DeviceAddress, _bluetoothConnectionManagerSettings.StoredDevicePins[device.DeviceAddress]))
{
Log(2, "Pairing device " + device.DeviceName + " succesful! ");
return true;
}
// When trying PINS, you really need to wait in between
Thread.Sleep(1000);
}
// loop through general pins PIN numbers that have been injected to see if they pair
foreach (string devicePin in GeneralPins)
{
Log(3, "Trying known general pin " + devicePin + " for device " + device.DeviceName);
var isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, devicePin);
if (isPaired)
{
_bluetoothConnectionManagerSettings.StoredDevicePins[device.DeviceAddress] = devicePin;
Log(2, "Pairing device " + device.DeviceName + " succesful! ");
return true;
}
// When trying PINS, you really need to wait in between
Thread.Sleep(1000);
}
// loop through common PIN numbers to see if they pair
foreach (string devicePin in CommonDevicePins)
{
Log(3, "Trying common pin " + devicePin + " for device " + device.DeviceName);
var isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, devicePin);
if (isPaired)
{
_bluetoothConnectionManagerSettings.StoredDevicePins[device.DeviceAddress] = devicePin;
StoreSettings();
Log(2, "Pairing device " + device.DeviceName + " succesful! ");
return true;
}
// When trying PINS, you really need to wait in between
Thread.Sleep(1000);
}
Log(2, "Pairing device " + device.DeviceName + " unsuccesfull ");
return true;
}
// Find the pin code for a Bluetooth adress
private string FindPin(string adress)
{
return (from devicePin in DevicePins where BluetoothUtils.StripBluetoothAdress(devicePin.Key) == adress select devicePin.Value).FirstOrDefault();
}
private bool TryConnection(BluetoothAddress bluetoothAddress, int timeOut)
{
if (bluetoothAddress == null) return false;
// Find
return (from bluetoothDeviceInfo in _deviceList where bluetoothDeviceInfo.DeviceAddress == bluetoothAddress select TryConnection(bluetoothDeviceInfo, timeOut)).FirstOrDefault();
}
private bool TryConnection(BluetoothDeviceInfo bluetoothDeviceInfo, int timeOut)
{
// Try specific settings
_bluetoothTransport.CurrentBluetoothDeviceInfo = bluetoothDeviceInfo;
return TryConnection(timeOut);
}
private bool TryConnection(int timeOut)
{
lock (_tryConnectionLock)
{
// Check if an (old) connection exists
if (_bluetoothTransport.CurrentBluetoothDeviceInfo == null) return false;
Connected = false;
Log(1, @"Trying Bluetooth device " + _bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceName);
if (_bluetoothTransport.Connect())
{
Log(3,
@"Connected with Bluetooth device " + _bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceName +
", requesting response");
DeviceStatus status = ArduinoAvailable(timeOut, 5);
Connected = (status == DeviceStatus.Available);
if (Connected)
{
Log(1,
"Connected with Bluetooth device " +
_bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceName);
StoreSettings();
}
else
{
Log(3,
@"Connected with Bluetooth device " +
_bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceName + ", received no response");
}
return Connected;
}
else
{
Log(3,
@"No connection made with Bluetooth device " + _bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceName );
}
return false;
}
}
protected override void StartScan()
{
base.StartScan();
if (ConnectionManagerMode == Mode.Scan)
{
_scanType = ScanType.None;
}
}
private bool QuickScan()
{
Log(3, "Performing quick scan");
const int longTimeOut = 1000;
const int shortTimeOut = 1000;
// First try if currentConnection is open or can be opened
if (TryConnection(longTimeOut)) return true;
// Do a quick rescan of all devices in range
QuickScanDevices();
if (PersistentSettings)
{
// Then try if last stored connection can be opened
Log(3, "Trying last stored connection");
if (TryConnection(_bluetoothConnectionManagerSettings.BluetoothAddress, longTimeOut)) return true;
}
// Then see if new devices have been added to the list
if (NewDevicesScan()) return true;
foreach (var device in _deviceList)
{
Thread.Sleep(100); // Bluetooth devices seem to work more reliably with some waits
Log(1, "Trying Device " + device.DeviceName + " (" + device.DeviceAddress + ") " );
if (TryConnection(device, shortTimeOut)) return true;
}
return false;
}
private bool ThoroughScan()
{
Log(3, "Performing thorough scan");
const int longTimeOut = 1000;
const int shortTimeOut = 1000;
// First try if currentConnection is open or can be opened
if (TryConnection(longTimeOut)) return true;
// Do a quick rescan of all devices in range
ThorougScanForDevices();
// Then try if last stored connection can be opened
Log(3, "Trying last stored connection");
if (TryConnection(_bluetoothConnectionManagerSettings.BluetoothAddress, longTimeOut)) return true;
// Then see if new devices have been added to the list
if (NewDevicesScan()) return true;
foreach (var device in _deviceList)
{
Thread.Sleep(100); // Bluetooth devices seem to work more reliably with some waits
if (PairDevice(device))
{
Log(1, "Trying Device " + device.DeviceName + " (" + device.DeviceAddress + ") ");
if (TryConnection(device, shortTimeOut)) return true;
}
}
return false;
}
private bool NewDevicesScan()
{
const int shortTimeOut = 200;
// Then see if port list has changed
var newDevices = NewDevicesInList();
if (newDevices.Count == 0) { return false; }
Log(1, "Trying new devices");
foreach (var device in newDevices)
{
if (TryConnection(device, shortTimeOut)) return true;
Thread.Sleep(100);
}
return false;
}
private List<BluetoothDeviceInfo> NewDevicesInList()
{
return (from device in _deviceList from prevdevice in _prevDeviceList where device.DeviceAddress != prevdevice.DeviceAddress select device).ToList();
}
protected override void StoreSettings()
{
if (!PersistentSettings) return;
_bluetoothConnectionManagerSettings.BluetoothAddress = _bluetoothTransport.CurrentBluetoothDeviceInfo.DeviceAddress;
_bluetoothConnectionStorer.StoreSettings(_bluetoothConnectionManagerSettings);
}
protected override sealed void ReadSettings()
{
if (!PersistentSettings) return;
_bluetoothConnectionManagerSettings = _bluetoothConnectionStorer.RetrieveSettings();
}
}
}

View File

@ -0,0 +1,76 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CommandMessenger.Transport.Bluetooth
{
public class BluetoothConnectionStorer : IBluetoothConnectionStorer
{
private readonly string _settingsFileName;
/// <summary>
/// Contructor of Store/Retreive object for SerialConnectionManagerSettings
/// The file is serialized as a simple binary file
/// </summary>
public BluetoothConnectionStorer()
{
_settingsFileName = @"BluetoothConnectionManagerSettings.cfg";
}
/// <summary>
/// Contructor of Store/Retreive object for SerialConnectionManagerSettings
/// The file is serialized as a simple binary file
/// </summary>
/// <param name="settingsFileName">Filename of the settings file</param>
public BluetoothConnectionStorer(string settingsFileName)
{
_settingsFileName = settingsFileName;
}
/// <summary>
/// Store SerialConnectionManagerSettings
/// </summary>
/// <param name="bluetoothConnectionManagerSettings">BluetoothConnectionManagerSettings</param>
public void StoreSettings(BluetoothConnectionManagerSettings bluetoothConnectionManagerSettings)
{
var fileStream = File.Create(_settingsFileName);
var serializer = new BinaryFormatter();
serializer.Serialize(fileStream, bluetoothConnectionManagerSettings);
fileStream.Close();
}
/// <summary>
/// Retreive SerialConnectionManagerSettings
/// </summary>
/// <returns>SerialConnectionManagerSettings</returns>
public BluetoothConnectionManagerSettings RetrieveSettings()
{
var bluetoothConnectionManagerSettings = new BluetoothConnectionManagerSettings();
if (File.Exists(_settingsFileName))
{
var fileStream = File.OpenRead(_settingsFileName);
var deserializer = new BinaryFormatter();
bluetoothConnectionManagerSettings = (BluetoothConnectionManagerSettings)deserializer.Deserialize(fileStream);
fileStream.Close();
}
return bluetoothConnectionManagerSettings;
}
}
}

View File

@ -0,0 +1,342 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
// Todo:
// remove isconnected for speedup.
// Test for disconnected bluetooth
namespace CommandMessenger.Transport.Bluetooth
{
/// <summary>
/// Manager for Bluetooth connection
/// </summary>
public class BluetoothTransport : ITransport
{
private const int BufferSize = 4096;
private NetworkStream _stream;
private readonly AsyncWorker _worker;
private readonly object _readLock = new object();
private readonly object _writeLock = new object();
private readonly byte[] _readBuffer = new byte[BufferSize];
private int _bufferFilled;
private static BluetoothDeviceInfo _runningBluetoothDeviceInfo;
private static bool _showAsConnected;
private static bool _lazyReconnect;
// Event queue for all listeners interested in NewLinesReceived events.
public event EventHandler DataReceived;
/// <summary>
/// Gets or sets Bluetooth device info
/// </summary>
public BluetoothDeviceInfo CurrentBluetoothDeviceInfo { get; set; }
/// <summary>
/// Get or set Lazy reconnection status. Only reconnect if really necessary
/// </summary>
public bool LazyReconnect
{
get { return _lazyReconnect; }
set { _lazyReconnect = value; }
}
/// <summary>
/// Return local Bluetooth client
/// </summary>
public BluetoothClient BluetoothClient
{
get { return BluetoothUtils.LocalClient; }
}
/// <summary>
/// Bluetooth transport constructor
/// </summary>
public BluetoothTransport()
{
_showAsConnected = false;
_lazyReconnect = true;
_worker = new AsyncWorker(Poll, "BluetoothTransport");
}
/// <summary>
/// Bluetooth transport destructor
/// </summary>
~BluetoothTransport()
{
Disconnect();
}
private bool Poll()
{
var bytes = UpdateBuffer();
if (bytes > 0 && DataReceived != null) DataReceived(this, EventArgs.Empty);
return true;
}
/// <summary> Connects to a serial port defined through the current settings. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Connect()
{
// Reconnecting to the same device seems to fail a lot of the time, so see
// if we can remain connected
if (_runningBluetoothDeviceInfo!=null && _runningBluetoothDeviceInfo.DeviceAddress == CurrentBluetoothDeviceInfo.DeviceAddress && _lazyReconnect) {
CurrentBluetoothDeviceInfo = _runningBluetoothDeviceInfo;
} else {
_runningBluetoothDeviceInfo = CurrentBluetoothDeviceInfo;
//BluetoothClient.Close();
}
// Closing serial port if it is open
//_stream = null;
// set pin of device to connect with
// check if device is paired
//CurrentBluetoothDeviceInfo.Refresh();
try
{
if (!CurrentBluetoothDeviceInfo.Authenticated)
{
//Console.WriteLine("Not authenticated");
_showAsConnected = false;
return _showAsConnected;
}
if (BluetoothClient.Connected && !LazyReconnect)
{
//Previously connected, setting up new connection"
BluetoothUtils.UpdateClient();
}
// synchronous connection method
if (!BluetoothClient.Connected || !_lazyReconnect)
BluetoothClient.Connect(CurrentBluetoothDeviceInfo.DeviceAddress, BluetoothService.SerialPort);
if (!Open())
{
// Desperate attempt: try full reset and open
_showAsConnected = UpdateConnectOpen();
return _showAsConnected;
}
// Check worker is not running as a precaution. This needs to be rechecked.
if (!_worker.IsRunning) _worker.Start();
_showAsConnected = true;
return _showAsConnected;
}
catch (SocketException)
{
return false;
}
catch (InvalidOperationException)
{
// Desperate attempt: try full reset and open
_showAsConnected = UpdateConnectOpen();
return _showAsConnected;
}
}
private bool UpdateConnectOpen()
{
BluetoothUtils.UpdateClient();
try
{
BluetoothClient.Connect(CurrentBluetoothDeviceInfo.DeviceAddress, BluetoothService.SerialPort);
}
catch
{
return false;
}
return Open();
}
/// <summary> Opens the serial port. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Open()
{
if (!BluetoothClient.Connected) return false;
lock (_writeLock) { lock (_readLock)
{
_stream = BluetoothClient.GetStream();
_stream.ReadTimeout = 2000;
_stream.WriteTimeout = 1000;
} }
return true;
}
/// <summary>
/// Returns connection status
/// </summary>
/// <returns>true when connected</returns>
public bool IsConnected()
{
// In case of lazy reconnect we will pretend to be disconnected
if (_lazyReconnect && !_showAsConnected) return false;
// If not, test if we are connected
return (BluetoothClient!=null) && BluetoothClient.Connected;
}
/// <summary>
/// Returns opened stream status
/// </summary>
/// <returns>true when open</returns>
public bool IsOpen()
{
// note: this does not always work. Perhaps do a scan
return IsConnected() && (_stream != null);
}
/// <summary> Closes the Bluetooth stream port. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Close()
{
lock (_writeLock)
{
lock (_readLock)
{
// No closing needed
if (_stream == null) return true;
_stream.Close();
_stream = null;
return true;
}
}
}
/// <summary> Disconnect the bluetooth stream. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Disconnect()
{
_showAsConnected = false;
// Check worker is running as a precaution.
if (_worker.IsRunning) _worker.Stop();
if (_lazyReconnect) return true;
return Close();
}
/// <summary> Writes a byte array to the bluetooth stream. </summary>
/// <param name="buffer"> The buffer to write. </param>
public void Write(byte[] buffer)
{
try
{
if (IsOpen())
{
lock (_writeLock)
{
_stream.Write(buffer,0,buffer.Length);
}
}
}
catch
{
//Do nothing
}
}
/// <summary> Retrieves the address of the local bluetooth radio. </summary>
/// <returns> The address of the local bluetooth radio. </returns>
public BluetoothAddress RetreiveLocalBluetoothAddress()
{
var primaryRadio = BluetoothRadio.PrimaryRadio;
if (primaryRadio == null) return null;
return primaryRadio.LocalAddress;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private int UpdateBuffer()
{
if (IsOpen())
{
try
{
var nbrDataRead = _stream.Read(_readBuffer, _bufferFilled, (BufferSize - _bufferFilled));
lock (_readLock)
{
_bufferFilled += nbrDataRead;
//Console.WriteLine("buf: {0}", _bufferFilled.ToString().Length);
}
return _bufferFilled;
}
catch (IOException)
{
//Console.WriteLine("buf: TO");
// Timeout (expected)
}
}
else
{
// In case of no connection
// Sleep a bit otherwise CPU load will go through roof
Thread.Sleep(25);
}
return _bufferFilled;
}
/// <summary> Reads the serial buffer into the string buffer. </summary>
public byte[] Read()
{
//if (IsOpen())
{
byte[] buffer;
lock (_readLock)
{
buffer = new byte[_bufferFilled];
Array.Copy(_readBuffer, buffer, _bufferFilled);
_bufferFilled = 0;
}
return buffer;
}
//return new byte[0];
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Disconnect();
}
}
}
}

View File

@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Net.Sockets;
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
namespace CommandMessenger.Transport.Bluetooth
{
public class BluetoothUtils
{
public static BluetoothEndPoint LocalEndpoint { get; private set; }
public static BluetoothClient LocalClient { get; private set; }
public static BluetoothRadio PrimaryRadio { get; private set; }
private static readonly Guid Guid = Guid.NewGuid();
private static readonly List<BluetoothDeviceInfo> DeviceList;
private static readonly List<string> CommonDevicePins = new List<string>
{
"0000",
"1111",
"1234"
};
private static NetworkStream _stream;
public struct SerialPort
{
public string Port;
public string DeviceId;
}
static BluetoothUtils()
{
// Define common Pin codes for Bluetooth devices
PrimaryRadio = BluetoothRadio.PrimaryRadio;
if (PrimaryRadio == null) {
//Console.WriteLine("No radio hardware or unsupported software stack");
return;
}
// Local bluetooth MAC address
var mac = PrimaryRadio.LocalAddress;
if (mac == null) {
//Console.WriteLine("No local Bluetooth MAC address found");
return;
}
DeviceList = new List<BluetoothDeviceInfo>();
// mac is mac address of local bluetooth device
//LocalEndpoint = new BluetoothEndPoint(mac, BluetoothService.SerialPort);
LocalEndpoint = new BluetoothEndPoint(mac, Guid);
// client is used to manage connections
LocalClient = new BluetoothClient(LocalEndpoint);
}
public static BluetoothDeviceInfo DeviceByAdress(string address)
{
try
{
return new BluetoothDeviceInfo(BluetoothAddress.Parse(address));
}
catch
{
return null;
}
}
public static void PrintPairedDevices()
{
DeviceList.AddRange(LocalClient.DiscoverDevices(255, true, true, false, false));
PrintDevices();
}
public static void PrintAllDevices()
{
DeviceList.AddRange(LocalClient.DiscoverDevices(65536, true, true, true,true));
PrintDevices();
}
private static BluetoothAddress LocalAddress()
{
if (PrimaryRadio == null)
{
Console.WriteLine("No radio hardware or unsupported software stack");
return null;
}
// Note that LocalAddress is null if the radio is powered-off.
//Console.WriteLine("* Radio, address: {0:C}", primaryRadio.LocalAddress);
return PrimaryRadio.LocalAddress;
}
public static void UpdateClient()
{
if (LocalClient != null)
{
LocalClient.Close();
LocalClient = new BluetoothClient(LocalEndpoint);
}
}
public static string StripBluetoothAdress(string bluetoothAdress)
{
var charsToRemove = new string[] {":", "-"};
return charsToRemove.Aggregate(bluetoothAdress, (current, c) => current.Replace(c, string.Empty));
}
public static bool PairDevice(BluetoothDeviceInfo device)
{
if (device.Authenticated) return true;
// loop through common PIN numbers to see if they pair
foreach (var devicePin in CommonDevicePins)
{
var isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, devicePin);
if (isPaired) break;
}
device.Update();
return device.Authenticated;
}
public static void AutoPairDevices()
{
// get a list of all paired devices
var paired = LocalClient.DiscoverDevices(255, false, true, false, false);
// check every discovered device if it is already paired
foreach (var device in DeviceList)
{
var isPaired = paired.Any(t => device.Equals(t));
// if the device is not paired, try to pair it
if (!isPaired)
{
// loop through common PIN numbers to see if they pair
foreach (var devicePin in CommonDevicePins)
{
isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, devicePin);
if (isPaired) break;
}
}
}
}
public static void ConnectDevice(BluetoothDeviceInfo device, string devicePin)
{
// set pin of device to connect with
if (devicePin != null) LocalClient.SetPin(devicePin);
// check if device is paired
if (device.Authenticated)
{
// synchronous connection method
LocalClient.Connect(device.DeviceAddress, BluetoothService.SerialPort);
if (LocalClient.Connected)
{
_stream = LocalClient.GetStream();
_stream.ReadTimeout = 500;
}
}
}
public void ConnectDevice(int deviceId)
{
var device = DeviceList[deviceId];
ConnectDevice(device, null);
}
public static List<SerialPort> GetSerialPorts()
{
var portIdentifiers = new[]
{
"bthenum","btmodem","btport"
};
var portList = new List<SerialPort>();
const string win32SerialPort = "Win32_SerialPort";
var query = new SelectQuery(win32SerialPort);
var managementObjectSearcher = new ManagementObjectSearcher(query);
var portslist = managementObjectSearcher.Get();
foreach (var port in portslist)
{
var managementObject = (ManagementObject) port;
var deviceId = managementObject.GetPropertyValue("DeviceID").ToString();
var pnpDeviceId = managementObject.GetPropertyValue("PNPDeviceID").ToString().ToLower();
if (portIdentifiers.Any(pnpDeviceId.Contains))
{
portList.Add(new SerialPort { Port = deviceId, DeviceId = pnpDeviceId });
}
}
return portList;
}
public static void PrintSerialPorts()
{
var portList = GetSerialPorts();
foreach (var port in portList)
{
Console.WriteLine("Port: {0}, name: {1}", port.Port, port.DeviceId );
}
}
public static void PrintLocalAddress()
{
var localBluetoothAddress = LocalAddress();
if (localBluetoothAddress == null) return;
Console.WriteLine("{0:C}", localBluetoothAddress);
}
public static void PrintDevice(BluetoothDeviceInfo device)
{
// log and save all found devices
Console.Write(device.DeviceName + " (" + device.DeviceAddress + "): Device is ");
Console.Write(device.Remembered ? "remembered" : "not remembered");
Console.Write(device.Authenticated ? ", paired" : ", not paired");
Console.WriteLine(device.Connected ? ", connected" : ", not connected");
}
private static void PrintDevices()
{
// log and save all found devices
foreach (var t in DeviceList)
{
PrintDevice(t);
}
}
}
}

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessenger.Transport.Bluetooth</RootNamespace>
<AssemblyName>CommandMessenger.Transport.Bluetooth</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="InTheHand.Net.Personal">
<HintPath>..\Bluetooth\InTheHand.Net.Personal.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BluetoothConnectionManager.cs" />
<Compile Include="BluetoothConnectionStorer.cs" />
<Compile Include="BluetoothTransport.cs" />
<Compile Include="BluetoothUtils.cs" />
<Compile Include="IBluetoothConnectionStorer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>Thijs Elenbaas</authors>
<owners>Thijs Elenbaas</owners>
<projectUrl>https://github.com/thijse/Arduino-CmdMessenger</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes></releaseNotes>
<copyright></copyright>
<tags>Arduino Communication Bluetooth</tags>
</metadata>
</package>

View File

@ -0,0 +1,27 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Transport.Bluetooth
{
public interface IBluetoothConnectionStorer
{
void StoreSettings(BluetoothConnectionManagerSettings bluetoothConnectionManagerSettings);
BluetoothConnectionManagerSettings RetrieveSettings();
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommandMessenger-Transport-Bluetooth")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1294722c-24e4-4f83-99d1-5cec70fd5962")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.7.0.0")]
[assembly: AssemblyFileVersion("3.7.0.0")]

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{27DF28C4-F702-4AF5-8104-133833253AA6}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessenger.Transport.Serial</RootNamespace>
<AssemblyName>CommandMessenger.Transport.Serial</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommandMessenger.Transport.Serial")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aca5bf54-8f0a-4481-b7b8-289e32d3d93a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1C05AD41-693D-440B-A787-AF196B0F9576}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessenger.Transport.Network</RootNamespace>
<AssemblyName>CommandMessenger.Transport.Network</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TcpConnectionManager.cs" />
<Compile Include="TcpTransport.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>Thijs Elenbaas</authors>
<owners>Thijs Elenbaas</owners>
<projectUrl>https://github.com/thijse/Arduino-CmdMessenger</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes></releaseNotes>
<copyright></copyright>
<tags>Arduino Communication Network</tags>
</metadata>
</package>

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommandMessenger.Transport.Network")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8933d9ef-cfcc-49ce-8053-ec002aee8da4")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.7.0.0")]
[assembly: AssemblyFileVersion("3.7.0.0")]

View File

@ -0,0 +1,56 @@
using System;
namespace CommandMessenger.Transport.Network
{
public class TcpConnectionManager : ConnectionManager
{
private readonly TcpTransport _transport;
private readonly object _tryConnectionLock = new object();
public TcpConnectionManager(TcpTransport tcpTransport, CmdMessenger cmdMessenger, int identifyCommandId = 0, string uniqueDeviceId = null)
: base(cmdMessenger, identifyCommandId, uniqueDeviceId)
{
DeviceScanEnabled = false;
if (tcpTransport == null)
throw new ArgumentNullException("tcpTransport", "Transport is null.");
_transport = tcpTransport;
ReadSettings();
}
protected override void DoWorkConnect()
{
lock (_tryConnectionLock)
{
Connected = false;
if (_transport.Connect())
{
int optimalTimeout = _transport.Timeout + 250;
DeviceStatus status = ArduinoAvailable(optimalTimeout);
Connected = (status == DeviceStatus.Available);
if (Connected)
{
Log(1, string.Format("Connected to {0}:{1}.", _transport.Host, _transport.Port));
StoreSettings();
ConnectionFoundEvent();
}
else
{
_transport.Disconnect();
}
}
}
}
protected override void DoWorkScan()
{
throw new NotSupportedException("ScanMode not supported by TcpConnectionManager.");
}
}
}

View File

@ -0,0 +1,147 @@
using System;
using System.Net.Sockets;
namespace CommandMessenger.Transport.Network
{
/// <summary>
/// Used with network devices.
/// Example: socat tcp-l:1234,reuseaddr,fork file:/dev/ttyACM0,nonblock,raw,echo=0,waitlock=/var/run/tty,b115200
/// </summary>
public class TcpTransport : ITransport
{
private const int BufferSize = 4096;
private readonly object _serialReadWriteLock = new object();
private readonly object _readLock = new object();
private readonly byte[] _readBuffer = new byte[BufferSize];
private int _bufferFilled;
private int _timeout;
private TcpClient _tcpClient;
public event EventHandler DataReceived;
public string Host { get; private set; }
public int Port { get; private set; }
public int Timeout
{
get { return _timeout; }
set
{
_timeout = value;
if (_tcpClient != null)
{
_tcpClient.ReceiveTimeout = _tcpClient.SendTimeout = _timeout;
}
}
}
public TcpTransport(string host, int port)
{
Timeout = 1000;
Host = host;
Port = port;
}
public bool Connect()
{
if (IsConnected())
throw new InvalidOperationException("Already connected.");
try
{
_tcpClient = new TcpClient();
_tcpClient.ReceiveTimeout = _tcpClient.SendTimeout = _timeout;
_tcpClient.Connect(Host, Port);
}
catch (SocketException)
{
return false;
}
_tcpClient.Client.BeginReceive(_readBuffer, 0, BufferSize, SocketFlags.None, OnBytesReceived, this);
return true;
}
public bool IsConnected()
{
return _tcpClient != null && _tcpClient.Client != null && _tcpClient.Connected;
}
public bool Disconnect()
{
if (_tcpClient != null)
{
_tcpClient.Close();
_tcpClient = null;
}
return true;
}
public void Write(byte[] buffer)
{
if (IsConnected())
{
lock (_serialReadWriteLock)
{
_tcpClient.Client.Send(buffer);
}
}
}
public byte[] Read()
{
if (IsConnected())
{
byte[] buffer;
lock (_readLock)
{
buffer = new byte[_bufferFilled];
Array.Copy(_readBuffer, buffer, _bufferFilled);
_bufferFilled = 0;
}
return buffer;
}
return new byte[0];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void OnBytesReceived(IAsyncResult result)
{
if (!IsConnected()) return;
lock (_readLock)
{
int nbrDataRead = _tcpClient.Client.EndReceive(result);
if (nbrDataRead <= 0)
{
Disconnect();
return;
}
_bufferFilled += nbrDataRead;
if (nbrDataRead > 0) DataReceived(this, EventArgs.Empty);
_tcpClient.Client.BeginReceive(_readBuffer, 0, BufferSize, SocketFlags.None, OnBytesReceived, this);
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Disconnect();
}
}
}
}

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{00D85F0B-00A5-41FA-8A99-428C0199C663}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessenger.Transport.Serial</RootNamespace>
<AssemblyName>CommandMessenger.Transport.Serial</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ISerialConnectionStorer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SerialConnectionManager.cs" />
<Compile Include="SerialConnectionStorer.cs" />
<Compile Include="SerialSettings.cs" />
<Compile Include="SerialTransport.cs" />
<Compile Include="SerialUtils.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>Thijs Elenbaas</authors>
<owners>Thijs Elenbaas</owners>
<projectUrl>https://github.com/thijse/Arduino-CmdMessenger</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes></releaseNotes>
<copyright></copyright>
<tags>Arduino Communication Serial</tags>
</metadata>
</package>

View File

@ -0,0 +1,27 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Transport.Serial
{
public interface ISerialConnectionStorer
{
void StoreSettings(SerialConnectionManagerSettings serialConnectionManagerSettings);
SerialConnectionManagerSettings RetrieveSettings();
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommandMessenger.Transport.Serial")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aca5bf54-8f0a-4481-b7b8-289e32d3d93a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.7.0.0")]
[assembly: AssemblyFileVersion("3.7.0.0")]

View File

@ -0,0 +1,402 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace CommandMessenger.Transport.Serial
{
/// <summary>
/// Class for storing last succesfull connection
/// </summary>
[Serializable]
public class SerialConnectionManagerSettings
{
public String Port{ get; set; }
public int BaudRate { get; set; }
}
/// <summary>
/// Connection manager for serial port connection
/// </summary>
public class SerialConnectionManager : ConnectionManager
{
private enum ScanType { None, Quick, Thorough }
private SerialConnectionManagerSettings _serialConnectionManagerSettings;
private readonly ISerialConnectionStorer _serialConnectionStorer;
private readonly SerialTransport _serialTransport;
private readonly object _tryConnectionLock = new object();
private ScanType _scanType = ScanType.None;
/// <summary>
/// Available serial port names in system.
/// </summary>
public string[] AvailableSerialPorts
{
get;
private set;
}
/// <summary>
/// In scan mode allow to try different baud rates besides that is configured in SerialSettings.
/// </summary>
public bool DeviceScanBaudRateSelection { get; set; }
/// <summary>
/// Connection manager for serial port connection
/// </summary>
public SerialConnectionManager(SerialTransport serialTransport, CmdMessenger cmdMessenger, int watchdogCommandId = 0, string uniqueDeviceId = null, ISerialConnectionStorer serialConnectionStorer = null) :
base(cmdMessenger, watchdogCommandId, uniqueDeviceId)
{
if (serialTransport == null)
throw new ArgumentNullException("serialTransport", "Transport is null.");
_serialTransport = serialTransport;
_serialConnectionStorer = serialConnectionStorer;
PersistentSettings = (_serialConnectionStorer != null);
DeviceScanBaudRateSelection = true;
UpdateAvailablePorts();
_serialConnectionManagerSettings = new SerialConnectionManagerSettings();
ReadSettings();
}
/// <summary>
/// Try connection
/// </summary>
/// <returns>Result</returns>
private DeviceStatus TryConnection(string portName = null, int baudRate = int.MinValue)
{
lock (_tryConnectionLock)
{
// Save current port and baud rate
string oldPortName = _serialTransport.CurrentSerialSettings.PortName;
int oldBaudRate = _serialTransport.CurrentSerialSettings.BaudRate;
// Update serial settings with new port and baud rate.
if (portName != null) _serialTransport.CurrentSerialSettings.PortName = portName;
if (baudRate != int.MinValue) _serialTransport.CurrentSerialSettings.BaudRate = baudRate;
if (!_serialTransport.CurrentSerialSettings.IsValid())
{
// Restore back previous settings if newly provided was invalid.
_serialTransport.CurrentSerialSettings.PortName = oldPortName;
_serialTransport.CurrentSerialSettings.BaudRate = oldBaudRate;
return DeviceStatus.NotAvailable;
}
Connected = false;
Log(1, @"Trying serial port " + _serialTransport.CurrentSerialSettings.PortName + " at " + _serialTransport.CurrentSerialSettings.BaudRate + " bauds.");
if (_serialTransport.Connect())
{
// Calculate optimal timeout for command. It should be not less than Serial Port timeout. Lets add additional 250ms.
int optimalTimeout = _serialTransport.CurrentSerialSettings.Timeout + 250;
DeviceStatus status = ArduinoAvailable(optimalTimeout);
Connected = (status == DeviceStatus.Available);
if (Connected)
{
Log(1, "Connected to serial port " + _serialTransport.CurrentSerialSettings.PortName + " at " + _serialTransport.CurrentSerialSettings.BaudRate + " bauds.");
StoreSettings();
}
else
{
_serialTransport.Disconnect();
}
return status;
}
return DeviceStatus.NotAvailable;
}
}
protected override void StartScan()
{
base.StartScan();
if (ConnectionManagerMode == Mode.Scan)
{
UpdateAvailablePorts();
_scanType = ScanType.None;
}
}
//Try to connect using current connections settings and trigger event if succesful
protected override void DoWorkConnect()
{
var activeConnection = false;
try
{
activeConnection = TryConnection() == DeviceStatus.Available;
}
catch
{
// Do nothing
}
if (activeConnection)
{
ConnectionFoundEvent();
}
}
// Perform scan to find connected systems
protected override void DoWorkScan()
{
// First try if currentConnection is open or can be opened
var activeConnection = false;
switch (_scanType)
{
case ScanType.None:
try
{
activeConnection = TryConnection() == DeviceStatus.Available;
}
catch
{
// Do nothing
}
_scanType = ScanType.Quick;
break;
case ScanType.Quick:
try
{
activeConnection = QuickScan();
}
catch
{
// Do nothing
}
_scanType = ScanType.Thorough;
break;
case ScanType.Thorough:
try
{
activeConnection = ThoroughScan();
}
catch
{
// Do nothing
}
_scanType = ScanType.Quick;
break;
}
// Trigger event when a connection was made
if (activeConnection)
{
ConnectionFoundEvent();
}
}
private bool QuickScan()
{
Log(3, "Performing quick scan.");
if (PersistentSettings)
{
// Then try if last stored connection can be opened
Log(3, "Trying last stored connection.");
if (TryConnection(_serialConnectionManagerSettings.Port, _serialConnectionManagerSettings.BaudRate) == DeviceStatus.Available)
return true;
}
// Quickly run through most used baud rates
var commonBaudRates = DeviceScanBaudRateSelection
? SerialUtils.CommonBaudRates
: new [] { _serialTransport.CurrentSerialSettings.BaudRate };
foreach (var portName in AvailableSerialPorts)
{
// Get baud rates collection
var baudRateCollection = DeviceScanBaudRateSelection
? SerialUtils.GetSupportedBaudRates(portName)
: new[] { _serialTransport.CurrentSerialSettings.BaudRate };
var baudRates = commonBaudRates.Where(baudRateCollection.Contains).ToList();
if (baudRates.Any())
{
Log(1, "Trying serial port " + portName + " using " + baudRateCollection.Length + " baud rate(s).");
// Now loop through baud rate collection
foreach (var commonBaudRate in baudRates)
{
// Stop scanning if state was changed
if (ConnectionManagerMode != Mode.Scan) return false;
DeviceStatus status = TryConnection(portName, commonBaudRate);
if (status == DeviceStatus.Available) return true;
if (status == DeviceStatus.IdentityMismatch) break; // break the loop and continue to next port.
}
}
// If port list has changed, interrupt scan and test new ports first
if (NewPortScan()) return true;
}
if (!AvailableSerialPorts.Any())
{
// Need to check for new ports if current ports list is empty
if (NewPortScan()) return true;
// Add small delay to reduce of Quick->Thorough->Quick->Thorough scan attempts - 400ms here + 100ms in main loop = ~500ms
Thread.Sleep(400);
}
return false;
}
private bool ThoroughScan()
{
Log(1, "Performing thorough scan.");
// Then try if last stored connection can be opened
if (PersistentSettings && TryConnection(_serialConnectionManagerSettings.Port, _serialConnectionManagerSettings.BaudRate) == DeviceStatus.Available)
return true;
// Slowly walk through
foreach (var portName in AvailableSerialPorts)
{
// Get baud rates collection
var baudRateCollection = DeviceScanBaudRateSelection
? SerialUtils.GetSupportedBaudRates(portName)
: new[] { _serialTransport.CurrentSerialSettings.BaudRate };
// Now loop through baud rate collection
if (baudRateCollection.Any())
{
Log(1, "Trying serial port " + portName + " using " + baudRateCollection.Length + " baud rate(s).");
foreach (var baudRate in baudRateCollection)
{
// Stop scanning if state was changed
if (ConnectionManagerMode != Mode.Scan) return false;
DeviceStatus status = TryConnection(portName, baudRate);
if (status == DeviceStatus.Available) return true;
if (status == DeviceStatus.IdentityMismatch) break; // break the loop and continue to next port.
}
}
// If port list has changed, interrupt scan and test new ports first
if (NewPortScan()) return true;
}
if (!AvailableSerialPorts.Any())
{
// Need to check for new ports if current ports list is empty
if (NewPortScan()) return true;
// Add small delay to reduce of Quick->Thorough->Quick->Thorough scan attempts - 400ms here + 100ms in main loop = ~500ms
Thread.Sleep(400);
}
return false;
}
private bool NewPortScan()
{
// Then see if port list has changed
var newPorts = NewPortInList();
if (!newPorts.Any()) { return false; }
//TODO: 4s - practical delay for Leonardo board, probably for other boards will be different. Need to investigate more on this.
const int waitTime = 4000;
Log(1, "New port(s) " + string.Join(",", newPorts) + " detected, wait for " + (waitTime / 1000.0) + "s before attempt to connect.");
// Wait a bit before new port will be available then try to connect
Thread.Sleep(waitTime);
// Quickly run through most used ports
var commonBaudRates = DeviceScanBaudRateSelection
? SerialUtils.CommonBaudRates
: new[] { _serialTransport.CurrentSerialSettings.BaudRate };
foreach (var portName in newPorts)
{
// Get baud rates collection
var baudRateCollection = DeviceScanBaudRateSelection
? SerialUtils.GetSupportedBaudRates(portName)
: new[] { _serialTransport.CurrentSerialSettings.BaudRate };
// First add commonBaudRates available
var sortedBaudRates = commonBaudRates.Where(baudRateCollection.Contains).ToList();
// Then add other BaudRates
sortedBaudRates.AddRange(baudRateCollection.Where(baudRate => !commonBaudRates.Contains(baudRate)));
foreach (var currentBaudRate in sortedBaudRates)
{
// Stop scanning if state was changed
if (ConnectionManagerMode != Mode.Scan) return false;
DeviceStatus status = TryConnection(portName, currentBaudRate);
if (status == DeviceStatus.Available) return true;
if (status == DeviceStatus.IdentityMismatch) break; // break the loop and continue to next port.
}
}
return false;
}
private void UpdateAvailablePorts()
{
AvailableSerialPorts = SerialUtils.GetPortNames();
}
private List<string> NewPortInList()
{
var currentPorts = SerialUtils.GetPortNames();
var newPorts = currentPorts.Except(AvailableSerialPorts).ToList();
// Actualize ports collection
AvailableSerialPorts = currentPorts;
return newPorts;
}
protected override void StoreSettings()
{
if (!PersistentSettings) return;
_serialConnectionManagerSettings.Port = _serialTransport.CurrentSerialSettings.PortName;
_serialConnectionManagerSettings.BaudRate = _serialTransport.CurrentSerialSettings.BaudRate;
_serialConnectionStorer.StoreSettings(_serialConnectionManagerSettings);
}
protected override sealed void ReadSettings()
{
if (!PersistentSettings) return;
_serialConnectionManagerSettings = _serialConnectionStorer.RetrieveSettings();
}
}
}

View File

@ -0,0 +1,76 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CommandMessenger.Transport.Serial
{
public class SerialConnectionStorer : ISerialConnectionStorer
{
private readonly string _settingsFileName;
/// <summary>
/// Contructor of Store/Retreive object for SerialConnectionManagerSettings
/// The file is serialized as a simple binary file
/// </summary>
public SerialConnectionStorer()
{
_settingsFileName = @"SerialConnectionManagerSettings.cfg";
}
/// <summary>
/// Contructor of Store/Retreive object for SerialConnectionManagerSettings
/// The file is serialized as a simple binary file
/// </summary>
/// <param name="settingsFileName">Filename of the settings file</param>
public SerialConnectionStorer(string settingsFileName)
{
_settingsFileName = settingsFileName;
}
/// <summary>
/// Store SerialConnectionManagerSettings
/// </summary>
/// <param name="serialConnectionManagerSettings">SerialConnectionManagerSettings</param>
public void StoreSettings(SerialConnectionManagerSettings serialConnectionManagerSettings)
{
var fileStream = File.Create(_settingsFileName);
var serializer = new BinaryFormatter();
serializer.Serialize(fileStream, serialConnectionManagerSettings);
fileStream.Close();
}
/// <summary>
/// Retreive SerialConnectionManagerSettings
/// </summary>
/// <returns>SerialConnectionManagerSettings</returns>
public SerialConnectionManagerSettings RetrieveSettings()
{
var serialConnectionManagerSettings = new SerialConnectionManagerSettings();
if (File.Exists(_settingsFileName))
{
var fileStream = File.OpenRead(_settingsFileName);
var deserializer = new BinaryFormatter();
serialConnectionManagerSettings = (SerialConnectionManagerSettings)deserializer.Deserialize(fileStream);
fileStream.Close();
}
return serialConnectionManagerSettings;
}
}
}

View File

@ -0,0 +1,87 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System.IO.Ports;
namespace CommandMessenger.Transport.Serial
{
/// <summary>
/// Class containing serial port configuration
/// </summary>
public class SerialSettings
{
#region Properties
/// <summary>
/// The port to use (for example: COM1 or /dev/ttyACM1).
/// </summary>
public string PortName { get; set; }
/// <summary>
/// Port baud rate.
/// </summary>
public int BaudRate { get; set; }
/// <summary>
/// One of the Parity values.
/// </summary>
public Parity Parity { get; set; }
/// <summary>
/// The data bits value.
/// </summary>
public int DataBits { get; set; }
/// <summary>
/// One of the StopBits values.
/// </summary>
public StopBits StopBits { get; set; }
/// <summary>
/// Set Data Terminal Ready.
/// </summary>
public bool DtrEnable { get; set; }
/// <summary>
/// Timeout for read and write operations to serial port.
/// </summary>
public int Timeout { get; set; }
#endregion
public SerialSettings()
{
StopBits = StopBits.One;
DataBits = 8;
Parity = Parity.None;
BaudRate = 9600;
PortName = string.Empty;
Timeout = 500; // 500ms is default value for SerialPort
}
/// <summary>
/// Check is serial settings configured properly.
/// </summary>
/// <returns></returns>
public bool IsValid()
{
return !string.IsNullOrEmpty(PortName) && BaudRate > 0;
}
}
}

View File

@ -0,0 +1,244 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.IO.Ports;
namespace CommandMessenger.Transport.Serial
{
/// <summary>Fas
/// Manager for serial port data
/// </summary>
public class SerialTransport : ITransport
{
private const int BufferSize = 4096;
private volatile bool _connected;
private readonly AsyncWorker _worker;
private readonly object _serialReadWriteLock = new object();
private readonly object _readLock = new object();
private readonly byte[] _readBuffer = new byte[BufferSize];
private int _bufferFilled;
private SerialPort _serialPort; // The serial port
private SerialSettings _currentSerialSettings = new SerialSettings(); // The current serial settings
public event EventHandler DataReceived; // Event queue for all listeners interested in NewLinesReceived events.
/// <summary> Gets or sets the current serial port settings. </summary>
/// <value> The current serial settings. </value>
public SerialSettings CurrentSerialSettings
{
get { return _currentSerialSettings; }
set { _currentSerialSettings = value; }
}
public SerialTransport()
{
_worker = new AsyncWorker(Poll, "SerialTransport");
}
private bool Poll()
{
var bytes = UpdateBuffer();
if (bytes > 0 && DataReceived != null) DataReceived(this, EventArgs.Empty);
// Return true as we always have work to do here. The delay is achieved by SerialPort.Read timeout.
return true;
}
/// <summary> Connects to a serial port defined through the current settings. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Connect()
{
if (!_currentSerialSettings.IsValid())
throw new InvalidOperationException("Unable to open connection - serial settings invalid.");
if (IsConnected())
throw new InvalidOperationException("Serial port is already opened.");
// Setting serial port settings
_serialPort = new SerialPort(
_currentSerialSettings.PortName,
_currentSerialSettings.BaudRate,
_currentSerialSettings.Parity,
_currentSerialSettings.DataBits,
_currentSerialSettings.StopBits)
{
DtrEnable = _currentSerialSettings.DtrEnable,
WriteTimeout = _currentSerialSettings.Timeout,
ReadTimeout = 1000, // read timeout is used for polling in worker thread
};
_connected = Open();
if (_connected) _worker.Start();
return _connected;
}
/// <summary> Query if the serial port is open. </summary>
/// <returns> true if open, false if not. </returns>
public bool IsConnected()
{
return _connected && _serialPort.IsOpen;
}
/// <summary> Stops listening to the serial port. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Disconnect()
{
bool result = Close();
if (_connected)
{
_connected = false;
_worker.Stop();
}
return result;
}
/// <summary> Writes a parameter to the serial port. </summary>
/// <param name="buffer"> The buffer to write. </param>
public void Write(byte[] buffer)
{
if (IsConnected())
{
try
{
lock (_serialReadWriteLock)
{
_serialPort.Write(buffer, 0, buffer.Length);
}
}
catch (TimeoutException)
{
// Timeout (expected)
}
catch
{
Disconnect();
}
}
}
/// <summary> Reads the serial buffer into the string buffer. </summary>
public byte[] Read()
{
if (IsConnected())
{
byte[] buffer;
lock (_readLock)
{
buffer = new byte[_bufferFilled];
Array.Copy(_readBuffer, buffer, _bufferFilled);
_bufferFilled = 0;
}
return buffer;
}
return new byte[0];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary> Opens the serial port. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
private bool Open()
{
if (!_connected && PortExists() && !_serialPort.IsOpen)
{
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
return _serialPort.IsOpen;
}
catch
{
// Ignore.
}
}
return false;
}
/// <summary> Closes the serial port. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
private bool Close()
{
if (!IsConnected()) return false;
try
{
_serialPort.Close();
return true;
}
catch
{
return false;
}
}
/// <summary> Queries if a current port exists. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
private bool PortExists()
{
return SerialUtils.PortExists(_serialPort.PortName);
}
private int UpdateBuffer()
{
if (IsConnected())
{
try
{
lock (_readLock)
{
var nbrDataRead = _serialPort.Read(_readBuffer, _bufferFilled, (BufferSize - _bufferFilled));
_bufferFilled += nbrDataRead;
}
return _bufferFilled;
}
catch (TimeoutException)
{
// Timeout (expected)
}
catch
{
Disconnect();
}
}
return 0;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Disconnect();
if (_serialPort != null) _serialPort.Dispose();
}
}
}
}

View File

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace CommandMessenger.Transport.Serial
{
/// <summary>
/// Utility methods for serial communication handling.
/// </summary>
public static class SerialUtils
{
private static readonly bool IsMonoRuntime = (Type.GetType("Mono.Runtime") != null);
/// <summary>
/// Commonly used baud rates.
/// </summary>
public static int[] CommonBaudRates
{
get
{
return new []
{
115200, // Arduino Uno, Mega, with AT8u2 USB
57600, // Arduino Duemilanove, FTDI Serial
9600 // Often used as default, but slow!
};
}
}
/// <summary> Queries if a given port exists. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public static bool PortExists(string serialPortName)
{
if (IsMonoRuntime)
{
return File.Exists(serialPortName);
}
else
{
return SerialPort.GetPortNames().Contains(serialPortName);
}
}
/// <summary>
/// Retrieve available serial ports.
/// </summary>
/// <returns>Array of serial port names.</returns>
public static string[] GetPortNames()
{
/**
* Under Mono SerialPort.GetPortNames() returns /dev/ttyS* devices,
* but Arduino is detected as ttyACM* or ttyUSB*
* */
if (IsMonoRuntime)
{
var searchPattern = new Regex("ttyACM.+|ttyUSB.+");
return Directory.GetFiles("/dev").Where(f => searchPattern.IsMatch(f)).ToArray();
}
else
{
return SerialPort.GetPortNames();
}
}
/// <summary>
/// Retrieves the possible baud rates for the provided serial port. Windows ONLY.
/// </summary>
/// <returns>List of supported baud rates.</returns>
public static int[] GetSupportedBaudRates(string serialPortName)
{
try
{
var serialPort = new SerialPort(serialPortName);
serialPort.Open();
if (serialPort.IsOpen)
{
var fieldInfo = serialPort.BaseStream.GetType()
.GetField("commProp", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo != null)
{
object p = fieldInfo.GetValue(serialPort.BaseStream);
var fieldInfoValue = p.GetType()
.GetField("dwSettableBaud",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (fieldInfoValue != null)
{
var dwSettableBaud = (Int32)fieldInfoValue.GetValue(p);
serialPort.Close();
return BaudRateMaskToActualRates(dwSettableBaud).ToArray();
}
}
}
}
catch
{
// Ignore.
}
// Can't determine possible baud rates, will use all possible values
return BaudRateMaskToActualRates(int.MaxValue).ToArray();
}
/// <summary>
/// Get the range of possible baud rates for serial port.
/// </summary>
/// <param name="possibleBaudRates">dwSettableBaud parameter from the COMMPROP Structure</param>
/// <returns>List of bad rates</returns>
private static List<int> BaudRateMaskToActualRates(int possibleBaudRates)
{
#pragma warning disable 219
// ReSharper disable InconsistentNaming
//const int BAUD_075 = 0x00000001;
//const int BAUD_110 = 0x00000002;
//const int BAUD_150 = 0x00000008;
const int BAUD_300 = 0x00000010;
const int BAUD_600 = 0x00000020;
const int BAUD_1200 = 0x00000040;
const int BAUD_1800 = 0x00000080;
const int BAUD_2400 = 0x00000100;
const int BAUD_4800 = 0x00000200;
const int BAUD_7200 = 0x00000400;
const int BAUD_9600 = 0x00000800;
const int BAUD_14400 = 0x00001000;
const int BAUD_19200 = 0x00002000;
const int BAUD_38400 = 0x00004000;
const int BAUD_56K = 0x00008000;
const int BAUD_57600 = 0x00040000;
const int BAUD_115200 = 0x00020000;
const int BAUD_128K = 0x00010000;
#pragma warning restore 219
var baudRateCollection = new List<int>();
// We start with the most common baudrates:
if ((possibleBaudRates & BAUD_115200) > 0)
baudRateCollection.Add(115200); // Maxspeed Arduino Uno, Mega, with AT8u2 USB
if ((possibleBaudRates & BAUD_9600) > 0)
baudRateCollection.Add(9600); // Often default speed
if ((possibleBaudRates & BAUD_57600) > 0)
baudRateCollection.Add(57600); // Maxspeed Arduino Duemilanove, FTDI Serial
// After that going from fastest to slowest baudrates:
if ((possibleBaudRates & BAUD_128K) > 0)
baudRateCollection.Add(128000);
if ((possibleBaudRates & BAUD_56K) > 0)
baudRateCollection.Add(56000);
if ((possibleBaudRates & BAUD_38400) > 0)
baudRateCollection.Add(38400);
if ((possibleBaudRates & BAUD_19200) > 0)
baudRateCollection.Add(19200);
if ((possibleBaudRates & BAUD_14400) > 0)
baudRateCollection.Add(14400);
if ((possibleBaudRates & BAUD_7200) > 0)
baudRateCollection.Add(7200);
if ((possibleBaudRates & BAUD_4800) > 0)
baudRateCollection.Add(4800);
if ((possibleBaudRates & BAUD_2400) > 0)
baudRateCollection.Add(2400);
if ((possibleBaudRates & BAUD_1800) > 0)
baudRateCollection.Add(1800);
if ((possibleBaudRates & BAUD_1200) > 0)
baudRateCollection.Add(1200);
if ((possibleBaudRates & BAUD_600) > 0)
baudRateCollection.Add(600);
if ((possibleBaudRates & BAUD_300) > 0)
baudRateCollection.Add(300);
// Skip old and slow rates.
/*if ((possibleBaudRates & BAUD_150) > 0)
baudRateCollection.Add(150);
if ((possibleBaudRates & BAUD_110) > 0)
baudRateCollection.Add(110);
if ((possibleBaudRates & BAUD_075) > 0)
baudRateCollection.Add(75);*/
return baudRateCollection;
}
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.Threading;
namespace CommandMessenger
{
public class AsyncWorker
{
public enum WorkerState
{
Stopped,
Running,
Suspended
}
/// <summary>
/// Main worker method to do some work.
/// </summary>
/// <returns>true is there is more work to do, otherwise false and worker will wait until signalled with SignalWorker().</returns>
public delegate bool AsyncWorkerJob();
private bool _isFaulted;
private volatile WorkerState _state = WorkerState.Stopped;
private volatile WorkerState _requestedState = WorkerState.Stopped;
private readonly object _lock = new object();
private readonly EventWaiter _eventWaiter = new EventWaiter();
private readonly AsyncWorkerJob _workerJob;
private Thread _workerTask;
public string Name { get; private set; }
public WorkerState State { get { return _state; } }
public bool IsRunning { get { return _state == WorkerState.Running; } }
public bool IsSuspended { get { return _state == WorkerState.Suspended; } }
public AsyncWorker(AsyncWorkerJob workerJob, string workerName = null)
{
if (workerJob == null) throw new ArgumentNullException("workerJob");
_workerJob = workerJob;
Name = workerName;
}
public void Start()
{
lock (_lock)
{
if (_state == WorkerState.Stopped)
{
_requestedState = _state = WorkerState.Running;
_eventWaiter.Reset();
_workerTask = new Thread(() =>
{
while (true)
{
if (_state == WorkerState.Stopped) break;
bool haveMoreWork = false;
if (_state == WorkerState.Running)
{
try
{
haveMoreWork = _workerJob();
}
catch
{
_requestedState = _state = WorkerState.Stopped;
_isFaulted = true;
throw;
}
// Check if state has been changed in workerJob thread.
if (_requestedState != _state && _requestedState == WorkerState.Stopped)
{
_state = _requestedState;
break;
}
}
if (!haveMoreWork || _state == WorkerState.Suspended) _eventWaiter.WaitOne(Timeout.Infinite);
_state = _requestedState;
}
});
_workerTask.Name = Name;
_workerTask.IsBackground = true;
_workerTask.Start();
SpinWait.SpinUntil(() => _workerTask.IsAlive);
}
else
{
throw new InvalidOperationException("The worker is already started.");
}
}
}
public void Stop()
{
lock (_lock)
{
if (_state == WorkerState.Running || _state == WorkerState.Suspended)
{
_requestedState = WorkerState.Stopped;
// Prevent deadlock by checking is we stopping from worker task or not.
if (Thread.CurrentThread.ManagedThreadId != _workerTask.ManagedThreadId)
{
_eventWaiter.Set();
_workerTask.Join();
}
}
else if (!_isFaulted)
{
// Probably not needed, added as a precaution.
throw new InvalidOperationException("The worker is already stopped.");
}
}
}
public void Suspend()
{
lock (_lock)
{
if (_state == WorkerState.Running)
{
_requestedState = WorkerState.Suspended;
_eventWaiter.Set();
SpinWait.SpinUntil(() => _requestedState == _state);
}
else
{
// Probably not needed, added as a precaution.
throw new InvalidOperationException("The worker is not running.");
}
}
}
public void Resume()
{
lock (_lock)
{
if (_state == WorkerState.Suspended)
{
_requestedState = WorkerState.Running;
_eventWaiter.Set();
SpinWait.SpinUntil(() => _requestedState == _state);
}
else
{
// Probably not needed, added as a precaution.
throw new InvalidOperationException("The worker is not in suspended state.");
}
}
}
/// <summary>
/// Signal worker to continue processing.
/// </summary>
public void Signal()
{
if (IsRunning) _eventWaiter.Set();
}
}
}

View File

@ -0,0 +1,319 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Text;
namespace CommandMessenger
{
public class BinaryConverter
{
private static Encoding _stringEncoder = Encoding.GetEncoding("ISO-8859-1"); // The string encoder
/// <summary> Sets the string encoder. </summary>
/// <value> The string encoder. </value>
public Encoding StringEncoder
{
set { _stringEncoder = value; }
}
/***** from binary value to string ****/
/// <summary> Convert a float into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(float value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert a Double into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(Double value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert an int into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(int value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert an unsigned int into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(uint value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert a short into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(short value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert an unsigned an unsigned short into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(ushort value)
{
try
{
byte[] byteArray = BitConverter.GetBytes(value);
return BytesToEscapedString(byteArray);
}
catch (Exception)
{
return null;
}
}
/// <summary> Convert a byte into a string representation. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> A string representation of this object. </returns>
public static string ToString(byte value)
{
try
{
return BytesToEscapedString(new byte[] {value});
}
catch (Exception)
{
return null;
}
}
/***** from string to binary value ****/
/// <summary> Converts a string to a float. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> Input string as a float? </returns>
public static float? ToFloat(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
if (bytes.Length < 4)
{
return null;
}
return BitConverter.ToSingle(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to a double. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> Input string as a Double? </returns>
public static Double? ToDouble(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return BitConverter.ToDouble(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to an int 32. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> This object as an Int32? </returns>
public static Int32? ToInt32(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return BitConverter.ToInt32(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to a u int 32. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> Input string as a UInt32? </returns>
public static UInt32? ToUInt32(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return BitConverter.ToUInt32(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to a u int 16. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> Input string as a UInt16? </returns>
public static UInt16? ToUInt16(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return BitConverter.ToUInt16(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to an int 16. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> This object as an Int16? </returns>
public static Int16? ToInt16(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return BitConverter.ToInt16(bytes, 0);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string representation to a byte. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> Input string as a byte? </returns>
public static byte? ToByte(String value)
{
try
{
byte[] bytes = EscapedStringToBytes(value);
return bytes[0];
}
catch (Exception)
{
return null;
}
}
/***** conversion functions ****/
/// <summary> Converts a byte array to escaped string. </summary>
/// <param name="byteArray"> Array of bytes. </param>
/// <returns> input value as an escaped string. </returns>
private static string BytesToEscapedString(byte[] byteArray)
{
try
{
string stringValue = _stringEncoder.GetString(byteArray);
return Escaping.Escape(stringValue);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts an escaped string to a bytes array. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> input value as an escaped string. </returns>
public static byte[] EscapedStringToBytes(String value)
{
try
{
string unEscapedValue = Escaping.Unescape(value);
return _stringEncoder.GetBytes(unEscapedValue);
}
catch (Exception)
{
return null;
}
}
/// <summary> Converts a string to a bytes array. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> input value as a byte array. </returns>
public static byte[] StringToBytes(string value)
{
return _stringEncoder.GetBytes(value);
}
/// <summary> Converts a char array to a bytes array. </summary>
/// <param name="value"> The value to be converted. </param>
/// <returns> input value as a byte array. </returns>
public static byte[] CharsToBytes(char[] value)
{
return _stringEncoder.GetBytes(value);
}
}
}

View File

@ -0,0 +1,445 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using CommandMessenger.Queue;
using CommandMessenger.Transport;
namespace CommandMessenger
{
public enum SendQueue
{
Default,
InFrontQueue,
AtEndQueue,
WaitForEmptyQueue,
ClearQueue,
}
public enum ReceiveQueue
{
Default,
WaitForEmptyQueue,
ClearQueue,
}
public enum UseQueue
{
UseQueue,
BypassQueue,
}
public enum BoardType
{
Bit16,
Bit32,
}
/// <summary> Command messenger main class </summary>
public class CmdMessenger : IDisposable
{
private CommunicationManager _communicationManager; // The communication manager
private MessengerCallbackFunction _defaultCallback; // The default callback
private Dictionary<int, MessengerCallbackFunction> _callbackList; // List of callbacks
private SendCommandQueue _sendCommandQueue; // The queue of commands to be sent
private ReceiveCommandQueue _receiveCommandQueue; // The queue of commands to be processed
/// <summary> Definition of the messenger callback function. </summary>
/// <param name="receivedCommand"> The received command. </param>
public delegate void MessengerCallbackFunction(ReceivedCommand receivedCommand);
/// <summary>
/// Event handler for one or more lines received
/// </summary>
public event EventHandler<CommandEventArgs> NewLineReceived;
/// <summary>
/// Event handler for a new line sent
/// </summary>
public event EventHandler<CommandEventArgs> NewLineSent;
/// <summary> Gets or sets a whether to print a line feed carriage return after each command. </summary>
/// <value> true if print line feed carriage return, false if not. </value>
public bool PrintLfCr
{
get { return _communicationManager.PrintLfCr; }
set { _communicationManager.PrintLfCr = value; }
}
/// <summary>
/// The control to invoke the callback on
/// </summary>
public Control ControlToInvokeOn { get; set; }
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
public CmdMessenger(ITransport transport, BoardType boardType = BoardType.Bit16)
{
Init(transport, boardType, ',', ';', '/', 60);
}
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="sendBufferMaxLength"> The maximum size of the send buffer</param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
public CmdMessenger(ITransport transport, int sendBufferMaxLength, BoardType boardType = BoardType.Bit16)
{
Init(transport, boardType, ',', ';', '/', sendBufferMaxLength);
}
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
/// <param name="fieldSeparator"> The field separator. </param>
public CmdMessenger(ITransport transport, BoardType boardType, char fieldSeparator)
{
Init(transport, boardType, fieldSeparator, ';', '/', 60);
}
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
/// <param name="fieldSeparator"> The field separator. </param>
/// <param name="sendBufferMaxLength"> The maximum size of the send buffer</param>
public CmdMessenger(ITransport transport, BoardType boardType, char fieldSeparator, int sendBufferMaxLength)
{
Init(transport, boardType, fieldSeparator, ';', '/', sendBufferMaxLength);
}
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
/// <param name="fieldSeparator"> The field separator. </param>
/// <param name="commandSeparator"> The command separator. </param>
public CmdMessenger(ITransport transport, BoardType boardType, char fieldSeparator, char commandSeparator)
{
Init(transport, boardType, fieldSeparator, commandSeparator, commandSeparator, 60);
}
/// <summary> Constructor. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
/// <param name="fieldSeparator"> The field separator. </param>
/// <param name="commandSeparator"> The command separator. </param>
/// <param name="escapeCharacter"> The escape character. </param>
/// <param name="sendBufferMaxLength"> The maximum size of the send buffer</param>
public CmdMessenger(ITransport transport, BoardType boardType, char fieldSeparator, char commandSeparator,
char escapeCharacter, int sendBufferMaxLength)
{
Init(transport, boardType, fieldSeparator, commandSeparator, escapeCharacter, sendBufferMaxLength);
}
/// <summary> Initializes this object. </summary>
/// <param name="transport"> The transport layer. </param>
/// <param name="boardType"> Embedded Processor type. Needed to translate variables between sides. </param>
/// <param name="fieldSeparator"> The field separator. </param>
/// <param name="commandSeparator"> The command separator. </param>
/// <param name="escapeCharacter"> The escape character. </param>
/// <param name="sendBufferMaxLength"> The maximum size of the send buffer</param>
private void Init(ITransport transport, BoardType boardType, char fieldSeparator, char commandSeparator,
char escapeCharacter, int sendBufferMaxLength)
{
ControlToInvokeOn = null;
//Logger.Open(@"sendCommands.txt");
Logger.DirectFlush = true;
_receiveCommandQueue = new ReceiveCommandQueue(HandleMessage);
_communicationManager = new CommunicationManager(transport, _receiveCommandQueue, boardType, commandSeparator, fieldSeparator, escapeCharacter);
_sendCommandQueue = new SendCommandQueue(_communicationManager, sendBufferMaxLength);
PrintLfCr = false;
_receiveCommandQueue.NewLineReceived += (o, e) => InvokeNewLineEvent(NewLineReceived, e);
_sendCommandQueue.NewLineSent += (o, e) => InvokeNewLineEvent(NewLineSent, e);
Escaping.EscapeChars(fieldSeparator, commandSeparator, escapeCharacter);
_callbackList = new Dictionary<int, MessengerCallbackFunction>();
_sendCommandQueue.Start();
_receiveCommandQueue.Start();
}
/// <summary>
/// Disposal of CmdMessenger
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary> Sets a control to invoke on. </summary>
/// <param name="controlToInvokeOn"> The control to invoke on. </param>
[Obsolete("Use ControlToInvokeOn property instead.")]
public void SetControlToInvokeOn(Control controlToInvokeOn)
{
ControlToInvokeOn = controlToInvokeOn;
}
/// <summary> Stop listening and end serial port connection. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Disconnect()
{
return _communicationManager.Disconnect();
}
/// <summary> Starts serial port connection and start listening. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Connect()
{
return _communicationManager.Connect();
}
/// <summary> Attaches default callback for unsupported commands. </summary>
/// <param name="newFunction"> The callback function. </param>
public void Attach(MessengerCallbackFunction newFunction)
{
_defaultCallback = newFunction;
}
/// <summary> Attaches default callback for certain Message ID. </summary>
/// <param name="messageId"> Command ID. </param>
/// <param name="newFunction"> The callback function. </param>
public void Attach(int messageId, MessengerCallbackFunction newFunction)
{
_callbackList[messageId] = newFunction;
}
/// <summary> Gets or sets the time stamp of the last command line received. </summary>
/// <value> The last line time stamp. </value>
public long LastReceivedCommandTimeStamp
{
get
{
return _communicationManager.LastLineTimeStamp;
}
}
/// <summary> Handle message. </summary>
/// <param name="receivedCommand"> The received command. </param>
private void HandleMessage(ReceivedCommand receivedCommand)
{
MessengerCallbackFunction callback = null;
if (receivedCommand.Ok)
{
if (_callbackList.ContainsKey(receivedCommand.CmdId))
{
callback = _callbackList[receivedCommand.CmdId];
}
else
{
if (_defaultCallback != null) callback = _defaultCallback;
}
}
else
{
// Empty command
receivedCommand = new ReceivedCommand { CommunicationManager = _communicationManager };
}
InvokeCallBack(callback, receivedCommand);
}
/// <summary> Sends a command.
/// If no command acknowledge is requested, the command will be send asynchronously: it will be put on the top of the send queue
/// If a command acknowledge is requested, the command will be send synchronously: the program will block until the acknowledge command
/// has been received or the timeout has expired.
/// Based on ClearQueueState, the send- and receive-queues are left intact or are cleared</summary>
/// <param name="sendCommand"> The command to sent. </param>
/// <param name="sendQueueState"> Property to optionally clear/wait the send queue</param>
/// <param name="receiveQueueState"> Property to optionally clear/wait the send queue</param>
/// <returns> A received command. The received command will only be valid if the ReqAc of the command is true. </returns>
public ReceivedCommand SendCommand(SendCommand sendCommand, SendQueue sendQueueState = SendQueue.InFrontQueue, ReceiveQueue receiveQueueState = ReceiveQueue.Default)
{
return SendCommand(sendCommand, sendQueueState, receiveQueueState, UseQueue.UseQueue);
}
/// <summary> Sends a command.
/// If no command acknowledge is requested, the command will be send asynchronously: it will be put on the top of the send queue
/// If a command acknowledge is requested, the command will be send synchronously: the program will block until the acknowledge command
/// has been received or the timeout has expired.
/// Based on ClearQueueState, the send- and receive-queues are left intact or are cleared</summary>
/// <param name="sendCommand"> The command to sent. </param>
/// <param name="sendQueueState"> Property to optionally clear/wait the send queue</param>
/// <param name="receiveQueueState"> Property to optionally clear/wait the send queue</param>
/// <param name="useQueue"> Property to optionally bypass the queue</param>
/// <returns> A received command. The received command will only be valid if the ReqAc of the command is true. </returns>
public ReceivedCommand SendCommand(SendCommand sendCommand, SendQueue sendQueueState, ReceiveQueue receiveQueueState, UseQueue useQueue)
{
var synchronizedSend = (sendCommand.ReqAc || useQueue == UseQueue.BypassQueue);
// When waiting for an acknowledge, it is typically best to wait for the ReceiveQueue to be empty
// This is thus the default state
if (sendCommand.ReqAc && receiveQueueState == ReceiveQueue.Default)
{
receiveQueueState = ReceiveQueue.WaitForEmptyQueue;
}
if (sendQueueState == SendQueue.ClearQueue )
{
// Clear receive queue
_receiveCommandQueue.Clear();
}
if (receiveQueueState == ReceiveQueue.ClearQueue )
{
// Clear send queue
_sendCommandQueue.Clear();
}
// If synchronized sending, the only way to get command at end of queue is by waiting
if (sendQueueState == SendQueue.WaitForEmptyQueue ||
(synchronizedSend && sendQueueState == SendQueue.AtEndQueue)
)
{
SpinWait.SpinUntil(() => _sendCommandQueue.IsEmpty);
}
if (receiveQueueState == ReceiveQueue.WaitForEmptyQueue)
{
SpinWait.SpinUntil(() => _receiveCommandQueue.IsEmpty);
}
if (synchronizedSend)
{
return SendCommandSync(sendCommand, sendQueueState);
}
if (sendQueueState != SendQueue.AtEndQueue)
{
// Put command at top of command queue
_sendCommandQueue.SendCommand(sendCommand);
}
else
{
// Put command at bottom of command queue
_sendCommandQueue.QueueCommand(sendCommand);
}
return new ReceivedCommand { CommunicationManager = _communicationManager };
}
/// <summary> Synchronized send a command. </summary>
/// <param name="sendCommand"> The command to sent. </param>
/// <param name="sendQueueState"> Property to optionally clear/wait the send queue. </param>
/// <returns> . </returns>
public ReceivedCommand SendCommandSync(SendCommand sendCommand, SendQueue sendQueueState)
{
// Directly call execute command
var resultSendCommand = _communicationManager.ExecuteSendCommand(sendCommand, sendQueueState);
InvokeNewLineEvent(NewLineSent, new CommandEventArgs(sendCommand));
return resultSendCommand;
}
/// <summary> Put the command at the back of the sent queue.</summary>
/// <param name="sendCommand"> The command to sent. </param>
public void QueueCommand(SendCommand sendCommand)
{
_sendCommandQueue.QueueCommand(sendCommand);
}
/// <summary> Put a command wrapped in a strategy at the back of the sent queue.</summary>
/// <param name="commandStrategy"> The command strategy. </param>
public void QueueCommand(CommandStrategy commandStrategy)
{
_sendCommandQueue.QueueCommand(commandStrategy);
}
/// <summary> Adds a general command strategy to the receive queue. This will be executed on every enqueued and dequeued command. </summary>
/// <param name="generalStrategy"> The general strategy for the receive queue. </param>
public void AddReceiveCommandStrategy(GeneralStrategy generalStrategy)
{
_receiveCommandQueue.AddGeneralStrategy(generalStrategy);
}
/// <summary> Adds a general command strategy to the send queue. This will be executed on every enqueued and dequeued command. </summary>
/// <param name="generalStrategy"> The general strategy for the send queue. </param>
public void AddSendCommandStrategy(GeneralStrategy generalStrategy)
{
_sendCommandQueue.AddGeneralStrategy(generalStrategy);
}
/// <summary> Clears the receive queue. </summary>
public void ClearReceiveQueue()
{
_receiveCommandQueue.Clear();
}
/// <summary> Clears the send queue. </summary>
public void ClearSendQueue()
{
_sendCommandQueue.Clear();
}
/// <summary> Helper function to Invoke or directly call event. </summary>
/// <param name="newLineHandler"> The event handler. </param>
/// <param name="newLineArgs"></param>
private void InvokeNewLineEvent(EventHandler<CommandEventArgs> newLineHandler, CommandEventArgs newLineArgs)
{
if (newLineHandler == null || (ControlToInvokeOn != null && ControlToInvokeOn.IsDisposed)) return;
if (ControlToInvokeOn != null)
{
//Asynchronously call on UI thread
ControlToInvokeOn.BeginInvoke((MethodInvoker)(() => newLineHandler(this, newLineArgs)));
}
else
{
//Directly call
newLineHandler(this, newLineArgs);
}
}
/// <summary> Helper function to Invoke or directly call callback function. </summary>
/// <param name="messengerCallbackFunction"> The messenger callback function. </param>
/// <param name="command"> The command. </param>
private void InvokeCallBack(MessengerCallbackFunction messengerCallbackFunction, ReceivedCommand command)
{
if (messengerCallbackFunction == null || (ControlToInvokeOn != null && ControlToInvokeOn.IsDisposed)) return;
if (ControlToInvokeOn != null)
{
//Asynchronously call on UI thread
ControlToInvokeOn.BeginInvoke(new MessengerCallbackFunction(messengerCallbackFunction), (object)command);
}
else
{
//Directly call
messengerCallbackFunction(command);
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
ControlToInvokeOn = null;
_communicationManager.Dispose();
_sendCommandQueue.Dispose();
_receiveCommandQueue.Dispose();
}
}
}
}

View File

@ -0,0 +1,80 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace CommandMessenger
{
/// <summary> A command to be send by CmdMessenger </summary>
public class Command
{
internal CommunicationManager CommunicationManager;
protected readonly List<string> CmdArgs = new List<string>(); // The argument list of the command, first one is the command ID
/// <summary> Gets or sets the command ID. </summary>
/// <value> The command ID. </value>
public int CmdId { get; set; }
/// <summary> Gets the command arguments. </summary>
/// <value> The arguments, first one is the command ID </value>
public String[] Arguments
{
get { return CmdArgs.ToArray(); }
}
/// <summary> Gets or sets the time stamp. </summary>
/// <value> The time stamp. </value>
public long TimeStamp { get; set; }
/// <summary> Constructor. </summary>
public Command()
{
CmdId = -1;
CmdArgs = new List<string>();
TimeStamp = TimeUtils.Millis;
}
/// <summary> Returns whether this is a valid & filled command. </summary>
/// <value> true if ok, false if not. </value>
public bool Ok
{
get { return (CmdId >= 0); }
}
public string CommandString()
{
if (CommunicationManager == null)
throw new InvalidOperationException("CommunicationManager was not set for command.");
var commandString = new StringBuilder(CmdId.ToString(CultureInfo.InvariantCulture));
foreach (var argument in Arguments)
{
commandString.Append(CommunicationManager.FieldSeparator).Append(argument);
}
commandString.Append(CommunicationManager.CommandSeparator);
return commandString.ToString();
}
}
}

View File

@ -0,0 +1,32 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
namespace CommandMessenger
{
public sealed class CommandEventArgs : EventArgs
{
public Command Command { get; private set; }
public CommandEventArgs(Command command)
{
Command = command;
}
}
}

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessenger</RootNamespace>
<AssemblyName>CommandMessenger</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ReceivedCommandSignal.cs" />
<Compile Include="ConnectionManager.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="EventWaiter.cs" />
<Compile Include="BinaryConverter.cs" />
<Compile Include="CmdMessenger.cs" />
<Compile Include="Command.cs" />
<Compile Include="CommunicationManager.cs" />
<Compile Include="CommandEventArgs.cs" />
<Compile Include="Queue\CollapseCommandStrategy.cs" />
<Compile Include="Queue\CommandQueue.cs" />
<Compile Include="Queue\CommandStrategy.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Queue\TopCommandStrategy.cs" />
<Compile Include="Queue\ReceiveCommandQueue.cs" />
<Compile Include="Queue\SendCommandQueue.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Queue\StaleGeneralStrategy.cs" />
<Compile Include="Queue\GeneralStrategy.cs" />
<Compile Include="Queue\ListQueue.cs" />
<Compile Include="ReceivedCommand.cs" />
<Compile Include="Escaped.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SendCommand.cs" />
<Compile Include="StructSerializer.cs" />
<Compile Include="Transport\ITransport.cs" />
<Compile Include="StringUtils.cs" />
<Compile Include="TimeUtils.cs" />
<Compile Include="Logger.cs" />
<Compile Include="AsyncWorker.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>Thijs Elenbaas</authors>
<owners>Thijs Elenbaas</owners>
<projectUrl>https://github.com/thijse/Arduino-CmdMessenger</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes></releaseNotes>
<copyright></copyright>
<tags>Arduino Communication</tags>
</metadata>
</package>

View File

@ -0,0 +1,323 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Text;
using CommandMessenger.Queue;
using CommandMessenger.Transport;
namespace CommandMessenger
{
/// <summary>
/// Manager for data over transport layer.
/// </summary>
public class CommunicationManager : IDisposable
{
private readonly Encoding _stringEncoder = Encoding.GetEncoding("ISO-8859-1"); // The string encoder
private readonly object _sendCommandDataLock = new object(); // The process serial data lock
private readonly object _parseLinesLock = new object();
private readonly ReceiveCommandQueue _receiveCommandQueue;
private readonly ITransport _transport;
private readonly IsEscaped _isEscaped; // The is escaped
private string _buffer = string.Empty;
/// <summary> The field separator </summary>
public char FieldSeparator { get; private set; }
/// <summary>The command separator </summary>
public char CommandSeparator { get; private set; }
/// <summary> The escape character </summary>
public char EscapeCharacter { get; private set; }
/// <summary> Gets or sets a whether to print a line feed carriage return after each command. </summary>
/// <value> true if print line feed carriage return, false if not. </value>
public bool PrintLfCr { get; set; }
public BoardType BoardType { get; set; }
/// <summary> Gets or sets the time stamp of the last received line. </summary>
/// <value> time stamp of the last received line. </value>
public long LastLineTimeStamp { get; private set; }
/// <summary> Constructor. </summary>
/// <param name="receiveCommandQueue"></param>
/// <param name="boardType">The Board Type. </param>
/// <param name="commandSeparator">The End-Of-Line separator. </param>
/// <param name="fieldSeparator"></param>
/// <param name="escapeCharacter"> The escape character. </param>
/// <param name="transport"> The Transport Layer</param>
public CommunicationManager(ITransport transport, ReceiveCommandQueue receiveCommandQueue,
BoardType boardType, char commandSeparator, char fieldSeparator, char escapeCharacter)
{
_transport = transport;
_transport.DataReceived += NewDataReceived;
_receiveCommandQueue = receiveCommandQueue;
BoardType = boardType;
CommandSeparator = commandSeparator;
FieldSeparator = fieldSeparator;
EscapeCharacter = escapeCharacter;
_isEscaped = new IsEscaped();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void NewDataReceived(object o, EventArgs e)
{
ParseLines();
}
/// <summary> Connects to a transport layer defined through the current settings. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Connect()
{
return !_transport.IsConnected() && _transport.Connect();
}
/// <summary> Stops listening to the transport layer </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Disconnect()
{
return _transport.IsConnected() && _transport.Disconnect();
}
/// <summary> Writes a string to the transport layer. </summary>
/// <param name="value"> The string to write. </param>
public void WriteLine(string value)
{
Write(value + "\r\n");
}
/// <summary> Writes a parameter to the transport layer followed by a NewLine. </summary>
/// <typeparam name="T"> Generic type parameter. </typeparam>
/// <param name="value"> The value. </param>
public void WriteLine<T>(T value)
{
WriteLine(value.ToString());
}
/// <summary> Writes a parameter to the transport layer. </summary>
/// <typeparam name="T"> Generic type parameter. </typeparam>
/// <param name="value"> The value. </param>
public void Write<T>(T value)
{
Write(value.ToString());
}
/// <summary> Writes a string to the transport layer. </summary>
/// <param name="value"> The string to write. </param>
public void Write(string value)
{
byte[] writeBytes = _stringEncoder.GetBytes(value);
_transport.Write(writeBytes);
}
/// <summary> Directly executes the send command operation. </summary>
/// <param name="sendCommand"> The command to sent. </param>
/// <param name="sendQueueState"> Property to optionally clear the send and receive queues. </param>
/// <returns> A received command. The received command will only be valid if the ReqAc of the command is true. </returns>
public ReceivedCommand ExecuteSendCommand(SendCommand sendCommand, SendQueue sendQueueState)
{
// Disable listening, all callbacks are disabled until after command was sent
ReceivedCommand ackCommand;
lock (_sendCommandDataLock)
{
sendCommand.CommunicationManager = this;
sendCommand.InitArguments();
if (sendCommand.ReqAc)
{
// Stop processing receive queue before sending. Wait until receive queue is actualy done
_receiveCommandQueue.Suspend();
}
if (sendCommand.ReqAc)
{
_receiveCommandQueue.PrepareForCmd(sendCommand.AckCmdId, sendQueueState);
WriteCommand(sendCommand);
var rc = BlockedTillReply(sendCommand.Timeout);
ackCommand = rc ?? new ReceivedCommand();
}
else
{
WriteCommand(sendCommand);
ackCommand = new ReceivedCommand();
}
ackCommand.CommunicationManager = this;
}
if (sendCommand.ReqAc)
{
// Stop processing receive queue before sending
_receiveCommandQueue.Resume();
}
return ackCommand;
}
/// <summary> Directly executes the send string operation. </summary>
/// <param name="commandString"> The string to sent. </param>
/// <param name="sendQueueState"> Property to optionally clear the send and receive queues. </param>
/// <returns> The received command is added for compatibility. It will not yield a response. </returns>
public ReceivedCommand ExecuteSendString(String commandString, SendQueue sendQueueState)
{
lock (_sendCommandDataLock)
{
if (PrintLfCr)
{
WriteLine(commandString);
}
else
{
Write(commandString);
}
}
return new ReceivedCommand { CommunicationManager = this };
}
/// <summary> Blocks until acknowledgement reply has been received. </summary>
/// <param name="timeout"> Timeout on acknowledge command. </param>
/// <returns> A received command. </returns>
private ReceivedCommand BlockedTillReply(int timeout)
{
// Wait for matching command
return _receiveCommandQueue.WaitForCmd(timeout) ?? new ReceivedCommand();
}
private void ParseLines()
{
lock(_parseLinesLock)
{
var data = _transport.Read();
_buffer += _stringEncoder.GetString(data);
do
{
string currentLine = ParseLine();
if (string.IsNullOrEmpty(currentLine)) break;
LastLineTimeStamp = TimeUtils.Millis;
ProcessLine(currentLine);
}
while (true);
}
}
/// <summary> Processes the byte message and add to queue. </summary>
private void ProcessLine(string line)
{
// Read line from raw buffer and make command
var currentReceivedCommand = ParseMessage(line);
currentReceivedCommand.RawString = line;
// Set time stamp
currentReceivedCommand.TimeStamp = LastLineTimeStamp;
// And put on queue
_receiveCommandQueue.QueueCommand(currentReceivedCommand);
}
/// <summary> Parse message. </summary>
/// <param name="line"> The received command line. </param>
/// <returns> The received command. </returns>
private ReceivedCommand ParseMessage(string line)
{
// Trim and clean line
var cleanedLine = line.Trim('\r', '\n');
cleanedLine = Escaping.Remove(cleanedLine, CommandSeparator, EscapeCharacter);
return new ReceivedCommand(
Escaping.Split(cleanedLine, FieldSeparator, EscapeCharacter, StringSplitOptions.RemoveEmptyEntries)) { CommunicationManager = this };
}
/// <summary> Reads a float line from the buffer, if complete. </summary>
/// <returns> Whether a complete line was present in the buffer. </returns>
private string ParseLine()
{
if (!string.IsNullOrEmpty(_buffer))
{
// Check if an End-Of-Line is present in the string, and split on first
//var i = _buffer.IndexOf(CommandSeparator);
var i = FindNextEol();
if (i >= 0 && i < _buffer.Length)
{
var line = _buffer.Substring(0, i + 1);
if (!string.IsNullOrEmpty(line))
{
_buffer = _buffer.Substring(i + 1);
return line;
}
_buffer = _buffer.Substring(i + 1);
return string.Empty;
}
}
return string.Empty;
}
/// <summary> Searches for the next End-Of-Line. </summary>
/// <returns> The the location in the string of the next End-Of-Line. </returns>
private int FindNextEol()
{
int pos = 0;
while (pos < _buffer.Length)
{
var escaped = _isEscaped.EscapedChar(_buffer[pos]);
if (_buffer[pos] == CommandSeparator && !escaped)
{
return pos;
}
pos++;
}
return pos;
}
/// <summary>
/// Sends a command to the transport layer with or without a LFCR depending on the state of PrintLfCr
/// </summary>
/// <param name="sendCommand"></param>
private void WriteCommand(SendCommand sendCommand)
{
if (PrintLfCr)
WriteLine(sendCommand.CommandString());
else
Write(sendCommand.CommandString());
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Stop polling
_transport.DataReceived -= NewDataReceived;
}
}
}
}

View File

@ -0,0 +1,440 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
using System.Threading;
using System.Windows.Forms;
namespace CommandMessenger
{
public class ConnectionManagerProgressEventArgs : EventArgs
{
public int Level { get; set; }
public String Description { get; set; }
}
public enum Mode
{
Wait,
Connect,
Scan,
Watchdog
}
public enum DeviceStatus
{
NotAvailable,
Available,
IdentityMismatch
}
public abstract class ConnectionManager : IDisposable
{
public event EventHandler<EventArgs> ConnectionTimeout;
public event EventHandler<EventArgs> ConnectionFound;
public event EventHandler<ConnectionManagerProgressEventArgs> Progress;
protected Mode ConnectionManagerMode = Mode.Wait;
private readonly CmdMessenger _cmdMessenger;
private readonly AsyncWorker _worker;
private readonly int _identifyCommandId;
private readonly string _uniqueDeviceId;
private long _lastCheckTime;
private long _nextTimeOutCheck;
private uint _watchdogTries;
private bool _watchdogEnabled;
/// <summary>
/// Is connection manager currently connected to device.
/// </summary>
public bool Connected { get; protected set; }
public int WatchdogTimeout { get; set; }
public int WatchdogRetryTimeout { get; set; }
public uint WatchdogTries { get; set; }
/// <summary>
/// Enables or disables connection watchdog functionality using identify command and unique device id.
/// </summary>
public bool WatchdogEnabled
{
get { return _watchdogEnabled; }
set
{
if (value && string.IsNullOrEmpty(_uniqueDeviceId))
throw new InvalidOperationException("Watchdog can't be enabled without Unique Device ID.");
_watchdogEnabled = value;
}
}
/// <summary>
/// Enables or disables device scanning.
/// When disabled, connection manager will try to open connection to the device configured in the setting.
/// - For SerialConnection this means scanning for (virtual) serial ports,
/// - For BluetoothConnection this means scanning for a device on RFCOMM level
/// </summary>
public bool DeviceScanEnabled { get; set; }
/// <summary>
/// Enables or disables storing of last connection configuration in persistent file.
/// </summary>
public bool PersistentSettings { get; set; }
protected ConnectionManager(CmdMessenger cmdMessenger, int identifyCommandId = 0, string uniqueDeviceId = null)
{
if (cmdMessenger == null)
throw new ArgumentNullException("cmdMessenger", "Command Messenger is null.");
_cmdMessenger = cmdMessenger;
_identifyCommandId = identifyCommandId;
_uniqueDeviceId = uniqueDeviceId;
WatchdogTimeout = 3000;
WatchdogRetryTimeout = 1500;
WatchdogTries = 3;
WatchdogEnabled = false;
PersistentSettings = false;
DeviceScanEnabled = true;
_worker = new AsyncWorker(DoWork, "ConnectionManager");
if (!string.IsNullOrEmpty(uniqueDeviceId))
_cmdMessenger.Attach(identifyCommandId, OnIdentifyResponse);
}
/// <summary>
/// Start connection manager.
/// </summary>
public virtual void StartConnectionManager()
{
if (!_worker.IsRunning) _worker.Start();
if (DeviceScanEnabled)
{
StartScan();
}
else
{
StartConnect();
}
}
/// <summary>
/// Stop connection manager.
/// </summary>
public virtual void StopConnectionManager()
{
if (_worker.IsRunning) _worker.Stop();
Disconnect();
}
protected virtual void ConnectionFoundEvent()
{
ConnectionManagerMode = Mode.Wait;
if (WatchdogEnabled) StartWatchDog();
InvokeEvent(ConnectionFound, EventArgs.Empty);
}
protected virtual void ConnectionTimeoutEvent()
{
ConnectionManagerMode = Mode.Wait;
Disconnect();
InvokeEvent(ConnectionTimeout, EventArgs.Empty);
if (WatchdogEnabled)
{
StopWatchDog();
if (DeviceScanEnabled)
{
StartScan();
}
else
{
StartConnect();
}
}
}
protected virtual void Log(int level, string logMessage)
{
var args = new ConnectionManagerProgressEventArgs {Level = level, Description = logMessage};
InvokeEvent(Progress, args);
}
protected virtual void OnIdentifyResponse(ReceivedCommand responseCommand)
{
if (responseCommand.Ok && !string.IsNullOrEmpty(_uniqueDeviceId))
{
ValidateDeviceUniqueId(responseCommand);
}
}
private void InvokeEvent<TEventHandlerArguments>(EventHandler<TEventHandlerArguments> eventHandler,
TEventHandlerArguments eventHandlerArguments) where TEventHandlerArguments : EventArgs
{
var ctrlToInvoke = _cmdMessenger.ControlToInvokeOn;
if (eventHandler == null || (ctrlToInvoke != null && ctrlToInvoke.IsDisposed)) return;
if (ctrlToInvoke != null )
{
try { ctrlToInvoke.BeginInvoke((MethodInvoker)(() => eventHandler(this, eventHandlerArguments))); } catch { }
}
else
{
//Invoke here
try { eventHandler.BeginInvoke(this, eventHandlerArguments, null, null); } catch { }
}
}
private bool DoWork()
{
// Switch between waiting, device scanning and watchdog
switch (ConnectionManagerMode)
{
case Mode.Scan:
DoWorkScan();
break;
case Mode.Connect:
DoWorkConnect();
break;
case Mode.Watchdog:
DoWorkWatchdog();
break;
}
Thread.Sleep(100);
return true;
}
/// <summary>
/// Check if Arduino is available
/// </summary>
/// <param name="timeOut">Timout for waiting on response</param>
/// <returns>Check result.</returns>
protected DeviceStatus ArduinoAvailable(int timeOut)
{
var challengeCommand = new SendCommand(_identifyCommandId, _identifyCommandId, timeOut);
var responseCommand = _cmdMessenger.SendCommand(challengeCommand, SendQueue.InFrontQueue, ReceiveQueue.Default, UseQueue.BypassQueue);
if (responseCommand.Ok && !string.IsNullOrEmpty(_uniqueDeviceId))
{
return ValidateDeviceUniqueId(responseCommand) ? DeviceStatus.Available : DeviceStatus.IdentityMismatch;
}
return responseCommand.Ok ? DeviceStatus.Available : DeviceStatus.NotAvailable;
}
/// <summary>
/// Check if Arduino is available
/// </summary>
/// <param name="timeOut">Timout for waiting on response</param>
/// <param name="tries">Number of tries</param>
/// <returns>Check result.</returns>
protected DeviceStatus ArduinoAvailable(int timeOut, int tries)
{
for (var i = 1; i <= tries; i++)
{
Log(3, "Polling Arduino, try # " + i);
DeviceStatus status = ArduinoAvailable(timeOut);
if (status == DeviceStatus.Available
|| status == DeviceStatus.IdentityMismatch) return status;
}
return DeviceStatus.NotAvailable;
}
protected virtual bool ValidateDeviceUniqueId(ReceivedCommand responseCommand)
{
bool valid = _uniqueDeviceId == responseCommand.ReadStringArg();
if (!valid)
{
Log(3, "Invalid device response. Device ID mismatch.");
}
return valid;
}
//Try to connect using current connections settings
protected abstract void DoWorkConnect();
// Perform scan to find connected systems
protected abstract void DoWorkScan();
protected virtual void DoWorkWatchdog()
{
var lastLineTimeStamp = _cmdMessenger.LastReceivedCommandTimeStamp;
var currentTimeStamp = TimeUtils.Millis;
// If timeout has not elapsed, wait till next watch time
if (currentTimeStamp < _nextTimeOutCheck) return;
// if a command has been received recently, set next check time
if (lastLineTimeStamp >= _lastCheckTime)
{
Log(3, "Successful watchdog response.");
_lastCheckTime = currentTimeStamp;
_nextTimeOutCheck = _lastCheckTime + WatchdogTimeout;
_watchdogTries = 0;
return;
}
// Apparently, other side has not reacted in time
// If too many tries, notify and stop
if (_watchdogTries >= WatchdogTries)
{
Log(2, "Watchdog received no response after final try #" + WatchdogTries);
_watchdogTries = 0;
ConnectionManagerMode = Mode.Wait;
ConnectionTimeoutEvent();
return;
}
// We'll try another time
// We queue the command in order to not be intrusive, but put it in front to get a quick answer
_cmdMessenger.SendCommand(new SendCommand(_identifyCommandId));
_watchdogTries++;
_lastCheckTime = currentTimeStamp;
_nextTimeOutCheck = _lastCheckTime + WatchdogRetryTimeout;
Log(3, _watchdogTries == 1 ?
"Watchdog detected no communication for " + WatchdogTimeout/1000.0 + "s, asking for response"
: "Watchdog received no response, performing try #" + _watchdogTries);
}
/// <summary>
/// Disconnect from Arduino
/// </summary>
/// <returns>true if sucessfully disconnected</returns>
private bool Disconnect()
{
if (Connected)
{
Connected = false;
return _cmdMessenger.Disconnect();
}
return true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Start watchdog. Will check if connection gets interrupted
/// </summary>
protected virtual void StartWatchDog()
{
if (ConnectionManagerMode != Mode.Watchdog && Connected)
{
Log(1, "Starting Watchdog.");
_lastCheckTime = TimeUtils.Millis;
_nextTimeOutCheck = _lastCheckTime + WatchdogTimeout;
_watchdogTries = 0;
ConnectionManagerMode = Mode.Watchdog;
}
}
/// <summary>
/// Stop watchdog.
/// </summary>
protected virtual void StopWatchDog()
{
if (ConnectionManagerMode == Mode.Watchdog)
{
Log(1, "Stopping Watchdog.");
ConnectionManagerMode = Mode.Wait;
}
}
/// <summary>
/// Start scanning for devices
/// </summary>
protected virtual void StartScan()
{
if (ConnectionManagerMode != Mode.Scan && !Connected)
{
Log(1, "Starting device scan.");
ConnectionManagerMode = Mode.Scan;
}
}
/// <summary>
/// Stop scanning for devices
/// </summary>
protected virtual void StopScan()
{
if (ConnectionManagerMode == Mode.Scan)
{
Log(1, "Stopping device scan.");
ConnectionManagerMode = Mode.Wait;
}
}
/// <summary>
/// Start connect to device
/// </summary>
protected virtual void StartConnect()
{
if (ConnectionManagerMode != Mode.Connect && !Connected)
{
Log(1, "Start connecting to device.");
ConnectionManagerMode = Mode.Connect;
}
}
/// <summary>
/// Stop connect to device
/// </summary>
protected virtual void StopConnect()
{
if (ConnectionManagerMode == Mode.Connect)
{
Log(1, "Stop connecting to device.");
ConnectionManagerMode = Mode.Wait;
}
}
protected virtual void StoreSettings() { }
protected virtual void ReadSettings() { }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
StopConnectionManager();
}
}
}
}

View File

@ -0,0 +1,173 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace CommandMessenger
{
/// <summary> Class for bookkeeping which characters in the stream are escaped. </summary>
public class IsEscaped
{
private char _lastChar = '\0'; // The last character
// Returns if the character is escaped
// Note create new instance for every independent string
/// <summary>Returns if the character is escaped.
/// Note create new instance for every independent string </summary>
/// <param name="currChar"> The currebt character. </param>
/// <returns> true if the character is escaped, false if not. </returns>
public bool EscapedChar(char currChar)
{
bool escaped = (_lastChar == Escaping.EscapeCharacter);
_lastChar = currChar;
// special case: the escape char has been escaped:
if (_lastChar == Escaping.EscapeCharacter && escaped)
{
_lastChar = '\0';
}
return escaped;
}
}
/// <summary> Utility class providing escaping functions </summary>
public static class Escaping
{
// Remove all occurrences of removeChar unless it is escaped by escapeChar
private static char _fieldSeparator = ','; // The field separator
private static char _commandSeparator = ';'; // The command separator
private static char _escapeCharacter = '/'; // The escape character
/// <summary> Gets the escape character. </summary>
/// <value> The escape character. </value>
public static char EscapeCharacter
{
get { return _escapeCharacter; }
}
/// <summary> Sets custom escape characters. </summary>
/// <param name="fieldSeparator"> The field separator. </param>
/// <param name="commandSeparator"> The command separator. </param>
/// <param name="escapeCharacter"> The escape character. </param>
public static void EscapeChars(char fieldSeparator, char commandSeparator, char escapeCharacter)
{
_fieldSeparator = fieldSeparator;
_commandSeparator = commandSeparator;
_escapeCharacter = escapeCharacter;
}
/// <summary> Removes all occurences of a specific character unless escaped. </summary>
/// <param name="input"> The input. </param>
/// <param name="removeChar"> The character to remove. </param>
/// <param name="escapeChar"> The escape character. </param>
/// <returns> The string with all removeChars removed. </returns>
public static string Remove(string input, char removeChar, char escapeChar)
{
var output = string.Empty;
var escaped = new IsEscaped();
for (var i = 0; i < input.Length; i++)
{
char inputChar = input[i];
bool isEscaped = escaped.EscapedChar(inputChar);
if (inputChar != removeChar || isEscaped)
{
output += inputChar;
}
}
return output;
}
// Split String on separator character unless it is escaped by escapeChar
/// <summary> Splits. </summary>
/// <param name="input"> The input. </param>
/// <param name="separator"> The separator. </param>
/// <param name="escapeCharacter"> The escape character. </param>
/// <param name="stringSplitOptions"> Options for controlling the string split. </param>
/// <returns> The split string. </returns>
public static String[] Split(string input, char separator, char escapeCharacter,
StringSplitOptions stringSplitOptions)
{
var word = string.Empty;
var result = new List<string>();
for (var i = 0; i < input.Length; i++)
{
var t = input[i];
if (t == separator)
{
result.Add(word);
word = string.Empty;
}
else
{
if (t == escapeCharacter)
{
word += t;
if (i < input.Length - 1) t = input[++i];
}
word += t;
}
}
result.Add(word);
if (stringSplitOptions == StringSplitOptions.RemoveEmptyEntries) result.RemoveAll(item => item == string.Empty);
return result.ToArray();
}
/// <summary> Escapes the input string. </summary>
/// <param name="input"> The unescaped input string. </param>
/// <returns> Escaped output string. </returns>
public static string Escape(string input)
{
var escapeChars = new[]
{
_escapeCharacter.ToString(CultureInfo.InvariantCulture),
_fieldSeparator.ToString(CultureInfo.InvariantCulture),
_commandSeparator.ToString(CultureInfo.InvariantCulture),
"\0"
};
input = escapeChars.Aggregate(input,
(current, escapeChar) =>
current.Replace(escapeChar, _escapeCharacter + escapeChar));
return input;
}
/// <summary> Unescapes the input string. </summary>
/// <param name="input"> The escaped input string. </param>
/// <returns> The unescaped output string. </returns>
public static string Unescape(string input)
{
string output = string.Empty;
// Move unescaped characters right
for (var fromChar = 0; fromChar < input.Length; fromChar++)
{
if (input[fromChar] == _escapeCharacter)
{
fromChar++;
}
output += input[fromChar];
}
return output;
}
}
}

View File

@ -0,0 +1,118 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System.Threading;
namespace CommandMessenger
{
// Functionality comparable to AutoResetEvent (http://www.albahari.com/threading/part2.aspx#_AutoResetEvent)
// but implemented using the monitor class: not inter processs, but ought to be more efficient.
public class EventWaiter
{
public enum WaitState
{
TimeOut,
Normal
}
private readonly object _key = new object();
private bool _block;
/// <summary>
/// start blocked (waiting for signal)
/// </summary>
public EventWaiter()
{
lock (_key)
{
_block = true;
Monitor.Pulse(_key);
}
}
/// <summary>
/// start blocked or signalled.
/// </summary>
/// <param name="set">If true, first Wait will directly continue</param>
public EventWaiter(bool set)
{
lock (_key)
{
_block = !set;
Monitor.Pulse(_key);
}
}
/// <summary>
/// Wait function. Blocks until signal is set or time-out
/// </summary>
/// <param name="timeOut">time-out in ms</param>
/// <returns></returns>
public WaitState WaitOne(int timeOut)
{
lock (_key)
{
// Check if signal has already been raised before the wait function is entered
if (!_block)
{
// If so, reset event for next time and exit wait loop
_block = true;
return WaitState.Normal;
}
// Wait under conditions
bool noTimeOut = true;
while (noTimeOut && _block)
{
noTimeOut = Monitor.Wait(_key, timeOut);
}
// Block Wait for next entry
_block = true;
// Return whether the Wait function was quit because of an Set event or timeout
return noTimeOut ? WaitState.Normal : WaitState.TimeOut;
}
}
/// <summary>
/// Sets signal, will unblock thread in Wait function
/// </summary>
public void Set()
{
lock (_key)
{
_block = false;
Monitor.Pulse(_key);
}
}
/// <summary>
/// Resets signal, will block threads entering Wait function
/// </summary>
public void Reset()
{
lock (_key)
{
_block = true;
}
}
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.IO;
using System.Text;
namespace CommandMessenger
{
static class Logger
{
private static readonly Encoding StringEncoder = Encoding.GetEncoding("ISO-8859-1"); // The string encoder
private static FileStream _fileStream;
static Logger()
{
LogFileName = null;
IsEnabled = true;
}
static public bool IsEnabled { get; set; }
static public bool IsOpen { get; private set; }
static public bool DirectFlush { get; set; }
/// <summary> Gets or sets the log file name. </summary>
/// <value> The logfile name . </value>
static public String LogFileName { get; private set; }
static public bool Open()
{
return Open(LogFileName);
}
static public bool Open(string logFileName)
{
if (IsOpen && LogFileName == logFileName) return true;
LogFileName = logFileName;
if (IsOpen)
{
try
{
_fileStream.Close();
}
catch (Exception) { }
IsOpen = false;
}
try
{
_fileStream = new FileStream(logFileName, FileMode.Create, FileAccess.ReadWrite);
}
catch (Exception)
{
return false;
}
IsOpen = true;
return true;
}
static public void Close()
{
if (!IsOpen) return;
try
{
_fileStream.Close();
}
catch (Exception) { }
IsOpen = false;
}
static public void Log(string logString)
{
if (!IsEnabled || !IsOpen) return;
var writeBytes = StringEncoder.GetBytes(logString);
_fileStream.Write(writeBytes, 0, writeBytes.Length);
if (DirectFlush) _fileStream.Flush();
}
static public void LogLine(string logString)
{
Log(logString + Environment.NewLine);
}
}
}

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CmdMessenger")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("64af8ae8-9076-46b5-97aa-fe9c9a0c8b40")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.7.1.0")]
[assembly: AssemblyFileVersion("3.7.1.0")]

View File

@ -0,0 +1,49 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Queue
{
/// <summary> Collapse command strategy.
/// The purpose of the strategy is to avoid duplicates of a certain command on the queue
/// to avoid lagging </summary>
public class CollapseCommandStrategy : CommandStrategy
{
/// <summary> Collapse strategy. </summary>
/// <param name="command"> The command that will be collapsed on the queue. </param>
public CollapseCommandStrategy(Command command) : base(command)
{}
/// <summary> Add command (strategy) to command queue. </summary>
public override void Enqueue()
{
// find if there already is a command with the same CmdId
var index = CommandQueue.FindIndex(strategy => strategy.Command.CmdId == Command.CmdId);
if (index < 0)
{
// if not, add to the back of the queue
CommandQueue.Enqueue(this);
}
else
{
// if on the queue, replace with new command
CommandQueue[index] = this;
}
}
}
}

View File

@ -0,0 +1,119 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
namespace CommandMessenger.Queue
{
// Command queue base object.
public abstract class CommandQueue : IDisposable
{
private readonly AsyncWorker _worker;
protected readonly ListQueue<CommandStrategy> Queue = new ListQueue<CommandStrategy>(); // Buffer for commands
protected readonly List<GeneralStrategy> GeneralStrategies = new List<GeneralStrategy>(); // Buffer for command independent strategies
public bool IsRunning { get { return _worker.IsRunning; } }
public bool IsSuspended { get { return _worker.IsSuspended; } }
/// <summary>Gets count of records in queue. NOT THREAD-SAFE.</summary>
public int Count
{
get { return Queue.Count; }
}
/// <summary>Gets is queue is empty. NOT THREAD-SAFE.</summary>
public bool IsEmpty
{
get { return Queue.Count == 0; }
}
/// <summary> Clears the queue. </summary>
public void Clear()
{
lock (Queue) Queue.Clear();
}
protected CommandQueue()
{
_worker = new AsyncWorker(ProcessQueue, "CommandQueue");
}
/// <summary> Adds a general strategy. This strategy is applied to all queued and dequeued commands. </summary>
/// <param name="generalStrategy"> The general strategy. </param>
public void AddGeneralStrategy(GeneralStrategy generalStrategy)
{
// Give strategy access to queue
generalStrategy.CommandQueue = Queue;
// Add to general strategy list
GeneralStrategies.Add(generalStrategy);
}
/// <summary>
/// Queue the command wrapped in a command strategy.
/// Call SignalWaiter method to continue processing of queue.
/// </summary>
/// <param name="commandStrategy"> The command strategy. </param>
public abstract void QueueCommand(CommandStrategy commandStrategy);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Start()
{
_worker.Start();
}
public void Stop()
{
_worker.Stop();
Clear();
}
public void Suspend()
{
_worker.Suspend();
}
public void Resume()
{
_worker.Resume();
}
protected void SignalWorker()
{
_worker.Signal();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Stop();
}
}
/// <summary> Process the queue. </summary>
protected abstract bool ProcessQueue();
}
}

View File

@ -0,0 +1,52 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Queue
{
/// <summary> Base command strategy. </summary>
public class CommandStrategy
{
/// <summary> Base command strategy. </summary>
/// <param name="command"> The command to be wrapped in a strategy. </param>
public CommandStrategy(Command command)
{
Command = command;
}
/// <summary> Gets or sets the command queue. </summary>
/// <value> A Queue of commands. </value>
public ListQueue<CommandStrategy> CommandQueue { get; set; }
/// <summary> Gets or sets the command. </summary>
/// <value> The command wrapped in the strategy. </value>
public Command Command { get; private set; }
/// <summary> Add command (strategy) to command queue. </summary>
public virtual void Enqueue()
{
CommandQueue.Enqueue(this);
}
/// <summary> Remove this command (strategy) from command queue. </summary>
public virtual void DeQueue()
{
CommandQueue.Remove(this);
}
}
}

View File

@ -0,0 +1,39 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Queue
{
/// <summary> Base of general strategy. </summary>
public class GeneralStrategy
{
/// <summary> Gets or sets the command queue. </summary>
/// <value> A Queue of commands. </value>
public ListQueue<CommandStrategy> CommandQueue { get; set; }
/// <summary> GenerAdd command (strategy) to command queue. </summary>
public virtual void OnEnqueue()
{
}
/// <summary> Remove this command (strategy) from command queue. </summary>
public virtual void OnDequeue()
{
}
}
}

View File

@ -0,0 +1,59 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System.Collections.Generic;
namespace CommandMessenger.Queue
{
/// <summary> Queue class. </summary>
/// <typeparam name="T"> Type of object to queue. </typeparam>
public class ListQueue<T> : List<T>
{
/// <summary> Adds item to front of queue. </summary>
/// <param name="item"> The item to queue. </param>
public void EnqueueFront(T item)
{
Insert(Count, item);
}
/// <summary> Adds item to back of queue. </summary>
/// <param name="item"> The item to queue. </param>
public void Enqueue(T item)
{
Add(item);
}
/// <summary> fetches item from front of queue. </summary>
/// <returns> The item to dequeue. </returns>
public T Dequeue()
{
var t = base[0];
RemoveAt(0);
return t;
}
/// <summary> look at item at front of queue without removing it from the queue. </summary>
/// <returns> The item to peek at. </returns>
public T Peek()
{
return base[0];
}
}
}

View File

@ -0,0 +1,124 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
namespace CommandMessenger.Queue
{
/// <summary> Queue of received commands. </summary>
public class ReceiveCommandQueue : CommandQueue
{
public delegate void HandleReceivedCommandDelegate(ReceivedCommand receivedCommand);
public event EventHandler<CommandEventArgs> NewLineReceived;
private readonly HandleReceivedCommandDelegate _receivedCommandHandler;
private readonly ReceivedCommandSignal _receivedCommandSignal = new ReceivedCommandSignal();
public ReceiveCommandQueue(HandleReceivedCommandDelegate receivedCommandHandler)
{
_receivedCommandHandler = receivedCommandHandler;
}
/// <summary> Dequeue the received command. </summary>
/// <returns> The received command. </returns>
public ReceivedCommand DequeueCommand()
{
lock (Queue)
{
return DequeueCommandInternal();
}
}
protected override bool ProcessQueue()
{
ReceivedCommand dequeueCommand;
bool hasMoreWork;
lock (Queue)
{
dequeueCommand = DequeueCommandInternal();
hasMoreWork = !IsEmpty;
}
if (dequeueCommand != null)
{
_receivedCommandHandler(dequeueCommand);
}
return hasMoreWork;
}
public void PrepareForCmd(int cmdId, SendQueue sendQueueState)
{
_receivedCommandSignal.PrepareForWait(cmdId, sendQueueState);
}
public ReceivedCommand WaitForCmd(int timeOut)
{
return _receivedCommandSignal.WaitForCmd(timeOut);
}
/// <summary> Queue the received command. </summary>
/// <param name="receivedCommand"> The received command. </param>
public void QueueCommand(ReceivedCommand receivedCommand)
{
QueueCommand(new CommandStrategy(receivedCommand));
}
/// <summary> Queue the command wrapped in a command strategy. </summary>
/// <param name="commandStrategy"> The command strategy. </param>
public override void QueueCommand(CommandStrategy commandStrategy)
{
if (IsSuspended)
{
// Directly send this command to waiting thread
var addToQueue = _receivedCommandSignal.ProcessCommand((ReceivedCommand)commandStrategy.Command);
// check if the item needs to be added to the queue for later processing. If not return directly
if (!addToQueue) return;
}
lock (Queue)
{
// Process all generic enqueue strategies
Queue.Enqueue(commandStrategy);
foreach (var generalStrategy in GeneralStrategies) { generalStrategy.OnEnqueue(); }
}
if (!IsSuspended)
{
// Give a signal to indicate that a new item has been queued
SignalWorker();
if (NewLineReceived != null) NewLineReceived(this, new CommandEventArgs(commandStrategy.Command));
}
}
private ReceivedCommand DequeueCommandInternal()
{
ReceivedCommand receivedCommand = null;
if (!IsEmpty)
{
foreach (var generalStrategy in GeneralStrategies) { generalStrategy.OnDequeue(); }
var commandStrategy = Queue.Dequeue();
receivedCommand = (ReceivedCommand)commandStrategy.Command;
}
return receivedCommand;
}
}
}

View File

@ -0,0 +1,182 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Threading;
namespace CommandMessenger.Queue
{
/// <summary> Queue of received commands. </summary>
public class SendCommandQueue : CommandQueue
{
public event EventHandler<CommandEventArgs> NewLineSent;
private readonly CommunicationManager _communicationManager;
private readonly int _sendBufferMaxLength = 62;
private string _sendBuffer = string.Empty;
private int _commandCount;
public uint MaxQueueLength { get; set; }
/// <summary> send command queue constructor. </summary>
/// <param name="communicationManager">The communication manager instance</param>
/// <param name="sendBufferMaxLength">Length of the send buffer</param>
public SendCommandQueue(CommunicationManager communicationManager, int sendBufferMaxLength)
{
MaxQueueLength = 5000;
_communicationManager = communicationManager;
_sendBufferMaxLength = sendBufferMaxLength;
}
protected override bool ProcessQueue()
{
SendCommandsFromQueue();
lock (Queue) return !IsEmpty;
}
/// <summary> Sends the commands from queue. All commands will be combined until either
/// the SendBufferMaxLength has been reached or if a command requires an acknowledge
/// </summary>
private void SendCommandsFromQueue()
{
_commandCount = 0;
_sendBuffer = string.Empty;
CommandStrategy eventCommandStrategy = null;
// while maximum buffer string is not reached, and command in queue
while (_sendBuffer.Length < _sendBufferMaxLength && Queue.Count > 0)
{
lock (Queue)
{
var commandStrategy = !IsEmpty ? Queue.Peek() : null;
if (commandStrategy != null)
{
if (commandStrategy.Command != null)
{
var sendCommand = (SendCommand)commandStrategy.Command;
if (sendCommand.ReqAc)
{
if (_commandCount > 0)
{
break;
}
SendSingleCommandFromQueue(commandStrategy);
}
else
{
eventCommandStrategy = commandStrategy;
AddToCommandString(commandStrategy);
}
}
}
}
// event callback outside lock for performance
if (eventCommandStrategy != null)
{
if (NewLineSent != null) NewLineSent(this, new CommandEventArgs(eventCommandStrategy.Command));
eventCommandStrategy = null;
}
}
// Now check if a command string has been filled
if (_sendBuffer.Length > 0)
{
_communicationManager.ExecuteSendString(_sendBuffer, SendQueue.InFrontQueue);
}
}
/// <summary> Sends a float command from the queue. </summary>
/// <param name="commandStrategy"> The command strategy to send. </param>
private void SendSingleCommandFromQueue(CommandStrategy commandStrategy)
{
// Dequeue
lock (Queue)
{
commandStrategy.DeQueue();
// Process all generic dequeue strategies
foreach (var generalStrategy in GeneralStrategies) { generalStrategy.OnDequeue(); }
}
// Send command
if (commandStrategy.Command != null)
_communicationManager.ExecuteSendCommand((SendCommand)commandStrategy.Command, SendQueue.InFrontQueue);
}
/// <summary> Adds a commandStrategy to the commands string. </summary>
/// <param name="commandStrategy"> The command strategy to add. </param>
private void AddToCommandString(CommandStrategy commandStrategy)
{
// Dequeue
lock (Queue)
{
commandStrategy.DeQueue();
// Process all generic dequeue strategies
foreach (var generalStrategy in GeneralStrategies) { generalStrategy.OnDequeue(); }
}
// Add command
if (commandStrategy.Command != null)
{
_commandCount++;
_sendBuffer += commandStrategy.Command.CommandString();
if (_communicationManager.PrintLfCr) { _sendBuffer += "\r\n"; }
}
}
/// <summary> Sends a command. Note that the command is put at the front of the queue </summary>
/// <param name="sendCommand"> The command to sent. </param>
public void SendCommand(SendCommand sendCommand)
{
// Add command to front of queue
QueueCommand(new TopCommandStrategy(sendCommand));
}
/// <summary> Queue the send command. </summary>
/// <param name="sendCommand"> The command to sent. </param>
public void QueueCommand(SendCommand sendCommand)
{
QueueCommand(new CommandStrategy(sendCommand));
}
/// <summary> Queue the send command wrapped in a command strategy. </summary>
/// <param name="commandStrategy"> The command strategy. </param>
public override void QueueCommand(CommandStrategy commandStrategy)
{
while (Queue.Count > MaxQueueLength)
{
Thread.Yield();
}
lock (Queue)
{
// Process commandStrategy enqueue associated with command
commandStrategy.CommandQueue = Queue;
commandStrategy.Command.CommunicationManager = _communicationManager;
((SendCommand)commandStrategy.Command).InitArguments();
commandStrategy.Enqueue();
// Process all generic enqueue strategies
foreach (var generalStrategy in GeneralStrategies) { generalStrategy.OnEnqueue(); }
}
SignalWorker();
}
}
}

View File

@ -0,0 +1,57 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Queue
{
/// <summary> Stale strategy. Any command older than the time-out is removed from the queue</summary>
public class StaleGeneralStrategy : GeneralStrategy
{
private readonly long _commandTimeOut;
/// <summary> Stale strategy. Any command older than the time-out is removed from the queue</summary>
/// <param name="commandTimeOut"> The time-out for any commands on the queue. </param>
public StaleGeneralStrategy(long commandTimeOut)
{
_commandTimeOut = commandTimeOut;
}
/// <summary> Remove this command (strategy) from command queue. </summary>
public override void OnDequeue()
{
// Remove commands that have gone stale
//Console.WriteLine("Before StaleStrategy {0}", CommandQueue.Count);
var currentTime = TimeUtils.Millis;
// Work from oldest to newest
for (var item = 0; item < CommandQueue.Count; item++)
{
var age = currentTime - CommandQueue[item].Command.TimeStamp;
if (age > _commandTimeOut && CommandQueue.Count > 1 )
{
CommandQueue.RemoveAt(item);
}
else
{
// From here on commands are newer, so we can stop
break;
}
}
//Console.WriteLine("After StaleStrategy {0}", CommandQueue.Count);
}
}
}

View File

@ -0,0 +1,38 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger.Queue
{
/// <summary> Top strategy. The command is added to the front of the queue</summary>
public class TopCommandStrategy : CommandStrategy
{
/// <summary> Top strategy. The command is added to the front of the queue</summary>
/// <param name="command"> The command to add to the front of the queue. </param>
public TopCommandStrategy(Command command) : base(command)
{
}
/// <summary> Add command (strategy) to command queue. </summary>
public override void Enqueue()
{
//Console.WriteLine("Enqueue {0}", CommandQueue.Count);
CommandQueue.EnqueueFront(this);
}
}
}

View File

@ -0,0 +1,375 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Globalization;
namespace CommandMessenger
{
/// <summary> A command received from CmdMessenger </summary>
public class ReceivedCommand : Command
{
private int _parameter = -1; // The parameter
private bool _dumped = true; // true if parameter has been dumped
/// <summary> Gets or sets the command input. </summary>
/// <value> The raw string. </value>
public string RawString { get; set; }
/// <summary> Default constructor. </summary>
public ReceivedCommand()
{
}
/// <summary> Constructor. </summary>
/// <param name="rawArguments"> All command arguments, first one is command ID </param>
public ReceivedCommand(string[] rawArguments)
{
int cmdId;
CmdId = (rawArguments != null && rawArguments.Length !=0 && int.TryParse(rawArguments[0], out cmdId)) ? cmdId : -1;
if (CmdId < 0) return;
if (rawArguments.Length > 1)
{
var array = new string[rawArguments.Length - 1];
Array.Copy(rawArguments, 1, array, 0, array.Length);
CmdArgs.AddRange(array);
}
}
/// <summary> Fetches the next argument. </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Next()
{
// If this parameter has already been read, see if there is another one
if (_dumped)
{
if (_parameter < CmdArgs.Count-1)
{
_parameter++;
_dumped = false;
return true;
}
return false;
}
return true;
}
/// <summary> returns if a next command is available </summary>
/// <returns> true if it succeeds, false if it fails. </returns>
public bool Available()
{
return Next();
}
// ***** String based **** /
/// <summary> Reads the current argument as short value. </summary>
/// <returns> The short value. </returns>
public Int16 ReadInt16Arg()
{
if (Next())
{
Int16 current;
if (Int16.TryParse(CmdArgs[_parameter], NumberStyles.Integer, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
return 0;
}
/// <summary> Reads the current argument as unsigned short value. </summary>
/// <returns> The unsigned short value. </returns>
public UInt16 ReadUInt16Arg()
{
if (Next())
{
UInt16 current;
if (UInt16.TryParse(CmdArgs[_parameter], NumberStyles.Integer, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
return 0;
}
/// <summary> Reads the current argument as boolean value. </summary>
/// <returns> The boolean value. </returns>
public bool ReadBoolArg()
{
return (ReadInt32Arg() != 0);
}
/// <summary> Reads the current argument as int value. </summary>
/// <returns> The int value. </returns>
public Int32 ReadInt32Arg()
{
if (Next())
{
Int32 current;
if (Int32.TryParse(CmdArgs[_parameter], NumberStyles.Integer, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
return 0;
}
/// <summary> Reads the current argument as unsigned int value. </summary>
/// <returns> The unsigned int value. </returns>
public UInt32 ReadUInt32Arg()
{
if (Next())
{
UInt32 current;
if (UInt32.TryParse(CmdArgs[_parameter], NumberStyles.Integer, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
return 0;
}
/// <summary> Reads the current argument as a float value. </summary>
/// <returns> The float value. </returns>
public float ReadFloatArg()
{
if (Next())
{
float current;
if (float.TryParse(CmdArgs[_parameter], NumberStyles.Float, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
return 0;
}
/// <summary> Reads the current argument as a double value. </summary>
/// <returns> The unsigned double value. </returns>
public Double ReadDoubleArg()
{
if (CommunicationManager == null)
throw new InvalidOperationException("CommunicationManager was not set for command.");
if (Next())
{
if (CommunicationManager.BoardType == BoardType.Bit16)
{
float current;
if (float.TryParse(CmdArgs[_parameter], NumberStyles.Float, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return (Double) current;
}
}
else
{
Double current;
if (Double.TryParse(CmdArgs[_parameter], NumberStyles.Float, CultureInfo.InvariantCulture, out current))
{
_dumped = true;
return current;
}
}
}
return 0;
}
/// <summary> Reads the current argument as a string value. </summary>
/// <returns> The string value. </returns>
public String ReadStringArg()
{
if (Next())
{
if (CmdArgs[_parameter] != null)
{
_dumped = true;
return CmdArgs[_parameter];
}
}
return string.Empty;
}
// ***** Binary **** /
/// <summary> Reads the current binary argument as a float value. </summary>
/// <returns> The float value. </returns>
public float ReadBinFloatArg()
{
if (Next())
{
var current = BinaryConverter.ToFloat(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (float) current;
}
}
return 0;
}
/// <summary> Reads the current binary argument as a double value. </summary>
/// <returns> The double value. </returns>
public Double ReadBinDoubleArg()
{
if (CommunicationManager == null)
throw new InvalidOperationException("CommunicationManager was not set for command.");
if (Next())
{
if (CommunicationManager.BoardType == BoardType.Bit16)
{
var current = BinaryConverter.ToFloat(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (double) current;
}
}
else
{
var current = BinaryConverter.ToDouble(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (double)current;
}
}
}
return 0;
}
/// <summary> Reads the current binary argument as a short value. </summary>
/// <returns> The short value. </returns>
public Int16 ReadBinInt16Arg()
{
if (Next())
{
var current = BinaryConverter.ToInt16(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (Int16) current;
}
}
return 0;
}
/// <summary> Reads the current binary argument as a unsigned short value. </summary>
/// <returns> The unsigned short value. </returns>
public UInt16 ReadBinUInt16Arg()
{
if (Next())
{
var current = BinaryConverter.ToUInt16(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (UInt16) current;
}
}
return 0;
}
/// <summary> Reads the current binary argument as a int value. </summary>
/// <returns> The int32 value. </returns>
public Int32 ReadBinInt32Arg()
{
if (Next())
{
var current = BinaryConverter.ToInt32(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (Int32) current;
}
}
return 0;
}
/// <summary> Reads the current binary argument as a unsigned int value. </summary>
/// <returns> The unsigned int value. </returns>
public UInt32 ReadBinUInt32Arg()
{
if (Next())
{
var current = BinaryConverter.ToUInt32(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (UInt32) current;
}
}
return 0;
}
/// <summary> Reads the current binary argument as a string value. </summary>
/// <returns> The string value. </returns>
public String ReadBinStringArg()
{
if (Next())
{
if (CmdArgs[_parameter] != null)
{
_dumped = true;
return Escaping.Unescape(CmdArgs[_parameter]);
}
}
return string.Empty;
}
/// <summary> Reads the current binary argument as a boolean value. </summary>
/// <returns> The boolean value. </returns>
public bool ReadBinBoolArg()
{
if (Next())
{
var current = BinaryConverter.ToByte(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (current != 0);
}
}
return false;
}
/// <summary> Reads the current binary argument as a byte value. </summary>
/// <returns> The boolean value. </returns>
public byte ReadBinByteArg()
{
if (Next())
{
var current = BinaryConverter.ToByte(CmdArgs[_parameter]);
if (current != null)
{
_dumped = true;
return (byte)current;
}
}
return 0;
}
}
}

View File

@ -0,0 +1,77 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
namespace CommandMessenger
{
// This class will trigger the main thread when a specific command is received on the ReceiveCommandQueue thread
// this is used when synchronously waiting for an acknowledge command in BlockedTillReply
public class ReceivedCommandSignal
{
private int _cmdIdToMatch;
private SendQueue _sendQueueState;
private ReceivedCommand _receivedCommand;
private readonly object _lock = new object();
private readonly EventWaiter _waiter = new EventWaiter();
/// <summary>
/// Prepares for the WaitForCmd to be used.
/// </summary>
/// <param name="cmdId"></param>
/// <param name="sendQueueState"></param>
public void PrepareForWait(int cmdId, SendQueue sendQueueState)
{
lock (_lock)
{
_receivedCommand = null;
_cmdIdToMatch = cmdId;
_sendQueueState = sendQueueState;
}
}
/// <summary>
/// Wait function. PrepareForWait must be called before to prepare!
/// </summary>
/// <param name="timeOut">time-out in ms</param>
/// <returns></returns>
public ReceivedCommand WaitForCmd(int timeOut)
{
return _waiter.WaitOne(timeOut) == EventWaiter.WaitState.TimeOut ? null : _receivedCommand;
}
/// <summary>
/// Process command. See if it needs to be send to the main thread (false) or be used in queue (true)
/// </summary>
public bool ProcessCommand(ReceivedCommand receivedCommand)
{
lock (_lock)
{
if (receivedCommand.CmdId == _cmdIdToMatch)
{
_receivedCommand = receivedCommand;
_waiter.Set();
return false;
}
return (_sendQueueState != SendQueue.ClearQueue);
}
}
}
}

View File

@ -0,0 +1,391 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
namespace CommandMessenger
{
/// <summary> A command to be send by CmdMessenger </summary>
public class SendCommand : Command
{
private readonly List<Action> _lazyArguments = new List<Action>();
/// <summary> Indicates if we want to wait for an acknowledge command. </summary>
/// <value> true if request acknowledge, false if not. </value>
public bool ReqAc { get; set; }
/// <summary> Gets or sets the acknowledge command ID. </summary>
/// <value> the acknowledge command ID. </value>
public int AckCmdId { get; set; }
/// <summary> Gets or sets the time we want to wait for the acknowledge command. </summary>
/// <value> The timeout on waiting for an acknowledge</value>
public int Timeout { get; set; }
/// <summary> Constructor. </summary>
/// <param name="cmdId"> the command ID. </param>
public SendCommand(int cmdId)
{
Init(cmdId, false, 0, 0);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, string argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="arguments"> The arguments. </param>
public SendCommand(int cmdId, string[] arguments)
{
Init(cmdId, false, 0, 0);
AddArguments(arguments);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, float argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, double argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, UInt16 argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, Int16 argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, UInt32 argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, Int32 argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
public SendCommand(int cmdId, bool argument)
{
Init(cmdId, false, 0, 0);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, string argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="arguments"> The arguments. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, string[] arguments, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArguments(arguments);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, float argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, double argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, Int16 argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, UInt16 argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, Int32 argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Constructor. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="argument"> The argument. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
public SendCommand(int cmdId, UInt32 argument, int ackCmdId, int timeout)
{
Init(cmdId, true, ackCmdId, timeout);
AddArgument(argument);
}
/// <summary> Initializes this object. </summary>
/// <param name="cmdId"> Command ID </param>
/// <param name="reqAc"> true to request ac. </param>
/// <param name="ackCmdId"> Acknowledge command ID. </param>
/// <param name="timeout"> The timeout on waiting for an acknowledge</param>
private void Init(int cmdId, bool reqAc, int ackCmdId, int timeout)
{
ReqAc = reqAc;
CmdId = cmdId;
AckCmdId = ackCmdId;
Timeout = timeout;
}
// ***** String based **** /
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(string argument)
{
if (argument != null)
_lazyArguments.Add(() => CmdArgs.Add(argument));
}
/// <summary> Adds command arguments. </summary>
/// <param name="arguments"> The arguments. </param>
public void AddArguments(string[] arguments)
{
if (arguments != null)
_lazyArguments.Add(() => CmdArgs.AddRange(arguments));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(float argument)
{
_lazyArguments.Add(() => CmdArgs.Add(argument.ToString("R", CultureInfo.InvariantCulture)));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(Double argument)
{
_lazyArguments.Add(() =>
{
if (CommunicationManager.BoardType == BoardType.Bit16)
{
// Not completely sure if this is needed for plain text sending.
var floatArg = (float) argument;
CmdArgs.Add(floatArg.ToString("R", CultureInfo.InvariantCulture));
}
else
{
CmdArgs.Add(argument.ToString("R", CultureInfo.InvariantCulture));
}
});
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(Int16 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(argument.ToString(CultureInfo.InvariantCulture)));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(UInt16 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(argument.ToString(CultureInfo.InvariantCulture)));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(Int32 argument)
{
// Make sure the other side can read this: on a 16 processor, read as Long
_lazyArguments.Add(() => CmdArgs.Add(argument.ToString(CultureInfo.InvariantCulture)));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(UInt32 argument)
{
// Make sure the other side can read this: on a 16 processor, read as Long
_lazyArguments.Add(() => CmdArgs.Add(argument.ToString(CultureInfo.InvariantCulture)));
}
/// <summary> Adds a command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddArgument(bool argument)
{
AddArgument((Int16) (argument ? 1 : 0));
}
// ***** Binary **** /
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(string argument)
{
_lazyArguments.Add(() => CmdArgs.Add(Escaping.Escape(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(float argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(Double argument)
{
_lazyArguments.Add(() => CmdArgs.Add(CommunicationManager.BoardType == BoardType.Bit16
? BinaryConverter.ToString((float)argument)
: BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(Int16 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(UInt16 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(Int32 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(UInt32 argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument)));
}
/// <summary> Adds a binary command argument. </summary>
/// <param name="argument"> The argument. </param>
public void AddBinArgument(bool argument)
{
_lazyArguments.Add(() => CmdArgs.Add(BinaryConverter.ToString(argument ? (byte) 1 : (byte) 0)));
}
internal void InitArguments()
{
CmdArgs.Clear();
foreach (var action in _lazyArguments)
{
action.Invoke();
}
}
}
}

View File

@ -0,0 +1,40 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System.Text;
namespace CommandMessenger
{
/// <summary> String utilities. </summary>
public class StringUtils
{
/// <summary> Convert string from one codepage to another. </summary>
/// <param name="input"> The string. </param>
/// <param name="fromEncoding"> input encoding codepage. </param>
/// <param name="toEncoding"> output encoding codepage. </param>
/// <returns> the encoded string. </returns>
static public string ConvertEncoding(string input, Encoding fromEncoding, Encoding toEncoding)
{
var byteArray = fromEncoding.GetBytes(input);
var asciiArray = Encoding.Convert(fromEncoding, toEncoding, byteArray);
var finalString = toEncoding.GetString(asciiArray);
return finalString;
}
}
}

View File

@ -0,0 +1,53 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System.Runtime.InteropServices;
namespace CommandMessenger
{
/// <summary> Helper object to convert structures to byte arrays and vice versa. </summary>
class StructSerializer
{
/// <summary> Convert an object to a byte array. </summary>
/// <param name="obj"> The object. </param>
/// <returns> The byte array. </returns>
public static byte[] ObjectToByteArray(object obj)
{
var length = Marshal.SizeOf(obj);
var byteArray = new byte[length];
var arrayPointer = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(obj, arrayPointer, true);
Marshal.Copy(arrayPointer, byteArray, 0, length);
Marshal.FreeHGlobal(arrayPointer);
return byteArray;
}
/// <summary> Convert an byte array to an object. </summary>
/// <param name="bytearray"> The bytearray. </param>
/// <param name="obj"> [in,out] The object. </param>
public static void ByteArrayToObject(byte[] bytearray, ref object obj)
{
var length = Marshal.SizeOf(obj);
var arrayPointer = Marshal.AllocHGlobal(length);
Marshal.Copy(bytearray, 0, arrayPointer, length);
obj = Marshal.PtrToStructure(arrayPointer, obj.GetType());
Marshal.FreeHGlobal(arrayPointer);
}
}
}

View File

@ -0,0 +1,49 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
namespace CommandMessenger
{
/// <summary>Class to get a timestamp </summary>
public static class TimeUtils
{
public static readonly DateTime Jan1St1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); // 1 January 1970
/// <summary> Gets the milliseconds since 1 Jan 1970. </summary>
/// <value> The milliseconds since 1 Jan 1970. </value>
public static long Millis { get { return (long)((DateTime.Now.ToUniversalTime() - Jan1St1970).TotalMilliseconds); } }
/// <summary> Gets the seconds since 1 Jan 1970. </summary>
/// <value> The seconds since 1 Jan 1970. </value>
public static long Seconds { get { return (long)((DateTime.Now.ToUniversalTime() - Jan1St1970).TotalSeconds); } }
// Returns if it has been more than interval (in ms) ago. Used for periodic actions
public static bool HasExpired(ref long prevTime, long interval)
{
var millis = Millis;
if (millis - prevTime > interval)
{
prevTime = millis;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,62 @@
#region CmdMessenger - MIT - (c) 2013 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2013 - Thijs Elenbaas
*/
#endregion
using System;
namespace CommandMessenger.Transport
{
/// <summary> Interface for transport layer. </summary>
public interface ITransport: IDisposable
{
/// <summary>
/// Connect transport
/// </summary>
/// <returns></returns>
bool Connect();
/// <summary>
/// Disconnect transport
/// </summary>
/// <returns></returns>
bool Disconnect();
/// <summary>
/// Returns connection status
/// </summary>
/// <returns>true when connected</returns>
bool IsConnected();
/// <summary>
/// Bytes read over transport
/// </summary>
/// <returns></returns>
byte[] Read();
/// <summary>
/// Write bytes over transport
/// </summary>
/// <param name="buffer"></param>
void Write(byte[] buffer);
/// <summary>
/// Bytes have been received event.
/// </summary>
event EventHandler DataReceived;
}
}

View File

@ -0,0 +1,201 @@
// *** CommandMessengerTest ***
// This project runs unit tests on several parts on the mayor parts of the CmdMessenger library
// Note that the primary function is not to serve as an example, so the code may be less documented
// and clean than the example projects.
//Check bluetooth connection
//Merge with other tree
using System;
using System.IO.Ports;
using CommandMessenger;
using CommandMessenger.Transport.Bluetooth;
using CommandMessenger.Transport.Serial;
namespace CommandMessengerTests
{
public class CommandMessengerTest
{
private SetupConnection _setupConnection;
private Acknowledge _acknowledge;
private ClearTextData _clearTextData;
private BinaryTextData _binaryTextData;
private TransferSpeed _transferSpeed;
private MultipleArguments _multipleArguments;
public CommandMessengerTest()
{
// Set up board & transport mode
var teensy31 = new systemSettings()
{
Description = @"Teensy 3.1",
MinReceiveSpeed = 2000000, // Bits per second
MinSendSpeed = 1250000, // Bits per second
MinDirectSendSpeed = 45000, // Bits per second
BoardType = BoardType.Bit32, // 32-bit architecture, needed from binary value conversion
sendBufferMaxLength = 512, // Maximum send buffer size, optimally buffer size is similar to embedded controller
Transport = new SerialTransport
{
CurrentSerialSettings = new SerialSettings()
{
PortName = "COM15", // Can be different!
BaudRate = 115200, // Bits per second
DataBits = 8, // Data bits
Parity = Parity.None, // Bit parity
DtrEnable = false, // Some boards need to send this to enabled
},
}
};
var arduinoNano = new systemSettings()
{
Description = @"Arduino Nano /w AT mega328",
MinReceiveSpeed = 82000, // Bits per second
MinSendSpeed = 85000, // Bits per second
MinDirectSendSpeed = 52000, // Bits per second
BoardType = BoardType.Bit16, // 16-bit architecture, needed from binary value conversion
sendBufferMaxLength = 60, // Maximum send buffer size, optimally buffer size is similar to embedded controller
Transport = new SerialTransport
{
CurrentSerialSettings = new SerialSettings()
{
PortName = "COM16", // Can be different!
BaudRate = 115200, // Bits per second
DataBits = 8, // Data bits
Parity = Parity.None, // Bit parity
DtrEnable = false, // Some boards need to send this to enabled
},
}
};
var arduinoNanoBluetooth = new systemSettings()
{
Description = @"Arduino Nano /w AT mega328 - Bluetooth",
MinReceiveSpeed = 6500, // Bits per second
MinSendSpeed = 7400, // Bits per second
MinDirectSendSpeed = 8000, // Bits per second
BoardType = BoardType.Bit16, // 16-bit architecture, needed from binary value conversion
sendBufferMaxLength = 60, // Maximum send buffer size, optimally buffer size is similar to embedded controller
Transport = new BluetoothTransport()
{
CurrentBluetoothDeviceInfo = BluetoothUtils.DeviceByAdress("20:13:07:26:10:08"),
LazyReconnect = true, // Only reconnect if really necessary
}
};
var arduinoLeonardoOrProMicro = new systemSettings()
{
Description = @"Arduino Leonardo or Sparkfun ProMicro /w AT mega32u4",
MinReceiveSpeed = 82000, // Bits per second
MinSendSpeed = 90000, // Bits per second
MinDirectSendSpeed = 52000, // Bits per second
BoardType = BoardType.Bit16, // 16-bit architecture, needed from binary value conversion
sendBufferMaxLength = 60, // Maximum send buffer size, optimally buffer size is similar to embedded controller
Transport = new SerialTransport
{
CurrentSerialSettings = new SerialSettings()
{
PortName = "COM13", // Can be different!
BaudRate = 115200, // Bits per second
DataBits = 8, // Data bits
Parity = Parity.None, // Bit parity
DtrEnable = true, // Some boards need to send this to enabled
},
}
};
// Set up Command enumerators
var command = DefineCommands();
// Initialize tests, CHANGE "DEVICE" VARIABLE TO YOUR DEVICE!
var device = teensy31;
InitializeTests(device, command);
// Open log file for testing
Common.OpenTestLogFile(@"TestLogFile.txt");
// Run tests
RunTests();
Common.CloseTestLogFile();
}
private static Enumerator DefineCommands()
{
var command = new Enumerator();
// Set up default commands
command.Add(new[]
{
"CommError", // Command reports serial port comm error (only works for some comm errors)
"kComment", // Command to sent comment in argument
});
return command;
}
private void InitializeTests(systemSettings systemSettings, Enumerator command)
{
_setupConnection = new SetupConnection(systemSettings, command);
_acknowledge = new Acknowledge(systemSettings, command);
_clearTextData = new ClearTextData(systemSettings, command);
_binaryTextData = new BinaryTextData(systemSettings, command);
_multipleArguments = new MultipleArguments(systemSettings, command);
_transferSpeed = new TransferSpeed(systemSettings, command);
}
private void RunTests()
{
//Todo: implement autoconnect tests
// Test opening and closing connection
_setupConnection.RunTests();
// Test acknowledgment both on PC side and embedded side
_acknowledge.RunTests();
//// Test all plain text formats
_clearTextData.RunTests();
//// Test all binary formats
_binaryTextData.RunTests();
//// Test sending multiple arguments
_multipleArguments.RunTests();
//// Test large series for completeness (2-way)
//// todo
//// Test speed
_transferSpeed.RunTests();
//// Test load
//// todo
//// Test Strategies
//// todo
// Summary of tests
Common.TestSummary();
// Exit application
Exit();
}
public void Exit()
{
Console.WriteLine("Press any key to stop...");
Console.ReadKey();
Environment.Exit(0);
}
}
}

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B869057C-DAB3-4C19-958C-C89D3B1521EC}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommandMessengerTests</RootNamespace>
<AssemblyName>CommandMessengerTests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="InTheHand.Net.Personal">
<HintPath>..\Bluetooth\InTheHand.Net.Personal.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="systemSettings.cs" />
<Compile Include="Tests\ClearTextData.cs" />
<Compile Include="Enumerator.cs" />
<Compile Include="CommandMessengerTest.cs" />
<Compile Include="Tests\Acknowledge.cs" />
<Compile Include="Common.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tests\BinaryTextData.cs" />
<Compile Include="Tests\MultipleArguments.cs" />
<Compile Include="Tests\TransferSpeed.cs" />
<Compile Include="Tests\SetupConnection.cs" />
<Compile Include="Tests\Random.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommandMessenger.Transport.Bluetooth\CommandMessenger.Transport.Bluetooth.csproj">
<Project>{68BE9E17-9B05-4B66-BDE6-F2A4C50858EA}</Project>
<Name>CommandMessenger.Transport.Bluetooth</Name>
</ProjectReference>
<ProjectReference Include="..\CommandMessenger.Transport.Serial\CommandMessenger.Transport.Serial.csproj">
<Project>{00D85F0B-00A5-41FA-8A99-428C0199C663}</Project>
<Name>CommandMessenger.Transport.Serial</Name>
</ProjectReference>
<ProjectReference Include="..\CommandMessenger\CommandMessenger.csproj">
<Project>{3CF8F8FC-6F5C-46F8-94DC-C2E4C505ECA4}</Project>
<Name>CommandMessenger</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,289 @@
#region CmdMessenger - MIT - (c) 2014 Thijs Elenbaas.
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright 2014 - Thijs Elenbaas
*/
#endregion
using System;
using CommandMessenger;
using System.IO;
namespace CommandMessengerTests
{
public class Common
{
//private const string TestLogFile = "TestLogFile.txt";
private const string IdentTp = @" "; // indentation test part
private const string IdentTt = @" "; // indentation test
private const string IdentTs = @" "; // indentation test series
private const string IdentSt = @" ";
private const string IdentWn = @" ";
public static CmdMessenger CmdMessenger { get; set; }
public static bool Connected { get; private set; }
private static bool _loggingCommands = false;
private static bool _testStarted = false;
private static bool _testSetStarted = false;
private static string _testDescription = string.Empty;
private static string _testSetDescription = string.Empty;
private static int _testElementFailCount = 0;
private static int _testElementPassCount = 0;
private static int _testFailCount = 0;
private static int _testPassCount = 0;
private static int _testSetFailCount = 0;
private static int _testSetPassCount = 0;
private static StreamWriter _streamWriter;
public static CmdMessenger Connect(systemSettings systemSettings)
{
if (CmdMessenger == null)
{
CmdMessenger = new CmdMessenger(systemSettings.Transport, systemSettings.sendBufferMaxLength,
systemSettings.BoardType);
CmdMessenger.PrintLfCr = true;
}
// Attach to NewLineReceived and NewLineSent for logging purposes
LogCommands(true);
Connected = CmdMessenger.Connect();
return CmdMessenger;
}
public static void LogCommands(bool logCommands)
{
if (logCommands && !_loggingCommands)
{
CmdMessenger.NewLineReceived += NewLineReceived;
CmdMessenger.NewLineSent += NewLineSent;
_loggingCommands = true;
}
else if (!logCommands && _loggingCommands)
{
// ReSharper disable DelegateSubtraction
CmdMessenger.NewLineReceived -= NewLineReceived;
CmdMessenger.NewLineSent -= NewLineSent;
_loggingCommands = false;
// ReSharper restore DelegateSubtraction
}
}
public static void OpenTestLogFile(string testLogFile)
{
_streamWriter = new StreamWriter(testLogFile);
}
public static void CloseTestLogFile()
{
_streamWriter.Close();
_streamWriter = null;
}
public static void Disconnect()
{
LogCommands(false);
CmdMessenger.Disconnect();
Connected = false;
//CmdMessenger.Dispose();
}
// Remove beeps
public static string Silence(string input)
{
var output = input.Replace('\x0007', ' ');
return output;
}
public static void StartTestSet(string testSetDescription)
{
if (_testSetStarted)
{
EndTestSet();
}
_testSetDescription = testSetDescription;
WriteLine(IdentTs + "*************************************");
WriteLine(IdentTs + "*** Start test-set " + _testSetDescription + " ****");
WriteLine(IdentTs + "*************************************");
WriteLine();
_testFailCount = 0;
_testPassCount = 0;
_testSetStarted = true;
}
public static void EndTestSet()
{
WriteLine(IdentTs + "*************************************");
WriteLine(IdentTs + "*** End test-set " + _testSetDescription + " ****");
WriteLine(IdentTs + "*************************************");
WriteLine();
WriteLine(IdentTs + "Tests passed: " + _testPassCount);
WriteLine(IdentTs + "Tests failed: " + _testFailCount);
WriteLine();
if (_testFailCount > 0)
{
_testSetFailCount++;
}
else
{
_testSetPassCount++;
}
_testSetStarted = false;
}
public static void StartTest(string testDescription)
{
if (_testStarted)
{
EndTest();
}
_testDescription = testDescription;
WriteLine(IdentTt + "*** Start test " + _testDescription + " ****");
WriteLine();
_testElementPassCount = 0;
_testElementFailCount = 0;
_testStarted = true;
}
public static void EndTest()
{
WriteLine(IdentTt + "*** End test " + _testDescription + " ****");
WriteLine();
if (_testElementPassCount + _testElementFailCount == 0)
{
WriteLine(IdentTt + "No tests done");
}
else
{
if (_testElementFailCount > 0)
{
_testFailCount++;
WriteLine(IdentTt + "Test failed" );
}
else
{
_testPassCount++;
WriteLine(IdentTt + "Test passed");
}
if (_testElementPassCount + _testElementFailCount > 1)
{
WriteLine(IdentTt + "Test parts passed: " + _testElementPassCount);
WriteLine(IdentTt + "Test parts failed: " + _testElementFailCount);
}
WriteLine();
}
_testStarted = false;
if (_streamWriter != null) { _streamWriter.Flush(); }
}
public static void TestSummary()
{
WriteLine(IdentTs + "*** Test Summary ****");
WriteLine();
if (_testSetPassCount > 0 && _testSetFailCount > 0)
{
WriteLine(IdentTs + "Some test sets failed!! ");
}
else if (_testSetPassCount > 0 && _testSetFailCount == 0)
{
WriteLine(IdentTs + "All test sets passed!! ");
}
else if (_testSetPassCount == 0 && _testSetFailCount > 0)
{
WriteLine(IdentTs + "All test sets failed!! ");
} if (_testSetPassCount == 0 && _testSetFailCount == 0)
{
WriteLine(IdentTs + "No tests done!! ");
}
WriteLine(IdentTs + "Test sets passed: " + _testSetPassCount);
WriteLine(IdentTs + "Test sets failed: " + _testSetFailCount);
if (_streamWriter != null) { _streamWriter.Flush(); }
}
public static void TestOk(string resultDescription)
{
WriteLine(IdentTp + "OK: " + resultDescription);
_testElementPassCount++;
}
public static void TestNotOk(string resultDescription)
{
WriteLine(IdentTp + "Not OK: " + resultDescription);
_testElementFailCount++;
}
public static void NewLineReceived(object sender, CommandEventArgs e)
{
var message = e.Command.CommandString();
//var message = CmdMessenger.CurrentReceivedLine;
WriteLine(IdentSt + "Received > " + Silence(message));
}
public static void NewLineSent(object sender, CommandEventArgs e)
{
//// Log data to text box
var message = e.Command.CommandString();
WriteLine(IdentSt + "Sent > " + Silence(message));
}
public static void OnUnknownCommand(ReceivedCommand arguments)
{
// In response to unknown commands and corrupt messages
WriteLine(IdentWn + "Warn > Command without attached callback received");
}
public static void WriteLine()
{
WriteLine("");
}
public static void WriteLine(string message)
{
Console.WriteLine(message);
if (_streamWriter!=null) {
_streamWriter.WriteLine(message);
}
}
public static void Write(string message)
{
Console.Write(message);
if (_streamWriter!=null) {
_streamWriter.Write(message);
}
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
namespace CommandMessengerTests
{
public class Enumerator
{
readonly Dictionary<string, int> _enumTable;
private int _enumCounter;
public Enumerator()
{
_enumTable = new Dictionary<string, int>();
_enumCounter = 0;
}
public void Add(string enumDescriptor)
{
_enumTable.Add(enumDescriptor, _enumCounter++);
}
public void Add(string[] enumDescriptors)
{
foreach (var enumDescriptor in enumDescriptors)
{
Add(enumDescriptor);
}
}
public int this[string enumDescriptor]
{
get
{
if (_enumTable.ContainsKey(enumDescriptor))
{
return _enumTable[enumDescriptor];
}
else
{
throw new ArgumentException("This enum does not exist");
}
}
set { _enumTable[enumDescriptor] = value; }
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
namespace CommandMessengerTests
{
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
new CommandMessengerTest();
}
}
}

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommandMessengerTests")]
[assembly: AssemblyDescription("A messaging library for the Arduino and .NET/Mono platform")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("CmdMessenger")]
[assembly: AssemblyCopyright("Copyright © Thijs Elenbaas 2013, 2014, 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3e948731-3f90-4c91-8e5b-574879664de1")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,185 @@
using System;
using System.Threading;
using CommandMessenger;
namespace CommandMessengerTests
{
public class Acknowledge
{
private CmdMessenger _cmdMessenger;
readonly Enumerator _command;
private bool _acknowledgementByEmbeddedFinished;
private readonly systemSettings _systemSettings;
public Acknowledge(systemSettings systemSettings, Enumerator command)
{
_systemSettings = systemSettings;
_command = command;
DefineCommands();
}
// ------------------ Command Callbacks -------------------------
private void DefineCommands()
{
_command.Add(new[]
{
"AskUsIfReady", // Command asking other side to check if we acknowledge
"YouAreReady" // Command to send to other side to tell them we received their acknowledgment
});
}
private void AttachCommandCallBacks()
{
_cmdMessenger.Attach(_command["AreYouReady"], OnAreYouReadyCommand);
_cmdMessenger.Attach(_command["YouAreReady"], OnYouAreReadyCommand);
}
// ------------------ Command Callbacks -------------------------
private void OnAreYouReadyCommand(ReceivedCommand arguments)
{
// In response to AreYouReady ping. We send an ACK acknowledgment to say that we are ready
_cmdMessenger.SendCommand(new SendCommand(_command["Ack"], "We are ready"));
}
private void OnYouAreReadyCommand(ReceivedCommand arguments)
{
// in response to YouAreReady message
TestSendCommandWithAcknowledgementByArduinoFinished(arguments);
}
// ------------------ Test functions -------------------------
public void RunTests()
{
// Wait a bit before starting the test
Thread.Sleep(1000);
// Test opening and closing connection
Common.StartTestSet("Waiting for acknowledgments");
SetUpConnection();
// Test acknowledgments
TestSendCommandWithAcknowledgement();
TestSendCommandWithAcknowledgementByArduino();
WaitForAcknowledgementByEmbeddedFinished();
TestSendCommandWithAcknowledgementAfterQueued();
// Test closing connection
CloseConnection();
Common.EndTestSet();
}
public void SetUpConnection()
{
try
{
_cmdMessenger = Common.Connect(_systemSettings);
AttachCommandCallBacks();
if (!Common.Connected)
{
Common.TestNotOk("Not connected after opening connection");
}
}
catch (Exception)
{
Common.TestNotOk("CmdMessenger application could not be created");
Common.EndTestSet();
}
if (!_systemSettings.Transport.IsConnected())
{
Common.TestOk("No issues during opening connection");
}
}
public void CloseConnection()
{
try
{
Common.Disconnect();
}
catch (Exception)
{
}
}
// Test: Send a test command with acknowledgment needed
public void TestSendCommandWithAcknowledgement()
{
Common.StartTest("Test sending command and receiving acknowledgment");
var receivedCommand = _cmdMessenger.SendCommand(new SendCommand(_command["AreYouReady"], _command["Ack"], 1000)) ;
if (receivedCommand.Ok)
{
Common.TestOk("Acknowledgment for command AreYouReady");
}
else
{
Common.TestNotOk("No acknowledgment for command AreYouReady");
}
Common.EndTest();
}
public void TestSendCommandWithAcknowledgementAfterQueued()
{
Common.StartTest("Test sending command and receiving acknowledgment after larger queue");
// Quickly sent a bunch of commands, that will be combined in a command string
for (var i = 0; i < 100; i++)
{
_cmdMessenger.QueueCommand(new SendCommand(_command["AreYouReady"]));
}
// Now wait for an acknowledge, terminating the command string
var receivedCommand = _cmdMessenger.SendCommand(new SendCommand(_command["AreYouReady"], _command["Ack"], 1000), SendQueue.Default, ReceiveQueue.WaitForEmptyQueue);
if (receivedCommand.Ok)
{
Common.TestOk("Acknowledgment for command AreYouReady");
}
else
{
Common.TestNotOk("No acknowledgment for command AreYouReady");
}
Common.EndTest();
}
public void TestSendCommandWithAcknowledgementByArduino()
{
Common.StartTest("TestSendCommandWithAcknowledgementByArduino");
//SendCommandAskUsIfReady();
_acknowledgementByEmbeddedFinished = false;
_cmdMessenger.SendCommand(new SendCommand(_command["AskUsIfReady"]));
// We will exit here, but the test has just begun:
// - Next the arduino will call us with AreYouReady command which will trigger OnAreYouReadyCommand()
// - After this the Command TestAckSendCommandArduinoFinish will be called by Arduino with results
}
public void TestSendCommandWithAcknowledgementByArduinoFinished(ReceivedCommand command)
{
var result = command.ReadBoolArg();
if (!result)
{
Common.TestNotOk("Incorrect response");
}
_acknowledgementByEmbeddedFinished = true;
}
public void WaitForAcknowledgementByEmbeddedFinished()
{
for (var i = 0; i < 10; i++)
{
if (_acknowledgementByEmbeddedFinished)
{
Common.TestOk("Received acknowledge from processor");
return;
}
System.Threading.Thread.Sleep(1000);
}
Common.TestNotOk("Received no acknowledge from processor");
}
}
}

View File

@ -0,0 +1,338 @@
using System;
using System.Threading;
using CommandMessenger;
namespace CommandMessengerTests
{
public class BinaryTextData
{
private CmdMessenger _cmdMessenger;
readonly Enumerator _command;
private readonly systemSettings _systemSettings;
public BinaryTextData(systemSettings systemSettings, Enumerator command)
{
_systemSettings = systemSettings;
_command = command;
DefineCommands();
}
// ------------------ Command Callbacks -------------------------
private void DefineCommands()
{
}
private void AttachCommandCallBacks()
{
}
// ------------------ Test functions -------------------------
public void RunTests()
{
// Wait a bit before starting the test
Thread.Sleep(1000);
Common.StartTestSet("Clear binary data");
SetUpConnection();
TestSendBoolData();
TestSendEscStringData();
TestSendBinInt16Data();
TestSendBinInt32Data();
TestSendBinFloatData();
TestSendBinDoubleData();
CloseConnection();
Common.EndTestSet();
}
public void SetUpConnection()
{
_cmdMessenger = Common.Connect(_systemSettings);
AttachCommandCallBacks();
if (!Common.Connected)
{
Common.TestNotOk("Not connected after opening connection");
}
}
public void CloseConnection()
{
Common.Disconnect();
}
public void TestSendBoolData()
{
Common.StartTest("Ping-pong of random binary bool values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongBinBool(Random.RandomizeBool());
}
Common.EndTest();
}
public void TestSendBinInt16Data()
{
Common.StartTest("Ping-pong of random binary Int16 values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongBinInt16(Random.RandomizeInt16(Int16.MinValue, Int16.MaxValue), 0);
}
Common.EndTest();
}
public void TestSendBinInt32Data()
{
Common.StartTest("Ping-pong of random binary Int32 values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongBinInt32(Random.RandomizeInt32(Int32.MinValue, Int32.MaxValue), 0);
}
Common.EndTest();
}
private void TestSendBinFloatData()
{
Common.StartTest("Ping-pong of handpicked binary float values");
// Try some typical numbers
ValuePingPongBinFloat(0.0F) ;
ValuePingPongBinFloat(1.0F);
ValuePingPongBinFloat(15.0F);
ValuePingPongBinFloat(65535.0F);
ValuePingPongBinFloat(0.00390625F);
ValuePingPongBinFloat(0.00000000023283064365386962890625F);
Common.EndTest();
//Craft difficult floating point values, using all special characters.
//These should all be handled correctly by escaping
Common.StartTest("Ping-pong of floating point values, using all special characters");
for (int a = 0; a < 5; a++)
{
for (int b = 0; b < 5; b++)
{
for (int c = 0; c < 5; c++)
{
for (int d = 0; d < 5; d++)
{
var charA = IntToSpecialChar(a);
var charB = IntToSpecialChar(b);
var charC = IntToSpecialChar(c);
var charD = IntToSpecialChar(d);
ValuePingPongBinFloat(CreateFloat(new[] { charA, charB, charC, charD }));
}
}
}
}
Common.EndTest();
Common.StartTest("Ping-pong of random binary float values");
// Try a lot of random numbers
for (int i = 0; i < 1000; i++)
{
ValuePingPongBinFloat(Random.RandomizeFloat(-float.MaxValue / 100.0f, float.MaxValue / 100.0f));
}
Common.EndTest();
}
public void TestSendBinDoubleData()
{
var range = (_systemSettings.BoardType == BoardType.Bit32) ? double.MaxValue : float.MaxValue;
var stepsize = range / 100;
Common.StartTest("Ping-pong of increasing binary double values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongBinDouble(i * stepsize);
}
Common.EndTest();
Common.StartTest("Ping-pong of random binary double values");
for (var i = 0; i < 100; i++)
{
// Bigger values than this go wrong, due to truncation
ValuePingPongBinDouble(Random.RandomizeDouble(-range, range));
}
Common.EndTest();
}
private void TestSendEscStringData()
{
Common.StartTest("Echo strings");
ValuePingPongEscString("abcdefghijklmnopqrstuvwxyz"); // No special characters, but escaped
ValuePingPongEscString("abcde,fghijklmnopqrs,tuvwxyz"); // escaped parameter separators
ValuePingPongEscString("abcde,fghijklmnopqrs,tuvwxyz,"); // escaped parameter separators at end
ValuePingPongEscString("abc,defghij/klmnop//qr;stuvwxyz/"); // escaped escape char at end
ValuePingPongEscString("abc,defghij/klmnop//qr;stuvwxyz//"); // double escaped escape char at end
Common.EndTest();
}
private void ValuePingPongBinBool(bool value)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.BBool);
pingCommand.AddBinArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBinBoolArg();
if (result == value)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongBinInt16(Int16 value, Int16 accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.BInt16);
pingCommand.AddBinArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok) Common.TestNotOk("No response on ValuePing command");
var result = pongCommand.ReadBinInt16Arg();
var difference = Math.Abs(result - value);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongBinInt32(Int32 value, Int32 accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.BInt32);
pingCommand.AddBinArgument((Int32)value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBinInt32Arg();
var difference = Math.Abs(result - value);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongBinFloat(float value)
{
const float accuracy = float.Epsilon;
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.BFloat);
pingCommand.AddBinArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBinFloatArg();
var difference = Math.Abs(result - value);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongBinDouble(double value)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.BDouble);
pingCommand.AddBinArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBinDoubleArg();
var difference = Math.Abs(result - value);
//
// For 16bit, because of double-float-float-double casting a small error is introduced
var accuracy = (_systemSettings.BoardType == BoardType.Bit32) ? double.Epsilon : Math.Abs(value * 1e-6);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongEscString(string value)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((int)DataType.EscString);
pingCommand.AddBinArgument(value); // Adding a string as binary command will escape it
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBinStringArg();
if (value == result)
{
Common.TestOk("Value as expected");
}
else
{
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
}
// Utility functions
private char IntToSpecialChar(int i)
{
switch (i)
{
case 0:
return ';'; // End of line
case 1:
return ','; // End of parameter
case 3:
return '/'; // Escaping next char
case 4:
return '\0'; // End of byte array
default:
return 'a'; // Normal character
}
}
float CreateFloat(char[] chars)
{
var bytes = BinaryConverter.CharsToBytes(chars);
return BitConverter.ToSingle(bytes, 0);
}
}
}

View File

@ -0,0 +1,324 @@
using System;
using System.Threading;
using CommandMessenger;
namespace CommandMessengerTests
{
enum DataType : int
{
Bool,
Int16,
Int32,
Float,
FloatSci,
Double,
DoubleSci,
Char,
String,
BBool,
BInt16,
BInt32,
BFloat,
BDouble,
BChar,
EscString,
};
public class ClearTextData
{
private CmdMessenger _cmdMessenger;
readonly Enumerator _command;
private systemSettings _systemSettings;
public ClearTextData(systemSettings systemSettings, Enumerator command)
{
_systemSettings = systemSettings;
_command = command;
DefineCommands();
}
// ------------------ Command Callbacks -------------------------
private void DefineCommands()
{
_command.Add(new[]
{
"ValuePing" , // Send value
"ValuePong" , // Receive value
});
}
private void AttachCommandCallBacks()
{
}
// ------------------ Test functions -------------------------
public void RunTests()
{
// Wait a bit before starting the test
Thread.Sleep(1000);
Common.StartTestSet("Clear text data");
SetUpConnection();
TestSendBoolData();
TestSendInt16Data();
TestSendInt32Data();
TestSendFloatData();
TestSendFloatSciData();
TestSendDoubleSciData();
CloseConnection();
Common.EndTestSet();
}
public void SetUpConnection()
{
_cmdMessenger = Common.Connect(_systemSettings);
AttachCommandCallBacks();
if (!Common.Connected)
{
Common.TestNotOk("Not connected after opening connection");
}
}
public void CloseConnection()
{
Common.Disconnect();
}
public void TestSendBoolData()
{
Common.StartTest("Ping-pong of random plain-text bool values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongBool(Random.RandomizeBool());
}
Common.EndTest();
}
public void TestSendInt16Data()
{
Common.StartTest("Ping-pong of random Int16 values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongInt16(Random.RandomizeInt16(Int16.MinValue, Int16.MaxValue), 0);
}
Common.EndTest();
}
public void TestSendInt32Data()
{
Common.StartTest("Ping-pong of random Int32 values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongInt32(Random.RandomizeInt32(Int32.MinValue, Int32.MaxValue), 0);
}
Common.EndTest();
}
public void TestSendFloatData()
{
// UInt32.MaxValue is the maximum range of the normal print float implementation
const float stepsize = (float)UInt32.MaxValue / 100;
Common.StartTest("Ping-pong of increasing float values");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
// Bigger values than this go wrong, due to truncation
ValuePingPongFloat(i * stepsize );
}
Common.EndTest();
Common.StartTest("Ping-pong of random float values");
for (var i = 0; i < 100; i++)
{
// Bigger values than this go wrong, due to truncation
ValuePingPongFloat(Random.RandomizeFloat(-UInt32.MaxValue, UInt32.MaxValue));
}
Common.EndTest();
}
public void TestSendFloatSciData()
{
const float stepsize = (float)float.MaxValue/100.0f;
Common.StartTest("Ping-pong of increasing float values, returned in scientific format");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
// Bigger values than this go wrong, due to truncation
ValuePingPongFloatSci(i * stepsize, (float)0.05);
}
Common.EndTest();
Common.StartTest("Ping-pong of random float values, returned in scientific format");
for (var i = 0; i < 100; i++)
{
ValuePingPongFloatSci(Random.RandomizeFloat(-float.MaxValue / 100.0f, float.MaxValue / 100.0f), 0.05f);
}
Common.EndTest();
}
public void TestSendDoubleSciData()
{
var range = (_systemSettings.BoardType==BoardType.Bit32)?double.MaxValue:float.MaxValue;
var stepsize = range/100;
Common.StartTest("Ping-pong of increasing double values, returned in scientific format");
// Try a lot of random numbers
for (var i = 0; i < 100; i++)
{
ValuePingPongDoubleSci(i * stepsize, 0.05f);
}
Common.EndTest();
Common.StartTest("Ping-pong of random double values, returned in scientific format");
for (var i = 0; i < 100; i++)
{
// Bigger values than this go wrong, due to truncation
ValuePingPongDoubleSci(Random.RandomizeDouble(-range, range), 0.05f);
}
Common.EndTest();
}
private void ValuePingPongInt16(Int16 value, Int16 accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.Int16);
pingCommand.AddArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok) Common.TestNotOk("No response on ValuePing command");
var result = pongCommand.ReadInt16Arg();
var difference = Math.Abs(result - value);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongInt32(Int32 value, Int32 accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.Int32);
pingCommand.AddArgument((Int32)value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadInt32Arg();
var difference = Math.Abs(result - value);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongBool(bool value)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.Bool);
pingCommand.AddArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadBoolArg();
if (result == value)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongFloat(float value)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.Float);
pingCommand.AddArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadFloatArg();
var difference = Math.Abs(result - value);
var accuracy = Math.Abs(value * 2e-7);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongFloatSci(float value, float accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.FloatSci);
pingCommand.AddArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadFloatArg();
var difference = RelativeError(value, result);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private void ValuePingPongDoubleSci(double value, double accuracy)
{
var pingCommand = new SendCommand(_command["ValuePing"], _command["ValuePong"], 1000);
pingCommand.AddArgument((Int16)DataType.DoubleSci);
pingCommand.AddArgument(value);
var pongCommand = _cmdMessenger.SendCommand(pingCommand);
if (!pongCommand.Ok)
{
Common.TestNotOk("No response on ValuePing command");
return;
}
var result = pongCommand.ReadDoubleArg();
var difference = RelativeError(value, result);
if (difference <= accuracy)
Common.TestOk("Value as expected");
else
Common.TestNotOk("unexpected value received: " + result + " instead of " + value);
}
private static double RelativeError(double value, double result)
{
var difference = (Math.Abs(result) > double.Epsilon) ? Math.Abs(result - value) / result : Math.Abs(result - value);
if (Double.IsNaN(difference)) difference = 0;
return difference;
}
}
}

Some files were not shown because too many files have changed in this diff Show More