From 641e9e9d86617c0199a5650f5546615c9058bf8f Mon Sep 17 00:00:00 2001 From: Jan Michel Date: Tue, 16 Jul 2013 11:40:14 +0200 Subject: [PATCH] added interface for VXI devices --- web/htdocs/index.html | 1 + web/htdocs/tools/pwr/pwr.htm | 4 +- web/htdocs/tools/pwr/pwr_hmp.htm | 24 +- web/htdocs/tools/{pwr => }/scripts.js | 6 +- web/htdocs/tools/{pwr => }/styles.css | 5 + web/htdocs/tools/vxi/index.html | 62 ++ web/htdocs/tools/vxi/source/CHANGELOG.txt | 218 +++++++ .../vxi/source/GNU_General_Public_License.txt | 340 ++++++++++ web/htdocs/tools/vxi/source/Makefile | 40 ++ web/htdocs/tools/vxi/source/README.txt | 98 +++ web/htdocs/tools/vxi/source/vxi11.x | 317 ++++++++++ web/htdocs/tools/vxi/source/vxi11_cmd.cc | 78 +++ web/htdocs/tools/vxi/source/vxi11_user.cc | 589 ++++++++++++++++++ web/htdocs/tools/vxi/source/vxi11_user.h | 87 +++ web/htdocs/tools/vxi/vxi.pl | 40 ++ web/htdocs/tools/vxi/vxi11_cmd | Bin 0 -> 64074 bytes 16 files changed, 1884 insertions(+), 25 deletions(-) rename web/htdocs/tools/{pwr => }/scripts.js (89%) rename web/htdocs/tools/{pwr => }/styles.css (89%) create mode 100644 web/htdocs/tools/vxi/index.html create mode 100644 web/htdocs/tools/vxi/source/CHANGELOG.txt create mode 100644 web/htdocs/tools/vxi/source/GNU_General_Public_License.txt create mode 100644 web/htdocs/tools/vxi/source/Makefile create mode 100644 web/htdocs/tools/vxi/source/README.txt create mode 100644 web/htdocs/tools/vxi/source/vxi11.x create mode 100644 web/htdocs/tools/vxi/source/vxi11_cmd.cc create mode 100644 web/htdocs/tools/vxi/source/vxi11_user.cc create mode 100644 web/htdocs/tools/vxi/source/vxi11_user.h create mode 100755 web/htdocs/tools/vxi/vxi.pl create mode 100755 web/htdocs/tools/vxi/vxi11_cmd diff --git a/web/htdocs/index.html b/web/htdocs/index.html index 4a2e2a1..9f43836 100755 --- a/web/htdocs/index.html +++ b/web/htdocs/index.html @@ -48,6 +48,7 @@ The main documentation of the network can be found in these two documents:
diff --git a/web/htdocs/tools/pwr/pwr.htm b/web/htdocs/tools/pwr/pwr.htm index f7cfd0f..e0f8079 100644 --- a/web/htdocs/tools/pwr/pwr.htm +++ b/web/htdocs/tools/pwr/pwr.htm @@ -2,8 +2,8 @@ - - + + Power Supply Monitor and Access diff --git a/web/htdocs/tools/pwr/pwr_hmp.htm b/web/htdocs/tools/pwr/pwr_hmp.htm index 36aa4bf..381795b 100644 --- a/web/htdocs/tools/pwr/pwr_hmp.htm +++ b/web/htdocs/tools/pwr/pwr_hmp.htm @@ -2,14 +2,14 @@ - - + + Power Supply Monitor and Access -

Power Supply Access

+

Power Supply Access

@@ -46,12 +46,6 @@ - - -
-
- - + Access "all" Measuring Equipment + + + + +

VXI-11 Lab Equipment Control

+ +
IP-Address +
Command(s) +
+
Response +
+ +

+This page allows to access any lab equipment that runs the VXI-11 protocol (such as (Tektronix|Agilent) (Scopes|SignalGenerators|MultiMeters)). +The lib is C-based, so it needs to be recompiled if the available x64-binary does not work. The code was mainly downloaded from +[VXI-11 library], but small changes were applied to the tool. The source code is included in the repository. Enter the IP of the device and any command and you'll receive the answer. + + + + + + + + + + + + + + + + + + diff --git a/web/htdocs/tools/vxi/source/CHANGELOG.txt b/web/htdocs/tools/vxi/source/CHANGELOG.txt new file mode 100644 index 0000000..230d9dc --- /dev/null +++ b/web/htdocs/tools/vxi/source/CHANGELOG.txt @@ -0,0 +1,218 @@ +------------------------------------------------------------------------------ +vxi11_1.10 - 9/09/2010 + +Bug fix (thanks to Stephan Mahr): in vxi11_close(), remove the IP address +from the global array that keeps track of them so that if the same device +is opened again, then a new client is created, rather than it attempting +to use the old one (which was destroyed on the previous close). + +------------------------------------------------------------------------------ +vxi11_1.09 - 7/06/2010 + +Moved over to bazaar VCS (from RCS). + +Makefile cleanups. Fixed signed/unsigned comparisons. Use consistent (and +sane) struct separator spacing in code. + +Fix int casting on printf statements to fix new compiler warnings/errors +(thanks to Shouri Chatterjee for pointing this out). + +------------------------------------------------------------------------------ +vxi11_1.08 - 3/09/2009 + +Added a sanity check for link->maxRecvSize to make sure it's >0. This gets +around a bug in some versions of the Agilent Infiniium scope software. + +Changed the erroneous strncpy() to memcpy() in vxi11_send, as we could be +sending binary data (not just strings). + +Changed a lot of char *'s to const char *'s in an attempt to get rid of +pedantic gcc compiler warnings. + +------------------------------------------------------------------------------ +vxi11_1.07 - 9/10/2007 + +Minor change to vxi11_receive_data_block(), this fn now copes with instruments +that return just "#0" (for whatever reason). Suggestion by Jarek Sadowski, +gratefully received. + +------------------------------------------------------------------------------ +vxi11_1.06 - 31/08/2007 + +Bug fix in vxi11_receive(), to ensure that no more than "len" bytes are ever +received (and so avoiding a segmentation fault). This was a bug introduced in +release 1.04 whilst making some other changes to the vxi11_receive() fn. + +Many thanks to Rob Penny for spotting the bug and providing a patch. + +------------------------------------------------------------------------------ +vxi11_1.05 - 11/07/2007 + +Added the ability to specify a "device name" when calling vxi11_open_device(). +For regular VXI11-based instruments, such as scopes and AFGs, the device name +is usually "hard wired" to be "inst0", and up to now this has been hard wired +into the vxi11_user code. However, devices such as LAN to GPIB gateways need +some way of distinguishing between different devices... they are a single +client (one IP address), with multiple devices. + +The vxi11_user fn, vxi11_open_device(), now takes a third argument +(char *device). +This gets passed to the core vxi11_open_device() fn (the one that deals with +separate clients and links), and the core vxi11_open_link() fn; these two +core functions have also had an extra parameter added accordingly. In order +to not break the API, a wrapper function is provided in the form of the +original vxi11_open_device() fn, that just takes 2 arguments +(char *ip, CLINK *clink), this then passes "inst0" as the device argument. +Backwards-compatible wrappers for the core functions have NOT been provided. +These are generally not used from userland anyway. Hopefully this won't +upset anyone! + +vxi11_cmd, the simple test utility, has also been updated. You can now, +optionally, pass the device_name as a second argument (after the ip +address). The source has been renamed to vxi11_cmd.cc (from vxi11_cmd.c), as +it is C++ code not C. + +Some minor tidying up in vxi11_user.h + +With thanks to Oliver Schulz for bringing LAN to GPIB gateways to my +attention, for suggesting changes to the vxi11_user library to allow them to +be accommodated, and for tidying some things up. + +------------------------------------------------------------------------------ +vxi11_1.04 - 10/07/2007 + +Patch applied, which was kindly provided by Robert Larice. This sorts out +the confusion (on my part) about the structures returned by the rpcgen +generated *_1() functions... these are statically allocated temporary structs, +apparently. In the words of Robert Larice: + +****** +Hello Dr. Sharples, + + I'm sending some patches for your nice gem "vxi11_1.03" + + In the source code there were some strange comments, concerning + a commented free() around ... Manfred S. ... + and some notes, suggesting you had trouble to get more than one link + working. + + I think thats caused by some misuse of the rpcgen generated subroutines. + 1) those rpcgen generated *_1 functions returned pointers to + statically allocated temporary structs. + those where meant to be instantly copied to the user's space, + which wasn't done + thus instead of + Device_ReadResp *read_resp; + read_resp = device_read_1(...) + one should have written someting like: + Device_ReadResp *read_resp; + read_resp = malloc(...) + memcpy(read_resp, device_read_1(...), ...) + 2) but a better fix is to use the rpcgen -M Flag + which allows to pass the memory space as a third argument + so one can write + Device_ReadResp *read_resp; + read_resp = malloc(...) + device_read_1(..., read_resp, ...) + furthermore this is now automatically thread save + 3) the rpcgen function device_read_1 + expects a target buffer to be passed via read_resp + which was not done. + 4) the return value of vxi11_receive() was computed incorrectly + 5) minor, Makefile typo's + CFLAGS versus + CLFAGS + +****** + +Robert didn't have more than one device to try the patch with, but I've just +tried it and everything seems fine. So I've removed all references to the +VXI11_ENABLE_MULTIPLE_CLIENTS global variable, and removed the call to +vxi11_open_link() from the vxi11_send() fn. There has been an associated +tidying of functions, and removal of some comments. + +Thanks once again to Robert Larice for the patch and the explanation! + +------------------------------------------------------------------------------ +vxi11_1.03 - 29/01/2007 + +Some bug-fixes (thanks to Manfred S.), and extra awareness of the +possibility that instruments could time out after receiving a query WITHOUT +causing an error condition. In some cases (prior to these changes) this +could have resulted in a segmentation fault. + +Specifically: + +(1) removed call to ANSI free() fn in vxi11_receive, which according to + Manfred S. "is not necessary and wrong (crashes)". + +(2) added extra check in vxi11_receive() to see if read_resp==NULL. + read_resp can apparently be NULL if (eg) you send an instrument a + query, but the instrument is so busy with something else for so long + that it forgets the original query. So this extra check is for that + situation, and vxi11_receive returns -VXI11_NULL_READ_RESP to the + calling function. + +(3) vxi11_send_and_receive() is now aware of the possibility of being + returned -VXI11_NULL_READ_RESP. If so, it re-sends the query, until + either getting a "regular" read error (read_resp->error!=0) or a + successful read. + +(4) Similar to (2)... added extra check in vxi11_send() to see if + write_resp==NULL. If so, return -VXI11_NULL_WRITE_RESP. As with (3), + send_and_receive() is now aware of this possibility. + +------------------------------------------------------------------------------ +vxi11_1.02 - 25/08/2006 + +Important changes to the core vxi11_send() function, which should be +invisible to the user. + +For those interested, the function now takes note of the value of +link->maxRecvSize, which is the maximum number of bytes that the vxi11 +intrument you're talking to can receive in one go. For many instruments +this may be a few kB, which isn't a problem for sending short commands; +however, sending large chunks of data (for example sending waveforms +to instruments) may exceed this maxRecvSize. The core vxi11_send() function +has been re-written to ensure that only a maximum of [maxRecvSize] bytes are +written in one go... the function sits in a loop until all the message/ +data is written. + +Also tidied up some of the return values (specifically with regard to +vxi11_send() and vxi11_send_data_block() ). + +------------------------------------------------------------------------------ +vxi11_1.01 - 06/07/2006 + +Fair few changes since v1.00, all in vxi11_user.c and vxi11_user.h + +Found I was having problems talking to multiple links on the same +client, if I created a different client for each one. So introduced +a few global variables to keep track of all the ip addresses of +clients that the library is asked to create, and only creating new +clients if the ip address is different. This puts a limit of how +many unique ip addresses (clients) a single process can connect to. +Set this value at 256 (should hopefully be enough!). + +Next I found that talking to different clients on different ip +addresses didn't work. It turns out that create_link_1() creates +a static structure. This this link is associated with a given +client (and hence a given IP address), then the only way I could +think of making things work was to add a call to an +vxi11_open_link() function before each send command (no idea what +this adds to overheads and it's very messy!) - at least I was +able to get this to only happen when we are using more than one +client/ip address. + +Also, while I was at it, I re-ordered the functions a little - +starts with core user functions, extra user functions, then core +library functions at the end. Added a few more comments. Tidied +up. Left some debugging info in, but commented out. + +------------------------------------------------------------------------------ +vxi11_1.00 - 23/06/2006 + +Initial release. + +------------------------------------------------------------------------------ + diff --git a/web/htdocs/tools/vxi/source/GNU_General_Public_License.txt b/web/htdocs/tools/vxi/source/GNU_General_Public_License.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/web/htdocs/tools/vxi/source/GNU_General_Public_License.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/web/htdocs/tools/vxi/source/Makefile b/web/htdocs/tools/vxi/source/Makefile new file mode 100644 index 0000000..5963860 --- /dev/null +++ b/web/htdocs/tools/vxi/source/Makefile @@ -0,0 +1,40 @@ +VERSION=1.08 + +#CFLAGS = -Wall -g +CFLAGS = -g +INSTALL = install +prefix = /usr/local +CXX = g++ + +.PHONY : install clean dist distclean + +vxi11_cmd: vxi11_cmd.o vxi11_user.o vxi11_clnt.o vxi11_xdr.o + $(CXX) $(CFLAGS) -o $@ $^ + +vxi11_cmd.o: vxi11_cmd.cc vxi11_user.cc vxi11.h + $(CXX) $(CFLAGS) -c $< -o $@ + +vxi11_user.o: vxi11_user.cc vxi11.h + $(CXX) $(CFLAGS) -c $< -o $@ + +vxi11.h vxi11_clnt.c vxi11_xdr.c : vxi11.x + rpcgen -M vxi11.x + +TAGS: $(wildcard *.c) $(wildcard *.h) $(wildcard *.cc) + etags $^ + +clean: + rm -f *.o vxi11_cmd vxi11.h vxi11_svc.c vxi11_xdr.c vxi11_clnt.c TAGS + +install: vxi11_cmd + $(INSTALL) vxi11_cmd $(DESTDIR)$(prefix)/bin/ + +dist : distclean + mkdir vxi11-$(VERSION) + cp -p vxi11_cmd.cc vxi11_user.cc vxi11_user.h vxi11.x vxi11-$(VERSION)/ + cp -p Makefile CHANGELOG.txt README.txt GNU_General_Public_License.txt vxi11-$(VERSION)/ + tar -zcf vxi11-$(VERSION).tar.gz vxi11-$(VERSION) + +distclean : + rm -rf vxi11-$(VERSION) + rm -f vxi11-$(VERSION).tar.gz diff --git a/web/htdocs/tools/vxi/source/README.txt b/web/htdocs/tools/vxi/source/README.txt new file mode 100644 index 0000000..a75fda3 --- /dev/null +++ b/web/htdocs/tools/vxi/source/README.txt @@ -0,0 +1,98 @@ +RPC PROTOCOL FOR COMMUNICATING WITH VXI11-ENABLED DEVICES OVER ETHERNET FROM LINUX +================================================================================== +(including instruments such as oscilloscopes, by manufacturers such as +Agilent and Tektronix, amongst others). + +By Steve D. Sharples, June 2006. + +This is a collection of source code that will allow you to talk to ethernet- +enabled instruments that use the VXI11 protocol, from Linux. This includes +a wide range of instruments (including oscilloscopes, logic analysers, +function generators etc) by a wide range of manufacturers (including +Tektronix and Agilent to name just a couple). An interactive "send and +receive" utility is included as an example. + +You may want to build on to this libraries for your specific instruments - +I'm currently working on libraries for talking to Agilent Infiniium scopes, +and will probably do the same for Tektronix scopes too. Basically if you've +got a Programmer's Reference for your instrument, and this code, you should +be able to cobble something together. + +This collection of code has been produced because I grew frustrated at how +difficult it seemed to be to do a relatively simple task. None of the +major manufacturers had any "out of the box" Linux solutions to talking to +their instruments (although often I would talk to technical folks who would +try their best to help). One of the solutions offered was to use something +called NI VISA; parts of this are closed source, it was enormous, and I had +worries about legacy issues with changing PC hardware. + +Via Guy McBride at Agilent, I obtained a copy of a vxi11.x RPC file similar +to the one included here (although no-one at Agilent seemed to know or care +where it came from). After lots of searching on the information superhighway +I located what I believe is the original source (or something like it); see +the section on vxi11.x below. This source seems to have literally been written +from the published VXI11 protocol. I also received from Agilent a simple +example program that showed you how to use the protocol; working from this +and the (open) source that uses the vxi11.x that is included here, I wrote +vxi11_cmd and the user libraries. + +This collection of source code consists of: + +(1) vxi11.x +This file, vxi11.x, is the amalgamation of vxi11core.rpcl and vxi11intr.rpcl +which are part of the asynDriver (R4-5) EPICS module, which, at time of +writing, is available from: +http://www.aps.anl.gov/epics/modules/soft/asyn/index.html +More general information about EPICS is available from: +http://www.aps.anl.gov/epics/ +This code is open source, and is covered under the copyright notice and +software license agreement shown below, and also at: +http://www.aps.anl.gov/epics/license/open.php + +It is intended as a lightweight base for the vxi11 rpc protocol. If you +run rpcgen on this file, it will generate C files and headers, from which +it is relatively simple to write C programs to communicate with a range +of ethernet-enabled instruments, such as oscilloscopes and function +generators by manufacturers such as Agilent and Tektronix (amongst many +others). + +(2) vxi11_user.cc (and vxi11_user.h) +These are (fairly) friendly user libraries. At the core are 4 key functions: +vxi11_open(), vxi11_close(), vxi11_send() and vxi11_receive(). These allow +you to talk to your device. There are also some other functions that I +considered to be generally useful (send_and_receive, functions for sending +and receiving fixed length data blocks etc) that are all non-instrument- +specific. + +(3) vxi11_cmd.c +This is a fairly simple interactive utility that allows you to send +commands and queries to your vxi11-enabled instrument, which you +locate by way of IP address. I recommend you start with *IDN? It shows you +how the vxi11_user library works + +(4) Makefile +Type "make" to compile the source above. Type "make clean" to remove +old object files and ./vxi11_cmd. Type "make install" to copy +./vxi11_cmd to /usr/local/bin/ + +(5) GNU_General_Public_License.txt +Fairly obvious. All programs, source, readme files etc NOT covered by any +other license (e.g. vxi11.x, which is covered by its own open source +license) are covered by this license. + +These programs are 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 2 +of the License, or (at your option) any later version. + +These programs are distributed in the hope that they 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +The author's email address is steve.no.spam.sharples@nottingham.ac.uk +(you can work it out!) diff --git a/web/htdocs/tools/vxi/source/vxi11.x b/web/htdocs/tools/vxi/source/vxi11.x new file mode 100644 index 0000000..27f1293 --- /dev/null +++ b/web/htdocs/tools/vxi/source/vxi11.x @@ -0,0 +1,317 @@ +/* This file, vxi11.x, is the amalgamation of vxi11core.rpcl and vxi11intr.rpcl + * which are part of the asynDriver (R4-5) EPICS module, which, at time of + * writing, is available from: + * http://www.aps.anl.gov/epics/modules/soft/asyn/index.html + * More general information about EPICS is available from: + * http://www.aps.anl.gov/epics/ + * This code is open source, and is covered under the copyright notice and + * software license agreement shown below, and also at: + * http://www.aps.anl.gov/epics/license/open.php + * + * In order to comply with section 4.3 of the software license agreement, here + * is a PROMINENT NOTICE OF CHNAGES TO THE SOFTWARE + * =========================================== + * (1) This file, vxi11.x, is the concatenation of the files vxi11core.rpcl and + * vxi11intr.rpcl + * (2) Tab spacing has been tidied up + * + * It is intended as a lightweight base for the vxi11 rpc protocol. If you + * run rpcgen on this file, it will generate C files and headers, from which + * it is relatively simple to write C programs to communicate with a range + * of ethernet-enabled instruments, such as oscilloscopes and function + * generated by manufacturers such as Agilent and Tektronix (amongst many + * others). + * + * For what it's worth, this concatenation was done by Steve Sharples at + * the University of Nottingham, UK, on 1 June 2006. + * + * Copyright notice and software license agreement follow, then the + * original comments from vxi11core.rpcl etc. + * + ****************************************************************************** + * Copyright © 2006 . All + * rights reserved. + ****************************************************************************** + * + ****************************************************************************** + * vxi11.x is distributed subject to the following license conditions: + * SOFTWARE LICENSE AGREEMENT + * Software: vxi11.x + * + * 1. The "Software", below, refers to vxi11.x (in either source code, or + * binary form and accompanying documentation). Each licensee is addressed + * as "you" or "Licensee." + * + * 2. The copyright holders shown above and their third-party licensors hereby + * grant Licensee a royalty-free nonexclusive license, subject to the + * limitations stated herein and U.S. Government license rights. + * + * 3. You may modify and make a copy or copies of the Software for use within + * your organization, if you meet the following conditions: + * 1. Copies in source code must include the copyright notice and this + * Software License Agreement. + * 2. Copies in binary form must include the copyright notice and this + * Software License Agreement in the documentation and/or other + * materials provided with the copy. + * + * 4. You may modify a copy or copies of the Software or any portion of it, + * thus forming a work based on the Software, and distribute copies of such + * work outside your organization, if you meet all of the following + * conditions: + * 1. Copies in source code must include the copyright notice and this + * Software License Agreement; + * 2. Copies in binary form must include the copyright notice and this + * Software License Agreement in the documentation and/or other + * materials provided with the copy; + * 3. Modified copies and works based on the Software must carry + * prominent notices stating that you changed specified portions of + * the Software. + * + * 5. Portions of the Software resulted from work developed under a U.S. + * Government contract and are subject to the following license: the + * Government is granted for itself and others acting on its behalf a + * paid-up, nonexclusive, irrevocable worldwide license in this computer + * software to reproduce, prepare derivative works, and perform publicly + * and display publicly. + * + * 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY OF + * ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED + * STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR EMPLOYEES: (1) + * DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY + * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF THE + * SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE + * PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION + * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. + * + * 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR + * THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF + * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, + * CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, + * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY + * REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, + * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE POSSIBILITY OF SUCH + * LOSS OR DAMAGES. + ****************************************************************************** + */ + +/****************************************************************************** + * + * vxi11core.rpcl + * + * This file is best viewed with a tabwidth of 4 + * + ****************************************************************************** + * + * TODO: + * + ****************************************************************************** + * + * Original Author: someone from VXIbus Consortium + * Current Author: Benjamin Franksen + * Date: 03-06-97 + * + * RPCL description of the core- and abort-channel of the TCP/IP Instrument + * Protocol Specification. + * + * + * Modification Log: + * ----------------- + * .00 03-06-97 bfr created this file + * + ****************************************************************************** + * + * Notes: + * + * This stuff is literally from + * + * VXI-11, Ref 1.0 : TCP/IP Instrument Protocol Specification + * + */ + +typedef long Device_Link; + +enum Device_AddrFamily +{ + DEVICE_TCP, + DEVICE_UDP +}; + +typedef long Device_Flags; + +typedef long Device_ErrorCode; + +struct Device_Error +{ + Device_ErrorCode error; +}; + +struct Create_LinkParms +{ + long clientId; /* implementation specific value */ + bool lockDevice; /* attempt to lock the device */ + unsigned long lock_timeout; /* time to wait for lock */ + string device<>; /* name of device */ +}; +struct Create_LinkResp +{ + Device_ErrorCode error; + Device_Link lid; + unsigned short abortPort; /* for the abort RPC */ + unsigned long maxRecvSize; /* max # of bytes accepted on write */ +}; +struct Device_WriteParms +{ + Device_Link lid; /* link id from create_link */ + unsigned long io_timeout; /* time to wait for I/O */ + unsigned long lock_timeout; /* time to wait for lock */ + Device_Flags flags; /* flags with options */ + opaque data<>; /* the data length and the data itself */ +}; +struct Device_WriteResp +{ + Device_ErrorCode error; + unsigned long size; /* # of bytes written */ +}; +struct Device_ReadParms +{ + Device_Link lid; /* link id from create_link */ + unsigned long requestSize; /* # of bytes requested */ + unsigned long io_timeout; /* time to wait for I/O */ + unsigned long lock_timeout; /* time to wait for lock */ + Device_Flags flags; /* flags with options */ + char termChar; /* valid if flags & termchrset */ +}; +struct Device_ReadResp +{ + Device_ErrorCode error; + long reason; /* why read completed */ + opaque data<>; /* the data length and the data itself */ +}; +struct Device_ReadStbResp +{ + Device_ErrorCode error; + unsigned char stb; /* the returned status byte */ +}; +struct Device_GenericParms +{ + Device_Link lid; /* link id from create_link */ + Device_Flags flags; /* flags with options */ + unsigned long lock_timeout; /* time to wait for lock */ + unsigned long io_timeout; /* time to wait for I/O */ +}; +struct Device_RemoteFunc +{ + unsigned long hostAddr; /* host servicing interrupt */ + unsigned long hostPort; /* valid port # on client */ + unsigned long progNum; /* DEVICE_INTR */ + unsigned long progVers; /* DEVICE_INTR_VERSION */ + Device_AddrFamily progFamily; /* DEVICE_UDP | DEVICE_TCP */ +}; +struct Device_EnableSrqParms +{ + Device_Link lid; /* link id from create_link */ + bool enable; /* enable or disable intr's */ + opaque handle<40>; /* host specific data */ +}; +struct Device_LockParms +{ + Device_Link lid; /* link id from create_link */ + Device_Flags flags; /* contains the waitlock flag */ + unsigned long lock_timeout; /* time to wait for lock */ +}; +struct Device_DocmdParms +{ + Device_Link lid; /* link id from create_link */ + Device_Flags flags; /* flags with options */ + unsigned long io_timeout; /* time to wait for I/O */ + unsigned long lock_timeout; /* time to wait for lock */ + long cmd; /* which command to execute */ + bool network_order; /* client's byte order */ + long datasize; /* size of individual data elements */ + opaque data_in<>; /* docmd data parameters */ +}; +struct Device_DocmdResp +{ + Device_ErrorCode error; + opaque data_out<>; /* returned data parameters */ +}; + +program DEVICE_ASYNC +{ + version DEVICE_ASYNC_VERSION + { + Device_Error device_abort (Device_Link) = 1; + } = 1; +} = 0x0607B0; + +program DEVICE_CORE +{ + version DEVICE_CORE_VERSION + { + Create_LinkResp create_link (Create_LinkParms) = 10; + Device_WriteResp device_write (Device_WriteParms) = 11; + Device_ReadResp device_read (Device_ReadParms) = 12; + Device_ReadStbResp device_readstb (Device_GenericParms) = 13; + Device_Error device_trigger (Device_GenericParms) = 14; + Device_Error device_clear (Device_GenericParms) = 15; + Device_Error device_remote (Device_GenericParms) = 16; + Device_Error device_local (Device_GenericParms) = 17; + Device_Error device_lock (Device_LockParms) = 18; + Device_Error device_unlock (Device_Link) = 19; + Device_Error device_enable_srq (Device_EnableSrqParms) = 20; + Device_DocmdResp device_docmd (Device_DocmdParms) = 22; + Device_Error destroy_link (Device_Link) = 23; + Device_Error create_intr_chan (Device_RemoteFunc) = 25; + Device_Error destroy_intr_chan (void) = 26; + } = 1; +} = 0x0607AF; + +/****************************************************************************** + * + * vxi11intr.rpcl + * + * This file is best viewed with a tabwidth of 4 + * + ****************************************************************************** + * + * TODO: + * + ****************************************************************************** + * + * Original Author: someone from VXIbus Consortium + * Current Author: Benjamin Franksen + * Date: 03-06-97 + * + * RPCL description of the intr-channel of the TCP/IP Instrument Protocol + * Specification. + * + * + * Modification Log: + * ----------------- + * .00 03-06-97 bfr created this file + * + ****************************************************************************** + * + * Notes: + * + * This stuff is literally from + * + * "VXI-11, Ref 1.0 : TCP/IP Instrument Protocol Specification" + * + */ + +struct Device_SrqParms +{ + opaque handle<>; +}; + +program DEVICE_INTR +{ + version DEVICE_INTR_VERSION + { + void device_intr_srq (Device_SrqParms) = 30; + } = 1; +} = 0x0607B1; diff --git a/web/htdocs/tools/vxi/source/vxi11_cmd.cc b/web/htdocs/tools/vxi/source/vxi11_cmd.cc new file mode 100644 index 0000000..79e2a5e --- /dev/null +++ b/web/htdocs/tools/vxi/source/vxi11_cmd.cc @@ -0,0 +1,78 @@ +/* vxi11_cmd.c + * Copyright (C) 2006 Steve D. Sharples + * + * A simple interactive utility that allows you to send commands and queries to + * a device enabled with the VXI11 RPC ethernet protocol. Uses the files + * generated by rpcgen vxi11.x, and the vxi11_user.h user libraries. + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The author's email address is steve.sharples@nottingham.ac.uk + */ + +#include "vxi11_user.h" +#define BUF_LEN 100000 + +int main(int argc, char *argv[]) { + +static char *device_ip; +static char *device_name; +static char *cmd; +char buf[BUF_LEN]; +int ret; +long bytes_returned; +CLINK *clink; + + clink = new CLINK; + + if (argc < 3) { + printf("usage: %s your.inst.ip.addr cmd [device_name]\n",argv[0]); + exit(1); + } + + device_ip = argv[1]; + cmd = argv[2]; + if (argc > 3) { + device_name = argv[3]; + ret=vxi11_open_device(device_ip,clink,device_name); + } + else { + ret=vxi11_open_device(device_ip,clink); + } + + if (ret != 0) { + printf("Error: could not open device %s, quitting\n",device_ip); + exit(2); + } + + memset(buf, 0, BUF_LEN); // initialize buffer +// printf("Input command or query ('q' to exit): "); +// fgets(cmd,256,stdin); + if (vxi11_send(clink, cmd) < 0) return -34; + if (strstr(cmd, "?") != 0) { + bytes_returned = vxi11_receive(clink, buf, BUF_LEN); + if (bytes_returned > 0) { + printf("%s",buf); + } + else if (bytes_returned == -15) { + printf("*** [ NOTHING RECEIVED ] ***\n"); + } +// else break; + } + + ret=vxi11_close_device(device_ip,clink); + return 0; + } + diff --git a/web/htdocs/tools/vxi/source/vxi11_user.cc b/web/htdocs/tools/vxi/source/vxi11_user.cc new file mode 100644 index 0000000..c1510f2 --- /dev/null +++ b/web/htdocs/tools/vxi/source/vxi11_user.cc @@ -0,0 +1,589 @@ +/* vxi11_user.cc + * Copyright (C) 2006 Steve D. Sharples + * + * User library for opening, closing, sending to and receiving from + * a device enabled with the VXI11 RPC ethernet protocol. Uses the files + * generated by rpcgen vxi11.x. + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The author's email address is steve.sharples@nottingham.ac.uk + */ + +#include "vxi11_user.h" + +/***************************************************************************** + * GENERAL NOTES + ***************************************************************************** + * + * There are four functions at the heart of this library: + * + * int vxi11_open_device(char *ip, CLIENT **client, VXI11_LINK **link) + * int vxi11_close_device(char *ip, CLIENT *client, VXI11_LINK *link) + * int vxi11_send(CLIENT *client, VXI11_LINK *link, char *cmd, unsigned long len) + * long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) + * + * Note that all 4 of these use separate client and link structures. All the + * other functions are built on these four core functions, and the first layer + * of abstraction is to combine the CLIENT and VXI11_LINK structures into a + * single entity, which I've called a CLINK. For the send and receive + * functions, this is just a simple wrapper. For the open and close functions + * it's a bit more complicated, because we somehow have to keep track of + * whether we've already opened a device with the same IP address before (in + * which case we need to recycle a previously created client), or whether + * we've still got any other links to a given IP address left when we are + * asked to close a clink (in which case we can sever the link, but have to + * keep the client open). This is so the person using this library from + * userland does not have to keep track of whether they are talking to a + * different physical instrument or not each time they establish a connection. + * + * So the base functions that the user will probably want to use are: + * + * int vxi11_open_device(char *ip, CLINK *clink) + * int vxi11_close_device(char *ip, CLINK *clink) + * int vxi11_send(CLINK *clink, char *cmd, unsigned long len) + * --- or --- (if sending just text) + * int vxi11_send(CLINK *clink, char *cmd) + * long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) + * + * There are then useful (to me, anyway) more specific functions built on top + * of these: + * + * int vxi11_send_data_block(CLINK *clink, char *cmd, char *buffer, unsigned long len) + * long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) + * long vxi11_send_and_receive(CLINK *clink, char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) + * long vxi11_obtain_long_value(CLINK *clink, char *cmd, unsigned long timeout) + * double vxi11_obtain_double_value(CLINK *clink, char *cmd, unsigned long timeout) + * + * (then there are some shorthand wrappers for the above without specifying + * the timeout due to sheer laziness---explore yourself) + */ + + +/* Global variables. Keep track of multiple links per client. We need this + * because: + * - we'd like the library to be able to cope with multiple links to a given + * client AND multiple links to multiple clients + * - we'd like to just refer to a client/link ("clink") as a single + * entity from user land, we don't want to worry about different + * initialisation procedures, depending on whether it's an instrument + * with the same IP address or not + */ +char VXI11_IP_ADDRESS[VXI11_MAX_CLIENTS][20]; +CLIENT *VXI11_CLIENT_ADDRESS[VXI11_MAX_CLIENTS]; +int VXI11_DEVICE_NO = 0; +int VXI11_LINK_COUNT[VXI11_MAX_CLIENTS]; + +/***************************************************************************** + * KEY USER FUNCTIONS - USE THESE FROM YOUR PROGRAMS OR INSTRUMENT LIBRARIES * + *****************************************************************************/ + +/* OPEN FUNCTIONS * + * ============== */ + +/* Use this function from user land to open a device and create a link. Can be + * used multiple times for the same device (the library will keep track).*/ +int vxi11_open_device(const char *ip, CLINK *clink, char *device) { +int ret; +int l; +int device_no=-1; + +// printf("before doing anything, clink->link = %ld\n", clink->link); + /* Have a look to see if we've already initialised an instrument with + * this IP address */ + for (l=0; l= VXI11_MAX_CLIENTS) { + printf("Error: maximum of %d clients allowed\n",VXI11_MAX_CLIENTS); + ret = -VXI11_MAX_CLIENTS; + } + /* Create a new client, keep a note of where the client pointer + * is, for this IP address. Because it's a new client, this + * must be link number 1. Keep track of how many devices we've + * opened so we don't run out of storage space. */ + else { + ret = vxi11_open_device(ip, &(clink->client), &(clink->link), device); + strncpy(VXI11_IP_ADDRESS[VXI11_DEVICE_NO],ip,20); + VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO] = clink->client; + VXI11_LINK_COUNT[VXI11_DEVICE_NO]=1; +// printf("Open function, could not find ip address %s.\n",ip); +// printf("So now, VXI11_IP_ADDRESS[%d]=%s,\n",VXI11_DEVICE_NO,VXI11_IP_ADDRESS[VXI11_DEVICE_NO]); +// printf("VXI11_CLIENT_ADDRESS[%d]=%ld,\n",VXI11_DEVICE_NO,VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO]); +// printf(" clink->client=%ld,\n",clink->client); +// printf("VXI11_LINK_COUNT[%d]=%d.\n",VXI11_DEVICE_NO,VXI11_LINK_COUNT[VXI11_DEVICE_NO]); + VXI11_DEVICE_NO++; + } + } + /* already got a client for this IP address */ + else { + /* Copy the client pointer address. Just establish a new link + * (not a new client). Add one to the link count */ + clink->client = VXI11_CLIENT_ADDRESS[device_no]; + ret = vxi11_open_link(ip, &(clink->client), &(clink->link), device); +// printf("Found an ip address, copying client from VXI11_CLIENT_ADDRESS[%d]\n",device_no); + VXI11_LINK_COUNT[device_no]++; +// printf("Have just incremented VXI11_LINK_COUNT[%d], it's now %d\n",device_no,VXI11_LINK_COUNT[device_no]); + } +// printf("after creating link, clink->link = %ld\n", clink->link); + return ret; + } + +/* This is a wrapper function, used for the situations where there is only one + * "device" per client. This is the case for most (if not all) VXI11 + * instruments; however, it is _not_ the case for devices such as LAN to GPIB + * gateways. These are single clients that communicate to many instruments + * (devices). In order to differentiate between them, we need to pass a device + * name. This gets used in the vxi11_open_link() fn, as the link_parms.device + * value. */ +int vxi11_open_device(const char *ip, CLINK *clink) { + char device[6]; + strncpy(device,"inst0",6); + return vxi11_open_device(ip, clink, device); + } + + + +/* CLOSE FUNCTION * + * ============== */ + +/* Use this function from user land to close a device and/or sever a link. Can + * be used multiple times for the same device (the library will keep track).*/ +int vxi11_close_device(const char *ip, CLINK *clink) { +int l,ret; +int device_no = -1; + + /* Which instrument are we referring to? */ + for (l=0; l 1 ) { + ret = vxi11_close_link(ip,clink->client, clink->link); + VXI11_LINK_COUNT[device_no]--; + } + /* Found the IP, it's the last link, so close the device (link + * AND client) */ + else { + ret = vxi11_close_device(ip, clink->client, clink->link); + /* Remove the IP address, so that if we re-open the same device + * we do it properly */ + memset(VXI11_IP_ADDRESS[device_no], 0, 20); + } + } + return ret; + } + + +/* SEND FUNCTIONS * + * ============== */ + +/* A _lot_ of the time we are sending text strings, and can safely rely on + * strlen(cmd). */ +int vxi11_send(CLINK *clink, const char *cmd) { + return vxi11_send(clink, cmd, strlen(cmd)); + } + +/* We still need the version of the function where the length is set explicitly + * though, for when we are sending fixed length data blocks. */ +int vxi11_send(CLINK *clink, const char *cmd, unsigned long len) { + return vxi11_send(clink->client, clink->link, cmd, len); + } + + +/* RECEIVE FUNCTIONS * + * ================= */ + +/* Lazy wrapper for when I can't be bothered to specify a read timeout */ +long vxi11_receive(CLINK *clink, char *buffer, unsigned long len) { + return vxi11_receive(clink, buffer, len, VXI11_READ_TIMEOUT); + } + +long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) { + return vxi11_receive(clink->client, clink->link, buffer, len, timeout); + } + + + +/***************************************************************************** + * USEFUL ADDITIONAL HIGHER LEVER USER FUNCTIONS - USE THESE FROM YOUR * + * PROGRAMS OR INSTRUMENT LIBRARIES * + *****************************************************************************/ + +/* SEND FIXED LENGTH DATA BLOCK FUNCTION * + * ===================================== */ +int vxi11_send_data_block(CLINK *clink, const char *cmd, char *buffer, unsigned long len) { +char *out_buffer; +int cmd_len=strlen(cmd); +int ret; + + out_buffer=new char[cmd_len+10+len]; + sprintf(out_buffer,"%s#8%08lu",cmd,len); + memcpy(out_buffer+cmd_len+10,buffer,(unsigned long) len); + ret = vxi11_send(clink, out_buffer, (unsigned long) (cmd_len+10+len)); + delete[] out_buffer; + return ret; + } + + +/* RECEIVE FIXED LENGTH DATA BLOCK FUNCTION * + * ======================================== */ + +/* This function reads a response in the form of a definite-length block, such + * as when you ask for waveform data. The data is returned in the following + * format: + * #800001000<1000 bytes of data> + * ||\______/ + * || | + * || \---- number of bytes of data + * |\--------- number of digits that follow (in this case 8, with leading 0's) + * \---------- always starts with # + */ +long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) { +/* I'm not sure what the maximum length of this header is, I'll assume it's + * 11 (#9 + 9 digits) */ +unsigned long necessary_buffer_size; +char *in_buffer; +int ret; +int ndigits; +unsigned long returned_bytes; +int l; +char scan_cmd[20]; + necessary_buffer_size=len+12; + in_buffer=new char[necessary_buffer_size]; + ret=vxi11_receive(clink, in_buffer, necessary_buffer_size, timeout); + if (ret < 0) return ret; + if (in_buffer[0] != '#') { + printf("vxi11_user: data block error: data block does not begin with '#'\n"); + printf("First 20 characters received were: '"); + for(l=0;l<20;l++) { + printf("%c",in_buffer[l]); + } + printf("'\n"); + return -3; + } + + /* first find out how many digits */ + sscanf(in_buffer,"#%1d",&ndigits); + /* some instruments, if there is a problem acquiring the data, return only "#0" */ + if (ndigits > 0) { + /* now that we know, we can convert the next bytes into an unsigned long */ + sprintf(scan_cmd,"#%%1d%%%dlu",ndigits); + sscanf(in_buffer,scan_cmd,&ndigits,&returned_bytes); + memcpy(buffer, in_buffer+(ndigits+2), returned_bytes); + delete[] in_buffer; + return (long) returned_bytes; + } + else return 0; + } + + +/* SEND AND RECEIVE FUNCTION * + * ========================= */ + +/* This is mainly a useful function for the overloaded vxi11_obtain_value() + * fn's, but is also handy and useful for user and library use */ +long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) { +int ret; +long bytes_returned; + do { + ret = vxi11_send(clink, cmd); + if (ret != 0) { + if (ret != -VXI11_NULL_WRITE_RESP) { + printf("Error: vxi11_send_and_receive: could not send cmd.\n"); + printf(" The function vxi11_send returned %d. ",ret); + return -1; + } + else printf("(Info: VXI11_NULL_WRITE_RESP in vxi11_send_and_receive, resending query)\n"); + } + + bytes_returned = vxi11_receive(clink, buf, buf_len, timeout); + if (bytes_returned <= 0) { + if (bytes_returned >-VXI11_NULL_READ_RESP) { + printf("Error: vxi11_send_and_receive: problem reading reply.\n"); + printf(" The function vxi11_receive returned %ld. ",bytes_returned); + return -2; + } + else printf("(Info: VXI11_NULL_READ_RESP in vxi11_send_and_receive, resending query)\n"); + } + } while (bytes_returned == -VXI11_NULL_READ_RESP || ret == -VXI11_NULL_WRITE_RESP); + return 0; + } + + +/* FUNCTIONS TO RETURN A LONG INTEGER VALUE SENT AS RESPONSE TO A QUERY * + * ==================================================================== */ +long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout) { +char buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */ + memset(buf, 0, 50); + if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) { + printf("Returning 0\n"); + return 0; + } + return strtol(buf, (char **)NULL, 10); + } + +/* Lazy wrapper function with default read timeout */ +long vxi11_obtain_long_value(CLINK *clink, const char *cmd) { + return vxi11_obtain_long_value(clink, cmd, VXI11_READ_TIMEOUT); + } + + +/* FUNCTIONS TO RETURN A DOUBLE FLOAT VALUE SENT AS RESPONSE TO A QUERY * + * ==================================================================== */ +double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout) { +char buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */ +double val; + memset(buf, 0, 50); + if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) { + printf("Returning 0.0\n"); + return 0.0; + } + val = strtod(buf, (char **)NULL); + return val; + } + +/* Lazy wrapper function with default read timeout */ +double vxi11_obtain_double_value(CLINK *clink, const char *cmd) { + return vxi11_obtain_double_value(clink, cmd, VXI11_READ_TIMEOUT); + } + + +/***************************************************************************** + * CORE FUNCTIONS - YOU SHOULDN'T NEED TO USE THESE FROM YOUR PROGRAMS OR * + * INSTRUMENT LIBRARIES * + *****************************************************************************/ + +/* OPEN FUNCTIONS * + * ============== */ +int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) { + + *client = clnt_create(ip, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp"); + + if (*client == NULL) { + clnt_pcreateerror(ip); + return -1; + } + + return vxi11_open_link(ip, client, link, device); + } + +int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) { + +Create_LinkParms link_parms; + + /* Set link parameters */ + link_parms.clientId = (long) *client; + link_parms.lockDevice = 0; + link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT; + link_parms.device = device; + + *link = (Create_LinkResp *) calloc(1, sizeof(Create_LinkResp)); + + if (create_link_1(&link_parms, *link, *client) != RPC_SUCCESS) { + clnt_perror(*client, ip); + return -2; + } + return 0; + } + + +/* CLOSE FUNCTIONS * + * =============== */ +int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link) { +int ret; + + ret = vxi11_close_link(ip, client, link); + + clnt_destroy(client); + + return ret; + } + +int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link) { +Device_Error dev_error; + memset(&dev_error, 0, sizeof(dev_error)); + + if (destroy_link_1(&link->lid, &dev_error, client) != RPC_SUCCESS) { + clnt_perror(client,ip); + return -1; + } + + return 0; + } + + +/* SEND FUNCTIONS * + * ============== */ + +/* A _lot_ of the time we are sending text strings, and can safely rely on + * strlen(cmd). */ +int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd) { + return vxi11_send(client, link, cmd, strlen(cmd)); + } + +/* We still need the version of the function where the length is set explicitly + * though, for when we are sending fixed length data blocks. */ +int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len) { +Device_WriteParms write_parms; +unsigned int bytes_left = len; +char *send_cmd; + + send_cmd = new char[len]; + memcpy(send_cmd, cmd, len); + + write_parms.lid = link->lid; + write_parms.io_timeout = VXI11_DEFAULT_TIMEOUT; + write_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT; + +/* We can only write (link->maxRecvSize) bytes at a time, so we sit in a loop, + * writing a chunk at a time, until we're done. */ + + do { + Device_WriteResp write_resp; + memset(&write_resp, 0, sizeof(write_resp)); + + if (bytes_left <= link->maxRecvSize) { + write_parms.flags = 8; + write_parms.data.data_len = bytes_left; + } + else { + write_parms.flags = 0; + /* We need to check that maxRecvSize is a sane value (ie >0). Believe it + * or not, on some versions of Agilent Infiniium scope firmware the scope + * returned "0", which breaks Rule B.6.3 of the VXI-11 protocol. Nevertheless + * we need to catch this, otherwise the program just hangs. */ + if (link->maxRecvSize > 0) { + write_parms.data.data_len = link->maxRecvSize; + } + else { + write_parms.data.data_len = 4096; /* pretty much anything should be able to cope with 4kB */ + } + } + write_parms.data.data_val = send_cmd + (len - bytes_left); + + if(device_write_1(&write_parms, &write_resp, client) != RPC_SUCCESS) { + delete[] send_cmd; + return -VXI11_NULL_WRITE_RESP; /* The instrument did not acknowledge the write, just completely + dropped it. There was no vxi11 comms error as such, the + instrument is just being rude. Usually occurs when the instrument + is busy. If we don't check this first, then the following + line causes a seg fault */ + } + if (write_resp.error != 0) { + printf("vxi11_user: write error: %d\n", (int)write_resp.error); + delete[] send_cmd; + return -(write_resp.error); + } + bytes_left -= write_resp.size; + } while (bytes_left > 0); + + delete[] send_cmd; + return 0; + } + + +/* RECEIVE FUNCTIONS * + * ================= */ + +// It appeared that this function wasn't correctly dealing with more data available than specified in len. +// This patch attempts to fix this issue. RDP 2007/8/13 + +/* wrapper, for default timeout */ long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len) { return vxi11_receive(client, link, buffer, len, VXI11_READ_TIMEOUT); + } + +#define RCV_END_BIT 0x04 // An end indicator has been read +#define RCV_CHR_BIT 0x02 // A termchr is set in flags and a character which matches termChar is transferred +#define RCV_REQCNT_BIT 0x01 // requestSize bytes have been transferred. This includes a request size of zero. + +long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) { +Device_ReadParms read_parms; +Device_ReadResp read_resp; +unsigned long curr_pos = 0; + + read_parms.lid = link->lid; + read_parms.requestSize = len; + read_parms.io_timeout = timeout; /* in ms */ + read_parms.lock_timeout = timeout; /* in ms */ + read_parms.flags = 0; + read_parms.termChar = 0; + + do { + memset(&read_resp, 0, sizeof(read_resp)); + + read_resp.data.data_val = buffer + curr_pos; + read_parms.requestSize = len - curr_pos; // Never request more total data than originally specified in len + + if(device_read_1(&read_parms, &read_resp, client) != RPC_SUCCESS) { + return -VXI11_NULL_READ_RESP; /* there is nothing to read. Usually occurs after sending a query + which times out on the instrument. If we don't check this first, + then the following line causes a seg fault */ + } + if (read_resp.error != 0) { + /* Read failed for reason specified in error code. + * (From published VXI-11 protocol, section B.5.2) + * 0 no error + * 1 syntax error + * 3 device not accessible + * 4 invalid link identifier + * 5 parameter error + * 6 channel not established + * 8 operation not supported + * 9 out of resources + * 11 device locked by another link + * 12 no lock held by this link + * 15 I/O timeout + * 17 I/O error + * 21 invalid address + * 23 abort + * 29 channel already established + */ + + printf("vxi11_user: read error: %d\n", (int)read_resp.error); + return -(read_resp.error); + } + + if((curr_pos + read_resp.data.data_len) <= len) { + curr_pos += read_resp.data.data_len; + } + if( (read_resp.reason & RCV_END_BIT) || (read_resp.reason & RCV_CHR_BIT) ) { + break; + } + else if( curr_pos == len ) { + printf("xvi11_user: read error: buffer too small. Read %d bytes without hitting terminator.\n", (int)curr_pos ); + return -100; + } + } while(1); + return (curr_pos); /*actual number of bytes received*/ + + } + diff --git a/web/htdocs/tools/vxi/source/vxi11_user.h b/web/htdocs/tools/vxi/source/vxi11_user.h new file mode 100644 index 0000000..07898e9 --- /dev/null +++ b/web/htdocs/tools/vxi/source/vxi11_user.h @@ -0,0 +1,87 @@ +/* vxi11_user.h + * Copyright (C) 2006 Steve D. Sharples + * + * User library for opening, closing, sending to and receiving from + * a device enabled with the VXI11 RPC ethernet protocol. Uses the files + * generated by rpcgen vxi11.x. + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The author's email address is steve.sharples@nottingham.ac.uk + */ + +#ifndef __VXI11_USER__ +#define __VXI11_USER__ +#include +#include +#include + +#include +#include "vxi11.h" + +#define VXI11_DEFAULT_TIMEOUT 10000 /* in ms */ +#define VXI11_READ_TIMEOUT 2000 /* in ms */ +#define VXI11_CLIENT CLIENT +#define VXI11_LINK Create_LinkResp +#define VXI11_MAX_CLIENTS 256 /* maximum no of unique IP addresses/clients */ +#define VXI11_NULL_READ_RESP 50 /* vxi11_receive() return value if a query + * times out ON THE INSTRUMENT (and so we have + * to resend the query again) */ +#define VXI11_NULL_WRITE_RESP 51 /* vxi11_send() return value if a sent command + * times out ON THE INSTURMENT. */ + +struct CLINK { + VXI11_CLIENT *client; + VXI11_LINK *link; + } ; +typedef struct CLINK CLINK; + +/* The four main functions: open, close, send, receieve (plus a couple of wrappers) */ +/* In fact all 6 of these are wrappers to the original functions listed at the + * bottom, that use separate CLIENT and VXI11_LINK structures. It was easier to + * write wrappers for these functions than to re-write the original functions + * themselves. These are the 4 (or 6 if you like) key user functions that you + * should probably be using. They all use the CLINK structure. */ +int vxi11_open_device(const char *ip, CLINK *clink); +int vxi11_open_device(const char *ip, CLINK *clink, char *device); +int vxi11_close_device(const char *ip, CLINK *clink); +int vxi11_send(CLINK *clink, const char *cmd); +int vxi11_send(CLINK *clink, const char *cmd, unsigned long len); +long vxi11_receive(CLINK *clink, char *buffer, unsigned long len); +long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout); + +/* Utility functions, that use send() and receive(). Use these too. */ +int vxi11_send_data_block(CLINK *clink, const char *cmd, char *buffer, unsigned long len); +long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout); +long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout); +long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout); +double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout); +long vxi11_obtain_long_value(CLINK *clink, const char *cmd); +double vxi11_obtain_double_value(CLINK *link, const char *cmd); + +/* When I first wrote this library I used separate client and links. I've + * retained the original functions and just written clink wrappers for them + * (see above) as it's perhaps a little clearer this way. Probably not worth + * delving this deep in use, but it's where the real nitty gritty is. */ +int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, char *device); +int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, char *device); +int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link); +int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link); +int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd); +int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len); +long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len); +long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout); + +#endif diff --git a/web/htdocs/tools/vxi/vxi.pl b/web/htdocs/tools/vxi/vxi.pl new file mode 100755 index 0000000..9eb723c --- /dev/null +++ b/web/htdocs/tools/vxi/vxi.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -w +#print "Content-type: text/html\n\n"; + + +use strict; +use warnings; +use URI::Escape; +use Data::Dumper; + +my $executable = "./vxi11_cmd"; +if ($ENV{'SERVER_SOFTWARE'} =~ /HTTPi/i) { + $executable = "htdocs/tools/vxi/vxi11_cmd"; + } + +my $envstring = $ENV{'QUERY_STRING'}; +$envstring =~ s/%20/ /g; + +my @new_command = split('&',$envstring); +my $dev_ip = shift(@new_command); +my $cmds = shift(@new_command); + + +$cmds = uri_unescape($cmds); +my @new_cmds = split('\n',$cmds); + +foreach my $c (@new_cmds) { + chomp $c; + my $call = "$executable $dev_ip $c 2>&1"; + my @o = qx($call); + if($c =~ /\?/) { + foreach my $l (@o) { + print $l; + } + } + print "---\n"; + } + + + +return 1; \ No newline at end of file diff --git a/web/htdocs/tools/vxi/vxi11_cmd b/web/htdocs/tools/vxi/vxi11_cmd new file mode 100755 index 0000000000000000000000000000000000000000..5ce7ffacb6b2317736b7fe1c31aa6fd549cd280a GIT binary patch literal 64074 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

oIs>YF}=XR=|D-d;1uP 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 B1`YrK literal 0 HcmV?d00001 -- 2.43.0