├── .cdtproject ├── .cproject ├── .gitignore ├── .project ├── .settings └── org.eclipse.cdt.core.prefs ├── CompactLogix.txt ├── INSTALL ├── LICENSE ├── Makefile ├── Manual.md ├── README.md ├── changes.txt ├── configure ├── CONFIG ├── CONFIG_SITE ├── CONFIG_SITE.local ├── CONFIG_SITE.local.darwin-aarch64 ├── CONFIG_SITE.local.darwin-x86 ├── CONFIG_SITE.local.linux-x86_64 ├── Makefile ├── RELEASE ├── RULES ├── RULES.ioc ├── RULES_DIRS └── RULES_TOP ├── ether_ipApp ├── Makefile ├── doc │ ├── CIP_sniff.txt │ ├── bool.txt │ ├── readme.txt │ └── stats.txt └── src │ ├── Makefile │ ├── devEtherIP.c │ ├── dl_list.c │ ├── dl_list.h │ ├── drvEtherIP.c │ ├── drvEtherIP.h │ ├── drvEtherIPRegister.cpp │ ├── eip_bool.h │ ├── ether_ip-3-14.dbd │ ├── ether_ip-lint.dbd │ ├── ether_ip-lsio.dbd │ ├── ether_ip.c │ ├── ether_ip.h │ ├── ether_ip.rc │ └── ether_ip_test.c ├── iocBoot ├── Makefile ├── iocether_ip │ ├── Makefile │ ├── README │ ├── run_iocLogServer.sh │ ├── st.cmd │ ├── st.cmd.3_14 │ ├── st.cmd.host │ └── string.db └── nfsCommands └── testether_ipApp ├── Db ├── Makefile ├── ai.db ├── ana.db ├── ao.db ├── bi.db ├── bin.db ├── bo.db ├── brad.db ├── brad2.db ├── eip_stat.db ├── lint.db ├── mbbi.db ├── mbbo.db ├── mkana.pl ├── mkbin.pl ├── mod.db ├── ramp.db └── test.db ├── Makefile ├── RSLogix └── test.L5K ├── edm └── test_db.edl ├── opi └── EtherIPStats.bob └── src ├── Makefile └── eipIocMain.cpp /.cdtproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | make 60 | 61 | 62 | true 63 | true 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | make 69 | 70 | true 71 | true 72 | true 73 | 74 | 75 | make 76 | 77 | distclean 78 | false 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## generated from EPICS build 2 | configure/RELEASE.local 3 | bin/ 4 | db/ 5 | dbd/ 6 | include/ 7 | lib/ 8 | O.* 9 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ether_ip 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.make.core.makeBuilder 10 | clean,full,incremental, 11 | 12 | 13 | org.eclipse.cdt.core.errorOutputParser 14 | org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; 15 | 16 | 17 | org.eclipse.cdt.make.core.append_environment 18 | true 19 | 20 | 21 | org.eclipse.cdt.make.core.build.arguments 22 | 23 | 24 | 25 | org.eclipse.cdt.make.core.build.command 26 | make 27 | 28 | 29 | org.eclipse.cdt.make.core.build.location 30 | 31 | 32 | 33 | org.eclipse.cdt.make.core.build.target.auto 34 | all 35 | 36 | 37 | org.eclipse.cdt.make.core.build.target.clean 38 | clean 39 | 40 | 41 | org.eclipse.cdt.make.core.build.target.inc 42 | all 43 | 44 | 45 | org.eclipse.cdt.make.core.enableAutoBuild 46 | false 47 | 48 | 49 | org.eclipse.cdt.make.core.enableCleanBuild 50 | true 51 | 52 | 53 | org.eclipse.cdt.make.core.enableFullBuild 54 | true 55 | 56 | 57 | org.eclipse.cdt.make.core.enabledIncrementalBuild 58 | true 59 | 60 | 61 | org.eclipse.cdt.make.core.environment 62 | 63 | 64 | 65 | org.eclipse.cdt.make.core.stopOnError 66 | false 67 | 68 | 69 | org.eclipse.cdt.make.core.useDefaultBuildCmd 70 | true 71 | 72 | 73 | 74 | 75 | org.eclipse.cdt.make.core.ScannerConfigBuilder 76 | 77 | 78 | 79 | 80 | 81 | org.eclipse.cdt.core.cnature 82 | org.eclipse.cdt.make.core.makeNature 83 | org.eclipse.cdt.make.core.ScannerConfigNature 84 | org.eclipse.cdt.core.ccnature 85 | 86 | 87 | -------------------------------------------------------------------------------- /.settings/org.eclipse.cdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Tue Dec 12 15:15:08 EST 2006 2 | eclipse.preferences.version=1 3 | indexerId=org.eclipse.cdt.core.nullindexer 4 | -------------------------------------------------------------------------------- /CompactLogix.txt: -------------------------------------------------------------------------------- 1 | This driver was written for ControlLogix, 2 | but also seems to work with CompactLogix, 3 | as reported by Mike Forster : 4 | 5 | We saw that when a record scan list became long (too many records with the same scan rate), 6 | all the elements in that scan list would stop responding. 7 | Eventually we traced this down to the EIP_buffer_limit which was the default 500. 8 | This limit at 500 allows data to be sent that is 554 bytes long. After some trial and 9 | error we found that the PLC responded correctly if the data sent was 548 bytes long or less. 10 | When we set EIP_buffer_limit to 494 our problems went away. 11 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | -*- outline -*- (use EMACS to see this in outline-mode) 2 | 3 | This readme is about compiling the driver & examples. 4 | See /ether_ipApp/doc for info on how to use the driver. 5 | 6 | * Intro 7 | The sources for this driver are on SourceForge, 8 | see http://sourceforge.net/apps/trac/epics/wiki/EtherIP 9 | 10 | Read-only Mercurial access: 11 | hg clone http://epics.hg.sourceforge.net:8000/hgroot/epics/ether_ip 12 | 13 | Read/write Mercurial access: 14 | hg clone ssh://USERNAME@epics.hg.sourceforge.net/hgroot/epics/ether_ip 15 | 16 | Tag: ether_ip- 17 | 18 | What you have to do is 19 | - compile the ether_ip driver/device sources 20 | - somehow load them in the IOC 21 | - include the DBD descriptions of the new device support 22 | so that EPICS knows about them 23 | - create EPICS records that use the new driver 24 | 25 | The directory structure and Makefiles were created by 26 | makeBaseApp.pl from EPICS R3.13.3 base and later updated 27 | for EPICS 3.14 base. 28 | - In iocBoot/iocether_ip/Makefile, select the ARCH for the test IOC 29 | (if you want to use that test setup). 30 | - You have to modify configure/RELEASE 31 | so that ETHER_IP point to where you put the stuff, e.g. 32 | ETHER_IP=/cs/epics/ADE/R3.14.11/support/ether_ip/current 33 | 34 | * I. ether_ip directory structure: 35 | ether_ip/ 36 | README -- this file 37 | ether_ipApp/ - the DRIVER/DEVICE. You need this! 38 | src -- contains the source code and dbd definitions. 39 | Used to build and install the library 40 | libether_ip.a -- containing driver object file 41 | and to install 42 | ether_ip.dbd -- the database definition file 43 | needed by databases using the driver. 44 | doc -- contains the driver specific documentation 45 | testether_ipApp/ - example of EPICS database for ether_ip. 46 | You can use this for reference and tests. 47 | src -- contains an example application that builds a 48 | epics module that uses the ether_ip driver. 49 | Db -- contains sample databases using the ether_ip driver. 50 | dm2k -- contains sample dm2k adl display files. 51 | 52 | configure -- configuration setup with RELEASE referring to 53 | location of ETHER_IP for use by the example in testether_ipApp 54 | 55 | Makefile -- setup to build 1st ether_ipApp, 56 | then config, 57 | then testether_ipApp, so that the 58 | examples will work. 59 | 60 | * II. Build 61 | 1. Your usual EPICS environment variables need to be set: 62 | EPICS_HOST_ARCH, PATH to include the EPICS base tools 63 | 2. cd where_the_ether_ip_sources_are 64 | 3. Edit configure/RELEASE for EPICS BASE and ETHER_IP directory 65 | locations. 66 | 4. Edit iocBoot/iocether_ip/Makefile for ARCH 67 | 5. make 68 | 69 | * III. Use of the ether_ip driver in your ioc application 70 | 1. Make sure that the driver code has been checked out and built 71 | as described above. The Make in the config directory 72 | depends on the proper directories already existing. 73 | 74 | 2. Set up ETHER_IP definitions in the configuration directory 75 | for your application by: 76 | 2.1. cd /configure 77 | 2.2. edit the file RELEASE file, add the line 78 | ETHER_IP=/cs/epics/ADE/R3.14.11/support/ether_ip/ 79 | 2.3. Make sure the new definitions take effect by 80 | make clean 81 | make 82 | in the configure directory. 83 | 3. Set up the dbd include file in your src directory to include 84 | ether_ip.dbd by adding the line 85 | include "ether_ip.dbd" 86 | after the line include "../base.dbd". 87 | 88 | Now that RELEASE file has been edited and config or configure has been 89 | rebuilt the Makefile will be able to find ether_ip.dbd 90 | when it expands your dbd file. 91 | 4. Set up your application to include the object libraries by 92 | including the following lines in Makefile: 93 | _LIBS += ether_ip 94 | ether_ip_DIR = $(ETHER_IP_LIB) 95 | 96 | ETHER_IP_LIB (3.14) will be automatically 97 | defined for every type of ioc for which your application is built. 98 | 99 | R. Wright, K. Kasemir 100 | 3.14 notes added by S. Allison 101 | 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000-2018 Oak Ridge National Laboratory. 2 | All rights reserved. 3 | 4 | EtherIP is distributed subject to the following license conditions: 5 | 6 | SOFTWARE LICENSE AGREEMENT 7 | Software: EtherIP 8 | 9 | 1. The "Software", below, refers to EtherIP (in either source code, or 10 | binary form and accompanying documentation). Each licensee is 11 | addressed as "you" or "Licensee." 12 | 13 | 2. The copyright holders shown above and their third-party licensors 14 | hereby grant Licensee a royalty-free nonexclusive license, subject to 15 | the limitations stated herein and U.S. Government license rights. 16 | 17 | 3. You may modify and make a copy or copies of the Software for use 18 | within your organization, if you meet the following conditions: 19 | a. Copies in source code must include the copyright notice and this 20 | Software License Agreement. 21 | b. Copies in binary form must include the copyright notice and this 22 | Software License Agreement in the documentation and/or other 23 | materials provided with the copy. 24 | 25 | 4. You may modify a copy or copies of the Software or any portion of it, 26 | thus forming a work based on the Software, and distribute copies of 27 | such work outside your organization, if you meet all of the following 28 | conditions: 29 | a. Copies in source code must include the copyright notice and this 30 | Software License Agreement; 31 | b. Copies in binary form must include the copyright notice and this 32 | Software License Agreement in the documentation and/or other 33 | materials provided with the copy; 34 | c. Modified copies and works based on the Software must carry 35 | prominent notices stating that you changed specified portions of 36 | the Software. 37 | 38 | 5. Portions of the Software resulted from work developed under a U.S. 39 | Government contract and are subject to the following license: the 40 | Government is granted for itself and others acting on its behalf a 41 | paid-up, nonexclusive, irrevocable worldwide license in this computer 42 | software to reproduce, prepare derivative works, and perform publicly 43 | and display publicly. 44 | 45 | 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY 46 | OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE 47 | UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR 48 | EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING 49 | BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 50 | FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME 51 | ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, 52 | OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE 53 | SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT 54 | THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE 55 | OR THAT ANY ERRORS WILL BE CORRECTED. 56 | 57 | 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR 58 | THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT 59 | OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, 60 | CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, 61 | INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY 62 | REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF 63 | CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR 64 | OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE 65 | POSSIBILITY OF SUCH LOSS OR DAMAGES. 66 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #Makefile at top of application tree 2 | TOP = . 3 | 4 | include $(TOP)/configure/CONFIG 5 | 6 | DIRS := configure 7 | 8 | DIRS += ether_ipApp 9 | ether_ipApp_DEPEND_DIRS = configure 10 | 11 | DIRS += testether_ipApp 12 | testether_ipApp_DEPEND_DIRS = ether_ipApp 13 | 14 | DIRS += iocBoot 15 | iocBoot_DEPEND_DIRS = configure 16 | 17 | include $(TOP)/configure/RULES_TOP 18 | 19 | 20 | test: 21 | bin/linux-x86/ether_ip_test -i 160.91.232.217 -v 10 K_RealArray_10[2] 22 | 23 | val: 24 | valgrind --trace-children=yes --leak-check=full --show-reachable=yes bin/linux-x86/ether_ip_test -i 160.91.232.217 -v 1 K_RealArray_10[2] 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | "EtherIP" driver/device support module for EPICS. 2 | 3 | Interfaces Allen Bradley PLCs (see www.ab.com) via Ethernet to EPICS IOCs 4 | * ControlLogix 5000, 5 | both original versions with separate controller and ENET module, 6 | and L8x series that includes a network port in the controller. 7 | * Compact Logix devices 8 | 9 | For EPICS R3.14.8 and higher, 10 | on Linux and several other operating systems supported by EPICS libCom. 11 | For earlier version of EPICS base, including R3.13, see tags/releases older than ether_ip-3-0. 12 | VxWorks 5.5 must also use an older version because its compiler requires 13 | K&R style declarations of all variables at the start of a code block. 14 | 15 | See 16 | * Manual.md for usage. 17 | * https://controlssoftware.sns.ornl.gov/etherip for more. 18 | * changes.txt for changes. 19 | * https://github.com/ornl-epics/etherip for a Java version of the basic communication library, not connected to an EPCIS IOC. 20 | * https://github.com/ottowayi/pycomm3 for an unrelated python library that supports many protocol features. 21 | 22 | -------------------------------------------------------------------------------- /configure/CONFIG: -------------------------------------------------------------------------------- 1 | # CONFIG - Load build configuration data 2 | # 3 | # Do not make changes to this file! 4 | 5 | # Allow user to override where the build rules come from 6 | RULES = $(EPICS_BASE) 7 | 8 | # RELEASE files point to other application tops 9 | include $(TOP)/configure/RELEASE 10 | -include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common 11 | ifdef T_A 12 | -include $(TOP)/configure/RELEASE.Common.$(T_A) 13 | -include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) 14 | endif 15 | 16 | CONFIG = $(RULES)/configure 17 | include $(CONFIG)/CONFIG 18 | 19 | # Override the Base definition: 20 | INSTALL_LOCATION = $(TOP) 21 | 22 | # CONFIG_SITE files contain other build configuration settings 23 | include $(TOP)/configure/CONFIG_SITE 24 | -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common 25 | ifdef T_A 26 | -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) 27 | -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) 28 | endif 29 | 30 | -------------------------------------------------------------------------------- /configure/CONFIG_SITE: -------------------------------------------------------------------------------- 1 | # CONFIG_SITE 2 | 3 | # Make any application-specific changes to the EPICS build 4 | # configuration variables in this file. 5 | # 6 | # Host/target specific settings can be specified in files named 7 | # CONFIG_SITE.$(EPICS_HOST_ARCH).Common 8 | # CONFIG_SITE.Common.$(T_A) 9 | # CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) 10 | 11 | # CHECK_RELEASE controls the consistency checking of the support 12 | # applications pointed to by the RELEASE* files. 13 | # Normally CHECK_RELEASE should be set to YES. 14 | # Set CHECK_RELEASE to NO to disable checking completely. 15 | # Set CHECK_RELEASE to WARN to perform consistency checking but 16 | # continue building even if conflicts are found. 17 | CHECK_RELEASE = YES 18 | 19 | # Set this when you only want to compile this application 20 | # for a subset of the cross-compiled target architectures 21 | # that Base is built for. 22 | #CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32 23 | 24 | # To install files into a location other than $(TOP) define 25 | # INSTALL_LOCATION here. 26 | #INSTALL_LOCATION= 27 | 28 | # Set this when the IOC and build host use different paths 29 | # to the install location. This may be needed to boot from 30 | # a Microsoft FTP server say, or on some NFS configurations. 31 | #IOCS_APPL_TOP = 32 | 33 | # For application debugging purposes, override the HOST_OPT and/ 34 | # or CROSS_OPT settings from base/configure/CONFIG_SITE 35 | #HOST_OPT = NO 36 | #CROSS_OPT = NO 37 | 38 | # These allow developers to override the CONFIG_SITE variable 39 | # settings without having to modify the configure/CONFIG_SITE 40 | # file itself. 41 | -include $(TOP)/../CONFIG_SITE.local 42 | -include $(TOP)/../configure/CONFIG_SITE.local 43 | -include $(TOP)/configure/CONFIG_SITE.local 44 | -include $(TOP)/configure/CONFIG_SITE.local.$(OS_CLASS) 45 | -include $(TOP)/configure/CONFIG_SITE.local.$(EPICS_HOST_ARCH) 46 | -include $(TOP)/configure/CONFIG_SITE.local.$(EPICS_HOST_ARCH).$(T_A) 47 | -------------------------------------------------------------------------------- /configure/CONFIG_SITE.local: -------------------------------------------------------------------------------- 1 | # Enables support for long ints 2 | SUPPORT_LINT=NO 3 | -------------------------------------------------------------------------------- /configure/CONFIG_SITE.local.darwin-aarch64: -------------------------------------------------------------------------------- 1 | #Support long int on Mac 2 | SUPPORT_LINT = YES 3 | -------------------------------------------------------------------------------- /configure/CONFIG_SITE.local.darwin-x86: -------------------------------------------------------------------------------- 1 | #Support long int on Mac 2 | SUPPORT_LINT = YES 3 | -------------------------------------------------------------------------------- /configure/CONFIG_SITE.local.linux-x86_64: -------------------------------------------------------------------------------- 1 | #Support long int on Linux 64 2 | SUPPORT_LINT = YES 3 | -------------------------------------------------------------------------------- /configure/Makefile: -------------------------------------------------------------------------------- 1 | TOP=.. 2 | 3 | include $(TOP)/configure/CONFIG 4 | 5 | TARGETS = $(CONFIG_TARGETS) 6 | CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) 7 | 8 | include $(TOP)/configure/RULES 9 | -------------------------------------------------------------------------------- /configure/RELEASE: -------------------------------------------------------------------------------- 1 | # RELEASE - Location of external support modules 2 | 3 | -include $(TOP)/../RELEASE.local 4 | -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local 5 | -include $(TOP)/configure/RELEASE.local 6 | -------------------------------------------------------------------------------- /configure/RULES: -------------------------------------------------------------------------------- 1 | # RULES 2 | 3 | include $(CONFIG)/RULES 4 | 5 | # Library should be rebuilt because LIBOBJS may have changed. 6 | $(LIBNAME): ../Makefile 7 | -------------------------------------------------------------------------------- /configure/RULES.ioc: -------------------------------------------------------------------------------- 1 | #RULES.ioc 2 | include $(CONFIG)/RULES.ioc 3 | -------------------------------------------------------------------------------- /configure/RULES_DIRS: -------------------------------------------------------------------------------- 1 | #RULES_DIRS 2 | include $(CONFIG)/RULES_DIRS 3 | -------------------------------------------------------------------------------- /configure/RULES_TOP: -------------------------------------------------------------------------------- 1 | #RULES_TOP 2 | include $(CONFIG)/RULES_TOP 3 | 4 | -------------------------------------------------------------------------------- /ether_ipApp/Makefile: -------------------------------------------------------------------------------- 1 | TOP = .. 2 | 3 | include $(TOP)/configure/CONFIG 4 | 5 | DIRS := src 6 | 7 | include $(TOP)/configure/RULES_DIRS 8 | -------------------------------------------------------------------------------- /ether_ipApp/doc/CIP_sniff.txt: -------------------------------------------------------------------------------- 1 | Sniffed from communication between RSLogix and PLC 2 | when "online": 3 | 4 | 0A 02 20 02 24 01 - multi request 5 | 6 | 06 00 - 6 items 7 | 8 | 0E00 = 14 - offset to service #1 9 | 1800 = 24 - offset to service #2 10 | 2200 = 34 - offset to service #3 11 | 3600 = 54 - offset to service #4 12 | 4200 = 66 - offset to service #5 13 | 4C00 = 76 - offset to service #6 14 | 15 | Service 1 @ count + 14 16 | 03 02 20 01 24 01 - GetAttributeList Class 1, instance 1 17 | 01 00 05 00 - Get attribute 5 18 | 19 | Service 2 20 | 03 02 20 69 24 00 - ... Class 0x69 21 | 01 00 0B 00 22 | 23 | 03 02 20 AC 24 01 - ... Class 0xAC 24 | 06 00 25 | 01 00 02 00 03 00 - Attribs 1, 2, 3, 5, 7, 9 26 | 05 00 07 00 09 00 27 | 28 | 03 02 20 8E 24 01 29 | 02 00 30 | 03 00 08 00 31 | 32 | 03 02 20 69 24 00 33 | 01 00 09 00 34 | 35 | 03 03 20 69 25 00 04 D8 36 | 01 00 04 00 37 | -------------------------------------------------------------------------------- /ether_ipApp/doc/bool.txt: -------------------------------------------------------------------------------- 1 | 2 | Some people have run into problems with binary records and BOOL tags. 3 | 4 | Symptom: 5 | The record can write a '1' to turn something on the PLC 'on', 6 | but writing a '0' to turn something 'off' seems to have no effect. 7 | 8 | Reason/explanation/underlying problem: 9 | The support for binary records (BO, MBBO, MBBODIRECT, ...) addresses bits. 10 | 11 | Problem 1: 12 | The protocol doesn't really distinguish between writing bits in a BOOL 13 | array and bits of a DINT. 14 | When writing BOOL[32], that's actually the same as writing DINT[1].0. 15 | Both refer to the 33rd bit (counting from 0) in some memory structure. 16 | So complcation #1 arises when your BO record writes to 'fred[2]', 17 | and you think that's DINT array 'fred', third element, 18 | but the driver will actually deal with the third bit of the first element. 19 | 20 | Problem 2: 21 | When we write a '1' to a bit in a BOOL array, 22 | that bit will read back as '1' 23 | (assuming that nothing else on the PLC changes it). 24 | The driver is specifically written to handle that case: 25 | The BO records will only touch bits. 26 | 27 | When you, however, write '1' to a scalar BOOL tag, 28 | it can read back as '255'. 29 | Which is OK, both values are considered 'on' by PLC 30 | logic. 31 | But if you now write a '0' to that tag, 32 | the driver will only write a zero bit, 33 | using the last readback. 34 | Meaning: it ends up writing 254 = 255 - bit 0, 35 | and the PLC will still consider that 'on'. 36 | 37 | 38 | --> Use binary records with BOOL arrays. 39 | That works, and is more efficient than single BOOL tags. 40 | If you have to deal with a single BOOL tag, 41 | remember that this really looks like an SINT with 42 | possible values 0 and 255, 43 | and use an AI or AO record for it. 44 | Maybe an AO record linked to a BO record. 45 | 46 | 47 | -------------------------------------------------------------------------------- /ether_ipApp/doc/stats.txt: -------------------------------------------------------------------------------- 1 | -*- outline -*- 2 | 3 | This is mostly a log of email messages that I sent out 4 | while testing the protocol and developling the driver.... 5 | 6 | * Test 1 7 | ======== 8 | 9 | On same hub: 10 | - PC (100baseT) 11 | - ControlLogix PLC, 1756-ENET interface (10baseT) 12 | "System Overhead Time Slice" set to 10% 13 | 14 | - For the measurements indicated with (SW), 15 | the PLC and the PC were connected to a switch. 16 | 17 | LastScanTime: 140...200 18 | 19 | Read values, single tag: 20 | REAL : 90.909091 values/sec, 11.000000 msec/value 21 | REAL :136.986301 values/sec, 7.300000 msec/value (SW) 22 | 140.845070 values/sec, 7.100000 msec/value (Connected) 23 | 24 | BOOL : -same- 25 | DINT : -same- 26 | PV : -same- 27 | PVs[2] : -same- 28 | PVs[5] : -same- 29 | PVs[8] : 58.823529 values/sec, 17.000000 msec/value 30 | PVs[10] : 45.454545 values/sec, 22.000000 msec/value 31 | PVs[20] : 45.454545 values/sec, 22.000000 msec/value 32 | PVs[30] : 37.037037 values/sec, 27.000000 msec/value 33 | PVs[40] : 30.303030 values/sec, 33.000000 msec/value 34 | 33.333333 values/sec, 30.000000 msec/value (SW) 35 | 33.333333 values/sec, 30.000000 msec/value (Connected) 36 | 37 | BITS[352]: 90.909091 values/sec, 11.000000 msec/value 38 | BITS[512]: 83.333333 values/sec, 12.000000 msec/value 39 | 40 | PV is user-defined type: 41 | REAL .VAL (C float) 42 | DINT .STAT (32 bit) 43 | DINT .TIME 44 | 45 | PVs[#] is similar, but each entry is an array of # elements: 46 | REAL .VAL[#] 47 | DINT .STAT[#] 48 | DINT .TIME[#] 49 | 50 | -> PVs[40] combines 120 numbers 51 | 52 | BITS[3]: BOOL[#], e.g. 512 booleans packed as 64 bytes 53 | 54 | 55 | When going "online" with RSLogix5000 at the same time, 56 | the 90.909091 values/sec, 11.000000 msec/value 57 | turn into 83.333333 values/sec, 12.000000 msec/value 58 | 59 | There is hardly any load on PC, the hub shows up to 3% 60 | -> this must be the limitation of the PLC/ENET for doing 61 | communications. 62 | 63 | Indeed: change "System Overhead Time Slice" set to 50% 64 | 65 | LastScanTime: 140...200 66 | 67 | REAL : 142.857143 values/sec, 7.000000 msec/value 68 | PVs[40] : 35.714286 values/sec, 28.000000 msec/value 69 | BITS[352]: 100.000000 values/sec, 10.000000 msec/value 70 | 71 | 72 | 73 | * Test 2 74 | ======== 75 | 76 | Hello: 77 | 78 | I have some updates on the Ethernet connection to ControlLogix. 79 | I won't explore the protocol further but start developement 80 | of an EPICS driver now. 81 | This leads to suggestions and questions on how to organize the data 82 | in the PLC for better communication with an EPICS IOC. 83 | 84 | The protocol 85 | ------------------ 86 | 87 | The name has changed: ControlNet-over-Ethernet has become EtherNet/IP. 88 | 89 | Tim McAdams, Technology Transfer and Services, Rockwell Automation 90 | confirmed that 91 | "EtherNet/IP is THE Ethernet protocol that the ControlLogix does and 92 | will support. RSLinx uses EtherNet/IP to communicate with ControlLogix, 93 | but also supports an older proprietary Ethernet protocol to communicate 94 | with PLC5 and SLC5/05 even though they also now support EtherNet/IP." 95 | 96 | So using EtherNet/IP is not some hack into the PLC 97 | but it's officially OK to do so. 98 | 99 | It's also confirmed by McAdams that ControlLogix does 100 | not, yet, support other parts of the protocol that 101 | would sent scheduled updates or updates on change. 102 | ControlLogix might support this in the future, 103 | the other PLCs won't. All it does now is "unconnected" 104 | and "Class 3 connected" messaging. 105 | 106 | I found a way to use the "Class 3 connected" 107 | method in EtherNet/IP. 108 | For non-Ethernet ControlNet, this reserves bandwidth 109 | and guaranties delivery (like TCP does anyway). 110 | For EtherNet/IP, it basically informs the PLC what to expect 111 | and the header for following packages is a few bytes smaller. 112 | But one can run into timeout problems this way, 113 | and the effort seems not to be worth it: 114 | 115 | Read single REAL value: 116 | original : 90.909091 values/sec, 11.000000 msec/value 117 | switched: 136.986301 values/sec, 7.300000 msec/value 118 | "Class 3": 140.845070 values/sec, 7.100000 msec/value 119 | (changed the office. PC and PLC used to be on a hub, 120 | now they are connected to a switch which gives slightly better results) 121 | 122 | So the following tests are done "unconnected" again. 123 | Not to be confused: 124 | It's still keeping the same socket open and registering 125 | the session only once, so the rates shown are for data transfer only, 126 | without initial connection/registration overhead. 127 | 128 | Test Results 129 | ------------ 130 | 131 | -- There is a tradeoff between fast and nice-to-use. -- 132 | 133 | For the SNS PLCs I assume 150 analogs and 350 binary channels. 134 | While older PLCs used "N7:10" 135 | which gives no clue about the meaning of the value, 136 | ControlLogix uses tags (<= 40 char), arrays, user-defined structures. 137 | 138 | We still have to document the meaning of "TSenWatIn", 139 | but I think this is nicer to use for 140 | "Temperature Sensor, Water at Inlet" than N7:10. 141 | It might also help to avoid errors: 142 | Misspelling it as TSensWatInn" gives an "invalid tag", 143 | using N7:11 would run fine but yield garbage. 144 | 145 | 1) Single transfer, readable tag 146 | -------------------------------- 147 | 148 | Over EtherNet/IP I can read simple tags like: 149 | "TempSensor1_Value" 150 | as well as drill down structure/array tags: 151 | "Sensors.Temperature[2].value" 152 | 153 | This makes for readable code, 154 | the tags could conform to some naming convention. 155 | 156 | Reading a single REAL value like this takes 7 ms. 157 | The same for a single BOOL value. 158 | 159 | Reading the ~350 binary values for the SNS water skid 160 | would take almost 3 secs. 161 | Each single one arrives within 7ms, but the overall 162 | time might be too slow. 163 | 164 | 2) Multi-request transfer, readable tags 165 | ---------------------------------------- 166 | Multiple read requests can be sent in one package. 167 | The package size for the request is limited to ~510 bytes, 168 | so is the response package size. 169 | When the tag names are long, fewer will fit a request. 170 | If the result contains arrays/structures, fewer will fit 171 | into the response. 172 | -> about 30 4-char REAL tags, 173 | 15 tags of the form "analog_10Hz.V21" 174 | 175 | When reading the individual elements of a structure 176 | like this, the per-value speed is increased over the 177 | previous example. 178 | 179 | structure analog_10Hz { REAL V1, REAL V2, ...., REAL V29 } 180 | Read as { analog_10Hz.V0, ..., analog_10Hz.V14 } 181 | (cannot read more than 15 items at once): 182 | 47.62 runs/sec, 21.00 msec/run -> 714.29 values/sec, 1.40 msec/value 183 | 184 | The overall latency is increased: 185 | The single value took 7ms. 186 | When requested as part of a multi-request, 187 | we have to wait for all the values (21ms). 188 | 189 | The 150 analogs could be read in ~0.21sec. 190 | Good enough? 191 | 192 | 3) Arrays 193 | --------- 194 | Arrays are still limited (size of response packet), 195 | but in an array the values are packet more tightly 196 | -> more values than in a multi-request. 197 | 198 | Transmitting a single tag that is a REAL[30]: 199 | 106.38 runs/sec, 9.40 msec/run -> 3191.49 values/sec, 0.31 msec/value 200 | 201 | Better: the 150 analogs, transfered as 5 REAL[30] arrays, 202 | would take 0.047secs 203 | 204 | They could be organized tag-wise like this: 205 | analog_10Hz = REAL[30] : array of analogs to be scanned @ 10Hz 206 | ... 207 | But the meaning of analog_10Hz[2] is unknown, 208 | we'd be back to the N7:10 addressing. 209 | 210 | Do we have to do this, pick speed over readability for analogs? 211 | 212 | For Binary arrays, the bits are actually packed: 213 | Array of BOOL[352] = 44 bytes, treated as 11 DINT: 214 | 130.43 runs/sec, 7.67 msec/run ->45913.04 values/sec, 0.02 msec/value 215 | 216 | MUCH BETTER: All the binaries for an SNS PLC can be transmitted 217 | in ~8msec! 218 | Again, the meaning of the individual bits has to be documented, 219 | but I think we have no other choice in this case. 220 | 221 | 4) Structures 222 | ------------- 223 | A structure can be transfered element-by-element. 224 | -> case 1 or 2. Neat but not fast. 225 | 226 | Structures can also be read as a whole: 227 | Structure "analog_10Hz" with 30 REAL elements, 228 | read as whole tag "analog_10Hz" (all 30 items): 229 | 75.76 runs/sec, 13.20 msec/run -> 2272.73 values/sec, 0.44 msec/value 230 | 231 | This provides 232 | + better performance than reading the individual structure elements 233 | + the PLC code looks nice because the elements can have 234 | meaningful names, unlike array elements 235 | --- the structure is not exposed: 236 | The result is raw data. 237 | Over EtherNet/IP I cannot see that e.g. the third element 238 | in the structure is a "REAL" called "FacilityWaterTemperature". 239 | 240 | I asked my Rockwell contact for some help about this, 241 | but is seems that a structure transfer would again 242 | mean that we have to document the structure content 243 | in a detailed manner, so overall it's similar to the array case. 244 | 245 | ------------------------------------------------------------------ 246 | 247 | So my conclusion for now is that I'll support 248 | a) Access to individual REALs and BOOLs. 249 | Requests will be combined as much as possible. 250 | Structure elements can be accessed like this, 251 | but it also increases the tag length 252 | -> fewer combined requests 253 | 254 | This is hopefully good enough for most analogs 255 | 256 | b) Access to arrays 257 | The array will be read as a whole. 258 | We have to document the meaning of each array element. 259 | 260 | This is necessary for most binaries 261 | and provides a tremendous jump in speed. 262 | 263 | Reading structures as a whole will not be supported. 264 | 265 | 266 | * Test 3 267 | ======== 268 | 269 | Read 20 BOOL tags, one-by-one in multi request 270 | 271 | SentUnconnected: 272 | 33.33 runs/sec, 30.00 msec/run -> 666.67 values/sec, 1.50 msec/value 273 | 274 | Connected: 275 | 34.09 runs/sec, 29.33 msec/run -> 681.82 values/sec, 1.47 msec/value 276 | 277 | 278 | Again: Not worth the effort of possible timeouts 279 | when there is no activity within the times 280 | specified in Forward_Open?! 281 | 282 | * Test 4 283 | ======== 284 | 285 | First test with driver running on an IOC under EPICS. 286 | 287 | -> dbnr 288 | 0130 ai 289 | 0001 ao 290 | 0354 bi 291 | 0005 bo 292 | 0001 calc 293 | 0002 mbbi 294 | 0002 mbbiDirect 295 | 0001 mbbo 296 | 0001 mbboDirect 297 | 298 | Most important: 299 | 300 | 352 BI records, reading the bits of a BOOL[352]. 301 | Driver scans at 10Hz, records receive I/O Intr. 302 | 303 | 7 AI records for displaying driver statistics 304 | about the PLC and the BI records. 305 | 306 | 120 AI records, reading 3 x REAL[40]. 307 | Driver scans at 1 Hz, records scan at 1 Hz. 308 | 309 | Histogram of BOOL[352] transfer: 310 | Transfer time hits 311 | ------------------ 312 | 0.006 40 313 | 0.007 13617 314 | 0.008 17542 315 | 0.009 5762 316 | 0.01 3101 317 | 0.011 2522 318 | 0.012 1922 319 | 0.013 1233 320 | 0.014 602 321 | 0.015 141 322 | 0.016 38 323 | 0.017 14 324 | 0.018 9 325 | 0.019 3 326 | 0.02 1 327 | 328 | I increased the vxWorks tick counter to get better statistics: 329 | sysClkRateSet 1000 (default is 60). 330 | 331 | After running for 1h, 15 min, the histogram of the BOOL[352] transfers 332 | shows that most transactions take 8ms, matching the previous protocol tests. 333 | Sometimes, though, it takes up to 20ms to get the BOOL array into the IOC. 334 | Why? General network traffic? Also: 335 | I've seen before that the PLC doesn't suffer under communication. 336 | Its time to run over the ladder logix stays the same. I guess this is 337 | because it has a dedicated communication time slice. 338 | So if the IOC asks for a value and the PLC is not ready to respond 339 | since the communication time is over, the transfer is handled in the 340 | next time slice. 341 | 342 | As for the analog values: 343 | They are transferred as three REAL[30] arrays. 344 | Each array arrives after about 9ms. 345 | The total transfer time for the three REAL[30] is: 346 | min 16 ms 347 | max 29 ms 348 | usual 18 ms 349 | 350 | (Note that most of the time it's 18ms, not 3x9ms. 351 | Two of those REAL[30] requests are actually combined in the driver 352 | because they fit the 500byte buffer limit.) 353 | 354 | Addendum: 355 | After one week, the max. scan time for the BOOLs[352] was 0.15secs, 356 | no errors. -------------------------------------------------------------------------------- /ether_ipApp/src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | TOP = ../.. 3 | 4 | include $(TOP)/configure/CONFIG 5 | #---------------------------------------- 6 | # ADD MACRO DEFINITIONS AFTER THIS LINE 7 | 8 | # On Darwin, got errors about unused assignment in recGblSetSevr 9 | USR_CFLAGS_Darwin += -Wno-unused-value 10 | 11 | # On WIN32, have to add socket library 12 | # to build the test program 13 | SYS_PROD_LIBS_WIN32 = wsock32 14 | 15 | ifdef BASE_3_15 16 | devEtherIP_CFLAGS += -DBUILD_LONG_STRING_SUPPORT 17 | USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET 18 | 19 | # Since base 3_15, on Linux 64 and Mac, support LINT 20 | ifeq ($(SUPPORT_LINT),YES) 21 | USR_CFLAGS += -DSUPPORT_LINT 22 | USR_CPPFLAGS += -DSUPPORT_LINT 23 | ETHER_IP_DBD = ether_ip-lint.dbd 24 | else 25 | ETHER_IP_DBD = ether_ip-lsio.dbd 26 | endif 27 | 28 | else 29 | ETHER_IP_DBD = ether_ip-3-14.dbd 30 | endif 31 | 32 | PROD_HOST += ether_ip_test 33 | ether_ip_test_SRCS += ether_ip_test.c 34 | ether_ip_test_LIBS += Com 35 | ether_ip_test_SYS_LIBS_solaris += socket 36 | ether_ip_test_SYS_LIBS_solaris += nsl 37 | 38 | DBD = ether_ip.dbd 39 | 40 | LIBRARY_IOC = ether_ip 41 | 42 | INC += eip_bool.h 43 | INC += dl_list.h 44 | INC += ether_ip.h 45 | INC += drvEtherIP.h 46 | 47 | # create munch file for dynamic loading will install in 48 | PROD_IOC_vxWorks += ether_ipLib 49 | 50 | LIB_LIBS += $(EPICS_BASE_IOC_LIBS) 51 | DLL_LIBS = ca Com 52 | SYS_DLL_LIBS = wsock32 53 | ether_ip_RCS_WIN32 = ether_ip.rc 54 | 55 | ether_ip_SRCS += dl_list.c 56 | ether_ip_SRCS += ether_ip.c 57 | ether_ip_SRCS += drvEtherIP.c 58 | ether_ip_SRCS += devEtherIP.c 59 | ether_ip_SRCS += drvEtherIPRegister.cpp 60 | 61 | # The PROD_IOC_vxWorks is built from the same srcs and must include 62 | # the registry routines. 63 | ether_ipLib_SRCS+=$(ether_ip_SRCS) 64 | ether_ipLib_SRCS+= ether_ip_registerRecordDeviceDriver.cpp 65 | 66 | include $(TOP)/configure/RULES 67 | #---------------------------------------- 68 | # ADD RULES AFTER THIS LINE 69 | 70 | ether_ip.dbd$(DEP): ../Makefile 71 | @$(RM) $@ 72 | @echo $(COMMON_DIR)/ether_ip.dbd:> $@ 73 | 74 | $(COMMON_DIR)/ether_ip.dbd: ../$(ETHER_IP_DBD) 75 | @$(RM) $@ 76 | $(CP) $< $@ 77 | -------------------------------------------------------------------------------- /ether_ipApp/src/dl_list.c: -------------------------------------------------------------------------------- 1 | #include "dl_list.h" 2 | 3 | /* Remove head node from list (doesn't free node) 4 | * Returns 0 for empty list 5 | */ 6 | void *DLL_decap (DL_List *list) 7 | { 8 | DLL_Node *node = list->first; 9 | if (! node) 10 | return 0; 11 | 12 | list->first = node->next; 13 | if (list->last == node) 14 | list->last = 0; 15 | return node; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /ether_ipApp/src/dl_list.h: -------------------------------------------------------------------------------- 1 | #ifndef DLL_init 2 | /* Double-linked list */ 3 | 4 | typedef struct __dll_node DLL_Node; 5 | struct __dll_node 6 | { 7 | DLL_Node *prev; 8 | DLL_Node *next; 9 | }; 10 | 11 | typedef struct 12 | { 13 | DLL_Node *first; 14 | DLL_Node *last; 15 | } DL_List; 16 | 17 | #define DLL_init(list) \ 18 | { \ 19 | (list)->first = 0; \ 20 | (list)->last = 0; \ 21 | } 22 | 23 | #define DLL_first(T, list) \ 24 | (T*)(list)->first 25 | 26 | #define DLL_last(T, list) \ 27 | (T*)(list)->last 28 | 29 | #define DLL_next(T, node) \ 30 | (T*)((DLL_Node*)(node))->next 31 | 32 | #define DLL_prev(T, node) \ 33 | (T*)((DLL_Node*)(node))->prev 34 | 35 | /* Add single node to end of list */ 36 | #define DLL_append(list, node) \ 37 | { \ 38 | if ((list)->first == 0) \ 39 | (list)->first = (DLL_Node*)(node); \ 40 | ((DLL_Node*)(node))->prev = (list)->last; \ 41 | ((DLL_Node*)(node))->next = 0; \ 42 | if ((list)->last) \ 43 | (list)->last->next = (DLL_Node*)(node); \ 44 | (list)->last = (DLL_Node*)(node); \ 45 | } 46 | 47 | /* Remove node from list (doesn't free node) */ 48 | #define DLL_unlink(list, node) \ 49 | { \ 50 | if ((list)->first == (DLL_Node*)(node)) \ 51 | (list)->first = ((DLL_Node*)(node))->next; \ 52 | else \ 53 | ((DLL_Node*)(node))->prev->next = ((DLL_Node*)(node))->next; \ 54 | if ((list)->last == (DLL_Node*)(node)) \ 55 | (list)->last =((DLL_Node*)(node))->prev; \ 56 | else \ 57 | ((DLL_Node*)(node))->next->prev = ((DLL_Node*)(node))->prev; \ 58 | ((DLL_Node*)(node))->prev = 0; \ 59 | ((DLL_Node*)(node))->next = 0; \ 60 | } 61 | 62 | /* Remove head node from list (doesn't free node) 63 | * Returns 0 for empty list 64 | */ 65 | void *DLL_decap (DL_List *list); 66 | 67 | #endif 68 | 69 | 70 | -------------------------------------------------------------------------------- /ether_ipApp/src/drvEtherIP.h: -------------------------------------------------------------------------------- 1 | /* drvEtherIP 2 | * 3 | * IOC driver that uses ether_ip routines, 4 | * keeping lists of PLCs and tags and scanlists etc. 5 | * 6 | * kasemirk@ornl.gov 7 | */ 8 | 9 | #ifndef ETHERIP_MAYOR 10 | 11 | #include "epicsMutex.h" 12 | #include "epicsEvent.h" 13 | #include "epicsThread.h" 14 | #include "epicsTime.h" 15 | 16 | #include "ether_ip.h" 17 | #include "dl_list.h" 18 | 19 | #define ETHERIP_MAYOR 3 20 | #define ETHERIP_MINOR 7 21 | 22 | /* For timing */ 23 | #define EIP_MIN_TIMEOUT 0.1 /* second */ 24 | #define EIP_MIN_CONN_TIMEOUT 1.0 /* second */ 25 | 26 | /* TCP port */ 27 | #define ETHERIP_PORT 0xAF12 28 | 29 | /* TCP timeout in millisec for connection and readback */ 30 | extern int EIP_TIMEOUT; 31 | 32 | typedef struct __TagInfo TagInfo; /* forwards */ 33 | typedef struct __ScanList ScanList; 34 | typedef struct __PLC PLC; 35 | 36 | /* THE singleton main structure for this driver 37 | * Note that each PLC entry has it's own lock 38 | * for the scanlists & statistics. 39 | * Each PLC's scan task uses that per-PLC lock, 40 | * calls to loop/add/list PLCs also use this 41 | * more global lock. 42 | */ 43 | typedef struct 44 | { 45 | DL_List PLCs; /* List of PLC structs */ 46 | epicsMutexId lock; 47 | } DrvEtherIP_Private; 48 | 49 | /* PLCInfo: 50 | * Per-PLC information 51 | * Generated with call to drvEtherIP_define_PLC 52 | * in IOC startup file. 53 | * 54 | * Holds 55 | * - EIPConnection for ether_ip protocol routines 56 | * - ScanList for this PLC, filled by device support 57 | */ 58 | struct __PLC 59 | { 60 | DLL_Node node; 61 | epicsMutexId lock; 62 | char *name; /* symbolic name, used to identify PLC */ 63 | char *ip_addr; /* IP or DNS name that IOC knows */ 64 | int slot; /* slot in ControlLogix Backplane: 0, ... */ 65 | size_t plc_errors; /* # of communication errors */ 66 | size_t slow_scans; /* Count: scan task is getting late */ 67 | EIPConnection *connection; 68 | DL_List scanlists; /* List of struct ScanList */ 69 | epicsThreadId scan_task_id; 70 | }; 71 | 72 | /* ScanList: 73 | * A list of TagInfos, 74 | * to be scanned at the same rate 75 | */ 76 | struct __ScanList 77 | { 78 | DLL_Node node; 79 | PLC *plc; /* PLC to which this Scanlist belongs */ 80 | eip_bool enabled; 81 | double period; /* scan period [secs] */ 82 | size_t list_errors; /* # of communication errors */ 83 | size_t sched_errors; /* # of scheduling errors */ 84 | epicsTimeStamp scan_time; /* stamp of last run time */ 85 | epicsTimeStamp scheduled_time; /* stamp for next run time */ 86 | double min_scan_time; /* statistics: scan time in seconds */ 87 | double max_scan_time; /* minimum, maximum, */ 88 | double last_scan_time; /* and most recent scan */ 89 | DL_List taginfos; /* List of struct TagInfo */ 90 | }; 91 | 92 | typedef void (*EIPCallback) (void *arg); 93 | typedef struct 94 | { 95 | DLL_Node node; 96 | EIPCallback callback; /* called for each value */ 97 | void *arg; 98 | } TagCallback; 99 | 100 | /* TagInfo: 101 | * Information for a single tag: 102 | * Actual tag, how many elements are requested, 103 | * size for request/response, ... 104 | * 105 | * TagInfos are held in list. 106 | * Naming in here: "tag" is a single TagInfo, 107 | * "tags" is used when the whole list is meant. 108 | * 109 | * A cip_request_size of 0 will cause this tag 110 | * to be skipped in read/write operations. 111 | * 112 | * See Locking info in drvEtherIP.c for details 113 | * on locking as well as cip_request/response size 114 | * and the do_write flag. 115 | */ 116 | struct __TagInfo 117 | { 118 | DLL_Node node; 119 | ScanList *scanlist; /* list this tag in on */ 120 | char *string_tag; /* tag as text */ 121 | ParsedTag *tag; /* tag, compiled */ 122 | size_t elements; /* array elements to read (or 1) */ 123 | size_t cip_r_request_size; /* byte-size of read request */ 124 | size_t cip_r_response_size;/* byte-size of read response */ 125 | size_t cip_w_request_size; /* byte-size of write request */ 126 | size_t cip_w_response_size;/* byte-size of write response */ 127 | epicsMutexId data_lock; /* see "locking" in drvEtherIP.c */ 128 | size_t data_size; /* total size of data buffer */ 129 | size_t valid_data_size; /* used portion of data, 0 for "invalid" */ 130 | eip_bool do_write; /* set by device, reset by driver */ 131 | eip_bool is_writing; /* driver copy of do_write for cycle */ 132 | CN_USINT *data; /* CIP data (type, raw data), with buffer capacity of data_size */ 133 | double transfer_time; /* time needed for last transfer */ 134 | DL_List callbacks; /* TagCallbacks for new values&write done */ 135 | }; 136 | 137 | #ifdef __cplusplus 138 | extern "C" { 139 | #endif 140 | 141 | extern double drvEtherIP_default_rate; 142 | 143 | void drvEtherIP_help(); 144 | 145 | int drvEtherIP_initialized(); 146 | 147 | void drvEtherIP_init(); 148 | 149 | long drvEtherIP_report(int level); 150 | 151 | void drvEtherIP_dump(); 152 | 153 | void drvEtherIP_reset_statistics(); 154 | 155 | eip_bool drvEtherIP_define_PLC(const char *PLC_name, 156 | const char *ip_addr, int slot); 157 | 158 | PLC *drvEtherIP_find_PLC(const char *PLC_name); 159 | 160 | TagInfo *drvEtherIP_add_tag(PLC *plc, double period, 161 | const char *string_tag, size_t elements); 162 | /* Register callbacks for "received new data" and "finished the write". 163 | * Note: The data is already locked (data_lock taken) 164 | * when the callback is called! 165 | */ 166 | void drvEtherIP_add_callback(PLC *plc, TagInfo *tag, 167 | EIPCallback callback, void *arg); 168 | void drvEtherIP_remove_callback(PLC *plc, TagInfo *tag, 169 | EIPCallback callback, void *arg); 170 | 171 | int drvEtherIP_restart(); 172 | 173 | /* Command-line communication test, 174 | * not used by the driver */ 175 | int drvEtherIP_read_tag(const char *ip_addr, 176 | int slot, 177 | const char *tag_name, 178 | int elements, 179 | int timeout); 180 | 181 | void drvEtherIP_Register(); 182 | 183 | #ifdef __cplusplus 184 | } 185 | #endif 186 | 187 | #endif 188 | 189 | -------------------------------------------------------------------------------- /ether_ipApp/src/drvEtherIPRegister.cpp: -------------------------------------------------------------------------------- 1 | /*drvEtherIPRegister.cpp */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "iocsh.h" 10 | #include "epicsExport.h" 11 | #include "drvEtherIP.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif /* __cplusplus */ 16 | 17 | static const iocshArg drvEtherIP_default_rateArg0 = {"value", iocshArgDouble}; 18 | static const iocshArg *const drvEtherIP_default_rateArgs[1] = {&drvEtherIP_default_rateArg0}; 19 | static const iocshFuncDef drvEtherIP_default_rateDef = {"drvEtherIP_default_rate", 1, drvEtherIP_default_rateArgs}; 20 | static void drvEtherIP_default_rateCall(const iocshArgBuf * args) { 21 | drvEtherIP_default_rate = args[0].dval; 22 | } 23 | 24 | static const iocshArg EIP_verbosityArg0 = {"value", iocshArgInt}; 25 | static const iocshArg *const EIP_verbosityArgs[1] = {&EIP_verbosityArg0}; 26 | static const iocshFuncDef EIP_verbosityDef = {"EIP_verbosity", 1, EIP_verbosityArgs}; 27 | static void EIP_verbosityCall(const iocshArgBuf * args) { 28 | EIP_verbosity = args[0].ival; 29 | } 30 | 31 | static const iocshArg EIP_timeoutArg0 = {"value", iocshArgInt}; 32 | static const iocshArg *const EIP_timeoutArgs[1] = {&EIP_timeoutArg0}; 33 | static const iocshFuncDef EIP_timeoutDef = {"EIP_timeout", 1, EIP_timeoutArgs}; 34 | static void EIP_timeoutCall(const iocshArgBuf * args) { 35 | EIP_TIMEOUT = args[0].ival; 36 | } 37 | 38 | static const iocshArg EIP_buffer_limitArg0 = {"bytes", iocshArgInt}; 39 | static const iocshArg *const EIP_buffer_limitArgs[1] = {&EIP_buffer_limitArg0}; 40 | static const iocshFuncDef EIP_buffer_limitDef = {"EIP_buffer_limit", 1, EIP_buffer_limitArgs}; 41 | static void EIP_buffer_limitCall(const iocshArgBuf * args) { 42 | printf("Changing buffer limit from %lu ", 43 | (unsigned long) EIP_buffer_limit); 44 | EIP_buffer_limit = args[0].ival; 45 | printf("to %lu bytes\n", 46 | (unsigned long) EIP_buffer_limit); 47 | } 48 | 49 | static const iocshFuncDef drvEtherIP_helpDef = 50 | {"drvEtherIP_help", 0, 0}; 51 | static void drvEtherIP_helpCall(const iocshArgBuf * args) { 52 | drvEtherIP_help(); 53 | } 54 | 55 | static const iocshFuncDef drvEtherIP_initDef = 56 | {"drvEtherIP_init", 0, 0}; 57 | static void drvEtherIP_initCall(const iocshArgBuf * args) { 58 | drvEtherIP_init(); 59 | } 60 | 61 | static const iocshFuncDef drvEtherIP_restartDef = 62 | {"drvEtherIP_restart", 0, 0}; 63 | static void drvEtherIP_restartCall(const iocshArgBuf * args) { 64 | drvEtherIP_restart(); 65 | } 66 | 67 | static const iocshFuncDef drvEtherIP_dumpDef = 68 | {"drvEtherIP_dump", 0, 0}; 69 | static void drvEtherIP_dumpCall(const iocshArgBuf * args) { 70 | drvEtherIP_dump(); 71 | } 72 | 73 | static const iocshFuncDef drvEtherIP_reset_statisticsDef = 74 | {"drvEtherIP_reset_statistics", 0, 0}; 75 | static void drvEtherIP_reset_statisticsCall(const iocshArgBuf * args) { 76 | drvEtherIP_reset_statistics(); 77 | } 78 | 79 | static const iocshArg drvEtherIP_reportArg0 = {"value", iocshArgInt}; 80 | static const iocshArg *const drvEtherIP_reportArgs[1] = {&drvEtherIP_reportArg0}; 81 | static const iocshFuncDef drvEtherIP_reportDef = {"drvEtherIP_report", 1, drvEtherIP_reportArgs}; 82 | static void drvEtherIP_reportCall(const iocshArgBuf * args) { 83 | drvEtherIP_report(args[0].ival); 84 | } 85 | 86 | static const iocshArg drvEtherIP_define_PLCArg0 = {"plc_name", iocshArgString}; 87 | static const iocshArg drvEtherIP_define_PLCArg1 = {"ip_addr" , iocshArgString}; 88 | static const iocshArg drvEtherIP_define_PLCArg2 = {"slot" , iocshArgInt }; 89 | static const iocshArg * const drvEtherIP_define_PLCArgs[3] = 90 | {&drvEtherIP_define_PLCArg0, &drvEtherIP_define_PLCArg1, &drvEtherIP_define_PLCArg2}; 91 | static const iocshFuncDef drvEtherIP_define_PLCDef = {"drvEtherIP_define_PLC", 3, drvEtherIP_define_PLCArgs}; 92 | static void drvEtherIP_define_PLCCall(const iocshArgBuf * args) { 93 | drvEtherIP_define_PLC(args[0].sval, args[1].sval, args[2].ival); 94 | } 95 | 96 | static const iocshArg drvEtherIP_read_tagArg0 = {"ip_addr" , iocshArgString}; 97 | static const iocshArg drvEtherIP_read_tagArg1 = {"slot" , iocshArgInt }; 98 | static const iocshArg drvEtherIP_read_tagArg2 = {"tag_name", iocshArgString}; 99 | static const iocshArg drvEtherIP_read_tagArg3 = {"elements", iocshArgInt }; 100 | static const iocshArg drvEtherIP_read_tagArg4 = {"timeout" , iocshArgInt }; 101 | static const iocshArg * const drvEtherIP_read_tagArgs[5] = 102 | {&drvEtherIP_read_tagArg0, &drvEtherIP_read_tagArg1, &drvEtherIP_read_tagArg2, 103 | &drvEtherIP_read_tagArg3, &drvEtherIP_read_tagArg4}; 104 | static const iocshFuncDef drvEtherIP_read_tagDef = {"drvEtherIP_read_tag", 5, drvEtherIP_read_tagArgs}; 105 | static void drvEtherIP_read_tagCall(const iocshArgBuf * args) { 106 | drvEtherIP_read_tag(args[0].sval, args[1].ival, args[2].sval, 107 | args[3].ival, args[4].ival); 108 | } 109 | 110 | void drvEtherIP_Register() { 111 | iocshRegister(&drvEtherIP_default_rateDef, drvEtherIP_default_rateCall); 112 | iocshRegister(&EIP_verbosityDef , EIP_verbosityCall); 113 | iocshRegister(&EIP_timeoutDef , EIP_timeoutCall); 114 | iocshRegister(&EIP_buffer_limitDef , EIP_buffer_limitCall); 115 | iocshRegister(&drvEtherIP_helpDef , drvEtherIP_helpCall); 116 | iocshRegister(&drvEtherIP_initDef , drvEtherIP_initCall); 117 | iocshRegister(&drvEtherIP_restartDef , drvEtherIP_restartCall); 118 | iocshRegister(&drvEtherIP_dumpDef , drvEtherIP_dumpCall); 119 | iocshRegister(&drvEtherIP_reset_statisticsDef, drvEtherIP_reset_statisticsCall); 120 | iocshRegister(&drvEtherIP_reportDef , drvEtherIP_reportCall); 121 | iocshRegister(&drvEtherIP_define_PLCDef, drvEtherIP_define_PLCCall); 122 | iocshRegister(&drvEtherIP_read_tagDef , drvEtherIP_read_tagCall); 123 | } 124 | #ifdef __cplusplus 125 | } 126 | #endif /* __cplusplus */ 127 | 128 | epicsExportRegistrar(drvEtherIP_Register); 129 | -------------------------------------------------------------------------------- /ether_ipApp/src/eip_bool.h: -------------------------------------------------------------------------------- 1 | #ifndef EIP_BOOL_H_ 2 | #define EIP_BOOL_H_ 3 | 4 | /* Andrew Johnson (ANL) found an issue with bool/true/false 5 | * and a PPC604-with-Altivec compiler, so we use eip_bool 6 | * and are careful not to redefine 'true'. 7 | */ 8 | typedef int eip_bool; 9 | #ifndef __cplusplus 10 | #ifndef true 11 | #define true 1 12 | #define false 0 13 | #endif 14 | #endif 15 | 16 | #endif /*EIP_BOOL_H_*/ 17 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip-3-14.dbd: -------------------------------------------------------------------------------- 1 | # EtherIP for EPICS 3.14 (no long string, no int64) 2 | 3 | # Device support definitions 4 | device(ai, INST_IO, devAiEtherIP, "EtherIP") 5 | device(bi, INST_IO, devBiEtherIP, "EtherIP") 6 | device(mbbi, INST_IO, devMbbiEtherIP, "EtherIP") 7 | device(mbbiDirect, INST_IO, devMbbiDirectEtherIP, "EtherIP") 8 | device(stringin, INST_IO, devSiEtherIP, "EtherIP") 9 | device(waveform, INST_IO, devWfEtherIP, "EtherIP") 10 | device(ao, INST_IO, devAoEtherIP, "EtherIP") 11 | device(bo, INST_IO, devBoEtherIP, "EtherIP") 12 | device(bo, INST_IO, devBoEtherIPReset, "EtherIPReset") 13 | device(mbbo, INST_IO, devMbboEtherIP, "EtherIP") 14 | device(mbboDirect, INST_IO, devMbboDirectEtherIP, "EtherIP") 15 | device(stringout, INST_IO, devSoEtherIP, "EtherIP") 16 | 17 | # Other registration 18 | driver(drvEtherIP) 19 | registrar(drvEtherIP_Register) 20 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip-lint.dbd: -------------------------------------------------------------------------------- 1 | # EtherIP for EPICS 3.15+ with long string and int64 support 2 | 3 | # Declarations to support dynamic loading 4 | recordtype(ai ) {} 5 | recordtype(int64in ) {} 6 | recordtype(bi ) {} 7 | recordtype(lsi ) {} 8 | recordtype(mbbi ) {} 9 | recordtype(mbbiDirect) {} 10 | recordtype(stringin ) {} 11 | recordtype(waveform ) {} 12 | recordtype(ao ) {} 13 | recordtype(int64out ) {} 14 | recordtype(bo ) {} 15 | recordtype(lso ) {} 16 | recordtype(mbbo ) {} 17 | recordtype(mbboDirect) {} 18 | recordtype(stringout ) {} 19 | 20 | # Device support definitions 21 | device(ai, INST_IO, devAiEtherIP, "EtherIP") 22 | device(int64in, INST_IO, devInt64inEtherIP, "EtherIP") 23 | device(bi, INST_IO, devBiEtherIP, "EtherIP") 24 | device(lsi, INST_IO, devLsiEtherIP, "EtherIP") 25 | device(mbbi, INST_IO, devMbbiEtherIP, "EtherIP") 26 | device(mbbiDirect, INST_IO, devMbbiDirectEtherIP, "EtherIP") 27 | device(stringin, INST_IO, devSiEtherIP, "EtherIP") 28 | device(waveform, INST_IO, devWfEtherIP, "EtherIP") 29 | device(ao, INST_IO, devAoEtherIP, "EtherIP") 30 | device(int64out, INST_IO, devInt64outEtherIP, "EtherIP") 31 | device(bo, INST_IO, devBoEtherIP, "EtherIP") 32 | device(bo, INST_IO, devBoEtherIPReset, "EtherIPReset") 33 | device(lso, INST_IO, devLsoEtherIP, "EtherIP") 34 | device(mbbo, INST_IO, devMbboEtherIP, "EtherIP") 35 | device(mbboDirect, INST_IO, devMbboDirectEtherIP, "EtherIP") 36 | device(stringout, INST_IO, devSoEtherIP, "EtherIP") 37 | 38 | # Other registration 39 | driver(drvEtherIP) 40 | registrar(drvEtherIP_Register) 41 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip-lsio.dbd: -------------------------------------------------------------------------------- 1 | # EtherIP for EPICS 3.15+ with long string support 2 | 3 | # Declarations to support dynamic loading 4 | recordtype(ai ) {} 5 | recordtype(bi ) {} 6 | recordtype(lsi ) {} 7 | recordtype(mbbi ) {} 8 | recordtype(mbbiDirect) {} 9 | recordtype(stringin ) {} 10 | recordtype(waveform ) {} 11 | recordtype(ao ) {} 12 | recordtype(bo ) {} 13 | recordtype(lso ) {} 14 | recordtype(mbbo ) {} 15 | recordtype(mbboDirect) {} 16 | recordtype(stringout ) {} 17 | 18 | # Device support definitions 19 | device(ai, INST_IO, devAiEtherIP, "EtherIP") 20 | device(bi, INST_IO, devBiEtherIP, "EtherIP") 21 | device(lsi, INST_IO, devLsiEtherIP, "EtherIP") 22 | device(mbbi, INST_IO, devMbbiEtherIP, "EtherIP") 23 | device(mbbiDirect, INST_IO, devMbbiDirectEtherIP, "EtherIP") 24 | device(stringin, INST_IO, devSiEtherIP, "EtherIP") 25 | device(waveform, INST_IO, devWfEtherIP, "EtherIP") 26 | device(ao, INST_IO, devAoEtherIP, "EtherIP") 27 | device(bo, INST_IO, devBoEtherIP, "EtherIP") 28 | device(bo, INST_IO, devBoEtherIPReset, "EtherIPReset") 29 | device(lso, INST_IO, devLsoEtherIP, "EtherIP") 30 | device(mbbo, INST_IO, devMbboEtherIP, "EtherIP") 31 | device(mbboDirect, INST_IO, devMbboDirectEtherIP, "EtherIP") 32 | device(stringout, INST_IO, devSoEtherIP, "EtherIP") 33 | 34 | # Other registration 35 | driver(drvEtherIP) 36 | registrar(drvEtherIP_Register) 37 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip.h: -------------------------------------------------------------------------------- 1 | /* ether_ip.h 2 | * 3 | * EtherNet/IP routines for Win32, Unix, vxWorks, and RTEMS. 4 | * 5 | * EtherNet/IP started as "ControlNet over Ethernet" (www.controlnet.org), 6 | * now defined as ODVA's "EtherNet/IP" (www.odva.org) 7 | * 8 | * Docs: "Spec" = ControlNet Spec. version 2.0, Errata 1 9 | * "ENET" = AB Publication 1756-RM005A-EN-E 10 | * 11 | * kasemirk@ornl.gov 12 | */ 13 | 14 | #ifndef NO_EPICS 15 | #include"epicsVersion.h" 16 | #endif 17 | 18 | 19 | /* The socket compatibility stuff is stolen from R3.13 osiSock.h. 20 | * The R3.14 osiSock is a tad different, so we use neither "as is" 21 | * but have it all in here. 22 | */ 23 | /* sys-independ. socket stuff, basically stolen from osiSock.h */ 24 | #ifdef _WIN32 25 | #include 26 | #pragma pack(push, 1) 27 | 28 | #define EIP_SOCKET SOCKET 29 | #define EIP_INVALID_SOCKET INVALID_SOCKET 30 | #define EIP_SOCKERRNO WSAGetLastError() 31 | #define EIP_socket_close(S) closesocket(S) 32 | #define EIP_socket_ioctl(A,B,C) ioctlsocket(A,B,C) 33 | #define EIP_SOCK_EWOULDBLOCK WSAEWOULDBLOCK 34 | #define EIP_SOCK_EINPROGRESS WSAEINPROGRESS 35 | /* end of Win32 settings */ 36 | 37 | #else 38 | 39 | #ifdef vxWorks 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | typedef int EIP_SOCKET; 59 | #define EIP_INVALID_SOCKET (-1) 60 | #define EIP_SOCKERRNO errno 61 | #define EIP_socket_close(S) close(S) 62 | #define EIP_socket_ioctl(A,B,C) ioctl(A,B,(int)C) 63 | #define EIP_SOCK_EWOULDBLOCK EWOULDBLOCK 64 | #define EIP_SOCK_EINPROGRESS EINPROGRESS 65 | /* end of vxWorks settings */ 66 | 67 | #else 68 | 69 | /* Unix settings */ 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | typedef int EIP_SOCKET; 82 | #define EIP_INVALID_SOCKET (-1) 83 | #define EIP_SOCKERRNO errno 84 | #define EIP_socket_close(S) close(S) 85 | #define EIP_socket_ioctl(A,B,C) ioctl(A,B,C) 86 | #define EIP_SOCK_EWOULDBLOCK EWOULDBLOCK 87 | #define EIP_SOCK_EINPROGRESS EINPROGRESS 88 | 89 | #ifdef SOLARIS 90 | #include 91 | #endif 92 | 93 | /* end of Unix settings */ 94 | #endif 95 | #endif 96 | 97 | #include "eip_bool.h" 98 | 99 | /* This could be an application on its own... 100 | * Rough idea: 101 | * 10: Dump all protocol details 102 | * 9: Hexdump each sent/received buffer 103 | * 104 | * 6: show driver details 105 | * 5: device will show write-related operations 106 | * 107 | * 2: show more error info: "xxx failed because..." 108 | * 1: show error messages: "xxx failed" 109 | * 0: keep quiet 110 | */ 111 | extern int EIP_verbosity; 112 | 113 | /* print if EIP_verbosity >= level */ 114 | void EIP_printf(int level, const char *format, ...); 115 | 116 | /* print with time stamp if EIP_verbosity >= level */ 117 | void EIP_printf_time(int level, const char *format, ...); 118 | 119 | void EIP_hexdump(int level, const void *_data, int len); 120 | 121 | /* There is a limit for the PLC transfer buffer. 122 | * Spec page 2-5 mentions 504 bytes as the CIP UCMM limit, 123 | * Spec page 156 in version 2.0, errata 2 mentions 511 bytes 124 | * as the Forward_Open connection size limit. 125 | * None if this tells me exactly where the limit is 126 | * and whether it's 500 or 504 or ?? 127 | * Is it in the PLC controller, so the Ethernet overhead 128 | * (encapsulation header) is handled by the ENET module 129 | * and does not count? 130 | * Is it in the ENET module, so the total including encapsulation 131 | * is limited? 132 | * 133 | * Test: Used command-line EtherIP tool to read elements of SINT[]. 134 | * 492 elements: Results in 538 byte response, OK 135 | * 493 elements: Results in 538 byte response, Fault 136 | * (error 0x06, Buffer too small, partial data only) 137 | * Likewise INT[] array: <=246 elements, <=538 byte response, OK 138 | * Note: That command-line tool sends a single read request. 139 | * The EPICS driver sends read/write requests within 140 | * a multi-request, so the individual array reads must 141 | * be slightly smaller for the driver! While the command-line 142 | * tool can read INT[246], the driver cannot! 143 | * 144 | * Possible conclusions: 145 | * -> Total response limited to 538 bytes? 146 | * 538 - EncapsulationHeader(24) = 514 147 | * - RR Data handle & timeout(6) = 508 148 | * - RR Data packet head.:count, address,..., length (10) = 498 149 | * -> MR_Response & data limited to 498 bytes? 150 | * 151 | * Tests w/ driver on IOC indicated that PLC also complains when 152 | * the _request_ reaches 538 bytes 153 | * -> request & response each limited to 538 bytes. 154 | * 155 | * These macros used to be the best guess for the buffer 156 | * plus the protocol overhead (encap. header), using 157 | * 538 - EIP_PROTOCOL_OVERHEAD 158 | * to fit the Multi-Request. 159 | * The response has an overhead of 40 bytes, see above: 24+6+10. 160 | * The request has an overhead of 52 bytes. 161 | * 162 | * However, the SNS CF PLCs seem to work best with a buffer limit 163 | * of 500, so that's now the EIP_DEFAULT_BUFFER_LIMIT. 164 | * 165 | * Overall, the driver uses a buffer of EIP_BUFFER_LIMIT bytes, 166 | * so nothing bigger than that can be transferred. 167 | */ 168 | 169 | /** Limit used by the driver when constructing requests and 170 | * watching expected responses 171 | */ 172 | extern int EIP_buffer_limit; 173 | 174 | /** Best estimate for EIP_buffer_limit */ 175 | #define EIP_DEFAULT_BUFFER_LIMIT 480 176 | 177 | /** Used to be used to determine EIP_DEFAULT_BUFFER_LIMIT, but 178 | * didn't work out 179 | */ 180 | #define EIP_PROTOCOL_OVERHEAD 52 181 | 182 | /** Absolute maximum for EIP_buffer_limit because this is the 183 | * buffer size that the driver uses to allocate the initial 184 | * buffer. 185 | */ 186 | #define EIP_BUFFER_SIZE 580 187 | 188 | /******************************************************** 189 | * ControlNet data types 190 | * Spec 5 p 3 191 | ********************************************************/ 192 | 193 | /* ControlNet uses Little Endian, not the common IP format! 194 | * Exception: 195 | * Addresses packed at sockaddr_in are in network order as usual. 196 | */ 197 | typedef signed char CN_SINT; 198 | typedef unsigned char CN_USINT; 199 | typedef unsigned short CN_UINT; 200 | typedef short CN_INT; 201 | typedef unsigned int CN_UDINT; 202 | typedef int CN_DINT; 203 | typedef float CN_REAL; 204 | typedef double CN_LREAL; 205 | #ifdef SUPPORT_LINT 206 | typedef long long CN_LINT; 207 | typedef unsigned long long CN_ULINT; 208 | #else 209 | #warning No 64 bit LINT/ULINT support 210 | #endif 211 | 212 | typedef enum 213 | { 214 | C_Identity = 0x01, 215 | C_MessageRouter = 0x02, 216 | C_ConnectionManager = 0x06 217 | } CN_Classes; 218 | 219 | /******************************************************** 220 | * Message Router PDU (Protocol Data Unit) 221 | ********************************************************/ 222 | 223 | typedef enum /* Spec 4, p.36 */ 224 | { 225 | S_Get_Attribute_All = 0x01, 226 | S_Get_Attribute_Single = 0x0E, 227 | S_CIP_MultiRequest = 0x0A, /* Logix5000 Data Access */ 228 | S_CIP_ReadData = 0x4C, /* Logix5000 Data Access */ 229 | S_CIP_WriteData = 0x4D, /* Logix5000 Data Access */ 230 | S_CM_Unconnected_Send = 0x52, 231 | S_CM_Forward_Open = 0x54, 232 | S_CM_Forward_Close = 0x4E 233 | } CN_Services; 234 | 235 | typedef struct 236 | { 237 | CN_USINT service; /* one of CN_Services */ 238 | CN_USINT path_size; /* in number of UINTs ! */ 239 | CN_USINT path[1]; /* defined as UINT (padded), but easier to handle as USINT */ 240 | /* request data: depends on service */ 241 | } MR_Request; 242 | 243 | typedef struct 244 | { 245 | CN_USINT service; 246 | CN_USINT reserved; 247 | CN_USINT general_status; 248 | CN_USINT extended_status_size; /* .. in UINT words ! */ 249 | /* maybe CN_UINT extended_status[] */ 250 | CN_USINT response[5]; /* depends on service */ 251 | } MR_Response; 252 | 253 | /* Dump a raw (net format) MR Response */ 254 | const CN_USINT *EIP_dump_raw_MR_Response(const CN_USINT *response, 255 | size_t response_size); 256 | 257 | /* MR_Response has fixed portion followed by (maybe) an extended 258 | * status and then (maybe) data. 259 | * Get pointer to data, fill data_size 260 | */ 261 | CN_USINT *EIP_raw_MR_Response_data (const CN_USINT *response, 262 | size_t response_size, 263 | size_t *data_size); 264 | 265 | /******************************************************** 266 | * CM, Connection Manager 267 | ********************************************************/ 268 | 269 | /* Network Connection Parameters, spec 4 p. 28 */ 270 | #define CM_NCP_Type0 0 271 | #define CM_NCP_Multicast (1 << 13) 272 | #define CM_NCP_Point2Point (2 << 13) 273 | 274 | #define CM_NCP_LowPriority 0 275 | #define CM_NCP_HighPriority (1 << 10) 276 | #define CM_NCP_ScheduledPriority (2 << 10) 277 | #define CM_NCP_Urgent (3 << 10) 278 | 279 | #define CM_NCP_Fixed 0 280 | #define CM_NCP_Variable (1 << 9) 281 | 282 | /* Transport and trigger, spec 4 p. 35 and 151 */ 283 | #define CM_Trig_Cyclic 0 284 | #define CM_Trig_Change (1 << 4) 285 | #define CM_Trig_App (2 << 4) 286 | 287 | #define CM_Transp_IsServer 0x80 288 | 289 | /* Data portion of CM_Unconnected_Send_Request */ 290 | typedef struct 291 | { 292 | CN_USINT priority_and_tick; 293 | CN_USINT connection_timeout_ticks; 294 | CN_UINT message_size; /* in bytes, w/o pad */ 295 | MR_Request message_router_PDU; /* padded to 16bit boundary */ 296 | /* then the second part follows */ 297 | } CM_Unconnected_Send_Request; 298 | 299 | typedef struct 300 | { 301 | CN_USINT path_size; /* in UINT words */ 302 | CN_USINT reserved; 303 | CN_USINT path[2]; 304 | } CM_Unconnected_Send_Request_2; 305 | 306 | /* Data portion of CM_Forward_Open */ 307 | typedef struct 308 | { 309 | CN_USINT priority_and_tick; 310 | CN_USINT connection_timeout_ticks; 311 | CN_UDINT O2T_CID; 312 | CN_UDINT T2O_CID; 313 | CN_UINT connection_serial; 314 | CN_UINT vendor_ID; /* vendor + originator_serial: */ 315 | CN_UDINT originator_serial; /* unique, hardcoded */ 316 | CN_USINT connection_timeout_multiplier; 317 | CN_USINT reserved[3]; 318 | CN_UDINT O2T_RPI; /* requested packet interval, microsecs */ 319 | CN_UINT O2T_connection_parameters; 320 | CN_UDINT T2O_RPI; 321 | CN_UINT T2O_connection_parameters; 322 | CN_USINT xport_type_and_trigger; 323 | CN_USINT connection_path_size; 324 | CN_USINT connection_path[1 /* path_size*2 */]; 325 | } CM_Forward_Open_Request; 326 | 327 | /* Data portion of reply to CM_Forward_Open */ 328 | typedef struct 329 | { 330 | CN_UDINT O2T_CID; 331 | CN_UDINT T2O_CID; 332 | CN_UINT connection_serial; 333 | CN_UINT vendor_ID; 334 | CN_UDINT originator_serial; 335 | CN_UDINT O2T_API; /* actual packet interval, microsecs */ 336 | CN_UDINT T2O_API; 337 | CN_USINT application_reply_size; 338 | CN_USINT reserved; 339 | CN_UINT application_reply[1 /* size*2 */]; 340 | } CM_Forward_Open_Good_Response; 341 | 342 | /* Data portion of CM_Forward_Close */ 343 | typedef struct 344 | { 345 | CN_USINT priority_and_tick; 346 | CN_USINT connection_timeout_ticks; 347 | CN_UINT connection_serial; 348 | CN_UINT vendor_ID; 349 | CN_UDINT originator_serial; 350 | CN_USINT connection_path_size; 351 | CN_USINT reserved; 352 | CN_USINT connection_path[1 /* path_size*2 */]; 353 | } CM_Forward_Close_Request; 354 | 355 | /* Data portion of reply to CM_Forward_Close */ 356 | typedef struct 357 | { 358 | CN_UINT connection_serial; 359 | CN_UINT vendor_ID; 360 | CN_UDINT originator_serial; 361 | CN_USINT application_reply_size; 362 | CN_USINT reserved; 363 | CN_UINT application_reply[1 /* size*2 */]; 364 | } CM_Forward_Close_Good_Response; 365 | 366 | size_t CM_Unconnected_Send_size (size_t message_size); 367 | 368 | /* Fill MR_Request with Unconnected_Send for message of given size, 369 | * path: backplane, given slot. 370 | * Returns pointer to message location in there 371 | * (to be filled, that's a nested, DIFFERENT request!) 372 | * or 0 on error 373 | */ 374 | CN_USINT *make_CM_Unconnected_Send(CN_USINT *request, size_t message_size, 375 | int slot); 376 | 377 | /******************************************************** 378 | * Logix5000 379 | ********************************************************/ 380 | 381 | /* Parses tags of the form 382 | * name.name[element].name[element].name 383 | * and converts them into list of path elements 384 | */ 385 | typedef struct __ParsedTag ParsedTag; 386 | struct __ParsedTag 387 | { 388 | enum 389 | { 390 | te_name, 391 | te_element 392 | } type; 393 | union 394 | { 395 | char *name; 396 | size_t element; 397 | } value; 398 | ParsedTag *next; 399 | }; 400 | 401 | /* (Abbreviated) type codes for CIP data 402 | * ENET p. 11 403 | */ 404 | typedef enum 405 | { 406 | T_CIP_BOOL = 0x00C1, 407 | T_CIP_SINT = 0x00C2, 408 | T_CIP_INT = 0x00C3, 409 | T_CIP_DINT = 0x00C4, 410 | T_CIP_LINT = 0x00C5, 411 | /*T_CIP_USINT = 0x00C6,*/ 412 | T_CIP_UINT = 0x00C7, 413 | /*T_CIP_UDINT = 0x00C8,*/ 414 | T_CIP_ULINT = 0x00C9, 415 | T_CIP_REAL = 0x00CA, 416 | T_CIP_LREAL = 0x00CB, 417 | T_CIP_STRING = 0x00D0, 418 | T_CIP_BITS = 0x00D3, 419 | T_CIP_STRUCT = 0x02A0 420 | } CIP_Type; 421 | 422 | /* These are experimental: 423 | * The ENEC doc just shows several structures 424 | * for TIMER, COUNTER, CONTROL and indicates that 425 | * the T_CIP_STRUCT = 0x02A0 is followed by 426 | * two more bytes, shown as "?? ??". 427 | * Looks like for strings, those are always 0x0FCE, 428 | * followed by INT length, INT 0, 82 characters and more zeroes 429 | */ 430 | typedef enum 431 | { 432 | T_CIP_STRUCT_STRING = 0x0FCE 433 | } CIP_STRUCT_Type; 434 | 435 | /* Size of appreviated type code. 436 | * We only use those with size 2, no structures */ 437 | #define CIP_Typecode_size 2 438 | 439 | /* Get typecode from type/data ptr "td". 440 | * (Does not work for structs) */ 441 | #define get_CIP_typecode(td) ( (CIP_Type) (((CN_USINT *)td)[0]) ) 442 | 443 | /* Determine byte size of CIP_Type */ 444 | size_t CIP_Type_size(CIP_Type type); 445 | 446 | /* Turn tag string into ParsedTag, 447 | * convert back into string and free it 448 | */ 449 | /* 450 | AB PLC has the max 82 chars for a tag string 451 | */ 452 | #define EIP_MAX_TAG_LENGTH 100 453 | ParsedTag *EIP_parse_tag(const char *tag); 454 | void EIP_copy_ParsedTag(char *buffer, const ParsedTag *tag); 455 | void EIP_free_ParsedTag(ParsedTag *tag); 456 | 457 | CN_USINT *make_CIP_ReadData(CN_USINT *request, 458 | const ParsedTag *tag, size_t elements); 459 | const CN_USINT *check_CIP_ReadData_Response(const CN_USINT *response, 460 | size_t response_size, 461 | size_t *data_size); 462 | 463 | /* Fill buffer with CIP WriteData request 464 | * for tag, type of CIP data, given number of elements. 465 | * Also copies data into buffer, 466 | * data has to be in network format already! 467 | */ 468 | CN_USINT *make_CIP_WriteData(CN_USINT *buf, size_t buf_size, const ParsedTag *tag, 469 | CIP_Type type, size_t elements, 470 | CN_USINT *raw_data); 471 | void dump_CIP_WriteRequest(const CN_USINT *request); 472 | /* Test CIP_WriteData response: If not OK, report error */ 473 | eip_bool check_CIP_WriteData_Response(const CN_USINT *response, 474 | size_t response_size); 475 | 476 | size_t CIP_MultiRequest_size(size_t count, size_t requests_size); 477 | size_t CIP_MultiResponse_size(size_t count, size_t responses_size); 478 | eip_bool prepare_CIP_MultiRequest(CN_USINT *request, size_t count); 479 | 480 | CN_USINT *CIP_MultiRequest_item(CN_USINT *request, 481 | size_t request_no, 482 | size_t single_request_size); 483 | 484 | eip_bool check_CIP_MultiRequest_Response(const CN_USINT *response, 485 | size_t response_size); 486 | void dump_CIP_MultiRequest_Response_Error(const CN_USINT *response, 487 | size_t response_size); 488 | const CN_USINT *get_CIP_MultiRequest_Response(const CN_USINT *response, 489 | size_t response_size, 490 | size_t reply_no, 491 | size_t *reply_size); 492 | 493 | /* dump CIP data, type and data are in raw format */ 494 | void dump_raw_CIP_data(const CN_USINT *raw_type_and_data, size_t elements); 495 | eip_bool get_CIP_double(const CN_USINT *raw_type_and_data, 496 | size_t element, double *result); 497 | eip_bool get_CIP_UDINT(const CN_USINT *raw_type_and_data, 498 | size_t element, CN_UDINT *result); 499 | eip_bool get_CIP_DINT(const CN_USINT *raw_type_and_data, 500 | size_t element, CN_DINT *result); 501 | #ifdef SUPPORT_LINT 502 | eip_bool get_CIP_LINT(const CN_USINT *raw_type_and_data, 503 | size_t element, CN_LINT *result); 504 | eip_bool put_CIP_LINT(const CN_USINT *raw_type_and_data, 505 | size_t element, CN_LINT value); 506 | #endif 507 | eip_bool get_CIP_USINT(const CN_USINT *raw_type_and_data, 508 | size_t element, CN_USINT *result); 509 | /* Fill buffer with up to 'size' characters (incl. ending '\0'). 510 | * Return true for success */ 511 | eip_bool get_CIP_STRING(const CN_USINT *raw_type_and_data, 512 | char *buffer, size_t size); 513 | eip_bool put_CIP_double(const CN_USINT *raw_type_and_data, 514 | size_t element, double value); 515 | eip_bool put_CIP_UDINT(const CN_USINT *raw_type_and_data, 516 | size_t element, CN_UDINT value); 517 | eip_bool put_CIP_DINT(const CN_USINT *raw_type_and_data, 518 | size_t element, CN_DINT value); 519 | 520 | /* 521 | * Fill raw_type_and_data with data and data length. Leave 522 | * the other portion of it unchanged. 523 | */ 524 | eip_bool put_CIP_STRING(const CN_USINT *raw_type_and_data, 525 | char *value, size_t size); 526 | 527 | /******************************************************** 528 | * Encapsulation 529 | ********************************************************/ 530 | 531 | #define TRANS_ID_LEN 8 532 | typedef struct 533 | { 534 | CN_USINT byte[TRANS_ID_LEN]; 535 | } TransactionID; 536 | 537 | 538 | typedef struct 539 | { 540 | CN_UINT command; /* Encapsulation_Command */ 541 | CN_UINT length; /* # bytes that follow this header */ 542 | CN_UDINT session; /* returned by EC_RegisterSession */ 543 | CN_UDINT status; 544 | TransactionID trans_id; /* anything I like */ 545 | CN_UDINT options; 546 | } EncapsulationHeader; 547 | 548 | #define sizeof_EncapsulationHeader 24 549 | 550 | 551 | /* Spec 4 p. 164 */ 552 | typedef enum 553 | { 554 | EC_Nop = 0x0000, 555 | EC_ListInterfaces = 0x0064, /* "Encapsulation Overview" slides */ 556 | EC_RegisterSession = 0x0065, 557 | EC_UnRegisterSession = 0x0066, 558 | EC_ListServices = 0x0004, 559 | EC_SendRRData = 0x006F, 560 | EC_SendUnitData = 0x0070, 561 | EC_IndicateStatus = 0x0072 /* "Encapsulation Overview" slides */ 562 | } Encapsulation_Command; 563 | 564 | typedef struct 565 | { 566 | EncapsulationHeader header; 567 | /* "Common Packet Format": count + stuff */ 568 | CN_UINT count; 569 | struct ServiceInfo 570 | { 571 | CN_UINT type; /* 0x100: "Communications" */ 572 | CN_UINT length; 573 | CN_UINT version; 574 | CN_UINT flags; 575 | CN_USINT name[16]; 576 | } service; /* 1 or more ... */ 577 | } ListServicesReply; 578 | 579 | typedef struct 580 | { 581 | EncapsulationHeader header; 582 | CN_UINT protocol_version; 583 | CN_UINT options; 584 | } RegisterSessionData; 585 | 586 | #define sizeof_RegisterSessionData (sizeof_EncapsulationHeader + 4) 587 | 588 | /* A single PDU encapsulated for SendRRData commands */ 589 | typedef struct 590 | { 591 | EncapsulationHeader header; 592 | CN_UDINT interface_handle; 593 | CN_UINT timeout; 594 | /* Common Packet Format: */ 595 | CN_UINT count; /* >= 2 */ 596 | 597 | /* Item 1 */ 598 | CN_UINT address_type; 599 | CN_UINT address_length; 600 | /* could have addr. info in here .. */ 601 | 602 | /* Item 2 */ 603 | CN_UINT data_type; 604 | CN_UINT data_length; 605 | 606 | /* Following: MR_Request or MR_Response */ 607 | } EncapsulationRRData; 608 | 609 | /* EncapsulationRRData up to the MR_Request/Response (exclusive) */ 610 | #define sizeof_EncapsulationRRData (sizeof_EncapsulationHeader + 16) 611 | 612 | 613 | /* A single PDU encapsulated for SendUnitData commands */ 614 | typedef struct 615 | { 616 | EncapsulationHeader header; 617 | CN_UDINT interface_handle; 618 | CN_UINT timeout; 619 | /* Common Packet Format: */ 620 | CN_UINT count; /* >= 2 */ 621 | 622 | /* Item 1 */ 623 | CN_UINT address_type; 624 | CN_UINT address_length; 625 | CN_UDINT CID; 626 | 627 | /* Item 2 */ 628 | CN_UINT data_type; 629 | CN_UINT data_length; 630 | CN_UINT PDU_sequence_number; 631 | union 632 | { /* PDU */ 633 | MR_Request request; 634 | MR_Response response; 635 | } rr; 636 | } EncapsulationUnitData; 637 | 638 | /******************************************************** 639 | * Utility types for this driver 640 | ********************************************************/ 641 | 642 | /* Parameters used to identify a Forward_Open connection */ 643 | typedef struct 644 | { 645 | /* Have to be set for make_CM_Forward_Open */ 646 | CN_USINT priority_and_tick; 647 | CN_USINT connection_timeout_ticks; 648 | CN_UDINT O2T_CID; 649 | CN_UDINT T2O_CID; 650 | CN_UINT connection_serial; 651 | CN_UINT vendor_ID; 652 | CN_UDINT originator_serial; 653 | } EIPConnectionParameters; 654 | 655 | /* Some values taken from the Itentity object 656 | * of the target 657 | */ 658 | typedef struct 659 | { 660 | CN_UINT vendor; 661 | CN_UINT device_type; 662 | CN_UINT revision; 663 | CN_UDINT serial_number; 664 | /* Table 10.3 in spec limits string to 32 chars, 665 | * but CompactLogix seems to return 52 characters. 666 | */ 667 | CN_USINT name[100]; 668 | } EIPIdentityInfo; 669 | 670 | /* Parameters & buffers for one EtherNet/IP connection. 671 | * sock == 0 is used to detect unused/shutdown connections. */ 672 | typedef struct 673 | { 674 | EIP_SOCKET sock; /* silk or nylon */ 675 | int slot; /* PLC's slot on backplane */ 676 | size_t transfer_buffer_limit; /* PLC limit */ 677 | size_t millisec_timeout; /* .. for socket calls */ 678 | CN_UDINT session; /* session ID, generated by target */ 679 | CN_USINT *buffer; /* buffer for read/write, EIP_BUFFER_SIZE */ 680 | EIPIdentityInfo info; 681 | EIPConnectionParameters params; 682 | } EIPConnection; 683 | 684 | #ifdef _WIN32 685 | #pragma pack(pop) 686 | #endif 687 | 688 | void generateTransactionId(TransactionID *pid); 689 | int compareTransactionIds(const TransactionID *p1, const TransactionID *p2); 690 | void transactionIdString(const TransactionID *pId, char *str, int maxChars); 691 | void extractTransactionId(const EncapsulationHeader *pHeader, TransactionID *pId); 692 | 693 | CN_USINT *EIP_make_SendRRData(EIPConnection *c, size_t length, const TransactionID *pId); 694 | 695 | /* Unpack reponse to SendRRData. 696 | * Fills data with details, returns pointer to raw MRResponse 697 | * that's enclosed in the RRData 698 | */ 699 | const CN_USINT *EIP_unpack_RRData(const CN_USINT *response, 700 | EncapsulationRRData *data); 701 | 702 | void EIP_dump_connection(const EIPConnection *c); 703 | 704 | /** Allocate EIPConnection */ 705 | EIPConnection *EIP_init(); 706 | 707 | /** Dispose EIPConnection */ 708 | void EIP_dispose(EIPConnection *c); 709 | 710 | /** Connect to PLC */ 711 | eip_bool EIP_startup(EIPConnection *c, 712 | const char *ip_addr, unsigned short port, 713 | int slot, 714 | size_t millisec_timeout); 715 | 716 | /** Disconnect from PLC */ 717 | void EIP_shutdown(EIPConnection *c); 718 | 719 | /** Send content of buffer. 720 | * Buffer must contain valid EncapsulationHeader 721 | * which describes the size of the message. 722 | * @return true when OK 723 | */ 724 | eip_bool EIP_send_connection_buffer(EIPConnection *c); 725 | 726 | /** Read message from PLC into connection's buffer: 727 | * EncapsulationHeader, which describes the length of 728 | * the message, followed by actual message. 729 | * @return true when a full reply was received 730 | */ 731 | eip_bool EIP_read_connection_buffer(EIPConnection *c); 732 | 733 | /* VxWorks has no strdup */ 734 | char *EIP_strdup(const char *text); 735 | 736 | /* Like EIP_strdup, but only copies the first 'len' chars */ 737 | char *EIP_strdup_n(const char *text, size_t len); 738 | 739 | /* Read a single tag in a single CIP_ReadData request, 740 | * report data & data_length 741 | * as well as sizes of CIP_ReadData request/response 742 | */ 743 | const CN_USINT *EIP_read_tag(EIPConnection *c, 744 | const ParsedTag *tag, size_t elements, 745 | size_t *data_size, 746 | size_t *request_size, size_t *response_size); 747 | 748 | eip_bool EIP_write_tag(EIPConnection *c, const ParsedTag *tag, 749 | CIP_Type type, size_t elements, CN_USINT *data, 750 | size_t *request_size, 751 | size_t *response_size); 752 | 753 | void *EIP_Get_Attribute_Single(EIPConnection *c, 754 | CN_Classes cls, CN_USINT instance, 755 | CN_USINT attr, size_t *len); 756 | 757 | /* EOF ether_ip.h */ 758 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "epicsVersion.h" 3 | 4 | VS_VERSION_INFO VERSIONINFO 5 | FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_UPDATE_LEVEL 6 | PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_UPDATE_LEVEL 7 | FILEFLAGSMASK 0x3fL 8 | #ifdef _DEBUG 9 | FILEFLAGS 0x1L 10 | #else 11 | FILEFLAGS 0x0L 12 | #endif 13 | FILEOS VOS__WINDOWS32 14 | FILETYPE VFT_UNKNOWN 15 | FILESUBTYPE 0x0L 16 | BEGIN 17 | BLOCK "StringFileInfo" 18 | BEGIN 19 | BLOCK "040904b0" 20 | BEGIN 21 | VALUE "Comments","Ether IP Library for EPICS\0" 22 | VALUE "CompanyName", "The EPICS collaboration\0" 23 | VALUE "FileDescription", "Ether IP Library\0" 24 | VALUE "FileVersion", EPICS_VERSION_STRING "\0" 25 | VALUE "InternalName", "ether_ip\0" 26 | VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" 27 | VALUE "OriginalFilename", "ether_ip.dll\0" 28 | VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" 29 | VALUE "ProductVersion", EPICS_VERSION_STRING "\0" 30 | END 31 | END 32 | BLOCK "VarFileInfo" 33 | BEGIN 34 | VALUE "Translation", 0x409, 1200 35 | END 36 | END 37 | -------------------------------------------------------------------------------- /ether_ipApp/src/ether_ip_test.c: -------------------------------------------------------------------------------- 1 | /* EtherNet/IP: ControlNet over Ethernet 2 | * 3 | * Test program for hosts. 4 | * 5 | * kasemirk@ornl.gov 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include"ether_ip.c" 15 | 16 | #ifdef DEFINE_CONNECTED_METHODS 17 | 18 | eip_bool ForwardOpen(EIPConnection *c) 19 | { 20 | size_t request_size = CM_Forward_Open_size (); 21 | MR_Request *request; 22 | EncapsulationRRData *data; 23 | CM_Forward_Open_Good_Response *open_response; 24 | 25 | calc_tick_time (245760, 26 | &c->params.priority_and_tick, 27 | &c->params.connection_timeout_ticks); 28 | c->params.O2T_CID = 0; 29 | c->params.T2O_CID = 2; 30 | c->params.connection_serial = 3; 31 | c->params.vendor_ID = 0xface; 32 | c->params.originator_serial = 0xeffec; 33 | 34 | request = EIP_make_SendRRData (c, request_size); 35 | if (! request) 36 | return false; 37 | 38 | if (! make_CM_Forward_Open (request, &c->params)) 39 | return false; 40 | /* dump_CM_Forward_Open (request); */ 41 | 42 | if (! EIP_send_connection_buffer (c)) 43 | { 44 | printf ("Forward_Open: send failed\n"); 45 | return 0; 46 | } 47 | 48 | if (! EIP_read_connection_buffer (c)) 49 | { 50 | printf ("Forward_Open: No response\n"); 51 | EIP_dump_connection (c); 52 | return 0; 53 | } 54 | data = (EncapsulationRRData *) c->buffer; 55 | 56 | if (data->rr.response.general_status != 0) 57 | { 58 | dump_CM_Forward_Open_Response (&data->rr.response, data->data_length); 59 | return false; 60 | } 61 | 62 | open_response = (CM_Forward_Open_Good_Response *) 63 | EIP_MR_Response_data (&data->rr.response, data->data_length, 0); 64 | c->params.O2T_CID = open_response->O2T_CID; 65 | 66 | #define CHECK(ITEM) \ 67 | if (open_response->ITEM != c->params.ITEM) \ 68 | { \ 69 | printf ("different " # ITEM "\n"); \ 70 | return false; \ 71 | } 72 | CHECK (T2O_CID) 73 | CHECK (connection_serial) 74 | CHECK (vendor_ID) 75 | CHECK (originator_serial) 76 | #undef CHECK 77 | 78 | return true; 79 | } 80 | 81 | eip_bool ForwardClose(EIPConnection *c) 82 | { 83 | size_t request_size = CM_Forward_Close_size (); 84 | MR_Request *request; 85 | EncapsulationRRData *data; 86 | CM_Forward_Close_Good_Response *close_response; 87 | 88 | request = EIP_make_SendRRData (c, request_size); 89 | if (! request) 90 | return false; 91 | 92 | if (! make_CM_Forward_Close (request, &c->params)) 93 | return false; 94 | /* hexdump (request, request_size); */ 95 | if (! EIP_send_connection_buffer (c)) 96 | { 97 | printf ("Forward_Close: send failed\n"); 98 | return 0; 99 | } 100 | 101 | if (! EIP_read_connection_buffer (c)) 102 | { 103 | printf ("Forward_Close: No response\n"); 104 | EIP_dump_connection (c); 105 | return 0; 106 | } 107 | data = (EncapsulationRRData *) c->buffer; 108 | close_response = (CM_Forward_Close_Good_Response *) 109 | EIP_MR_Response_data (&data->rr.response, data->data_length, 0); 110 | if (data->rr.response.general_status) 111 | { 112 | printf ("Forward_Close Error: "); 113 | if (data->rr.response.general_status == 0x01 && 114 | data->rr.response.extended_status_size == 1) 115 | { 116 | const CN_UINT *ext = MR_Response_ext_stat (&data->rr.response); 117 | switch (*ext) 118 | { 119 | case 0x0107: printf ("Connection not found\n"); 120 | return false; 121 | } 122 | } 123 | 124 | dump_MR_Response (&data->rr.response, data->data_length); 125 | return false; 126 | } 127 | 128 | #define CHECK(ITEM) \ 129 | if (close_response->ITEM != c->params.ITEM) \ 130 | { \ 131 | printf ("different " # ITEM "\n"); \ 132 | return false; \ 133 | } 134 | CHECK (connection_serial) 135 | CHECK (vendor_ID) 136 | CHECK (originator_serial) 137 | #undef CHECK 138 | 139 | return true; 140 | } 141 | 142 | eip_bool ReadConnected(EIPConnection *c, size_t count, const ParsedTag *tag[]) 143 | { 144 | size_t i, msg_size[40], requests_size = 0, multi_size, 145 | single_response_size; 146 | MR_Request *multi_request, *single_request; 147 | const MR_Response *response, *single_response; 148 | EncapsulationUnitData *unit_data; 149 | static CN_USINT sequence = 0; 150 | CN_USINT *data; 151 | size_t data_size; 152 | 153 | /* size indiv. requests, pack into multi request, then Unconn. send */ 154 | for (i=0; ibuffer; 186 | response = &unit_data->rr.response; 187 | 188 | if (! check_CIP_MultiRequest_Response (response)) 189 | { 190 | EIP_dump_connection (c); 191 | return false; 192 | } 193 | 194 | for (i=0; idata_length, 199 | i, 200 | &single_response_size); 201 | if (! single_response) 202 | return false; 203 | 204 | if (check_CIP_ReadData_Response (single_response, single_response_size)) 205 | { 206 | data = EIP_raw_MR_Response_data (single_response, single_response_size, 207 | &data_size); 208 | dump_raw_CIP_data (data, data_size); 209 | } 210 | } 211 | 212 | return true; 213 | } 214 | 215 | void TestConnected(EIPConnection *c, const ParsedTag *tag[]) 216 | { 217 | printf ("ForwardOpen:\n"); 218 | if (ForwardOpen (c)) 219 | printf ("OK.\n"); 220 | 221 | ReadConnected (c, 1, tag); 222 | 223 | printf ("ForwardClose:\n"); 224 | if (ForwardClose (c)) 225 | printf ("OK.\n"); 226 | } 227 | 228 | #endif /* DEFINE_CONNECTED_METHODS */ 229 | 230 | 231 | #if 0 232 | void stressC(EIPConnection *c, size_t count, const ParsedTag *tag[], size_t runs) 233 | { 234 | TimerValue t; 235 | double secs; 236 | size_t i; 237 | 238 | ForwardOpen (c); 239 | TimerInit (&t); 240 | TimerStart (&t); 241 | for (i=0; i", 251 | runs/secs, secs/runs*1000.0); 252 | printf ("%8.2f values/sec, %6.2f msec/value\n", 253 | runs*count/secs, secs/(runs*count)*1000.0); 254 | } 255 | else 256 | printf ("Try more runs\n"); 257 | ForwardClose (c); 258 | } 259 | 260 | void stress(EIPConnection *c, size_t count, const ParsedTag *tags[], size_t runs) 261 | { 262 | TimerValue t; 263 | double secs; 264 | size_t i; 265 | 266 | TimerInit (&t); 267 | TimerStart (&t); 268 | for (i=0; i", 281 | runs/secs, secs/runs*1000.0); 282 | printf ("%8.2f values/sec, %6.2f msec/value\n", 283 | runs*count/secs, secs/(runs*count)*1000.0); 284 | } 285 | else 286 | printf ("Try more runs\n"); 287 | } 288 | #endif 289 | 290 | void usage(const char *progname) 291 | { 292 | fprintf(stderr, "Usage: %s [tag]\n", progname); 293 | fprintf(stderr, "Options:\n"); 294 | fprintf(stderr, " -v verbosity (Numerical digit)\n"); 295 | fprintf(stderr, " -i ip (as 123.456.789.001 or DNS name)\n"); 296 | fprintf(stderr, " -p port\n"); 297 | fprintf(stderr, " -s PLC slot in ControlLogix crate (default: 0)\n"); 298 | fprintf(stderr, " -t timeout (ms)\n"); 299 | fprintf(stderr, " -a array size\n"); 300 | fprintf(stderr, " -w \n"); 301 | #ifdef SUPPORT_LINT 302 | fprintf(stderr, " -W <64 bit value to write>\n"); 303 | #endif 304 | fprintf(stderr, " -T times-to-do-all-this (default: 1)\n"); 305 | exit(-1); 306 | } 307 | 308 | int main (int argc, const char *argv[]) 309 | { 310 | EIPConnection *c = EIP_init(); 311 | const char *ip = "172.31.72.94"; 312 | unsigned short port = 0xAF12; 313 | int slot = 0; 314 | size_t timeout_ms = 5000; 315 | size_t elements = 1; 316 | ParsedTag *tag = 0; 317 | const char *arg; 318 | size_t i; 319 | CN_REAL writeval = 0.0; 320 | eip_bool write = false; 321 | #ifdef SUPPORT_LINT 322 | CN_LINT Writeval = 0x0000000000000000ll; 323 | eip_bool Write = false; 324 | #endif 325 | size_t test_runs = 1; 326 | #ifndef _WIN32 327 | struct timeval now; 328 | #endif 329 | double start, end, duration; 330 | 331 | #ifdef _WIN32 332 | /* Win32 socket init. */ 333 | WORD wVersionRequested; 334 | WSADATA wsaData; 335 | wVersionRequested = MAKEWORD(2, 2); 336 | WSAStartup(wVersionRequested, &wsaData); 337 | #endif 338 | 339 | EIP_verbosity = 5; 340 | if (argc < 2) { 341 | usage (argv[0]); 342 | return 1; 343 | } 344 | /* parse arguments */ 345 | for (i=1; i<(size_t)argc; ++i) 346 | { 347 | if (argv[i][0] == '-') 348 | { 349 | #define GETARG \ 350 | if (argv[i][2]) { arg = &argv[i ][2]; } \ 351 | else { arg = &argv[i+1][0]; ++i; } 352 | switch (argv[i][1]) 353 | { 354 | case 'v': 355 | GETARG 356 | if (arg) { 357 | if (isdigit(*arg) == 0) usage(argv[0]); 358 | else EIP_verbosity = atoi(arg); 359 | } 360 | else usage (argv[0]); 361 | break; 362 | case 'i': 363 | GETARG 364 | if (arg) ip = arg; 365 | else usage (argv[0]); 366 | 367 | break; 368 | case 'p': 369 | GETARG 370 | if (arg) port = (unsigned short) strtol(arg, 0, 0); 371 | else usage (argv[0]); 372 | break; 373 | case 's': 374 | GETARG 375 | if (arg) slot = (int) strtol(arg, 0, 0); 376 | else usage (argv[0]); 377 | break; 378 | case 'a': 379 | GETARG 380 | if (arg) elements = atoi(arg); 381 | else usage (argv[0]); 382 | break; 383 | case 't': 384 | GETARG 385 | if (arg) timeout_ms = atol(arg); 386 | else usage (argv[0]); 387 | break; 388 | case 'w': 389 | GETARG 390 | if (arg) { 391 | writeval = strtod(arg, NULL); 392 | write = true; 393 | } 394 | else usage (argv[0]); 395 | break; 396 | #ifdef SUPPORT_LINT 397 | case 'W': 398 | GETARG 399 | if (arg) { 400 | Writeval = strtoull(arg, NULL, 0); 401 | Write = true; 402 | } 403 | else usage (argv[0]); 404 | break; 405 | #endif 406 | case 'T': 407 | GETARG 408 | if (arg) { 409 | test_runs = atol(arg); 410 | if (test_runs <= 0) 411 | test_runs = 1; 412 | } 413 | else usage (argv[0]); 414 | break; 415 | default: 416 | usage (argv[0]); 417 | #undef GETARG 418 | } 419 | } 420 | else 421 | { 422 | tag = EIP_parse_tag(argv[i]); 423 | } 424 | } 425 | if (tag && EIP_verbosity >= 3) 426 | { 427 | char buffer[EIP_MAX_TAG_LENGTH]; 428 | EIP_copy_ParsedTag(buffer, tag); 429 | EIP_printf(3, "Tag '%s'\n", buffer); 430 | } 431 | #ifdef _WIN32 432 | start = (double) time(0); 433 | #else 434 | gettimeofday(&now, NULL); 435 | start = now.tv_sec + now.tv_usec/1000000.0; 436 | #endif 437 | for (i=0; i %f ms / tag\n", 474 | (unsigned)test_runs, 475 | duration, 476 | duration / test_runs * 1000.0); 477 | 478 | EIP_dispose(c); 479 | 480 | #ifdef _WIN32 481 | /* Win32 socket shutdown */ 482 | WSACleanup( ); 483 | #endif 484 | 485 | return 0; 486 | } 487 | -------------------------------------------------------------------------------- /iocBoot/Makefile: -------------------------------------------------------------------------------- 1 | TOP = .. 2 | include $(TOP)/configure/CONFIG 3 | 4 | DIRS += $(wildcard *ioc*) 5 | DIRS += $(wildcard as*) 6 | 7 | include $(EPICS_BASE)/configure/RULES_DIRS 8 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/Makefile: -------------------------------------------------------------------------------- 1 | TOP = ../.. 2 | 3 | include $(TOP)/configure/CONFIG 4 | #ARCH = RTEMS-svgm 5 | ARCH = vxWorks-ppc603 6 | TARGETS = cdCommands 7 | include $(TOP)/configure/RULES.ioc 8 | 9 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/README: -------------------------------------------------------------------------------- 1 | A bit confusing with all the different versions. 2 | 3 | Linux Host, R3.14.4 base: 4 | ../../bin/linux-x86/example st.cmd.host 5 | 6 | You might need to set this: 7 | export LD_LIBRARY_PATH=../../lib/linux-x86:$LD_LIBRARY_PATH; 8 | 9 | st.cmd.3_14 is meant for a vxWorks IOC under R3.14 10 | 11 | st.cmd is meant for Linux 12 | 13 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/run_iocLogServer.sh: -------------------------------------------------------------------------------- 1 | # This is just for getting an ioc log server to run, 2 | # for testing message capture from the driver. 3 | # Read the appdevguide. 4 | 5 | export EPICS_IOC_LOG_FILE_NAME=`pwd`/ioclog.txt 6 | export EPICS_IOC_LOG_PORT=6505 7 | iocLogServer 8 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/st.cmd: -------------------------------------------------------------------------------- 1 | #! ../../bin/linux-x86_64/eipIoc 2 | # 3.14 example startup file for a Host -*- shell-script -*- 3 | 4 | # Load dbd, register the drvEtherIP.. commands 5 | dbLoadDatabase("../../dbd/eipIoc.dbd") 6 | eipIoc_registerRecordDeviceDriver(pdbbase) 7 | 8 | 9 | # Initialize EtherIP driver, define PLCs 10 | drvEtherIP_init() 11 | 12 | # EIP_verbosity(10) 13 | # EIP_timeout(5000) 14 | 15 | drvEtherIP_define_PLC("plc1", "160.91.233.45", 0) 16 | 17 | #dbLoadRecords("db/mbbo.db", "PLC=plc1,IOC=test") 18 | #dbLoadRecords("../../db/test.db", "PLC=plc1,IOC=test") 19 | #dbLoadRecords("../../db/mod.db", "PLC=plc1,IOC=test") 20 | dbLoadRecords("string.db", "PLC=plc1,IOC=test") 21 | 22 | iocInit() 23 | 24 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/st.cmd.3_14: -------------------------------------------------------------------------------- 1 | # 3.14 ether_ip_test vxWorks startup file 2 | 3 | # Following will be needed for many board support packages 4 | #cd "" 5 | 6 | < cdCommands 7 | 8 | #< ../nfsCommands 9 | 10 | cd topbin 11 | ld < example.munch 12 | 13 | # Initialize EtherIP driver, define PLCs 14 | # ------------------------------------- 15 | drvEtherIP_init() 16 | 17 | # Tell vxWorks how to get to "myplc", 18 | # that is the ENET module of a ControlLogix PLC. 19 | # vxWorks doesn't generally use DNS, so if you 20 | # want to use names, you have to define them: 21 | hostAdd("myplc", "192.168.0.50") 22 | # You might need this, too, if the IP is on 23 | # a different subnet: routeAdd , 24 | # routeAdd("192.168.0.50, "128.165.160.241") 25 | # Test if we can get to the PLC: 26 | ping "myplc", 2 27 | 28 | # drvEtherIP_define_PLC , , 29 | # The driver/device uses the to indentify the PLC. 30 | # 31 | # can be an IP address in dot-notation 32 | # or a name that the IOC knows about (defined via hostAdd, 33 | # see step 4). 34 | # The IP address gets us to the ENET interface. 35 | # To get to the PLC itself, we need the slot that 36 | # it resides in. The first, left-most slot in the 37 | # ControlLogix crate is slot 0. 38 | drvEtherIP_define_PLC("plc1", "myplc", 0) 39 | 40 | # 10 - Truckload of detailed messages down to the raw send/receive buffers 41 | # 7 - Good value for operation 42 | # 0 - Nothing except very severe problems 43 | EIP_verbosity=7 44 | 45 | cd top 46 | dbLoadDatabase("dbd/example.dbd") 47 | example_registerRecordDeviceDriver(pdbbase) 48 | #dbLoadRecords("db/ramp.db", "IOC=snsioc4") 49 | #dbLoadRecords("db/eip_stat.db","PLC=plc1,IOC=snsioc4,TAG=REAL") 50 | #dbLoadRecords("db/ai.db", "PLC=plc1,IOC=snsioc4") 51 | #dbLoadRecords("db/ana.db", "PLC=plc1,IOC=snsioc4") 52 | #dbLoadRecords("db/bi.db", "PLC=plc1,IOC=snsioc4") 53 | #dbLoadRecords("db/bin.db", "PLC=plc1,IOC=snsioc4") 54 | #dbLoadRecords("db/mbbi.db", "PLC=plc1,IOC=snsioc4") 55 | #dbLoadRecords("db/ao.db", "PLC=plc1,IOC=snsioc4") 56 | #dbLoadRecords("db/bo.db", "PLC=plc1,IOC=snsioc4") 57 | #dbLoadRecords("db/mbbo.db", "PLC=plc1,IOC=snsioc4") 58 | dbLoadRecords("db/test.db", "PLC=plc1,IOC=plc1") 59 | 60 | cd startup 61 | iocInit() 62 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/st.cmd.host: -------------------------------------------------------------------------------- 1 | #! ../../bin/linux-x86/eipIoc 2 | # 3.14 example startup file for a Host -*- shell-script -*- 3 | 4 | # Load dbd, register the drvEtherIP.. commands 5 | dbLoadDatabase("../../dbd/eipIoc.dbd") 6 | eipIoc_registerRecordDeviceDriver(pdbbase) 7 | 8 | epicsEnvSet("EPICS_IOC_LOG_INET", "127.0.0.1") 9 | epicsEnvSet("EPICS_IOC_LOG_PORT", "6505") 10 | #iocLogInit 11 | 12 | # Initialize EtherIP driver, define PLCs 13 | EIP_buffer_limit(450) 14 | drvEtherIP_init() 15 | 16 | EIP_verbosity(7) 17 | 18 | # drvEtherIP_define_PLC , , 19 | # The driver/device uses the to indentify the PLC. 20 | # 21 | # can be an IP address in dot-notation 22 | # or a name about which the IOC knows. 23 | # The IP address gets us to the ENET interface. 24 | # To get to the PLC itself, we need the slot that 25 | # it resides in. The first, left-most slot in the 26 | # ControlLogix crate is slot 0. 27 | #drvEtherIP_define_PLC("plc1", "172.31.72.94", 0) 28 | #drvEtherIP_define_PLC("plc1", "172.31.74.78", 0) 29 | drvEtherIP_define_PLC("plc1", "160.91.233.45", 0) 30 | 31 | #dbLoadRecords("db/ramp.db", "IOC=test") 32 | dbLoadRecords("../../db/eip_stat.db", "PLC=plc1,IOC=test,TAG=DINT") 33 | #dbLoadRecords("db/ai.db", "PLC=plc1,IOC=test") 34 | #dbLoadRecords("db/ana.db", "PLC=plc1,IOC=test") 35 | #dbLoadRecords("db/bi.db", "PLC=plc1,IOC=test") 36 | #dbLoadRecords("db/bin.db", "PLC=plc1,IOC=test") 37 | #dbLoadRecords("db/mbbi.db", "PLC=plc1,IOC=test") 38 | #dbLoadRecords("db/ao.db", "PLC=plc1,IOC=test") 39 | #dbLoadRecords("db/bo.db", "PLC=plc1,IOC=test") 40 | #dbLoadRecords("db/mbbo.db", "PLC=plc1,IOC=test") 41 | #dbLoadRecords("../../db/test.db", "PLC=plc1,IOC=test") 42 | #dbLoadRecords("../../db/mod.db", "PLC=plc1,IOC=test") 43 | dbLoadRecords("../../db/brad.db", "PLC=plc1,IOC=test") 44 | 45 | iocInit() 46 | 47 | epicsThreadSleep 5 48 | 49 | drvEtherIP_report 10 50 | -------------------------------------------------------------------------------- /iocBoot/iocether_ip/string.db: -------------------------------------------------------------------------------- 1 | # Note that string data type of PLC is longer 2 | # than the string record's value, 3 | # so what you get might be truncated 4 | record(stringin, "string") 5 | { 6 | field(SCAN, "1 second") 7 | field(DTYP, "EtherIP") 8 | field(INP, "@$(PLC) testStr2") 9 | } 10 | 11 | record(stringout, "out") 12 | { 13 | field(DTYP, "EtherIP") 14 | field(OUT, "@$(PLC) testStr2") 15 | } 16 | 17 | record(lsi, "lsi") 18 | { 19 | field(SCAN, "1 second") 20 | field(DTYP, "EtherIP") 21 | field(SIZV, "60") 22 | field(INP, "@$(PLC) testStr3") 23 | } 24 | 25 | record(lso, "lso") 26 | { 27 | field(DTYP, "EtherIP") 28 | field(OUT, "@$(PLC) testStr3") 29 | field(SIZV, "60") 30 | } 31 | 32 | -------------------------------------------------------------------------------- /iocBoot/nfsCommands: -------------------------------------------------------------------------------- 1 | #Instructions for creating and using a real nfsCommands file 2 | # 3 | # in order to use nfs do the following: 4 | # 1) Create hostAdd and nfsMountAll commands for each nfs server 5 | # 2) In each st.cmd file add the following two commands BEFORE any load commands 6 | # ../nfs.cmd 7 | # cd " 8 | # 9 | # The hostAdd and nfsMountAll commands have the form: 10 | # hostAdd("","xxx.xxx.xxx.xxx") 11 | # nfsMountAll("") 12 | # 13 | # You can also mount subdirectories as follows: 14 | # nfsMount("", "/xxx/xxx/xxx", "/xxx") 15 | # 16 | # For example assume 17 | # 18 | # host is mercury with inet address 155.77.2.56 19 | # You want to mount the directory (which is a file system of mercury) 20 | # /home/mercury5/iocinfo 21 | # as 22 | # /iocinfo 23 | # 24 | # The commands would be 25 | # 26 | # hostAdd("mercury","155.77.2.56") 27 | # nfsMountAll("mercury") 28 | # nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") 29 | -------------------------------------------------------------------------------- /testether_ipApp/Db/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile.Host 2 | TOP = ../.. 3 | 4 | include $(TOP)/configure/CONFIG 5 | #---------------------------------------- 6 | # ADD MACRO DEFINITIONS AFTER THIS LINE 7 | 8 | #---------------------------------------------------- 9 | # Add databases, templates, substitutions like this 10 | 11 | DB += ai.db 12 | DB += ana.db 13 | DB += ao.db 14 | DB += bi.db 15 | DB += bin.db 16 | DB += bo.db 17 | DB += mbbi.db 18 | DB += mbbo.db 19 | DB += ramp.db 20 | DB += eip_stat.db 21 | DB += test.db 22 | DB += mod.db 23 | DB += brad.db 24 | DB += brad2.db 25 | 26 | include $(TOP)/configure/RULES 27 | #---------------------------------------- 28 | # ADD RULES AFTER THIS LINE 29 | -------------------------------------------------------------------------------- /testether_ipApp/Db/ai.db: -------------------------------------------------------------------------------- 1 | record(ai, "$(IOC):ai_REAL") 2 | { 3 | field(SCAN, ".1 second") 4 | field(DTYP, "EtherIP") 5 | field(INP, "@plc1 REAL") 6 | field(EGUF, "10") 7 | field(EGU, "Counts") 8 | field(HOPR, "10") 9 | field(LOPR, "0") 10 | field(HIHI, "8") 11 | field(HIGH, "6") 12 | field(LOW, "4") 13 | field(LOLO, "2") 14 | field(HHSV, "MAJOR") 15 | field(HSV, "MINOR") 16 | field(LSV, "MINOR") 17 | field(LLSV, "MAJOR") 18 | } 19 | 20 | record(ai, "$(IOC):ai_DINT") 21 | { 22 | field(SCAN, "I/O Intr") 23 | field(DTYP, "EtherIP") 24 | field(INP, "@plc1 DINT S .05") 25 | field(EGUF, "10") 26 | field(EGU, "Counts") 27 | field(HOPR, "10") 28 | field(LOPR, "0") 29 | field(HIHI, "8") 30 | field(HIGH, "6") 31 | field(LOW, "4") 32 | field(LOLO, "2") 33 | field(HHSV, "MAJOR") 34 | field(HSV, "MINOR") 35 | field(LSV, "MINOR") 36 | field(LLSV, "MAJOR") 37 | } 38 | 39 | record(ai, "$(IOC):ai_USER") 40 | { 41 | field(SCAN, "1 second") 42 | field(DTYP, "EtherIP") 43 | field(INP, "@plc1 USER.REAL") 44 | field(EGUF, "10") 45 | field(EGU, "Counts") 46 | field(HOPR, "10") 47 | field(LOPR, "0") 48 | field(HIHI, "8") 49 | field(HIGH, "6") 50 | field(LOW, "4") 51 | field(LOLO, "2") 52 | field(HHSV, "MAJOR") 53 | field(HSV, "MINOR") 54 | field(LSV, "MINOR") 55 | field(LLSV, "MAJOR") 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /testether_ipApp/Db/ana.db: -------------------------------------------------------------------------------- 1 | record(ai, "$(IOC):ai_REALs0") 2 | { 3 | field(SCAN, "1 second") 4 | field(DTYP, "EtherIP") 5 | field(INP, "@$(PLC) REALs[0]") 6 | field(EGUF, "10") 7 | field(EGU, "socks") 8 | field(HOPR, "10") 9 | field(LOPR, "0") 10 | } 11 | 12 | record(ai, "$(IOC):ai_REALs2_0") 13 | { 14 | field(SCAN, "1 second") 15 | field(DTYP, "EtherIP") 16 | field(INP, "@$(PLC) REALs2[0]") 17 | field(EGUF, "10") 18 | field(EGU, "socks") 19 | field(HOPR, "10") 20 | field(LOPR, "0") 21 | } 22 | 23 | record(ai, "$(IOC):ai_REALs3_0") 24 | { 25 | field(SCAN, "1 second") 26 | field(DTYP, "EtherIP") 27 | field(INP, "@$(PLC) REALs3[0]") 28 | field(EGUF, "10") 29 | field(EGU, "socks") 30 | field(HOPR, "10") 31 | field(LOPR, "0") 32 | } 33 | 34 | record(ai, "$(IOC):ai_REALs1") 35 | { 36 | field(SCAN, "1 second") 37 | field(DTYP, "EtherIP") 38 | field(INP, "@$(PLC) REALs[1]") 39 | field(EGUF, "10") 40 | field(EGU, "socks") 41 | field(HOPR, "10") 42 | field(LOPR, "0") 43 | } 44 | 45 | record(ai, "$(IOC):ai_REALs2_1") 46 | { 47 | field(SCAN, "1 second") 48 | field(DTYP, "EtherIP") 49 | field(INP, "@$(PLC) REALs2[1]") 50 | field(EGUF, "10") 51 | field(EGU, "socks") 52 | field(HOPR, "10") 53 | field(LOPR, "0") 54 | } 55 | 56 | record(ai, "$(IOC):ai_REALs3_1") 57 | { 58 | field(SCAN, "1 second") 59 | field(DTYP, "EtherIP") 60 | field(INP, "@$(PLC) REALs3[1]") 61 | field(EGUF, "10") 62 | field(EGU, "socks") 63 | field(HOPR, "10") 64 | field(LOPR, "0") 65 | } 66 | 67 | record(ai, "$(IOC):ai_REALs2") 68 | { 69 | field(SCAN, "1 second") 70 | field(DTYP, "EtherIP") 71 | field(INP, "@$(PLC) REALs[2]") 72 | field(EGUF, "10") 73 | field(EGU, "socks") 74 | field(HOPR, "10") 75 | field(LOPR, "0") 76 | } 77 | 78 | record(ai, "$(IOC):ai_REALs2_2") 79 | { 80 | field(SCAN, "1 second") 81 | field(DTYP, "EtherIP") 82 | field(INP, "@$(PLC) REALs2[2]") 83 | field(EGUF, "10") 84 | field(EGU, "socks") 85 | field(HOPR, "10") 86 | field(LOPR, "0") 87 | } 88 | 89 | record(ai, "$(IOC):ai_REALs3_2") 90 | { 91 | field(SCAN, "1 second") 92 | field(DTYP, "EtherIP") 93 | field(INP, "@$(PLC) REALs3[2]") 94 | field(EGUF, "10") 95 | field(EGU, "socks") 96 | field(HOPR, "10") 97 | field(LOPR, "0") 98 | } 99 | 100 | record(ai, "$(IOC):ai_REALs3") 101 | { 102 | field(SCAN, "1 second") 103 | field(DTYP, "EtherIP") 104 | field(INP, "@$(PLC) REALs[3]") 105 | field(EGUF, "10") 106 | field(EGU, "socks") 107 | field(HOPR, "10") 108 | field(LOPR, "0") 109 | } 110 | 111 | record(ai, "$(IOC):ai_REALs2_3") 112 | { 113 | field(SCAN, "1 second") 114 | field(DTYP, "EtherIP") 115 | field(INP, "@$(PLC) REALs2[3]") 116 | field(EGUF, "10") 117 | field(EGU, "socks") 118 | field(HOPR, "10") 119 | field(LOPR, "0") 120 | } 121 | 122 | record(ai, "$(IOC):ai_REALs3_3") 123 | { 124 | field(SCAN, "1 second") 125 | field(DTYP, "EtherIP") 126 | field(INP, "@$(PLC) REALs3[3]") 127 | field(EGUF, "10") 128 | field(EGU, "socks") 129 | field(HOPR, "10") 130 | field(LOPR, "0") 131 | } 132 | 133 | record(ai, "$(IOC):ai_REALs4") 134 | { 135 | field(SCAN, "1 second") 136 | field(DTYP, "EtherIP") 137 | field(INP, "@$(PLC) REALs[4]") 138 | field(EGUF, "10") 139 | field(EGU, "socks") 140 | field(HOPR, "10") 141 | field(LOPR, "0") 142 | } 143 | 144 | record(ai, "$(IOC):ai_REALs2_4") 145 | { 146 | field(SCAN, "1 second") 147 | field(DTYP, "EtherIP") 148 | field(INP, "@$(PLC) REALs2[4]") 149 | field(EGUF, "10") 150 | field(EGU, "socks") 151 | field(HOPR, "10") 152 | field(LOPR, "0") 153 | } 154 | 155 | record(ai, "$(IOC):ai_REALs3_4") 156 | { 157 | field(SCAN, "1 second") 158 | field(DTYP, "EtherIP") 159 | field(INP, "@$(PLC) REALs3[4]") 160 | field(EGUF, "10") 161 | field(EGU, "socks") 162 | field(HOPR, "10") 163 | field(LOPR, "0") 164 | } 165 | 166 | record(ai, "$(IOC):ai_REALs5") 167 | { 168 | field(SCAN, "1 second") 169 | field(DTYP, "EtherIP") 170 | field(INP, "@$(PLC) REALs[5]") 171 | field(EGUF, "10") 172 | field(EGU, "socks") 173 | field(HOPR, "10") 174 | field(LOPR, "0") 175 | } 176 | 177 | record(ai, "$(IOC):ai_REALs2_5") 178 | { 179 | field(SCAN, "1 second") 180 | field(DTYP, "EtherIP") 181 | field(INP, "@$(PLC) REALs2[5]") 182 | field(EGUF, "10") 183 | field(EGU, "socks") 184 | field(HOPR, "10") 185 | field(LOPR, "0") 186 | } 187 | 188 | record(ai, "$(IOC):ai_REALs3_5") 189 | { 190 | field(SCAN, "1 second") 191 | field(DTYP, "EtherIP") 192 | field(INP, "@$(PLC) REALs3[5]") 193 | field(EGUF, "10") 194 | field(EGU, "socks") 195 | field(HOPR, "10") 196 | field(LOPR, "0") 197 | } 198 | 199 | record(ai, "$(IOC):ai_REALs6") 200 | { 201 | field(SCAN, "1 second") 202 | field(DTYP, "EtherIP") 203 | field(INP, "@$(PLC) REALs[6]") 204 | field(EGUF, "10") 205 | field(EGU, "socks") 206 | field(HOPR, "10") 207 | field(LOPR, "0") 208 | } 209 | 210 | record(ai, "$(IOC):ai_REALs2_6") 211 | { 212 | field(SCAN, "1 second") 213 | field(DTYP, "EtherIP") 214 | field(INP, "@$(PLC) REALs2[6]") 215 | field(EGUF, "10") 216 | field(EGU, "socks") 217 | field(HOPR, "10") 218 | field(LOPR, "0") 219 | } 220 | 221 | record(ai, "$(IOC):ai_REALs3_6") 222 | { 223 | field(SCAN, "1 second") 224 | field(DTYP, "EtherIP") 225 | field(INP, "@$(PLC) REALs3[6]") 226 | field(EGUF, "10") 227 | field(EGU, "socks") 228 | field(HOPR, "10") 229 | field(LOPR, "0") 230 | } 231 | 232 | record(ai, "$(IOC):ai_REALs7") 233 | { 234 | field(SCAN, "1 second") 235 | field(DTYP, "EtherIP") 236 | field(INP, "@$(PLC) REALs[7]") 237 | field(EGUF, "10") 238 | field(EGU, "socks") 239 | field(HOPR, "10") 240 | field(LOPR, "0") 241 | } 242 | 243 | record(ai, "$(IOC):ai_REALs2_7") 244 | { 245 | field(SCAN, "1 second") 246 | field(DTYP, "EtherIP") 247 | field(INP, "@$(PLC) REALs2[7]") 248 | field(EGUF, "10") 249 | field(EGU, "socks") 250 | field(HOPR, "10") 251 | field(LOPR, "0") 252 | } 253 | 254 | record(ai, "$(IOC):ai_REALs3_7") 255 | { 256 | field(SCAN, "1 second") 257 | field(DTYP, "EtherIP") 258 | field(INP, "@$(PLC) REALs3[7]") 259 | field(EGUF, "10") 260 | field(EGU, "socks") 261 | field(HOPR, "10") 262 | field(LOPR, "0") 263 | } 264 | 265 | record(ai, "$(IOC):ai_REALs8") 266 | { 267 | field(SCAN, "1 second") 268 | field(DTYP, "EtherIP") 269 | field(INP, "@$(PLC) REALs[8]") 270 | field(EGUF, "10") 271 | field(EGU, "socks") 272 | field(HOPR, "10") 273 | field(LOPR, "0") 274 | } 275 | 276 | record(ai, "$(IOC):ai_REALs2_8") 277 | { 278 | field(SCAN, "1 second") 279 | field(DTYP, "EtherIP") 280 | field(INP, "@$(PLC) REALs2[8]") 281 | field(EGUF, "10") 282 | field(EGU, "socks") 283 | field(HOPR, "10") 284 | field(LOPR, "0") 285 | } 286 | 287 | record(ai, "$(IOC):ai_REALs3_8") 288 | { 289 | field(SCAN, "1 second") 290 | field(DTYP, "EtherIP") 291 | field(INP, "@$(PLC) REALs3[8]") 292 | field(EGUF, "10") 293 | field(EGU, "socks") 294 | field(HOPR, "10") 295 | field(LOPR, "0") 296 | } 297 | 298 | record(ai, "$(IOC):ai_REALs9") 299 | { 300 | field(SCAN, "1 second") 301 | field(DTYP, "EtherIP") 302 | field(INP, "@$(PLC) REALs[9]") 303 | field(EGUF, "10") 304 | field(EGU, "socks") 305 | field(HOPR, "10") 306 | field(LOPR, "0") 307 | } 308 | 309 | record(ai, "$(IOC):ai_REALs2_9") 310 | { 311 | field(SCAN, "1 second") 312 | field(DTYP, "EtherIP") 313 | field(INP, "@$(PLC) REALs2[9]") 314 | field(EGUF, "10") 315 | field(EGU, "socks") 316 | field(HOPR, "10") 317 | field(LOPR, "0") 318 | } 319 | 320 | record(ai, "$(IOC):ai_REALs3_9") 321 | { 322 | field(SCAN, "1 second") 323 | field(DTYP, "EtherIP") 324 | field(INP, "@$(PLC) REALs3[9]") 325 | field(EGUF, "10") 326 | field(EGU, "socks") 327 | field(HOPR, "10") 328 | field(LOPR, "0") 329 | } 330 | 331 | record(ai, "$(IOC):ai_REALs10") 332 | { 333 | field(SCAN, "1 second") 334 | field(DTYP, "EtherIP") 335 | field(INP, "@$(PLC) REALs[10]") 336 | field(EGUF, "10") 337 | field(EGU, "socks") 338 | field(HOPR, "10") 339 | field(LOPR, "0") 340 | } 341 | 342 | record(ai, "$(IOC):ai_REALs2_10") 343 | { 344 | field(SCAN, "1 second") 345 | field(DTYP, "EtherIP") 346 | field(INP, "@$(PLC) REALs2[10]") 347 | field(EGUF, "10") 348 | field(EGU, "socks") 349 | field(HOPR, "10") 350 | field(LOPR, "0") 351 | } 352 | 353 | record(ai, "$(IOC):ai_REALs3_10") 354 | { 355 | field(SCAN, "1 second") 356 | field(DTYP, "EtherIP") 357 | field(INP, "@$(PLC) REALs3[10]") 358 | field(EGUF, "10") 359 | field(EGU, "socks") 360 | field(HOPR, "10") 361 | field(LOPR, "0") 362 | } 363 | 364 | record(ai, "$(IOC):ai_REALs11") 365 | { 366 | field(SCAN, "1 second") 367 | field(DTYP, "EtherIP") 368 | field(INP, "@$(PLC) REALs[11]") 369 | field(EGUF, "10") 370 | field(EGU, "socks") 371 | field(HOPR, "10") 372 | field(LOPR, "0") 373 | } 374 | 375 | record(ai, "$(IOC):ai_REALs2_11") 376 | { 377 | field(SCAN, "1 second") 378 | field(DTYP, "EtherIP") 379 | field(INP, "@$(PLC) REALs2[11]") 380 | field(EGUF, "10") 381 | field(EGU, "socks") 382 | field(HOPR, "10") 383 | field(LOPR, "0") 384 | } 385 | 386 | record(ai, "$(IOC):ai_REALs3_11") 387 | { 388 | field(SCAN, "1 second") 389 | field(DTYP, "EtherIP") 390 | field(INP, "@$(PLC) REALs3[11]") 391 | field(EGUF, "10") 392 | field(EGU, "socks") 393 | field(HOPR, "10") 394 | field(LOPR, "0") 395 | } 396 | 397 | record(ai, "$(IOC):ai_REALs12") 398 | { 399 | field(SCAN, "1 second") 400 | field(DTYP, "EtherIP") 401 | field(INP, "@$(PLC) REALs[12]") 402 | field(EGUF, "10") 403 | field(EGU, "socks") 404 | field(HOPR, "10") 405 | field(LOPR, "0") 406 | } 407 | 408 | record(ai, "$(IOC):ai_REALs2_12") 409 | { 410 | field(SCAN, "1 second") 411 | field(DTYP, "EtherIP") 412 | field(INP, "@$(PLC) REALs2[12]") 413 | field(EGUF, "10") 414 | field(EGU, "socks") 415 | field(HOPR, "10") 416 | field(LOPR, "0") 417 | } 418 | 419 | record(ai, "$(IOC):ai_REALs3_12") 420 | { 421 | field(SCAN, "1 second") 422 | field(DTYP, "EtherIP") 423 | field(INP, "@$(PLC) REALs3[12]") 424 | field(EGUF, "10") 425 | field(EGU, "socks") 426 | field(HOPR, "10") 427 | field(LOPR, "0") 428 | } 429 | 430 | record(ai, "$(IOC):ai_REALs13") 431 | { 432 | field(SCAN, "1 second") 433 | field(DTYP, "EtherIP") 434 | field(INP, "@$(PLC) REALs[13]") 435 | field(EGUF, "10") 436 | field(EGU, "socks") 437 | field(HOPR, "10") 438 | field(LOPR, "0") 439 | } 440 | 441 | record(ai, "$(IOC):ai_REALs2_13") 442 | { 443 | field(SCAN, "1 second") 444 | field(DTYP, "EtherIP") 445 | field(INP, "@$(PLC) REALs2[13]") 446 | field(EGUF, "10") 447 | field(EGU, "socks") 448 | field(HOPR, "10") 449 | field(LOPR, "0") 450 | } 451 | 452 | record(ai, "$(IOC):ai_REALs3_13") 453 | { 454 | field(SCAN, "1 second") 455 | field(DTYP, "EtherIP") 456 | field(INP, "@$(PLC) REALs3[13]") 457 | field(EGUF, "10") 458 | field(EGU, "socks") 459 | field(HOPR, "10") 460 | field(LOPR, "0") 461 | } 462 | 463 | record(ai, "$(IOC):ai_REALs14") 464 | { 465 | field(SCAN, "1 second") 466 | field(DTYP, "EtherIP") 467 | field(INP, "@$(PLC) REALs[14]") 468 | field(EGUF, "10") 469 | field(EGU, "socks") 470 | field(HOPR, "10") 471 | field(LOPR, "0") 472 | } 473 | 474 | record(ai, "$(IOC):ai_REALs2_14") 475 | { 476 | field(SCAN, "1 second") 477 | field(DTYP, "EtherIP") 478 | field(INP, "@$(PLC) REALs2[14]") 479 | field(EGUF, "10") 480 | field(EGU, "socks") 481 | field(HOPR, "10") 482 | field(LOPR, "0") 483 | } 484 | 485 | record(ai, "$(IOC):ai_REALs3_14") 486 | { 487 | field(SCAN, "1 second") 488 | field(DTYP, "EtherIP") 489 | field(INP, "@$(PLC) REALs3[14]") 490 | field(EGUF, "10") 491 | field(EGU, "socks") 492 | field(HOPR, "10") 493 | field(LOPR, "0") 494 | } 495 | 496 | record(ai, "$(IOC):ai_REALs15") 497 | { 498 | field(SCAN, "1 second") 499 | field(DTYP, "EtherIP") 500 | field(INP, "@$(PLC) REALs[15]") 501 | field(EGUF, "10") 502 | field(EGU, "socks") 503 | field(HOPR, "10") 504 | field(LOPR, "0") 505 | } 506 | 507 | record(ai, "$(IOC):ai_REALs2_15") 508 | { 509 | field(SCAN, "1 second") 510 | field(DTYP, "EtherIP") 511 | field(INP, "@$(PLC) REALs2[15]") 512 | field(EGUF, "10") 513 | field(EGU, "socks") 514 | field(HOPR, "10") 515 | field(LOPR, "0") 516 | } 517 | 518 | record(ai, "$(IOC):ai_REALs3_15") 519 | { 520 | field(SCAN, "1 second") 521 | field(DTYP, "EtherIP") 522 | field(INP, "@$(PLC) REALs3[15]") 523 | field(EGUF, "10") 524 | field(EGU, "socks") 525 | field(HOPR, "10") 526 | field(LOPR, "0") 527 | } 528 | 529 | record(ai, "$(IOC):ai_REALs16") 530 | { 531 | field(SCAN, "1 second") 532 | field(DTYP, "EtherIP") 533 | field(INP, "@$(PLC) REALs[16]") 534 | field(EGUF, "10") 535 | field(EGU, "socks") 536 | field(HOPR, "10") 537 | field(LOPR, "0") 538 | } 539 | 540 | record(ai, "$(IOC):ai_REALs2_16") 541 | { 542 | field(SCAN, "1 second") 543 | field(DTYP, "EtherIP") 544 | field(INP, "@$(PLC) REALs2[16]") 545 | field(EGUF, "10") 546 | field(EGU, "socks") 547 | field(HOPR, "10") 548 | field(LOPR, "0") 549 | } 550 | 551 | record(ai, "$(IOC):ai_REALs3_16") 552 | { 553 | field(SCAN, "1 second") 554 | field(DTYP, "EtherIP") 555 | field(INP, "@$(PLC) REALs3[16]") 556 | field(EGUF, "10") 557 | field(EGU, "socks") 558 | field(HOPR, "10") 559 | field(LOPR, "0") 560 | } 561 | 562 | record(ai, "$(IOC):ai_REALs17") 563 | { 564 | field(SCAN, "1 second") 565 | field(DTYP, "EtherIP") 566 | field(INP, "@$(PLC) REALs[17]") 567 | field(EGUF, "10") 568 | field(EGU, "socks") 569 | field(HOPR, "10") 570 | field(LOPR, "0") 571 | } 572 | 573 | record(ai, "$(IOC):ai_REALs2_17") 574 | { 575 | field(SCAN, "1 second") 576 | field(DTYP, "EtherIP") 577 | field(INP, "@$(PLC) REALs2[17]") 578 | field(EGUF, "10") 579 | field(EGU, "socks") 580 | field(HOPR, "10") 581 | field(LOPR, "0") 582 | } 583 | 584 | record(ai, "$(IOC):ai_REALs3_17") 585 | { 586 | field(SCAN, "1 second") 587 | field(DTYP, "EtherIP") 588 | field(INP, "@$(PLC) REALs3[17]") 589 | field(EGUF, "10") 590 | field(EGU, "socks") 591 | field(HOPR, "10") 592 | field(LOPR, "0") 593 | } 594 | 595 | record(ai, "$(IOC):ai_REALs18") 596 | { 597 | field(SCAN, "1 second") 598 | field(DTYP, "EtherIP") 599 | field(INP, "@$(PLC) REALs[18]") 600 | field(EGUF, "10") 601 | field(EGU, "socks") 602 | field(HOPR, "10") 603 | field(LOPR, "0") 604 | } 605 | 606 | record(ai, "$(IOC):ai_REALs2_18") 607 | { 608 | field(SCAN, "1 second") 609 | field(DTYP, "EtherIP") 610 | field(INP, "@$(PLC) REALs2[18]") 611 | field(EGUF, "10") 612 | field(EGU, "socks") 613 | field(HOPR, "10") 614 | field(LOPR, "0") 615 | } 616 | 617 | record(ai, "$(IOC):ai_REALs3_18") 618 | { 619 | field(SCAN, "1 second") 620 | field(DTYP, "EtherIP") 621 | field(INP, "@$(PLC) REALs3[18]") 622 | field(EGUF, "10") 623 | field(EGU, "socks") 624 | field(HOPR, "10") 625 | field(LOPR, "0") 626 | } 627 | 628 | record(ai, "$(IOC):ai_REALs19") 629 | { 630 | field(SCAN, "1 second") 631 | field(DTYP, "EtherIP") 632 | field(INP, "@$(PLC) REALs[19]") 633 | field(EGUF, "10") 634 | field(EGU, "socks") 635 | field(HOPR, "10") 636 | field(LOPR, "0") 637 | } 638 | 639 | record(ai, "$(IOC):ai_REALs2_19") 640 | { 641 | field(SCAN, "1 second") 642 | field(DTYP, "EtherIP") 643 | field(INP, "@$(PLC) REALs2[19]") 644 | field(EGUF, "10") 645 | field(EGU, "socks") 646 | field(HOPR, "10") 647 | field(LOPR, "0") 648 | } 649 | 650 | record(ai, "$(IOC):ai_REALs3_19") 651 | { 652 | field(SCAN, "1 second") 653 | field(DTYP, "EtherIP") 654 | field(INP, "@$(PLC) REALs3[19]") 655 | field(EGUF, "10") 656 | field(EGU, "socks") 657 | field(HOPR, "10") 658 | field(LOPR, "0") 659 | } 660 | 661 | record(ai, "$(IOC):ai_REALs20") 662 | { 663 | field(SCAN, "1 second") 664 | field(DTYP, "EtherIP") 665 | field(INP, "@$(PLC) REALs[20]") 666 | field(EGUF, "10") 667 | field(EGU, "socks") 668 | field(HOPR, "10") 669 | field(LOPR, "0") 670 | } 671 | 672 | record(ai, "$(IOC):ai_REALs2_20") 673 | { 674 | field(SCAN, "1 second") 675 | field(DTYP, "EtherIP") 676 | field(INP, "@$(PLC) REALs2[20]") 677 | field(EGUF, "10") 678 | field(EGU, "socks") 679 | field(HOPR, "10") 680 | field(LOPR, "0") 681 | } 682 | 683 | record(ai, "$(IOC):ai_REALs3_20") 684 | { 685 | field(SCAN, "1 second") 686 | field(DTYP, "EtherIP") 687 | field(INP, "@$(PLC) REALs3[20]") 688 | field(EGUF, "10") 689 | field(EGU, "socks") 690 | field(HOPR, "10") 691 | field(LOPR, "0") 692 | } 693 | 694 | record(ai, "$(IOC):ai_REALs21") 695 | { 696 | field(SCAN, "1 second") 697 | field(DTYP, "EtherIP") 698 | field(INP, "@$(PLC) REALs[21]") 699 | field(EGUF, "10") 700 | field(EGU, "socks") 701 | field(HOPR, "10") 702 | field(LOPR, "0") 703 | } 704 | 705 | record(ai, "$(IOC):ai_REALs2_21") 706 | { 707 | field(SCAN, "1 second") 708 | field(DTYP, "EtherIP") 709 | field(INP, "@$(PLC) REALs2[21]") 710 | field(EGUF, "10") 711 | field(EGU, "socks") 712 | field(HOPR, "10") 713 | field(LOPR, "0") 714 | } 715 | 716 | record(ai, "$(IOC):ai_REALs3_21") 717 | { 718 | field(SCAN, "1 second") 719 | field(DTYP, "EtherIP") 720 | field(INP, "@$(PLC) REALs3[21]") 721 | field(EGUF, "10") 722 | field(EGU, "socks") 723 | field(HOPR, "10") 724 | field(LOPR, "0") 725 | } 726 | 727 | record(ai, "$(IOC):ai_REALs22") 728 | { 729 | field(SCAN, "1 second") 730 | field(DTYP, "EtherIP") 731 | field(INP, "@$(PLC) REALs[22]") 732 | field(EGUF, "10") 733 | field(EGU, "socks") 734 | field(HOPR, "10") 735 | field(LOPR, "0") 736 | } 737 | 738 | record(ai, "$(IOC):ai_REALs2_22") 739 | { 740 | field(SCAN, "1 second") 741 | field(DTYP, "EtherIP") 742 | field(INP, "@$(PLC) REALs2[22]") 743 | field(EGUF, "10") 744 | field(EGU, "socks") 745 | field(HOPR, "10") 746 | field(LOPR, "0") 747 | } 748 | 749 | record(ai, "$(IOC):ai_REALs3_22") 750 | { 751 | field(SCAN, "1 second") 752 | field(DTYP, "EtherIP") 753 | field(INP, "@$(PLC) REALs3[22]") 754 | field(EGUF, "10") 755 | field(EGU, "socks") 756 | field(HOPR, "10") 757 | field(LOPR, "0") 758 | } 759 | 760 | record(ai, "$(IOC):ai_REALs23") 761 | { 762 | field(SCAN, "1 second") 763 | field(DTYP, "EtherIP") 764 | field(INP, "@$(PLC) REALs[23]") 765 | field(EGUF, "10") 766 | field(EGU, "socks") 767 | field(HOPR, "10") 768 | field(LOPR, "0") 769 | } 770 | 771 | record(ai, "$(IOC):ai_REALs2_23") 772 | { 773 | field(SCAN, "1 second") 774 | field(DTYP, "EtherIP") 775 | field(INP, "@$(PLC) REALs2[23]") 776 | field(EGUF, "10") 777 | field(EGU, "socks") 778 | field(HOPR, "10") 779 | field(LOPR, "0") 780 | } 781 | 782 | record(ai, "$(IOC):ai_REALs3_23") 783 | { 784 | field(SCAN, "1 second") 785 | field(DTYP, "EtherIP") 786 | field(INP, "@$(PLC) REALs3[23]") 787 | field(EGUF, "10") 788 | field(EGU, "socks") 789 | field(HOPR, "10") 790 | field(LOPR, "0") 791 | } 792 | 793 | record(ai, "$(IOC):ai_REALs24") 794 | { 795 | field(SCAN, "1 second") 796 | field(DTYP, "EtherIP") 797 | field(INP, "@$(PLC) REALs[24]") 798 | field(EGUF, "10") 799 | field(EGU, "socks") 800 | field(HOPR, "10") 801 | field(LOPR, "0") 802 | } 803 | 804 | record(ai, "$(IOC):ai_REALs2_24") 805 | { 806 | field(SCAN, "1 second") 807 | field(DTYP, "EtherIP") 808 | field(INP, "@$(PLC) REALs2[24]") 809 | field(EGUF, "10") 810 | field(EGU, "socks") 811 | field(HOPR, "10") 812 | field(LOPR, "0") 813 | } 814 | 815 | record(ai, "$(IOC):ai_REALs3_24") 816 | { 817 | field(SCAN, "1 second") 818 | field(DTYP, "EtherIP") 819 | field(INP, "@$(PLC) REALs3[24]") 820 | field(EGUF, "10") 821 | field(EGU, "socks") 822 | field(HOPR, "10") 823 | field(LOPR, "0") 824 | } 825 | 826 | record(ai, "$(IOC):ai_REALs25") 827 | { 828 | field(SCAN, "1 second") 829 | field(DTYP, "EtherIP") 830 | field(INP, "@$(PLC) REALs[25]") 831 | field(EGUF, "10") 832 | field(EGU, "socks") 833 | field(HOPR, "10") 834 | field(LOPR, "0") 835 | } 836 | 837 | record(ai, "$(IOC):ai_REALs2_25") 838 | { 839 | field(SCAN, "1 second") 840 | field(DTYP, "EtherIP") 841 | field(INP, "@$(PLC) REALs2[25]") 842 | field(EGUF, "10") 843 | field(EGU, "socks") 844 | field(HOPR, "10") 845 | field(LOPR, "0") 846 | } 847 | 848 | record(ai, "$(IOC):ai_REALs3_25") 849 | { 850 | field(SCAN, "1 second") 851 | field(DTYP, "EtherIP") 852 | field(INP, "@$(PLC) REALs3[25]") 853 | field(EGUF, "10") 854 | field(EGU, "socks") 855 | field(HOPR, "10") 856 | field(LOPR, "0") 857 | } 858 | 859 | record(ai, "$(IOC):ai_REALs26") 860 | { 861 | field(SCAN, "1 second") 862 | field(DTYP, "EtherIP") 863 | field(INP, "@$(PLC) REALs[26]") 864 | field(EGUF, "10") 865 | field(EGU, "socks") 866 | field(HOPR, "10") 867 | field(LOPR, "0") 868 | } 869 | 870 | record(ai, "$(IOC):ai_REALs2_26") 871 | { 872 | field(SCAN, "1 second") 873 | field(DTYP, "EtherIP") 874 | field(INP, "@$(PLC) REALs2[26]") 875 | field(EGUF, "10") 876 | field(EGU, "socks") 877 | field(HOPR, "10") 878 | field(LOPR, "0") 879 | } 880 | 881 | record(ai, "$(IOC):ai_REALs3_26") 882 | { 883 | field(SCAN, "1 second") 884 | field(DTYP, "EtherIP") 885 | field(INP, "@$(PLC) REALs3[26]") 886 | field(EGUF, "10") 887 | field(EGU, "socks") 888 | field(HOPR, "10") 889 | field(LOPR, "0") 890 | } 891 | 892 | record(ai, "$(IOC):ai_REALs27") 893 | { 894 | field(SCAN, "1 second") 895 | field(DTYP, "EtherIP") 896 | field(INP, "@$(PLC) REALs[27]") 897 | field(EGUF, "10") 898 | field(EGU, "socks") 899 | field(HOPR, "10") 900 | field(LOPR, "0") 901 | } 902 | 903 | record(ai, "$(IOC):ai_REALs2_27") 904 | { 905 | field(SCAN, "1 second") 906 | field(DTYP, "EtherIP") 907 | field(INP, "@$(PLC) REALs2[27]") 908 | field(EGUF, "10") 909 | field(EGU, "socks") 910 | field(HOPR, "10") 911 | field(LOPR, "0") 912 | } 913 | 914 | record(ai, "$(IOC):ai_REALs3_27") 915 | { 916 | field(SCAN, "1 second") 917 | field(DTYP, "EtherIP") 918 | field(INP, "@$(PLC) REALs3[27]") 919 | field(EGUF, "10") 920 | field(EGU, "socks") 921 | field(HOPR, "10") 922 | field(LOPR, "0") 923 | } 924 | 925 | record(ai, "$(IOC):ai_REALs28") 926 | { 927 | field(SCAN, "1 second") 928 | field(DTYP, "EtherIP") 929 | field(INP, "@$(PLC) REALs[28]") 930 | field(EGUF, "10") 931 | field(EGU, "socks") 932 | field(HOPR, "10") 933 | field(LOPR, "0") 934 | } 935 | 936 | record(ai, "$(IOC):ai_REALs2_28") 937 | { 938 | field(SCAN, "1 second") 939 | field(DTYP, "EtherIP") 940 | field(INP, "@$(PLC) REALs2[28]") 941 | field(EGUF, "10") 942 | field(EGU, "socks") 943 | field(HOPR, "10") 944 | field(LOPR, "0") 945 | } 946 | 947 | record(ai, "$(IOC):ai_REALs3_28") 948 | { 949 | field(SCAN, "1 second") 950 | field(DTYP, "EtherIP") 951 | field(INP, "@$(PLC) REALs3[28]") 952 | field(EGUF, "10") 953 | field(EGU, "socks") 954 | field(HOPR, "10") 955 | field(LOPR, "0") 956 | } 957 | 958 | record(ai, "$(IOC):ai_REALs29") 959 | { 960 | field(SCAN, "1 second") 961 | field(DTYP, "EtherIP") 962 | field(INP, "@$(PLC) REALs[29]") 963 | field(EGUF, "10") 964 | field(EGU, "socks") 965 | field(HOPR, "10") 966 | field(LOPR, "0") 967 | } 968 | 969 | record(ai, "$(IOC):ai_REALs2_29") 970 | { 971 | field(SCAN, "1 second") 972 | field(DTYP, "EtherIP") 973 | field(INP, "@$(PLC) REALs2[29]") 974 | field(EGUF, "10") 975 | field(EGU, "socks") 976 | field(HOPR, "10") 977 | field(LOPR, "0") 978 | } 979 | 980 | record(ai, "$(IOC):ai_REALs3_29") 981 | { 982 | field(SCAN, "1 second") 983 | field(DTYP, "EtherIP") 984 | field(INP, "@$(PLC) REALs3[29]") 985 | field(EGUF, "10") 986 | field(EGU, "socks") 987 | field(HOPR, "10") 988 | field(LOPR, "0") 989 | } 990 | 991 | record(ai, "$(IOC):ai_REALs30") 992 | { 993 | field(SCAN, "1 second") 994 | field(DTYP, "EtherIP") 995 | field(INP, "@$(PLC) REALs[30]") 996 | field(EGUF, "10") 997 | field(EGU, "socks") 998 | field(HOPR, "10") 999 | field(LOPR, "0") 1000 | } 1001 | 1002 | record(ai, "$(IOC):ai_REALs2_30") 1003 | { 1004 | field(SCAN, "1 second") 1005 | field(DTYP, "EtherIP") 1006 | field(INP, "@$(PLC) REALs2[30]") 1007 | field(EGUF, "10") 1008 | field(EGU, "socks") 1009 | field(HOPR, "10") 1010 | field(LOPR, "0") 1011 | } 1012 | 1013 | record(ai, "$(IOC):ai_REALs3_30") 1014 | { 1015 | field(SCAN, "1 second") 1016 | field(DTYP, "EtherIP") 1017 | field(INP, "@$(PLC) REALs3[30]") 1018 | field(EGUF, "10") 1019 | field(EGU, "socks") 1020 | field(HOPR, "10") 1021 | field(LOPR, "0") 1022 | } 1023 | 1024 | record(ai, "$(IOC):ai_REALs31") 1025 | { 1026 | field(SCAN, "1 second") 1027 | field(DTYP, "EtherIP") 1028 | field(INP, "@$(PLC) REALs[31]") 1029 | field(EGUF, "10") 1030 | field(EGU, "socks") 1031 | field(HOPR, "10") 1032 | field(LOPR, "0") 1033 | } 1034 | 1035 | record(ai, "$(IOC):ai_REALs2_31") 1036 | { 1037 | field(SCAN, "1 second") 1038 | field(DTYP, "EtherIP") 1039 | field(INP, "@$(PLC) REALs2[31]") 1040 | field(EGUF, "10") 1041 | field(EGU, "socks") 1042 | field(HOPR, "10") 1043 | field(LOPR, "0") 1044 | } 1045 | 1046 | record(ai, "$(IOC):ai_REALs3_31") 1047 | { 1048 | field(SCAN, "1 second") 1049 | field(DTYP, "EtherIP") 1050 | field(INP, "@$(PLC) REALs3[31]") 1051 | field(EGUF, "10") 1052 | field(EGU, "socks") 1053 | field(HOPR, "10") 1054 | field(LOPR, "0") 1055 | } 1056 | 1057 | record(ai, "$(IOC):ai_REALs32") 1058 | { 1059 | field(SCAN, "1 second") 1060 | field(DTYP, "EtherIP") 1061 | field(INP, "@$(PLC) REALs[32]") 1062 | field(EGUF, "10") 1063 | field(EGU, "socks") 1064 | field(HOPR, "10") 1065 | field(LOPR, "0") 1066 | } 1067 | 1068 | record(ai, "$(IOC):ai_REALs2_32") 1069 | { 1070 | field(SCAN, "1 second") 1071 | field(DTYP, "EtherIP") 1072 | field(INP, "@$(PLC) REALs2[32]") 1073 | field(EGUF, "10") 1074 | field(EGU, "socks") 1075 | field(HOPR, "10") 1076 | field(LOPR, "0") 1077 | } 1078 | 1079 | record(ai, "$(IOC):ai_REALs3_32") 1080 | { 1081 | field(SCAN, "1 second") 1082 | field(DTYP, "EtherIP") 1083 | field(INP, "@$(PLC) REALs3[32]") 1084 | field(EGUF, "10") 1085 | field(EGU, "socks") 1086 | field(HOPR, "10") 1087 | field(LOPR, "0") 1088 | } 1089 | 1090 | record(ai, "$(IOC):ai_REALs33") 1091 | { 1092 | field(SCAN, "1 second") 1093 | field(DTYP, "EtherIP") 1094 | field(INP, "@$(PLC) REALs[33]") 1095 | field(EGUF, "10") 1096 | field(EGU, "socks") 1097 | field(HOPR, "10") 1098 | field(LOPR, "0") 1099 | } 1100 | 1101 | record(ai, "$(IOC):ai_REALs2_33") 1102 | { 1103 | field(SCAN, "1 second") 1104 | field(DTYP, "EtherIP") 1105 | field(INP, "@$(PLC) REALs2[33]") 1106 | field(EGUF, "10") 1107 | field(EGU, "socks") 1108 | field(HOPR, "10") 1109 | field(LOPR, "0") 1110 | } 1111 | 1112 | record(ai, "$(IOC):ai_REALs3_33") 1113 | { 1114 | field(SCAN, "1 second") 1115 | field(DTYP, "EtherIP") 1116 | field(INP, "@$(PLC) REALs3[33]") 1117 | field(EGUF, "10") 1118 | field(EGU, "socks") 1119 | field(HOPR, "10") 1120 | field(LOPR, "0") 1121 | } 1122 | 1123 | record(ai, "$(IOC):ai_REALs34") 1124 | { 1125 | field(SCAN, "1 second") 1126 | field(DTYP, "EtherIP") 1127 | field(INP, "@$(PLC) REALs[34]") 1128 | field(EGUF, "10") 1129 | field(EGU, "socks") 1130 | field(HOPR, "10") 1131 | field(LOPR, "0") 1132 | } 1133 | 1134 | record(ai, "$(IOC):ai_REALs2_34") 1135 | { 1136 | field(SCAN, "1 second") 1137 | field(DTYP, "EtherIP") 1138 | field(INP, "@$(PLC) REALs2[34]") 1139 | field(EGUF, "10") 1140 | field(EGU, "socks") 1141 | field(HOPR, "10") 1142 | field(LOPR, "0") 1143 | } 1144 | 1145 | record(ai, "$(IOC):ai_REALs3_34") 1146 | { 1147 | field(SCAN, "1 second") 1148 | field(DTYP, "EtherIP") 1149 | field(INP, "@$(PLC) REALs3[34]") 1150 | field(EGUF, "10") 1151 | field(EGU, "socks") 1152 | field(HOPR, "10") 1153 | field(LOPR, "0") 1154 | } 1155 | 1156 | record(ai, "$(IOC):ai_REALs35") 1157 | { 1158 | field(SCAN, "1 second") 1159 | field(DTYP, "EtherIP") 1160 | field(INP, "@$(PLC) REALs[35]") 1161 | field(EGUF, "10") 1162 | field(EGU, "socks") 1163 | field(HOPR, "10") 1164 | field(LOPR, "0") 1165 | } 1166 | 1167 | record(ai, "$(IOC):ai_REALs2_35") 1168 | { 1169 | field(SCAN, "1 second") 1170 | field(DTYP, "EtherIP") 1171 | field(INP, "@$(PLC) REALs2[35]") 1172 | field(EGUF, "10") 1173 | field(EGU, "socks") 1174 | field(HOPR, "10") 1175 | field(LOPR, "0") 1176 | } 1177 | 1178 | record(ai, "$(IOC):ai_REALs3_35") 1179 | { 1180 | field(SCAN, "1 second") 1181 | field(DTYP, "EtherIP") 1182 | field(INP, "@$(PLC) REALs3[35]") 1183 | field(EGUF, "10") 1184 | field(EGU, "socks") 1185 | field(HOPR, "10") 1186 | field(LOPR, "0") 1187 | } 1188 | 1189 | record(ai, "$(IOC):ai_REALs36") 1190 | { 1191 | field(SCAN, "1 second") 1192 | field(DTYP, "EtherIP") 1193 | field(INP, "@$(PLC) REALs[36]") 1194 | field(EGUF, "10") 1195 | field(EGU, "socks") 1196 | field(HOPR, "10") 1197 | field(LOPR, "0") 1198 | } 1199 | 1200 | record(ai, "$(IOC):ai_REALs2_36") 1201 | { 1202 | field(SCAN, "1 second") 1203 | field(DTYP, "EtherIP") 1204 | field(INP, "@$(PLC) REALs2[36]") 1205 | field(EGUF, "10") 1206 | field(EGU, "socks") 1207 | field(HOPR, "10") 1208 | field(LOPR, "0") 1209 | } 1210 | 1211 | record(ai, "$(IOC):ai_REALs3_36") 1212 | { 1213 | field(SCAN, "1 second") 1214 | field(DTYP, "EtherIP") 1215 | field(INP, "@$(PLC) REALs3[36]") 1216 | field(EGUF, "10") 1217 | field(EGU, "socks") 1218 | field(HOPR, "10") 1219 | field(LOPR, "0") 1220 | } 1221 | 1222 | record(ai, "$(IOC):ai_REALs37") 1223 | { 1224 | field(SCAN, "1 second") 1225 | field(DTYP, "EtherIP") 1226 | field(INP, "@$(PLC) REALs[37]") 1227 | field(EGUF, "10") 1228 | field(EGU, "socks") 1229 | field(HOPR, "10") 1230 | field(LOPR, "0") 1231 | } 1232 | 1233 | record(ai, "$(IOC):ai_REALs2_37") 1234 | { 1235 | field(SCAN, "1 second") 1236 | field(DTYP, "EtherIP") 1237 | field(INP, "@$(PLC) REALs2[37]") 1238 | field(EGUF, "10") 1239 | field(EGU, "socks") 1240 | field(HOPR, "10") 1241 | field(LOPR, "0") 1242 | } 1243 | 1244 | record(ai, "$(IOC):ai_REALs3_37") 1245 | { 1246 | field(SCAN, "1 second") 1247 | field(DTYP, "EtherIP") 1248 | field(INP, "@$(PLC) REALs3[37]") 1249 | field(EGUF, "10") 1250 | field(EGU, "socks") 1251 | field(HOPR, "10") 1252 | field(LOPR, "0") 1253 | } 1254 | 1255 | record(ai, "$(IOC):ai_REALs38") 1256 | { 1257 | field(SCAN, "1 second") 1258 | field(DTYP, "EtherIP") 1259 | field(INP, "@$(PLC) REALs[38]") 1260 | field(EGUF, "10") 1261 | field(EGU, "socks") 1262 | field(HOPR, "10") 1263 | field(LOPR, "0") 1264 | } 1265 | 1266 | record(ai, "$(IOC):ai_REALs2_38") 1267 | { 1268 | field(SCAN, "1 second") 1269 | field(DTYP, "EtherIP") 1270 | field(INP, "@$(PLC) REALs2[38]") 1271 | field(EGUF, "10") 1272 | field(EGU, "socks") 1273 | field(HOPR, "10") 1274 | field(LOPR, "0") 1275 | } 1276 | 1277 | record(ai, "$(IOC):ai_REALs3_38") 1278 | { 1279 | field(SCAN, "1 second") 1280 | field(DTYP, "EtherIP") 1281 | field(INP, "@$(PLC) REALs3[38]") 1282 | field(EGUF, "10") 1283 | field(EGU, "socks") 1284 | field(HOPR, "10") 1285 | field(LOPR, "0") 1286 | } 1287 | 1288 | record(ai, "$(IOC):ai_REALs39") 1289 | { 1290 | field(SCAN, "1 second") 1291 | field(DTYP, "EtherIP") 1292 | field(INP, "@$(PLC) REALs[39]") 1293 | field(EGUF, "10") 1294 | field(EGU, "socks") 1295 | field(HOPR, "10") 1296 | field(LOPR, "0") 1297 | } 1298 | 1299 | record(ai, "$(IOC):ai_REALs2_39") 1300 | { 1301 | field(SCAN, "1 second") 1302 | field(DTYP, "EtherIP") 1303 | field(INP, "@$(PLC) REALs2[39]") 1304 | field(EGUF, "10") 1305 | field(EGU, "socks") 1306 | field(HOPR, "10") 1307 | field(LOPR, "0") 1308 | } 1309 | 1310 | record(ai, "$(IOC):ai_REALs3_39") 1311 | { 1312 | field(SCAN, "1 second") 1313 | field(DTYP, "EtherIP") 1314 | field(INP, "@$(PLC) REALs3[39]") 1315 | field(EGUF, "10") 1316 | field(EGU, "socks") 1317 | field(HOPR, "10") 1318 | field(LOPR, "0") 1319 | } 1320 | 1321 | -------------------------------------------------------------------------------- /testether_ipApp/Db/ao.db: -------------------------------------------------------------------------------- 1 | 2 | record(ao, "$(IOC):ao_REAL2") 3 | { 4 | field(SCAN, "Passive") 5 | field(DTYP, "EtherIP") 6 | field(OUT, "@$(PLC) REAL2 S 1") 7 | field(HOPR, "10.0") 8 | field(LOPR, "0.0") 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /testether_ipApp/Db/bi.db: -------------------------------------------------------------------------------- 1 | 2 | record(bi, "$(IOC):BOOL") 3 | { 4 | field(SCAN, ".5 second") 5 | field(DTYP, "EtherIP") 6 | field(INP, "@$(PLC) BOOL") 7 | field(ZNAM, "False") 8 | field(ONAM, "True") 9 | field(ZSV, "NO_ALARM") 10 | field(OSV, "MAJOR") 11 | } 12 | 13 | record(bi, "$(IOC):bi_REAL") 14 | { 15 | field(SCAN, ".5 second") 16 | field(DTYP, "EtherIP") 17 | field(INP, "@$(PLC) REAL") 18 | field(ZNAM, "False") 19 | field(ONAM, "True") 20 | field(ZSV, "NO_ALARM") 21 | field(OSV, "MAJOR") 22 | } 23 | 24 | -------------------------------------------------------------------------------- /testether_ipApp/Db/bo.db: -------------------------------------------------------------------------------- 1 | 2 | record(bo, "$(IOC):bo0") 3 | { 4 | field(SCAN, "Passive") 5 | field(DTYP, "EtherIP") 6 | field(OUT, "@$(PLC) BOOLs[0] S 1") 7 | field(ZNAM, "False") 8 | field(ONAM, "True") 9 | } 10 | 11 | record(bo, "$(IOC):bo2") 12 | { 13 | field(SCAN, "Passive") 14 | field(DTYP, "EtherIP") 15 | field(OUT, "@$(PLC) BOOLs[2] S 1") 16 | field(ZNAM, "False") 17 | field(ONAM, "True") 18 | } 19 | 20 | record(bo, "$(IOC):bo33") 21 | { 22 | field(SCAN, "Passive") 23 | field(DTYP, "EtherIP") 24 | field(OUT, "@$(PLC) BOOLs[33] S 1") 25 | field(ZNAM, "False") 26 | field(ONAM, "True") 27 | } 28 | 29 | record(bo, "$(IOC):bo240") 30 | { 31 | field(SCAN, "Passive") 32 | field(DTYP, "EtherIP") 33 | field(OUT, "@$(PLC) BOOLs[240] S 1") 34 | field(ZNAM, "False") 35 | field(ONAM, "True") 36 | } 37 | 38 | record(bo, "$(IOC):bo_IOC") 39 | { 40 | field(SCAN, "Passive") 41 | field(DTYP, "EtherIP") 42 | field(OUT, "@$(PLC) IOC.BOOL S 1") 43 | field(ZNAM, "False") 44 | field(ONAM, "True") 45 | } 46 | 47 | -------------------------------------------------------------------------------- /testether_ipApp/Db/brad.db: -------------------------------------------------------------------------------- 1 | # EPICS Database file -*- shell-script -*- 2 | 3 | record(ai, "$(IOC):ai") 4 | { 5 | field(SCAN, "1 second") 6 | field(DTYP, "EtherIP") 7 | field(INP, "@$(PLC) DINT") 8 | field(PREC, "3") 9 | } 10 | 11 | 12 | record(stringin, "string") 13 | { 14 | field(SCAN, "1 second") 15 | field(DTYP, "EtherIP") 16 | field(INP, "@$(PLC) testStr2") 17 | } 18 | 19 | record(stringout, "out") 20 | { 21 | field(DTYP, "EtherIP") 22 | field(OUT, "@$(PLC) testStr2") 23 | } 24 | 25 | -------------------------------------------------------------------------------- /testether_ipApp/Db/brad2.db: -------------------------------------------------------------------------------- 1 | # EPICS Database file -*- shell-script -*- 2 | 3 | record(ai, "$(IOC):ai") 4 | { 5 | field(SCAN, "1 second") 6 | field(DTYP, "EtherIP") 7 | field(INP, "@$(PLC) L85E_RealArray40[0]") 8 | field(PREC, "3") 9 | } 10 | record(bi, "$(IOC):ai1") 11 | { 12 | field(SCAN, "1 second") 13 | field(DTYP, "EtherIP") 14 | field(INP, "@$(PLC) L85E_RealArray40[1]") 15 | } 16 | record(bi, "$(IOC):ai1b") 17 | { 18 | field(SCAN, "1 second") 19 | field(DTYP, "EtherIP") 20 | field(INP, "@$(PLC) L85E_DINTArray40[0] B 1") 21 | } 22 | 23 | record(ao, "$(IOC):ao") 24 | { 25 | field(SCAN, "Passive") 26 | field(DTYP, "EtherIP") 27 | field(OUT, "@$(PLC) L85E_RealArray40[0] S 1") 28 | field(PREC, "3") 29 | } 30 | 31 | record(stringin, "$(IOC):si") 32 | { 33 | field(SCAN, "1 second") 34 | field(DTYP, "EtherIP") 35 | field(INP, "@$(PLC) L85E_String1") 36 | } 37 | 38 | record(stringout, "$(IOC):so") 39 | { 40 | field(DTYP, "EtherIP") 41 | field(OUT, "@$(PLC) L85E_String1 S 1") 42 | } 43 | 44 | -------------------------------------------------------------------------------- /testether_ipApp/Db/eip_stat.db: -------------------------------------------------------------------------------- 1 | # -*- shell-script -*- 2 | # Example for reading statistical information from EtherIP driver/device 3 | # 4 | # IOC: prefix to records 5 | # PLC: PLC to use 6 | # TAG: queried tag (leads to PLC's ScanList) 7 | # 8 | # The Alarm/warn thresholds in here are an example. 9 | # You'll have to check with your PLC setup what 10 | # the normal, expected transfer times are. 11 | 12 | # The error counter will unfortunately be "INVALID" while disconnected from the PLC, 13 | # then indicate the error count once we're re-connected. 14 | # Good enough? While disconnected, the fact is pretty obvious. 15 | # Then when all looks good again, this record will tell us how many disconnects there used to be. 16 | record(ai, "$(IOC):PLC_ERRORS") 17 | { 18 | field(SCAN, ".5 second") 19 | field(DTYP, "EtherIP") 20 | field(INP, "@$(PLC) $(TAG) PLC_ERRORS") 21 | field(EGUF, "5") 22 | field(EGU, "Errors") 23 | field(HOPR, "5") 24 | field(LOPR, "0") 25 | # Ideally, there are zero errors 26 | field(HIGH, "1") 27 | field(HSV, "MINOR") 28 | } 29 | 30 | record(ai, "$(IOC):PLC_TASK_SLOW") 31 | { 32 | field(SCAN, ".5 second") 33 | field(DTYP, "EtherIP") 34 | field(INP, "@$(PLC) $(TAG) PLC_TASK_SLOW") 35 | field(EGUF, "5") 36 | field(EGU, "Holdups") 37 | field(HOPR, "5") 38 | field(LOPR, "0") 39 | } 40 | 41 | record(ai, "$(IOC):LIST_TICKS") 42 | { 43 | field(SCAN, ".5 second") 44 | field(DTYP, "EtherIP") 45 | field(INP, "@$(PLC) $(TAG) LIST_TICKS") 46 | field(EGUF, "5") 47 | field(EGU, "vx ticks") 48 | field(HOPR, "5") 49 | field(LOPR, "0") 50 | } 51 | 52 | record(ai, "$(IOC):LIST_SCAN_TIME") 53 | { 54 | field(SCAN, ".5 second") 55 | field(DTYP, "EtherIP") 56 | field(INP, "@$(PLC) $(TAG) LIST_SCAN_TIME") 57 | field(EGUF, "1") 58 | field(EGU, "s") 59 | field(HOPR, "1") 60 | field(LOPR, "0") 61 | field(PREC, "5") 62 | # To support 10 Hz, should return within 0.1 sec 63 | field(HIGH, "0.1") 64 | field(HSV, "MINOR") 65 | } 66 | 67 | record(ai, "$(IOC):LIST_MIN_SCAN_TIME") 68 | { 69 | field(SCAN, ".5 second") 70 | field(DTYP, "EtherIP") 71 | field(INP, "@$(PLC) $(TAG) LIST_MIN_SCAN_TIME") 72 | field(EGUF, "1") 73 | field(EGU, "s") 74 | field(HOPR, "1") 75 | field(LOPR, "0") 76 | field(PREC, "5") 77 | field(HIGH, "0.01") 78 | field(HSV, "MINOR") 79 | } 80 | 81 | record(ai, "$(IOC):LIST_MAX_SCAN_TIME") 82 | { 83 | field(SCAN, ".5 second") 84 | field(DTYP, "EtherIP") 85 | field(INP, "@$(PLC) $(TAG) LIST_MAX_SCAN_TIME") 86 | field(EGUF, "1") 87 | field(EGU, "s") 88 | field(HOPR, "1") 89 | field(LOPR, "0") 90 | field(PREC, "5") 91 | field(HIGH, "1.0") 92 | field(HSV, "MINOR") 93 | } 94 | 95 | record(ai, "$(IOC):TAG_TRANSFER_TIME") 96 | { 97 | field(SCAN, ".5 second") 98 | field(DTYP, "EtherIP") 99 | field(INP, "@$(PLC) $(TAG) TAG_TRANSFER_TIME") 100 | field(EGUF, "1000000000") 101 | field(EGU, "s") 102 | field(HOPR, "1") 103 | field(LOPR, "0") 104 | field(PREC, "5") 105 | } 106 | 107 | # Resets when writing "1". 108 | # Self-resets after 1 second 109 | record(bo, "$(IOC):RESET_PLC_STATS") 110 | { 111 | field(DTYP, "EtherIPReset") 112 | field(DOL, "0") 113 | field(PINI, "YES") 114 | field(ZNAM, "Reset") 115 | field(ONAM, "Reset!") 116 | field(HIGH, "1") 117 | } 118 | -------------------------------------------------------------------------------- /testether_ipApp/Db/lint.db: -------------------------------------------------------------------------------- 1 | # Test PLC: 2 | # 3 | # eipIoc -p plc1=160.91.233.45 -m IOC=test -d testether_ipApp/Db/lint.db 4 | 5 | record(int64in, "$(IOC):i64in") 6 | { 7 | field(SCAN, "I/O Intr") 8 | field(DTYP, "EtherIP") 9 | field(INP, "@plc1 testLint S 1") 10 | field(EGU, "Counts") 11 | } 12 | 13 | record(int64out, "$(IOC):i64out") 14 | { 15 | field(SCAN, "Passive") 16 | field(DTYP, "EtherIP") 17 | field(OUT, "@plc1 testLint S 1") 18 | field(TPRO, "1") 19 | } 20 | 21 | record(int64in, "$(IOC):a64in0") 22 | { 23 | field(SCAN, "I/O Intr") 24 | field(DTYP, "EtherIP") 25 | field(INP, "@plc1 testLArray[0] S 1") 26 | field(EGU, "Counts") 27 | } 28 | 29 | record(int64out, "$(IOC):a64out0") 30 | { 31 | field(SCAN, "Passive") 32 | field(DTYP, "EtherIP") 33 | field(OUT, "@plc1 testLArray[0] S 1") 34 | field(TPRO, "1") 35 | } 36 | 37 | record(int64in, "$(IOC):a64in1") 38 | { 39 | field(SCAN, "I/O Intr") 40 | field(DTYP, "EtherIP") 41 | field(INP, "@plc1 testLArray[1] S 1") 42 | field(EGU, "Counts") 43 | } 44 | 45 | record(int64out, "$(IOC):a64out1") 46 | { 47 | field(SCAN, "Passive") 48 | field(DTYP, "EtherIP") 49 | field(OUT, "@plc1 testLArray[1] S 1") 50 | field(TPRO, "1") 51 | } 52 | 53 | record(int64in, "$(IOC):a64in9") 54 | { 55 | field(SCAN, "I/O Intr") 56 | field(DTYP, "EtherIP") 57 | field(INP, "@plc1 testLArray[9] S 1") 58 | field(EGU, "Counts") 59 | } 60 | 61 | record(int64out, "$(IOC):a64out9") 62 | { 63 | field(SCAN, "Passive") 64 | field(DTYP, "EtherIP") 65 | field(OUT, "@plc1 testLArray[9] S 1") 66 | field(TPRO, "1") 67 | } 68 | -------------------------------------------------------------------------------- /testether_ipApp/Db/mbbi.db: -------------------------------------------------------------------------------- 1 | record(mbbi, "$(IOC):mbbi0") 2 | { 3 | field(SCAN, "1 second") 4 | field(DTYP, "EtherIP") 5 | field(INP, "@$(PLC) BOOLs[30]") 6 | field(NOBT, "3") 7 | field(ZRVL, "0") 8 | field(ONVL, "1") 9 | field(TWVL, "2") 10 | field(THVL, "3") 11 | field(FRVL, "4") 12 | field(FVVL, "5") 13 | field(SXVL, "6") 14 | field(SVVL, "7") 15 | field(ZRST, "Zero-Zero-Zero") 16 | field(ONST, "Zero-Zero- One") 17 | field(TWST, "Zero-One -Zero") 18 | field(THST, "Zero-One - One") 19 | field(FRST, "One -Zero-Zero") 20 | field(FVST, "One -Zero- One") 21 | field(SXST, "One -One -Zero") 22 | field(SVST, "One -One - One") 23 | } 24 | 25 | record(mbbi, "$(IOC):mbbi1") 26 | { 27 | field(SCAN, "I/O Intr") 28 | field(DTYP, "EtherIP") 29 | field(INP, "@$(PLC) BOOLs[10] S .5") 30 | field(NOBT, "2") 31 | field(ZRVL, "0") 32 | field(ONVL, "1") 33 | field(TWVL, "2") 34 | field(THVL, "3") 35 | field(ZRST, "Zero-Zero") 36 | field(ONST, "Zero- One") 37 | field(TWST, "One -Zero") 38 | field(THST, "One - One") 39 | } 40 | 41 | record(mbbiDirect, "$(IOC):mbbid0") 42 | { 43 | field(SCAN, "I/O Intr") 44 | field(DTYP, "EtherIP") 45 | field(INP, "@$(PLC) BOOLs[20] S .5") 46 | field(NOBT, "5") 47 | } 48 | 49 | record(mbbiDirect, "$(IOC):mbbid1") 50 | { 51 | field(SCAN, "1 second") 52 | field(DTYP, "EtherIP") 53 | field(INP, "@$(PLC) BOOLs[30]") 54 | field(NOBT, "32") 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /testether_ipApp/Db/mbbo.db: -------------------------------------------------------------------------------- 1 | record(mbbo, "$(IOC):mbbo0") 2 | { 3 | field(SCAN, "Passive") 4 | field(DTYP, "EtherIP") 5 | field(OUT, "@$(PLC) BOOLs[30] S 0.5") 6 | field(NOBT, "3") 7 | field(ZRVL, "0") 8 | field(ONVL, "1") 9 | field(TWVL, "2") 10 | field(THVL, "3") 11 | field(FRVL, "4") 12 | field(FVVL, "5") 13 | field(SXVL, "6") 14 | field(SVVL, "7") 15 | field(ZRST, "Zero-Zero-Zero") 16 | field(ONST, "Zero-Zero- One") 17 | field(TWST, "Zero-One -Zero") 18 | field(THST, "Zero-One - One") 19 | field(FRST, "One -Zero-Zero") 20 | field(FVST, "One -Zero- One") 21 | field(SXST, "One -One -Zero") 22 | field(SVST, "One -One - One") 23 | } 24 | 25 | record(mbboDirect, "$(IOC):mbbod0") 26 | { 27 | field(SCAN, "Passive") 28 | field(DTYP, "EtherIP") 29 | field(OUT, "@$(PLC) BOOLs[35] S 0.1") 30 | field(NOBT, "10") 31 | } 32 | 33 | -------------------------------------------------------------------------------- /testether_ipApp/Db/mkana.pl: -------------------------------------------------------------------------------- 1 | $OUT=">ana.db"; 2 | 3 | open OUT or die "Cannot create $OUT"; 4 | 5 | select OUT; 6 | 7 | for ($i=0; $i<40; ++$i) 8 | { 9 | printf ("record(ai, \"\$(IOC):ai_REALs%d\")\n", $i); 10 | printf ("{\n"); 11 | printf (" field(SCAN, \"1 second\")\n"); 12 | printf (" field(DTYP, \"EtherIP\")\n"); 13 | printf (" field(INP, \"\@\$(PLC) REALs[%d]\")\n", $i); 14 | printf (" field(EGUF, \"10\")\n"); 15 | printf (" field(EGU, \"socks\")\n"); 16 | printf (" field(HOPR, \"10\")\n"); 17 | printf (" field(LOPR, \"0\")\n"); 18 | printf ("}\n"); 19 | printf ("\n"); 20 | printf ("record(ai, \"\$(IOC):ai_REALs2_%d\")\n", $i); 21 | printf ("{\n"); 22 | printf (" field(SCAN, \"1 second\")\n"); 23 | printf (" field(DTYP, \"EtherIP\")\n"); 24 | printf (" field(INP, \"\@\$(PLC) REALs2[%d]\")\n", $i); 25 | printf (" field(EGUF, \"10\")\n"); 26 | printf (" field(EGU, \"socks\")\n"); 27 | printf (" field(HOPR, \"10\")\n"); 28 | printf (" field(LOPR, \"0\")\n"); 29 | printf ("}\n"); 30 | printf ("\n"); 31 | printf ("record(ai, \"\$(IOC):ai_REALs3_%d\")\n", $i); 32 | printf ("{\n"); 33 | printf (" field(SCAN, \"1 second\")\n"); 34 | printf (" field(DTYP, \"EtherIP\")\n"); 35 | printf (" field(INP, \"\@\$(PLC) REALs3[%d]\")\n", $i); 36 | printf (" field(EGUF, \"10\")\n"); 37 | printf (" field(EGU, \"socks\")\n"); 38 | printf (" field(HOPR, \"10\")\n"); 39 | printf (" field(LOPR, \"0\")\n"); 40 | printf ("}\n"); 41 | printf ("\n"); 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /testether_ipApp/Db/mkbin.pl: -------------------------------------------------------------------------------- 1 | # Create binary record for testing EtherIP 2 | 3 | $OUT=">bin.db"; 4 | 5 | open OUT or die "Cannot create $OUT"; 6 | 7 | select OUT; 8 | for ($i=0; $i<352; ++$i) 9 | { 10 | printf ("record(bi, \"\$(IOC):bi%d\")\n", $i); 11 | printf ("{\n"); 12 | printf (" field(SCAN, \"I/O Intr\")\n"); 13 | printf (" field(DTYP, \"EtherIP\")\n"); 14 | printf (" field(INP, \"\@\$(PLC) BOOLs[%d] S 0.1\")\n", $i); 15 | printf (" field(ZNAM, \"False\")\n"); 16 | printf (" field(ONAM, \"True\")\n"); 17 | printf (" field(ZSV, \"NO_ALARM\")\n"); 18 | printf (" field(OSV, \"MAJOR\")\n"); 19 | printf ("}\n"); 20 | printf ("\n"); 21 | } 22 | 23 | close OUT; 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /testether_ipApp/Db/mod.db: -------------------------------------------------------------------------------- 1 | # EPICS Database file -*- shell-script -*- 2 | # 3 | # Testing access to SNS NC RF Modulator 4 | # Read a single REAL tag called "REAL" 5 | record(ai, "Vmon") 6 | { 7 | field(SCAN, "1 second") 8 | field(DTYP, "EtherIP") 9 | field(INP, "@$(PLC) P1R0[2]") 10 | field(PREC, "1") 11 | field(EGU, "kV") 12 | } 13 | 14 | record(ai, "Imon") 15 | { 16 | field(SCAN, "1 second") 17 | field(DTYP, "EtherIP") 18 | field(INP, "@$(PLC) P1R0[3]") 19 | field(PREC, "1") 20 | field(EGU, "A") 21 | } 22 | -------------------------------------------------------------------------------- /testether_ipApp/Db/ramp.db: -------------------------------------------------------------------------------- 1 | record(calc, "$(IOC):ramp") 2 | { 3 | field(DESC, "Random Number") 4 | field(SCAN, ".1 second") 5 | field(EGU,"flueps") 6 | field(CALC,"A>=B?0:A+0.5*RNDM") 7 | field(INPA,"$(IOC):ramp.VAL") 8 | field(INPB,"10") 9 | field(PREC,"4") 10 | field(LOPR,"0.0") 11 | field(HOPR,"10.0") 12 | } 13 | -------------------------------------------------------------------------------- /testether_ipApp/Db/test.db: -------------------------------------------------------------------------------- 1 | # EPICS Database file -*- shell-script -*- 2 | # The following record types are tested in here: 3 | # 4 | # ai, ao 5 | # bi, bo 6 | # mbbi 7 | # mbbiDirect 8 | # stringin 9 | # waveform 10 | # mbbo 11 | # mbboDirect 12 | # 13 | # ... in combination with these tags on the PLC, 14 | # where the tag names match their types, 15 | # using a very strange rule for upper-or lowercase 's': 16 | # 17 | # REAL REAL, REALs[40] 18 | # BOOL BOOL, BOOLs[352] 19 | # REAL REALs[40] 20 | # DINT DINTS[40] 21 | # INT INTs[40] 22 | # BOOL NESTED.BOOLs[32] 23 | # REAL NESTED.REALs[2] 24 | # INT NESTED.STRUCTS[5].INT 25 | # REAL NESTED.STRUCTS[5].REAL 26 | # DINT NESTED.STRUCTS[5].DINT 27 | # BOOL NESTED.STRUCTS[5].BOOL 28 | # STRING STRING 29 | # MainProgram.timer_1.TT 30 | # 31 | # Record/tag combinations that are not listed 32 | # in here might not work or give unexpected results. 33 | 34 | # ------------------------------------------------- 35 | # ai 36 | # ------------------------------------------------- 37 | # Read a single REAL tag called "REAL" 38 | record(ai, "$(IOC):aiREAL") 39 | { 40 | field(SCAN, "1 second") 41 | field(DTYP, "EtherIP") 42 | field(INP, "@$(PLC) REAL") 43 | field(PREC, "3") 44 | } 45 | # Read a single tag from an array called "REALs" 46 | record(ai, "$(IOC):aiREALs2") 47 | { 48 | field(SCAN, "1 second") 49 | field(DTYP, "EtherIP") 50 | field(INP, "@$(PLC) REALs[2]") 51 | field(PREC, "3") 52 | } 53 | # Similar, just different array elements. 54 | # NOTE: As a result, the driver will 55 | # read the array from elements 0 to 5. 56 | # 57 | # On the IOC, "drvEtherIP_report(10)" would show 58 | # something like this: 59 | # *** Tag 'REALs' @ 0x1B4CB08: 60 | # ... 61 | # compiled tag : REALs 62 | # elements : 6 63 | # -> the driver scans 6 elements from the tag "REALs" 64 | record(ai, "$(IOC):aiREALs5") 65 | { 66 | field(SCAN, "1 second") 67 | field(DTYP, "EtherIP") 68 | field(INP, "@$(PLC) REALs[5]") 69 | field(PREC, "3") 70 | } 71 | # Read a single tag from an array called "REALs", 72 | # but really only the single element ("E" flag)! 73 | # The driver does not combine this one 74 | # into an array request for elements 0..39 75 | # 76 | # On the IOC, "drvEtherIP_report(10)" would show 77 | # something like this: 78 | # *** Tag 'REALs[39]' @ 0x1B4CD58: 79 | # ... 80 | # compiled tag : REALs[39] 81 | # elements : 1 82 | # -> the driver scans "REALs[39]" as a single element. 83 | record(ai, "$(IOC):aiREALs39") 84 | { 85 | field(SCAN, "1 second") 86 | field(DTYP, "EtherIP") 87 | field(INP, "@$(PLC) REALs[39] E") 88 | field(PREC, "3") 89 | } 90 | # As before, but connected to a DINT 91 | record(ai, "$(IOC):aiDINT") 92 | { 93 | field(SCAN, ".1 second") 94 | field(DTYP, "EtherIP") 95 | field(INP, "@$(PLC) DINT") 96 | } 97 | record(ai, "$(IOC):aiDINTs1") 98 | { 99 | field(SCAN, ".1 second") 100 | field(DTYP, "EtherIP") 101 | field(INP, "@$(PLC) DINTs[1]") 102 | } 103 | record(ai, "$(IOC):aiDINTs2") 104 | { 105 | field(SCAN, ".1 second") 106 | field(DTYP, "EtherIP") 107 | field(INP, "@$(PLC) DINTs[2]") 108 | } 109 | record(ai, "$(IOC):aiDINTs3") 110 | { 111 | field(SCAN, ".1 second") 112 | field(DTYP, "EtherIP") 113 | field(INP, "@$(PLC) DINTs[3]") 114 | } 115 | # As before, but connected to an INT array element 116 | record(ai, "$(IOC):aiINTs2") 117 | { 118 | field(SCAN, ".1 second") 119 | field(DTYP, "EtherIP") 120 | field(INP, "@$(PLC) INTs[2]") 121 | } 122 | record(ai, "$(IOC):aiINTs3") 123 | { 124 | field(SCAN, ".1 second") 125 | field(DTYP, "EtherIP") 126 | field(INP, "@$(PLC) INTs[3]") 127 | } 128 | 129 | # ------------------------------------------------- 130 | # ao 131 | # ------------------------------------------------- 132 | # Output record, write to REAL tag. 133 | # Record is "Passive", will only process when 134 | # a) changed via operator display/ChannelAccess: 135 | # -> value is written to PLC 136 | # b) tag changed on PLC 137 | # -> record's VAL is updated 138 | # 139 | # "S" flag: though record is passive, driver 140 | # needs some scan period at which 141 | # it monitors the PLC for changes 142 | # of the tag. Here we pick 1 second. 143 | record(ao, "$(IOC):aoREAL") 144 | { 145 | field(SCAN, "Passive") 146 | field(DTYP, "EtherIP") 147 | field(OUT, "@$(PLC) REAL S 1") 148 | field(PREC, "3") 149 | } 150 | record(ao, "$(IOC):aoREALs2") 151 | { 152 | field(SCAN, "Passive") 153 | field(DTYP, "EtherIP") 154 | field(OUT, "@$(PLC) REALs[2] S 1") 155 | field(PREC, "3") 156 | } 157 | # Dito, but DINT 158 | record(ao, "$(IOC):aoDINT") 159 | { 160 | field(SCAN, "Passive") 161 | field(DTYP, "EtherIP") 162 | field(OUT, "@$(PLC) DINT S 1") 163 | } 164 | record(ao, "$(IOC):aoDINTs2") 165 | { 166 | field(SCAN, "Passive") 167 | field(DTYP, "EtherIP") 168 | field(OUT, "@$(PLC) DINTs[2] S 1") 169 | } 170 | record(ao, "$(IOC):aoINTs3") 171 | { 172 | field(SCAN, "Passive") 173 | field(DTYP, "EtherIP") 174 | field(OUT, "@$(PLC) INTs[3] S 1") 175 | } 176 | 177 | # ------------------------------------------------- 178 | # bi records 179 | # ------------------------------------------------- 180 | # Binary input, connected to a BOOL tag 181 | record(bi, "$(IOC):biBOOL") 182 | { 183 | field(SCAN, "1 second") 184 | field(DTYP, "EtherIP") 185 | field(INP, "@$(PLC) BOOL") 186 | field(ZNAM, "False") 187 | field(ONAM, "True") 188 | } 189 | # Binary input, connected to a DINT 190 | # -> reads bit 0 of DINT value 191 | record(bi, "$(IOC):biDINT") 192 | { 193 | field(SCAN, "1 second") 194 | field(DTYP, "EtherIP") 195 | field(INP, "@$(PLC) DINT") 196 | field(ZNAM, "False") 197 | field(ONAM, "True") 198 | } 199 | # .. reads bit 3 of DINT value 200 | record(bi, "$(IOC):biDINT_3") 201 | { 202 | field(SCAN, "1 second") 203 | field(DTYP, "EtherIP") 204 | field(INP, "@$(PLC) DINT B 3") 205 | field(ZNAM, "False") 206 | field(ONAM, "True") 207 | } 208 | # BOOL array element 209 | record(bi, "$(IOC):biBOOLs5") 210 | { 211 | field(SCAN, "1 second") 212 | field(DTYP, "EtherIP") 213 | field(INP, "@$(PLC) BOOLs[5]") 214 | field(ZNAM, "False") 215 | field(ONAM, "True") 216 | } 217 | # !! Note !! 218 | # The following binary records are NOT connected 219 | # to BOOL arrays. 220 | # Binary input, connected to a DINT element 221 | # The bi expects a BOOL array, not a DINT array. 222 | # This will *NOT* read array element #40 but bit #40 223 | # which is bit # 8 in the second DINT. 224 | # 225 | # You can check this by setting the "TPRO" field 226 | # of this record. TPRO=trace processing, logs 227 | # to IOC console when record is processed. 228 | # The EtherIP driver will show this additional information: 229 | # process: snsioc4:biDINTs40 230 | # link_text : 'plc1 DINTs[40]' 231 | # PLC_name : 'plc1' 232 | # string_tag : 'DINTs' 233 | # element : 1 <- element 1, not 40! 234 | # mask : 0x100 <- mask selects bit 8 235 | # 236 | # Hint: TPRO causes logging when the record is processed, 237 | # in this case every second, so you can't read the output 238 | # since you always get new stuff. 239 | # -> use channel access (edm, caput, ...) to set it to "1", 240 | # wait 3 seconds, then set it to "0" again, 241 | # and then inspect the messsages you got. 242 | record(bi, "$(IOC):biDINTs40_error") 243 | { 244 | field(SCAN, "1 second") 245 | field(DTYP, "EtherIP") 246 | field(INP, "@$(PLC) DINTs[40]") 247 | field(ZNAM, "False") 248 | field(ONAM, "True") 249 | } 250 | # Use the "B " flag to 251 | # override the default behaviour will explicit 252 | # bit addressing: 253 | # Read DINT[8], bit 5 254 | # 255 | # Set TPRO and you should get: 256 | # process: snsioc4:biDINTs8_5 257 | # link_text : 'plc1 DINTs[8] B 5' 258 | # PLC_name : 'plc1' 259 | # string_tag : 'DINTs' 260 | # element : 8 <- indeed element 8 261 | # mask : 0x20 <- mask selects bit 5 262 | record(bi, "$(IOC):biDINTs8_5") 263 | { 264 | field(SCAN, "1 second") 265 | field(DTYP, "EtherIP") 266 | field(INP, "@$(PLC) DINTs[8] B 5") 267 | field(ZNAM, "False") 268 | field(ONAM, "True") 269 | } 270 | 271 | # ------------------------------------------------- 272 | # bo records 273 | # ------------------------------------------------- 274 | record(bo, "$(IOC):boBOOL") 275 | { 276 | field(DTYP, "EtherIP") 277 | field(OUT, "@$(PLC) BOOL S 1") 278 | field(ZNAM, "False") 279 | field(ONAM, "True") 280 | } 281 | record(bo, "$(IOC):boBOOLs5") 282 | { 283 | field(DTYP, "EtherIP") 284 | field(OUT, "@$(PLC) BOOLs[5] S 1") 285 | field(ZNAM, "False") 286 | field(ONAM, "True") 287 | } 288 | record(bo, "$(IOC):boINTs3_2") 289 | { 290 | field(DTYP, "EtherIP") 291 | field(OUT, "@$(PLC) INTs[3] B 2 S 1") 292 | field(ZNAM, "False") 293 | field(ONAM, "True") 294 | } 295 | 296 | # ------------------------------------------------- 297 | # mbbi records 298 | # ------------------------------------------------- 299 | # BOOL array elements, decodes 2 bits 300 | record(mbbi, "$(IOC):mbbiBOOLs300") 301 | { 302 | field(SCAN, "1 second") 303 | field(DTYP, "EtherIP") 304 | field(INP, "@$(PLC) BOOLs[300]") 305 | field(NOBT, "2") 306 | field(ZRVL, "0") 307 | field(ONVL, "1") 308 | field(TWVL, "2") 309 | field(THVL, "3") 310 | field(ZRST, "Zero-Zero") 311 | field(ONST, "Zero- One") 312 | field(TWST, "One -Zero") 313 | field(THST, "One - One") 314 | } 315 | record(mbbi, "$(IOC):mbbiDINTs2_3") 316 | { 317 | field(SCAN, "1 second") 318 | field(DTYP, "EtherIP") 319 | field(INP, "@$(PLC) DINTs[2] B 3") 320 | field(NOBT, "2") 321 | field(ZRVL, "0") 322 | field(ONVL, "1") 323 | field(TWVL, "2") 324 | field(THVL, "3") 325 | field(ZRST, "Zero-Zero") 326 | field(ONST, "Zero- One") 327 | field(TWST, "One -Zero") 328 | field(THST, "One - One") 329 | } 330 | 331 | # ------------------------------------------------- 332 | # mbbo records 333 | # ------------------------------------------------- 334 | # BOOL array elements, writes 2 bits 335 | # Used SCAN to set period, no "S" flag needed in OUT 336 | # (but possibly not the ideal configuration) 337 | record(mbbo, "$(IOC):mbboBOOLs300") 338 | { 339 | field(SCAN, "1 second") 340 | field(DTYP, "EtherIP") 341 | field(OUT, "@$(PLC) BOOLs[300]") 342 | field(NOBT, "2") 343 | field(ZRVL, "0") 344 | field(ONVL, "1") 345 | field(TWVL, "2") 346 | field(THVL, "3") 347 | field(ZRST, "Zero-Zero") 348 | field(ONST, "Zero- One") 349 | field(TWST, "One -Zero") 350 | field(THST, "One - One") 351 | } 352 | 353 | # B flag again: write to DINT array element #4, bit31. 354 | # Note: This mbbo writes to 2 bits, so it crosses 355 | # DINT boundaries into element #5, bit 0. 356 | # This works for 32bit UDINT but not for INTs, 357 | # you might not want to depend on this feature. 358 | record(mbbo, "$(IOC):mbboDINTs4_31") 359 | { 360 | field(SCAN, "1 second") 361 | field(DTYP, "EtherIP") 362 | field(OUT, "@$(PLC) DINTs[4] B 31") 363 | field(NOBT, "2") 364 | field(ZRVL, "0") 365 | field(ONVL, "1") 366 | field(TWVL, "2") 367 | field(THVL, "3") 368 | field(ZRST, "Zero-Zero") 369 | field(ONST, "Zero- One") 370 | field(TWST, "One -Zero") 371 | field(THST, "One - One") 372 | } 373 | # Like mbboDINTs4_31, but targetting INT 374 | # which has only 16 bits. 375 | record(mbbo, "$(IOC):mbboINTs2_14") 376 | { 377 | field(SCAN, "1 second") 378 | field(DTYP, "EtherIP") 379 | field(OUT, "@$(PLC) INTs[2] B 14") 380 | field(NOBT, "2") 381 | field(ZRVL, "0") 382 | field(ONVL, "1") 383 | field(TWVL, "2") 384 | field(THVL, "3") 385 | field(ZRST, "Zero-Zero") 386 | field(ONST, "Zero- One") 387 | field(TWST, "One -Zero") 388 | field(THST, "One - One") 389 | } 390 | 391 | # INT, bit addressing. 392 | # Uses "I/O Intr" to process when data changes. 393 | # Since the SCAN field doesn't tell the driver 394 | # how often it should check, we need the "S" flag 395 | record(mbbiDirect, "$(IOC):mbbidINTs3_4") 396 | { 397 | field(SCAN, "I/O Intr") 398 | field(DTYP, "EtherIP") 399 | field(INP, "@plc1 INTs[3] B 4 S 1") 400 | field(NOBT, "5") 401 | } 402 | 403 | # ------------------------------------------------- 404 | # Other odd records 405 | # ------------------------------------------------- 406 | 407 | # Note that string data type of PLC is longer 408 | # than the string record's value, 409 | # so what you get might be truncated 410 | record(stringin, "$(IOC):string") 411 | { 412 | field(SCAN, "1 second") 413 | field(DTYP, "EtherIP") 414 | field(INP, "@$(PLC) STRING") 415 | } 416 | 417 | # IF the tag is of type REAL, 418 | # the waveform must use DOUBLE ! 419 | record(waveform, "$(IOC):REALwf") 420 | { 421 | field(SCAN, "1 second") 422 | field(DTYP, "EtherIP") 423 | field(INP, "@$(PLC) REALs") 424 | field(NELM, "40") 425 | field(FTVL, "DOUBLE") 426 | } 427 | 428 | # IF the tag is of type not-REAL, 429 | # the waveform must use LONG ! 430 | record(waveform, "$(IOC):DINTLwf") 431 | { 432 | field(SCAN, "1 second") 433 | field(DTYP, "EtherIP") 434 | field(INP, "@$(PLC) DINTs") 435 | field(NELM, "40") 436 | field(FTVL, "LONG") 437 | } 438 | 439 | # Some I/O Intr tests 440 | record(bi, "$(IOC):bi_int0") 441 | { 442 | field(SCAN, "I/O Intr") 443 | field(DTYP, "EtherIP") 444 | field(INP, "@$(PLC) DINTs[0] B 0 S 1") 445 | field(ZNAM, "False") 446 | field(ONAM, "True") 447 | } 448 | 449 | record(bi, "$(IOC):bi_int1") 450 | { 451 | field(SCAN, "I/O Intr") 452 | field(DTYP, "EtherIP") 453 | field(INP, "@$(PLC) DINTs[0] B 1 S 1") 454 | field(ZNAM, "False") 455 | field(ONAM, "True") 456 | } 457 | 458 | record(bi, "$(IOC):bi_int2") 459 | { 460 | field(SCAN, "I/O Intr") 461 | field(DTYP, "EtherIP") 462 | field(INP, "@$(PLC) DINTs[0] B 2 S 1") 463 | field(ZNAM, "False") 464 | field(ONAM, "True") 465 | } 466 | 467 | record(ao, "$(IOC):aoDINTs0") 468 | { 469 | field(SCAN, "Passive") 470 | field(DTYP, "EtherIP") 471 | field(OUT, "@$(PLC) DINTs[0] S 1") 472 | } 473 | -------------------------------------------------------------------------------- /testether_ipApp/Makefile: -------------------------------------------------------------------------------- 1 | TOP = .. 2 | 3 | include $(TOP)/configure/CONFIG 4 | 5 | DIRS := src 6 | DIRS += Db 7 | 8 | include $(TOP)/configure/RULES_DIRS 9 | -------------------------------------------------------------------------------- /testether_ipApp/RSLogix/test.L5K: -------------------------------------------------------------------------------- 1 | (********************************************* 2 | 3 | Import-Export 4 | Version := RSLogix 5000 v12.02 5 | Owner := kasemir, ORNL 6 | Exported := Wed Oct 06 11:00:39 2004 7 | **********************************************) 8 | IE_VER := 2.3; 9 | 10 | CONTROLLER EtherIP_Test (ProcessorType := 1756-L1, 11 | Major := 12, 12 | TimeSlice := 10, 13 | CommPath := AB_DF1-1\1, 14 | RedundancyEnabled := 0, 15 | KeepTestEditsOnSwitchOver := 0, 16 | DataTablePadPercentage := 50, 17 | SecurityCode := 0, 18 | SFCExecutionControl := CurrentActive, 19 | SFCRestartPosition := MostRecent, 20 | SFCLastScan := DontScan) 21 | DATATYPE Nested (FamilyType := NoFamily) 22 | BOOL BOOLs[32]; 23 | REAL REALs[2]; 24 | TestStruct STRUCTS[5] (Radix := Float); 25 | END_DATATYPE 26 | 27 | DATATYPE TestStruct (FamilyType := NoFamily) 28 | INT INT; 29 | REAL REAL; 30 | DINT DINT; 31 | SINT ZZZZZZZZZZTestStruct3 (Hidden := 1); 32 | BIT BOOL ZZZZZZZZZZTestStruct3 : 0; 33 | END_DATATYPE 34 | 35 | MODULE Local (Parent := Local, 36 | CatalogNumber := 1756-L1, 37 | Major := 12, 38 | Minor := 1, 39 | PortLabel := RxBACKPLANE, 40 | ChassisSize := 7, 41 | Slot := 0, 42 | Mode := 2#0000_0000_0000_0001, 43 | CompatibleModule := 0, 44 | KeyMask := 2#0000_0000_0001_1111) 45 | END_MODULE 46 | 47 | MODULE ENET (Parent := Local, 48 | CatalogNumber := 1756-ENET/B, 49 | Major := 2, 50 | Minor := 1, 51 | PortLabel := RxBACKPLANE, 52 | Slot := 1, 53 | NodeAddress := 172.31.72.94, 54 | Mode := 2#0000_0000_0000_0000, 55 | CompatibleModule := 0, 56 | KeyMask := 2#0000_0000_0000_0000) 57 | END_MODULE 58 | 59 | TAG 60 | BOOL : BOOL (RADIX := Decimal) := 1; 61 | BOOLs : BOOL[352] (RADIX := Decimal) := [2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 62 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 63 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 64 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 65 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 66 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 67 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 68 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 69 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 70 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 71 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 72 | ,2#0,2#0,2#0,2#0,2#1,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 73 | ,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 74 | ,2#0]; 75 | DINT : DINT (RADIX := Decimal) := 0; 76 | DINTS : DINT[40] (RADIX := Decimal) := [1,2,15,4,5,6,7,8,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 77 | INTs : INT[40] (RADIX := Decimal) := [0,0,-32768,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 78 | NESTED : Nested := [[2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0,2#0 79 | ,2#0,2#0,2#0,2#0,2#0],[0.00000000e+000,0.00000000e+000],[[0,0.00000000e+000,0,0],[0,4.20000000e+001,0,0 80 | ],[0,0.00000000e+000,0,0],[0,0.00000000e+000,0,0],[0,0.00000000e+000,0,0]]]; 81 | REAL : REAL (RADIX := Float) := 3.14000010e+000; 82 | REALs : REAL[40] (RADIX := Float) := [1.00000000e+000,2.00000000e+000,5.00000000e+001,4.00000000e+000,5.00000000e+000,6.00000000e+000 83 | ,7.00000000e+000,8.00000000e+000,9.00000000e+000,1.00000000e+001,0.00000000e+000,0.00000000e+000 84 | ,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000 85 | ,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000 86 | ,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000 87 | ,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000,0.00000000e+000 88 | ,0.00000000e+000,0.00000000e+000,0.00000000e+000,3.90000000e+001]; 89 | STRING : STRING := [3,'Hi!$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00' 90 | ]; 91 | END_TAG 92 | 93 | PROGRAM MainProgram (MAIN := MainRoutine, 94 | MODE := 0, 95 | DisableFlag := 0) 96 | TAG 97 | timer_1 : TIMER := [216121,200,0]; 98 | timer_2 : TIMER := [215921,200,0]; 99 | END_TAG 100 | 101 | ROUTINE MainRoutine 102 | RC: "Two timers, running mutually exclusive"; 103 | N: XIO(timer_2.TT)TON(timer_1,?,?); 104 | N: XIO(timer_1.TT)TON(timer_2,?,?); 105 | END_ROUTINE 106 | 107 | END_PROGRAM 108 | 109 | TASK MainTask (Type := CONTINUOUS, 110 | Watchdog := 500.000, 111 | Priority := 10, 112 | Rate := 10.000, 113 | EnableTimeout := No, 114 | DisableUpdateOutputs := No, 115 | InhibitTask := No) 116 | MainProgram; 117 | END_TASK 118 | 119 | CONFIG ASCII(XONXOFFEnable := 0, 120 | DeleteMode := 0, 121 | EchoMode := 0, 122 | TerminationChars := -243, 123 | AppendChars := 2573, 124 | BufferSize := 82) END_CONFIG 125 | 126 | CONFIG ControllerDevice END_CONFIG 127 | 128 | CONFIG CST(SystemTimeMasterID := 0) END_CONFIG 129 | 130 | CONFIG DF1(DuplicateDetection := 1, 131 | ErrorDetection := BCC Error, 132 | EmbeddedResponseEnable := 0, 133 | DF1Mode := Pt to Pt, 134 | ACKTimeout := 50, 135 | NAKReceiveLimit := 3, 136 | ENQTransmitLimit := 3, 137 | TransmitRetries := 3, 138 | StationAddress := 0, 139 | ReplyMessageWait := 5, 140 | PollingMode := 1, 141 | MasterMessageTransmit := 0, 142 | NormalPollNodeFile := , 143 | NormalPollGroupSize := 0, 144 | PriorityPollNodeFile := , 145 | ActiveStationFile := , 146 | SlavePollTimeout := 3000, 147 | EOTSuppression := 0, 148 | MaxStationAddress := 31, 149 | TokenHoldFactor := 1) END_CONFIG 150 | 151 | CONFIG ExtendedDevice END_CONFIG 152 | 153 | CONFIG FaultLog END_CONFIG 154 | 155 | CONFIG FileManager END_CONFIG 156 | 157 | CONFIG ICP END_CONFIG 158 | 159 | CONFIG PCCC END_CONFIG 160 | 161 | CONFIG Redundancy END_CONFIG 162 | 163 | CONFIG SerialPort(BaudRate := 19200, 164 | Parity := No Parity, 165 | DataBits := 8 Bits of Data, 166 | StopBits := 1 Stop Bit, 167 | ComDriverId := DF1, 168 | PendingComDriverId := DF1, 169 | RTSOffDelay := 0, 170 | RTSSendDelay := 0, 171 | ControlLine := No Handshake, 172 | PendingControlLine := No Handshake, 173 | RemoteModeChangeFlag := 0, 174 | PendingRemoteModeChangeFlag := 0, 175 | ModeChangeAttentionChar := 27, 176 | PendingModeChangeAttentionChar := 27, 177 | SystemModeCharacter := 83, 178 | PendingSystemModeCharacter := 83, 179 | UserModeCharacter := 85, 180 | PendingUserModeCharacter := 85) END_CONFIG 181 | 182 | CONFIG UserMemory END_CONFIG 183 | 184 | CONFIG WallClockTime(LocalTimeAdjustment := 0, 185 | TimeZone := 0) END_CONFIG 186 | 187 | END_CONTROLLER 188 | 189 | -------------------------------------------------------------------------------- /testether_ipApp/edm/test_db.edl: -------------------------------------------------------------------------------- 1 | 4 0 1 2 | beginScreenProperties 3 | major 4 4 | minor 0 5 | release 1 6 | x 1651 7 | y 131 8 | w 1013 9 | h 826 10 | font "helvetica-medium-r-18.0" 11 | ctlFont "helvetica-medium-r-18.0" 12 | btnFont "helvetica-medium-r-18.0" 13 | fgColor index 14 14 | bgColor index 50 15 | textColor index 5 16 | ctlFgColor1 index 0 17 | ctlFgColor2 index 34 18 | ctlBgColor1 index 38 19 | ctlBgColor2 index 17 20 | topShadowColor index 38 21 | botShadowColor index 0 22 | showGrid 23 | snapToGrid 24 | endScreenProperties 25 | 26 | # (Textupdate) 27 | object TextupdateClass 28 | beginObjectProperties 29 | major 10 30 | minor 0 31 | release 0 32 | x 90 33 | y 30 34 | w 60 35 | h 30 36 | controlPv "$(IOC):aiREAL" 37 | fgColor index 15 38 | fgAlarm 39 | bgColor index 10 40 | fill 41 | font "helvetica-medium-r-14.0" 42 | endObjectProperties 43 | 44 | # (X-Y Graph) 45 | object xyGraphClass 46 | beginObjectProperties 47 | major 4 48 | minor 1 49 | release 0 50 | # Geometry 51 | x 110 52 | y 500 53 | w 750 54 | h 270 55 | # Appearance 56 | border 57 | fgColor index 112 58 | bgColor index 133 59 | gridColor index 112 60 | font "helvetica-medium-r-18.0" 61 | # Operating Modes 62 | nPts 1 63 | # X axis properties 64 | showXAxis 65 | xAxisSrc "AutoScale" 66 | xMax 1 67 | # Y axis properties 68 | showYAxis 69 | yAxisSrc "AutoScale" 70 | yMax 1 71 | # Y2 axis properties 72 | y2AxisSrc "AutoScale" 73 | y2Max 1 74 | # Trace Properties 75 | numTraces 2 76 | yPv { 77 | 0 "$(IOC):REALwf" 78 | 1 "$(IOC):DINTLwf" 79 | } 80 | opMode { 81 | 0 "plot" 82 | } 83 | plotColor { 84 | 0 index 21 85 | 1 index 26 86 | } 87 | endObjectProperties 88 | 89 | # (Static Text) 90 | object activeXTextClass 91 | beginObjectProperties 92 | major 4 93 | minor 1 94 | release 0 95 | x 10 96 | y 40 97 | w 37 98 | h 16 99 | font "helvetica-medium-r-14.0" 100 | fontAlign "right" 101 | fgColor index 112 102 | bgColor index 133 103 | useDisplayBg 104 | value { 105 | "REAL" 106 | } 107 | endObjectProperties 108 | 109 | # (Static Text) 110 | object activeXTextClass 111 | beginObjectProperties 112 | major 4 113 | minor 1 114 | release 0 115 | x 10 116 | y 120 117 | w 61 118 | h 16 119 | font "helvetica-medium-r-14.0" 120 | fontAlign "right" 121 | fgColor index 112 122 | bgColor index 133 123 | useDisplayBg 124 | value { 125 | "REALs[5]" 126 | } 127 | endObjectProperties 128 | 129 | # (Textupdate) 130 | object TextupdateClass 131 | beginObjectProperties 132 | major 10 133 | minor 0 134 | release 0 135 | x 90 136 | y 110 137 | w 60 138 | h 30 139 | controlPv "$(IOC):aiREALs5" 140 | fgColor index 15 141 | fgAlarm 142 | bgColor index 10 143 | fill 144 | font "helvetica-medium-r-14.0" 145 | endObjectProperties 146 | 147 | # (Textupdate) 148 | object TextupdateClass 149 | beginObjectProperties 150 | major 10 151 | minor 0 152 | release 0 153 | x 90 154 | y 150 155 | w 60 156 | h 30 157 | controlPv "$(IOC):aiREALs39" 158 | fgColor index 15 159 | fgAlarm 160 | bgColor index 10 161 | fill 162 | font "helvetica-medium-r-14.0" 163 | endObjectProperties 164 | 165 | # (Static Text) 166 | object activeXTextClass 167 | beginObjectProperties 168 | major 4 169 | minor 1 170 | release 0 171 | x 10 172 | y 160 173 | w 70 174 | h 20 175 | font "helvetica-medium-r-14.0" 176 | fontAlign "right" 177 | fgColor index 112 178 | bgColor index 133 179 | useDisplayBg 180 | value { 181 | "REALs[39]" 182 | } 183 | endObjectProperties 184 | 185 | # (Textupdate) 186 | object TextupdateClass 187 | beginObjectProperties 188 | major 10 189 | minor 0 190 | release 0 191 | x 90 192 | y 230 193 | w 60 194 | h 30 195 | controlPv "$(IOC):aiDINTs1" 196 | fgColor index 15 197 | fgAlarm 198 | bgColor index 10 199 | fill 200 | font "helvetica-medium-r-14.0" 201 | endObjectProperties 202 | 203 | # (Static Text) 204 | object activeXTextClass 205 | beginObjectProperties 206 | major 4 207 | minor 1 208 | release 0 209 | x 10 210 | y 200 211 | w 34 212 | h 16 213 | font "helvetica-medium-r-14.0" 214 | fontAlign "right" 215 | fgColor index 112 216 | bgColor index 133 217 | useDisplayBg 218 | value { 219 | "DINT" 220 | } 221 | autoSize 222 | endObjectProperties 223 | 224 | # (Static Text) 225 | object activeXTextClass 226 | beginObjectProperties 227 | major 4 228 | minor 1 229 | release 0 230 | x 10 231 | y 400 232 | w 61 233 | h 16 234 | font "helvetica-medium-r-14.0" 235 | fontAlign "right" 236 | fgColor index 112 237 | bgColor index 133 238 | useDisplayBg 239 | value { 240 | "INTs[3]" 241 | } 242 | endObjectProperties 243 | 244 | # (Textupdate) 245 | object TextupdateClass 246 | beginObjectProperties 247 | major 10 248 | minor 0 249 | release 0 250 | x 90 251 | y 390 252 | w 60 253 | h 30 254 | controlPv "$(IOC):aiINTs3" 255 | fgColor index 15 256 | fgAlarm 257 | bgColor index 10 258 | fill 259 | font "helvetica-medium-r-14.0" 260 | endObjectProperties 261 | 262 | # (Textentry) 263 | object TextentryClass 264 | beginObjectProperties 265 | major 10 266 | minor 0 267 | release 0 268 | x 160 269 | y 70 270 | w 70 271 | h 40 272 | controlPv "$(IOC):aoREALs2" 273 | fgColor index 25 274 | fgAlarm 275 | bgColor index 5 276 | fill 277 | font "helvetica-medium-r-14.0" 278 | endObjectProperties 279 | 280 | # (Textentry) 281 | object TextentryClass 282 | beginObjectProperties 283 | major 10 284 | minor 0 285 | release 0 286 | x 160 287 | y 270 288 | w 70 289 | h 40 290 | controlPv "$(IOC):aoDINTs2" 291 | fgColor index 25 292 | fgAlarm 293 | bgColor index 5 294 | fill 295 | font "helvetica-medium-r-14.0" 296 | endObjectProperties 297 | 298 | # (Static Text) 299 | object activeXTextClass 300 | beginObjectProperties 301 | major 4 302 | minor 1 303 | release 0 304 | x 730 305 | y 770 306 | w 70 307 | h 20 308 | font "helvetica-medium-r-14.0" 309 | fontAlign "right" 310 | fgColor index 126 311 | bgColor index 133 312 | useDisplayBg 313 | value { 314 | "REALs" 315 | } 316 | endObjectProperties 317 | 318 | # (Static Text) 319 | object activeXTextClass 320 | beginObjectProperties 321 | major 4 322 | minor 1 323 | release 0 324 | x 810 325 | y 770 326 | w 50 327 | h 20 328 | font "helvetica-medium-r-14.0" 329 | fgColor index 130 330 | bgColor index 133 331 | useDisplayBg 332 | value { 333 | "DINTs" 334 | } 335 | endObjectProperties 336 | 337 | # (Textupdate) 338 | object TextupdateClass 339 | beginObjectProperties 340 | major 10 341 | minor 0 342 | release 0 343 | x 330 344 | y 30 345 | w 80 346 | h 30 347 | controlPv "$(IOC):biBOOL" 348 | fgColor index 15 349 | fgAlarm 350 | bgColor index 10 351 | fill 352 | font "helvetica-medium-r-14.0" 353 | endObjectProperties 354 | 355 | # (Static Text) 356 | object activeXTextClass 357 | beginObjectProperties 358 | major 4 359 | minor 1 360 | release 0 361 | x 260 362 | y 40 363 | w 57 364 | h 16 365 | font "helvetica-medium-r-14.0" 366 | fontAlign "right" 367 | fgColor index 112 368 | bgColor index 133 369 | useDisplayBg 370 | value { 371 | "bi, BOOL" 372 | } 373 | autoSize 374 | endObjectProperties 375 | 376 | # (Static Text) 377 | object activeXTextClass 378 | beginObjectProperties 379 | major 4 380 | minor 1 381 | release 0 382 | x 10 383 | y 10 384 | w 77 385 | h 16 386 | font "helvetica-bold-r-14.0" 387 | fgColor index 112 388 | bgColor index 133 389 | useDisplayBg 390 | value { 391 | "AI Records" 392 | } 393 | autoSize 394 | endObjectProperties 395 | 396 | # (Static Text) 397 | object activeXTextClass 398 | beginObjectProperties 399 | major 4 400 | minor 1 401 | release 0 402 | x 260 403 | y 10 404 | w 108 405 | h 16 406 | font "helvetica-bold-r-14.0" 407 | fgColor index 112 408 | bgColor index 133 409 | useDisplayBg 410 | value { 411 | "Binary Records" 412 | } 413 | autoSize 414 | endObjectProperties 415 | 416 | # (Textupdate) 417 | object TextupdateClass 418 | beginObjectProperties 419 | major 10 420 | minor 0 421 | release 0 422 | x 330 423 | y 70 424 | w 80 425 | h 30 426 | controlPv "$(IOC):biDINT" 427 | fgColor index 15 428 | fgAlarm 429 | bgColor index 10 430 | fill 431 | font "helvetica-medium-r-14.0" 432 | endObjectProperties 433 | 434 | # (Static Text) 435 | object activeXTextClass 436 | beginObjectProperties 437 | major 4 438 | minor 1 439 | release 0 440 | x 260 441 | y 80 442 | w 34 443 | h 16 444 | font "helvetica-medium-r-14.0" 445 | fontAlign "right" 446 | fgColor index 112 447 | bgColor index 133 448 | useDisplayBg 449 | value { 450 | "DINT" 451 | } 452 | autoSize 453 | endObjectProperties 454 | 455 | # (Static Text) 456 | object activeXTextClass 457 | beginObjectProperties 458 | major 4 459 | minor 1 460 | release 0 461 | x 260 462 | y 160 463 | w 63 464 | h 16 465 | font "helvetica-medium-r-14.0" 466 | fontAlign "right" 467 | fgColor index 112 468 | bgColor index 133 469 | useDisplayBg 470 | value { 471 | "BOOLs[5]" 472 | } 473 | autoSize 474 | endObjectProperties 475 | 476 | # (Textupdate) 477 | object TextupdateClass 478 | beginObjectProperties 479 | major 10 480 | minor 0 481 | release 0 482 | x 330 483 | y 150 484 | w 80 485 | h 30 486 | controlPv "$(IOC):biBOOLs5" 487 | fgColor index 15 488 | fgAlarm 489 | bgColor index 10 490 | fill 491 | font "helvetica-medium-r-14.0" 492 | endObjectProperties 493 | 494 | # (Textupdate) 495 | object TextupdateClass 496 | beginObjectProperties 497 | major 10 498 | minor 0 499 | release 0 500 | x 360 501 | y 340 502 | w 134 503 | h 30 504 | controlPv "$(IOC):mbbiBOOLs300" 505 | fgColor index 15 506 | fgAlarm 507 | bgColor index 10 508 | fill 509 | font "helvetica-medium-r-14.0" 510 | endObjectProperties 511 | 512 | # (Static Text) 513 | object activeXTextClass 514 | beginObjectProperties 515 | major 4 516 | minor 1 517 | release 0 518 | x 260 519 | y 340 520 | w 90 521 | h 40 522 | font "helvetica-medium-r-14.0" 523 | fontAlign "right" 524 | fgColor index 112 525 | bgColor index 133 526 | useDisplayBg 527 | value { 528 | "BOOLs[300], " 529 | "2 bits" 530 | } 531 | endObjectProperties 532 | 533 | # (Radio Box) 534 | object activeRadioButtonClass 535 | beginObjectProperties 536 | major 4 537 | minor 0 538 | release 0 539 | x 540 540 | y 300 541 | w 110 542 | h 120 543 | fgColor index 112 544 | bgColor index 50 545 | buttonColor index 133 546 | selectColor index 133 547 | topShadowColor index 107 548 | botShadowColor index 109 549 | controlPv "$(IOC):mbboBOOLs300" 550 | font "helvetica-medium-r-14.0" 551 | endObjectProperties 552 | 553 | # (Textupdate) 554 | object TextupdateClass 555 | beginObjectProperties 556 | major 10 557 | minor 0 558 | release 0 559 | x 820 560 | y 330 561 | w 134 562 | h 30 563 | controlPv "$(IOC):biDINTs8_5" 564 | fgColor index 15 565 | fgAlarm 566 | bgColor index 10 567 | fill 568 | font "helvetica-medium-r-14.0" 569 | endObjectProperties 570 | 571 | # (Static Text) 572 | object activeXTextClass 573 | beginObjectProperties 574 | major 4 575 | minor 1 576 | release 0 577 | x 720 578 | y 340 579 | w 90 580 | h 20 581 | font "helvetica-medium-r-14.0" 582 | fontAlign "right" 583 | fgColor index 112 584 | bgColor index 133 585 | useDisplayBg 586 | value { 587 | "DINTs[8].5" 588 | } 589 | endObjectProperties 590 | 591 | # (Static Text) 592 | object activeXTextClass 593 | beginObjectProperties 594 | major 4 595 | minor 1 596 | release 0 597 | x 720 598 | y 380 599 | w 90 600 | h 40 601 | font "helvetica-medium-r-14.0" 602 | fontAlign "right" 603 | fgColor index 112 604 | bgColor index 133 605 | useDisplayBg 606 | value { 607 | "INTs[2]," 608 | "bits 15&14" 609 | } 610 | endObjectProperties 611 | 612 | # (Menu Button) 613 | object activeMenuButtonClass 614 | beginObjectProperties 615 | major 4 616 | minor 0 617 | release 0 618 | x 820 619 | y 380 620 | w 130 621 | h 30 622 | fgColor index 25 623 | bgColor index 5 624 | inconsistentColor index 20 625 | topShadowColor index 107 626 | botShadowColor index 109 627 | controlPv "$(IOC):mbboINTs2_14" 628 | font "helvetica-medium-r-14.0" 629 | endObjectProperties 630 | 631 | # (Static Text) 632 | object activeXTextClass 633 | beginObjectProperties 634 | major 4 635 | minor 1 636 | release 0 637 | x 80 638 | y 470 639 | w 61 640 | h 16 641 | font "helvetica-medium-r-14.0" 642 | fontAlign "right" 643 | fgColor index 112 644 | bgColor index 133 645 | useDisplayBg 646 | value { 647 | "STRING" 648 | } 649 | endObjectProperties 650 | 651 | # (Textupdate) 652 | object TextupdateClass 653 | beginObjectProperties 654 | major 10 655 | minor 0 656 | release 0 657 | x 150 658 | y 460 659 | w 134 660 | h 30 661 | controlPv "$(IOC):string" 662 | fgColor index 15 663 | fgAlarm 664 | bgColor index 10 665 | fill 666 | font "helvetica-medium-r-14.0" 667 | endObjectProperties 668 | 669 | # (Static Text) 670 | object activeXTextClass 671 | beginObjectProperties 672 | major 4 673 | minor 1 674 | release 0 675 | x 70 676 | y 440 677 | w 160 678 | h 20 679 | font "helvetica-bold-r-14.0" 680 | fgColor index 112 681 | bgColor index 133 682 | useDisplayBg 683 | value { 684 | "Other Records" 685 | } 686 | endObjectProperties 687 | 688 | # (Static Text) 689 | object activeXTextClass 690 | beginObjectProperties 691 | major 4 692 | minor 1 693 | release 0 694 | x 160 695 | y 10 696 | w 85 697 | h 16 698 | font "helvetica-bold-r-14.0" 699 | fgColor index 112 700 | bgColor index 133 701 | useDisplayBg 702 | value { 703 | "AO Records" 704 | } 705 | autoSize 706 | endObjectProperties 707 | 708 | # (Textentry) 709 | object TextentryClass 710 | beginObjectProperties 711 | major 10 712 | minor 0 713 | release 0 714 | x 160 715 | y 30 716 | w 70 717 | h 40 718 | controlPv "$(IOC):aoREAL" 719 | fgColor index 25 720 | fgAlarm 721 | bgColor index 5 722 | fill 723 | font "helvetica-medium-r-14.0" 724 | endObjectProperties 725 | 726 | # (Textupdate) 727 | object TextupdateClass 728 | beginObjectProperties 729 | major 10 730 | minor 0 731 | release 0 732 | x 90 733 | y 70 734 | w 60 735 | h 30 736 | controlPv "$(IOC):aiREALs2" 737 | fgColor index 15 738 | fgAlarm 739 | bgColor index 10 740 | fill 741 | font "helvetica-medium-r-14.0" 742 | endObjectProperties 743 | 744 | # (Static Text) 745 | object activeXTextClass 746 | beginObjectProperties 747 | major 4 748 | minor 1 749 | release 0 750 | x 10 751 | y 80 752 | w 61 753 | h 16 754 | font "helvetica-medium-r-14.0" 755 | fontAlign "right" 756 | fgColor index 112 757 | bgColor index 133 758 | useDisplayBg 759 | value { 760 | "REALs[2]" 761 | } 762 | endObjectProperties 763 | 764 | # (Textupdate) 765 | object TextupdateClass 766 | beginObjectProperties 767 | major 10 768 | minor 0 769 | release 0 770 | x 90 771 | y 190 772 | w 60 773 | h 30 774 | controlPv "$(IOC):aiDINT" 775 | fgColor index 15 776 | fgAlarm 777 | bgColor index 10 778 | fill 779 | font "helvetica-medium-r-14.0" 780 | endObjectProperties 781 | 782 | # (Static Text) 783 | object activeXTextClass 784 | beginObjectProperties 785 | major 4 786 | minor 1 787 | release 0 788 | x 10 789 | y 240 790 | w 50 791 | h 16 792 | font "helvetica-medium-r-14.0" 793 | fontAlign "right" 794 | fgColor index 112 795 | bgColor index 133 796 | useDisplayBg 797 | value { 798 | "DINTs1" 799 | } 800 | autoSize 801 | endObjectProperties 802 | 803 | # (Static Text) 804 | object activeXTextClass 805 | beginObjectProperties 806 | major 4 807 | minor 1 808 | release 0 809 | x 10 810 | y 280 811 | w 50 812 | h 16 813 | font "helvetica-medium-r-14.0" 814 | fontAlign "right" 815 | fgColor index 112 816 | bgColor index 133 817 | useDisplayBg 818 | value { 819 | "DINTs2" 820 | } 821 | autoSize 822 | endObjectProperties 823 | 824 | # (Static Text) 825 | object activeXTextClass 826 | beginObjectProperties 827 | major 4 828 | minor 1 829 | release 0 830 | x 10 831 | y 320 832 | w 50 833 | h 16 834 | font "helvetica-medium-r-14.0" 835 | fontAlign "right" 836 | fgColor index 112 837 | bgColor index 133 838 | useDisplayBg 839 | value { 840 | "DINTs3" 841 | } 842 | autoSize 843 | endObjectProperties 844 | 845 | # (Textupdate) 846 | object TextupdateClass 847 | beginObjectProperties 848 | major 10 849 | minor 0 850 | release 0 851 | x 90 852 | y 270 853 | w 60 854 | h 30 855 | controlPv "$(IOC):aiDINTs2" 856 | fgColor index 15 857 | fgAlarm 858 | bgColor index 10 859 | fill 860 | font "helvetica-medium-r-14.0" 861 | endObjectProperties 862 | 863 | # (Textupdate) 864 | object TextupdateClass 865 | beginObjectProperties 866 | major 10 867 | minor 0 868 | release 0 869 | x 90 870 | y 310 871 | w 60 872 | h 30 873 | controlPv "$(IOC):aiDINTs3" 874 | fgColor index 15 875 | fgAlarm 876 | bgColor index 10 877 | fill 878 | font "helvetica-medium-r-14.0" 879 | endObjectProperties 880 | 881 | # (Textentry) 882 | object TextentryClass 883 | beginObjectProperties 884 | major 10 885 | minor 0 886 | release 0 887 | x 160 888 | y 190 889 | w 70 890 | h 40 891 | controlPv "$(IOC):aoDINT" 892 | fgColor index 25 893 | fgAlarm 894 | bgColor index 5 895 | fill 896 | font "helvetica-medium-r-14.0" 897 | endObjectProperties 898 | 899 | # (Textentry) 900 | object TextentryClass 901 | beginObjectProperties 902 | major 10 903 | minor 0 904 | release 0 905 | x 160 906 | y 390 907 | w 70 908 | h 40 909 | controlPv "$(IOC):aoINTs3" 910 | fgColor index 25 911 | fgAlarm 912 | bgColor index 5 913 | fill 914 | font "helvetica-medium-r-14.0" 915 | endObjectProperties 916 | 917 | # (Button) 918 | object activeButtonClass 919 | beginObjectProperties 920 | major 4 921 | minor 0 922 | release 0 923 | x 420 924 | y 30 925 | w 80 926 | h 30 927 | fgColor index 25 928 | onColor index 5 929 | offColor index 5 930 | inconsistentColor index 17 931 | topShadowColor index 107 932 | botShadowColor index 109 933 | controlPv "$(IOC):boBOOL" 934 | labelType "pvState" 935 | 3d 936 | font "helvetica-medium-r-12.0" 937 | objType "controls" 938 | endObjectProperties 939 | 940 | # (Textupdate) 941 | object TextupdateClass 942 | beginObjectProperties 943 | major 10 944 | minor 0 945 | release 0 946 | x 330 947 | y 110 948 | w 80 949 | h 30 950 | controlPv "$(IOC):biDINT_3" 951 | fgColor index 15 952 | fgAlarm 953 | bgColor index 10 954 | fill 955 | font "helvetica-medium-r-14.0" 956 | endObjectProperties 957 | 958 | # (Static Text) 959 | object activeXTextClass 960 | beginObjectProperties 961 | major 4 962 | minor 1 963 | release 0 964 | x 260 965 | y 120 966 | w 45 967 | h 16 968 | font "helvetica-medium-r-14.0" 969 | fontAlign "right" 970 | fgColor index 112 971 | bgColor index 133 972 | useDisplayBg 973 | value { 974 | "DINT.3" 975 | } 976 | autoSize 977 | endObjectProperties 978 | 979 | # (Button) 980 | object activeButtonClass 981 | beginObjectProperties 982 | major 4 983 | minor 0 984 | release 0 985 | x 420 986 | y 150 987 | w 80 988 | h 30 989 | fgColor index 25 990 | onColor index 5 991 | offColor index 5 992 | inconsistentColor index 17 993 | topShadowColor index 107 994 | botShadowColor index 109 995 | controlPv "$(IOC):boBOOLs5" 996 | labelType "pvState" 997 | 3d 998 | font "helvetica-medium-r-12.0" 999 | objType "controls" 1000 | endObjectProperties 1001 | 1002 | # (Static Text) 1003 | object activeXTextClass 1004 | beginObjectProperties 1005 | major 4 1006 | minor 1 1007 | release 0 1008 | x 260 1009 | y 320 1010 | w 175 1011 | h 16 1012 | font "helvetica-bold-r-14.0" 1013 | fgColor index 112 1014 | bgColor index 133 1015 | useDisplayBg 1016 | value { 1017 | "Multi-Bit Binary Records" 1018 | } 1019 | autoSize 1020 | endObjectProperties 1021 | 1022 | # (Textupdate) 1023 | object TextupdateClass 1024 | beginObjectProperties 1025 | major 10 1026 | minor 0 1027 | release 0 1028 | x 360 1029 | y 380 1030 | w 134 1031 | h 30 1032 | controlPv "$(IOC):mbbiDINTs2_3" 1033 | fgColor index 15 1034 | fgAlarm 1035 | bgColor index 10 1036 | fill 1037 | font "helvetica-medium-r-14.0" 1038 | endObjectProperties 1039 | 1040 | # (Static Text) 1041 | object activeXTextClass 1042 | beginObjectProperties 1043 | major 4 1044 | minor 1 1045 | release 0 1046 | x 260 1047 | y 380 1048 | w 90 1049 | h 40 1050 | font "helvetica-medium-r-14.0" 1051 | fontAlign "right" 1052 | fgColor index 112 1053 | bgColor index 133 1054 | useDisplayBg 1055 | value { 1056 | "DINTs[2].3 " 1057 | "2 bits" 1058 | } 1059 | endObjectProperties 1060 | 1061 | # (Lines) 1062 | object activeLineClass 1063 | beginObjectProperties 1064 | major 4 1065 | minor 0 1066 | release 0 1067 | x 500 1068 | y 360 1069 | w 30 1070 | h 0 1071 | lineColor index 112 1072 | fillColor index 133 1073 | lineWidth 2 1074 | arrows "from" 1075 | numPoints 2 1076 | xPoints { 1077 | 0 500 1078 | 1 530 1079 | } 1080 | yPoints { 1081 | 0 360 1082 | 1 360 1083 | } 1084 | endObjectProperties 1085 | 1086 | # (Textupdate) 1087 | object TextupdateClass 1088 | beginObjectProperties 1089 | major 10 1090 | minor 0 1091 | release 0 1092 | x 330 1093 | y 190 1094 | w 80 1095 | h 30 1096 | controlPv "$(IOC):biDINTs40_error" 1097 | fgColor index 15 1098 | fgAlarm 1099 | bgColor index 10 1100 | fill 1101 | font "helvetica-medium-r-14.0" 1102 | endObjectProperties 1103 | 1104 | # (Static Text) 1105 | object activeXTextClass 1106 | beginObjectProperties 1107 | major 4 1108 | minor 1 1109 | release 0 1110 | x 250 1111 | y 200 1112 | w 69 1113 | h 16 1114 | font "helvetica-medium-r-14.0" 1115 | fontAlign "right" 1116 | fgColor index 112 1117 | bgColor index 133 1118 | useDisplayBg 1119 | value { 1120 | "DINTs[1].8" 1121 | } 1122 | autoSize 1123 | endObjectProperties 1124 | 1125 | # (Textupdate) 1126 | object TextupdateClass 1127 | beginObjectProperties 1128 | major 10 1129 | minor 0 1130 | release 0 1131 | x 330 1132 | y 230 1133 | w 80 1134 | h 30 1135 | controlPv "$(IOC):biDINTs8_5" 1136 | fgColor index 15 1137 | fgAlarm 1138 | bgColor index 10 1139 | fill 1140 | font "helvetica-medium-r-14.0" 1141 | endObjectProperties 1142 | 1143 | # (Static Text) 1144 | object activeXTextClass 1145 | beginObjectProperties 1146 | major 4 1147 | minor 1 1148 | release 0 1149 | x 250 1150 | y 240 1151 | w 69 1152 | h 16 1153 | font "helvetica-medium-r-14.0" 1154 | fontAlign "right" 1155 | fgColor index 112 1156 | bgColor index 133 1157 | useDisplayBg 1158 | value { 1159 | "DINTs[8].5" 1160 | } 1161 | autoSize 1162 | endObjectProperties 1163 | 1164 | # (Button) 1165 | object activeButtonClass 1166 | beginObjectProperties 1167 | major 4 1168 | minor 0 1169 | release 0 1170 | x 420 1171 | y 270 1172 | w 80 1173 | h 30 1174 | fgColor index 25 1175 | onColor index 5 1176 | offColor index 5 1177 | inconsistentColor index 17 1178 | topShadowColor index 107 1179 | botShadowColor index 109 1180 | controlPv "$(IOC):boINTs3_2" 1181 | labelType "pvState" 1182 | 3d 1183 | font "helvetica-medium-r-12.0" 1184 | objType "controls" 1185 | endObjectProperties 1186 | 1187 | # (Static Text) 1188 | object activeXTextClass 1189 | beginObjectProperties 1190 | major 4 1191 | minor 1 1192 | release 0 1193 | x 350 1194 | y 280 1195 | w 59 1196 | h 16 1197 | font "helvetica-medium-r-14.0" 1198 | fontAlign "right" 1199 | fgColor index 112 1200 | bgColor index 133 1201 | useDisplayBg 1202 | value { 1203 | "INTs[3].2" 1204 | } 1205 | autoSize 1206 | endObjectProperties 1207 | 1208 | # (Textupdate) 1209 | object TextupdateClass 1210 | beginObjectProperties 1211 | major 10 1212 | minor 0 1213 | release 0 1214 | x 90 1215 | y 350 1216 | w 60 1217 | h 30 1218 | controlPv "$(IOC):aiINTs2" 1219 | fgColor index 15 1220 | fgAlarm 1221 | bgColor index 10 1222 | fill 1223 | font "helvetica-medium-r-14.0" 1224 | endObjectProperties 1225 | 1226 | # (Static Text) 1227 | object activeXTextClass 1228 | beginObjectProperties 1229 | major 4 1230 | minor 1 1231 | release 0 1232 | x 10 1233 | y 360 1234 | w 61 1235 | h 16 1236 | font "helvetica-medium-r-14.0" 1237 | fontAlign "right" 1238 | fgColor index 112 1239 | bgColor index 133 1240 | useDisplayBg 1241 | value { 1242 | "INTs[2]" 1243 | } 1244 | endObjectProperties 1245 | 1246 | -------------------------------------------------------------------------------- /testether_ipApp/opi/EtherIPStats.bob: -------------------------------------------------------------------------------- 1 | 2 | 3 | Display 4 | 5 | Label 6 | TITLE 7 | Ether IP Stats 8 | 0 9 | 0 10 | 550 11 | 31 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | true 21 | 22 | 23 | Label_1 24 | PLC Errors: 25 | 50 26 | 27 | 28 | Text Update 29 | $(S=test):PLC_ERRORS 30 | 110 31 | 50 32 | 33 | 34 | Label_3 35 | List Scan Time: 36 | 80 37 | 38 | 39 | Text Update_1 40 | $(S=test):LIST_SCAN_TIME 41 | 110 42 | 80 43 | 44 | 45 | Label_4 46 | Min. Scan Time: 47 | 110 48 | 110 49 | 50 | 51 | Text Update_2 52 | $(S=test):LIST_MIN_SCAN_TIME 53 | 110 54 | 110 55 | 56 | 57 | Label_5 58 | Max Scan Time: 59 | 140 60 | 110 61 | 62 | 63 | Text Update_3 64 | $(S=test):LIST_MAX_SCAN_TIME 65 | 110 66 | 140 67 | 68 | 69 | Label_6 70 | Slow Transfers: 71 | 170 72 | 110 73 | 74 | 75 | Text Update_4 76 | $(S=test):PLC_TASK_SLOW 77 | 110 78 | 170 79 | 80 | 81 | Boolean Button 82 | $(S=test):RESET_PLC_STATS 83 | 110 84 | 200 85 | 90 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | false 95 | true 96 | 1 97 | 98 | 99 | -------------------------------------------------------------------------------- /testether_ipApp/src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | TOP = ../.. 3 | 4 | include $(TOP)/configure/CONFIG 5 | #---------------------------------------- 6 | # ADD MACRO DEFINITIONS AFTER THIS LINE 7 | 8 | # Since base 3_15, on Linux 64 and Mac, support LINT 9 | ifdef BASE_3_15 10 | ifeq ($(EPICS_HOST_ARCH),linux-x86_64) 11 | USR_CFLAGS += -DSUPPORT_LINT 12 | USR_CPPFLAGS += -DSUPPORT_LINT 13 | else ifeq ($(EPICS_HOST_ARCH),darwin-x86) 14 | USR_CFLAGS += -DSUPPORT_LINT 15 | USR_CPPFLAGS += -DSUPPORT_LINT 16 | else ifeq ($(EPICS_HOST_ARCH),darwin-aarch64) 17 | USR_CFLAGS += -DSUPPORT_LINT 18 | USR_CPPFLAGS += -DSUPPORT_LINT 19 | endif 20 | endif 21 | 22 | # .dbd will be created from Include.dbd 23 | DBD += eipIoc.dbd 24 | 25 | eipIoc_DBD += base.dbd 26 | eipIoc_DBD += ether_ip.dbd 27 | 28 | # Hard-code location of DBD in product 29 | FINAL_LOCATION = $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) 30 | EIP_DBD = $(FINAL_LOCATION)/dbd/eipIoc.dbd 31 | USR_CPPFLAGS += -DEIP_DBD=\"$(EIP_DBD)\" 32 | 33 | PROD_IOC = eipIoc 34 | 35 | # _registerRecordDeviceDriver.cpp will be created from .dbd 36 | eipIoc_SRCS += eipIoc_registerRecordDeviceDriver.cpp 37 | eipIoc_SRCS_DEFAULT += eipIocMain.cpp 38 | eipIoc_SRCS_vxWorks += -nil- 39 | 40 | USR_CXXFLAGS += -DUSE_TYPED_RSET 41 | 42 | eipIoc_LIBS += ether_ip 43 | 44 | # Add PVA if available 45 | ifdef EPICS_QSRV_MAJOR_VERSION 46 | eipIoc_DBD += PVAServerRegister.dbd 47 | eipIoc_DBD += qsrv.dbd 48 | eipIoc_LIBS += qsrv 49 | eipIoc_LIBS += $(EPICS_BASE_PVA_CORE_LIBS) 50 | endif 51 | 52 | ether_ip_DIR = $(INSTALL_LIB) 53 | 54 | eipIoc_LIBS += $(EPICS_BASE_IOC_LIBS) 55 | 56 | include $(TOP)/configure/RULES 57 | -------------------------------------------------------------------------------- /testether_ipApp/src/eipIocMain.cpp: -------------------------------------------------------------------------------- 1 | /* eipIocMain.cpp 2 | * kasemirk@ornl.gov 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "drvEtherIP.h" 21 | 22 | #ifndef EIP_DBD 23 | #error This needs to be compiled with -DEIP_DBD=/path/to/installed/eip.dbd 24 | #endif 25 | 26 | extern "C" 27 | { 28 | extern void eipIoc_registerRecordDeviceDriver(struct dbBase *); 29 | } 30 | 31 | static void usage() 32 | { 33 | printf("Usage:\n"); 34 | printf(" eipIoc st.cmd\n"); 35 | printf("or\n"); 36 | printf(" eipIoc -p Plc1=IP,slot [-m macro=value] -d database.db [-d another.db]\n"); 37 | printf("\n"); 38 | printf("In the first case, this program acts as an ordinary IOC, executing a startup file\n"); 39 | printf("\n"); 40 | printf("The second invocation is for a command-line mode similar to the 'softIoc' from EPICS base,\n"); 41 | printf("extended with options to communicate via etherIp.\n"); 42 | } 43 | 44 | static bool definePlc(const char *def) 45 | { 46 | // Create modifyable copy 47 | char *copy = strdup(def); 48 | 49 | // Optional slot in "Plc1=IP,slot" 50 | int slot = 0; 51 | char *c = strrchr(copy, ','); 52 | if (c) 53 | { 54 | slot = atoi(c+1); 55 | *c = '\0'; 56 | } 57 | 58 | // Split "Plc1=IP" at '=' 59 | c = strrchr(copy, '='); 60 | if (! c) 61 | { 62 | free(copy); 63 | fprintf(stderr, "Missing '=' in PLC definition '%s'\n", def); 64 | return false; 65 | } 66 | *c = '\0'; 67 | const char *plc = copy; 68 | const char *ip = c+1; 69 | 70 | printf("drvEtherIP_define_PLC(\"%s\", \"%s\", %d);\n", plc, ip, slot); 71 | drvEtherIP_define_PLC(plc, ip, slot); 72 | free(copy); 73 | 74 | return true; 75 | } 76 | 77 | int main(int argc,char *argv[]) 78 | { 79 | const char *dbd_file = EIP_DBD; 80 | char *macros = strdup(""); 81 | 82 | if (argc == 1) 83 | { 84 | usage(); 85 | return 0; 86 | } 87 | else if (argc == 2 && argv[1][0] != '-') 88 | { 89 | iocsh(argv[1]); 90 | epicsThreadSleep(.2); 91 | } 92 | else 93 | { 94 | printf("# Command-line mode\n"); 95 | printf("#\n"); 96 | printf("# The following commands are executed from cmdline options\n"); 97 | printf("\n"); 98 | printf("dbLoadDatabase(\"%s\")\n", dbd_file); 99 | dbLoadDatabase((char *)dbd_file, NULL, NULL); 100 | 101 | printf("eipIoc_registerRecordDeviceDriver(pdbbase)\n"); 102 | eipIoc_registerRecordDeviceDriver(pdbbase); 103 | printf("drvEtherIP_init()\n"); 104 | drvEtherIP_init(); 105 | 106 | int ch; 107 | while ( (ch = getopt(argc, argv, "p:m:d:h")) != -1) 108 | { 109 | switch (ch) 110 | { 111 | case 'h': 112 | usage(); 113 | return 0; 114 | case 'p': 115 | if (! definePlc(optarg)) 116 | return -2; 117 | break; 118 | case 'm': 119 | free(macros); 120 | macros = strdup(optarg); 121 | break; 122 | case 'd': 123 | printf("dbLoadRecords(\"%s\", \"%s\");\n", optarg, macros); 124 | dbLoadRecords(optarg, macros); 125 | break; 126 | default: 127 | usage(); 128 | return -1; 129 | } 130 | } 131 | printf("iocInit()\n"); 132 | 133 | printf("\n"); 134 | printf("#\n"); 135 | printf("# End of command-line options, entering IOC shell\n"); 136 | printf("#\n"); 137 | printf("\n"); 138 | 139 | iocInit(); 140 | epicsThreadSleep(0.5); 141 | } 142 | 143 | iocsh(NULL); 144 | 145 | return 0; 146 | } 147 | --------------------------------------------------------------------------------