The TwinCAT-EPICS IOC synchronizes TwinCAT variables with EPICS channels.
Further documentation is available at:
The IOC handles communication between two interfaces: TwinCAT and EPICS. As such, it makes use of the TwinCAT ADS Communication Library to communicate with the PLC on one side and the libraries of the EPICS Base on the other side.
The IOC keeps an internal database of the records that it manages. This gets initialized through parsing the tpy file of a TwinCAT project, which is generated every time the project is built. This tpy file will provide the names and memory locations of TwinCAT symbols on one PLC. The IOC uses this information to read and write data via ADS commands to TwinCAT. Reading and writing are done via two scanner threads, each executing a read or write command every n ms, with n set by tcSetScanRate at IOC startup (see the example st.cmd below). Multiple tpy files can be loaded in a single IOC, i.e. the IOC can manage records on multiple PLCs.
To notify EPICS that a record value has been updated, the IOC will generate an interrupt or a callback request to EPICS, telling EPICS to process that record. EPICS record processing includes grabbing the value from the IOC's internal memory, checking alarms, processing linked records, and updating channel access clients (e.g. MEDM). Conversely, when EPICS receives a new value for a record through channel access, it will process the record, and write the new value to the IOC's internal memory.
The setup of the IOC's internal database is designed to prevent the overlap of TwinCAT/EPICS read/write requests on the same record, which could cause data values to become lost.
The internal data value has flags to indicate that the value has been written by one side (TwinCAT or EPICS), but has not yet been read by the other side. Thus, if EPICS has written a new value to the IOC, TwinCAT cannot overwrite that value until it has read the old value. This logic guarantees that data values do not get lost within the IOC, that a value written on one side always makes it to the other side, preventing collisions of write requests. We also prevent the crossing of a read request with a write request by having the read/write scanners on the TwinCAT side use mutexes when accessing the IOC's database. Note that simultaneous reading of the IOC database does not pose any problems, so this case is not prevented.
In addition, the IOC runs a scanner thread for each PLC that slowly crawls through the internal database and pushes data values to EPICS. It will cover the entire database once per minute, which guarantees that the record values seen in EPICS/MEDM are never more than a minute old.
At startup, the IOC will parse tpy files to generate EPICS databases. The user can specify which types of records are exported, conversion rules between TwinCAT names and EPICS names, and whether to split databases into multiple files. The user can also allow the parser to generate other lists alongside the EPICS database, for example channel lists and burt restore files. A full list of the available options can be found in the section TwinCAT EPICS Options.
Current
Added features:
Bug Fixes:
Example to set EPICS_BASE: "setx EPICS_BASE C:\EPICS\base-3.15.9\". Typically TWINCAT3DIR will be set, if TwinCAT was installed on the machine. For Example, if TWINCAT3DIR was set to "C:\TwinCAT\3.1", the ADS include files should be located in ..\AdsApi\TcAdsDll\Include.
Version 2.3
Added features:
Bug Fixes:
Version 2.2
Added features:
Bug Fixes:
Version 2.1
Added features:
Bug Fixes:
Version 2.0
Added features:
Bug Fixes:
Version 1.3
Added features:
Bug Fixes:
Version 1.2
Added features:
Bug Fixes:
Version 1.1
Added features:
Bug Fixes:
Version 1.0
Initial release.
While the core functionality of the IOC is already in place and fully tested, several additional features are being developed and will be implemented soon:
Upgrades/Adaptations
The program was written in C++ and built using Visual Studio 2022, the TwinCAT ADS Communication Library that comes with TwinCAT 3.1, and EPICS Base version 3.15.9. Code documentation is generated by Doxygen and can be found in this repository.
Instructions for building the ioc from the source can be found below.
Build expat (now supported using the vcpkg manager):
The most recent version uses the vcpkg version of expat. No need for compiling them yourself.
The expat libraries are provided in C:\SlowControls\EPICS\Utilities\expat. In case a new built is required, download from http://expat.sourceforge.net/. Build static, multithreaded release (/MD) and debug (/MDd) versions and save them as libexpatMT.lib and libexpatMTD.lib in the above directory.
Generate tc device support:
The tc device support files are provided and only need to be regenerated when new records are added. Run createTcDeviceSupport.ps1 in the TCatDeviceSupport subdirectory.
The startup requires several user inputs. A set of commands is given as a parameter to tcIoc.exe which specifies these inputs. Below is an example:
dbLoadDatabase("./tCat.dbd",0,0) #load database definition file for TwinCAT device support
tCat_registerRecordDeviceDriver(pdbbase) #register TwinCAT support with EPICS
callbackSetQueueSize(5000) #set the size of the EPICS callback buffer (default: 2000)
tcSetScanRate(10, 5) #set the time between requests to TwinCAT (10ms here)
#and the slowdown multiple for pushing values to EPICS (5x here)
tcLoadRecords("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.tpy", "") #load symbols from a tpy file
iocInit() #initialize the IOC
A complete list of TwinCAT EPICS commands can be found on page TwinCAT EPICS Commands.
To start the IOC,
On some systems it may be required to install the Visual C++ Redistributable for Visual Studio 2012
The available commands are:
tcSetAlias
Sets an alias name for a TwinCAT PLC. This name is used to define the info records. The alias name is applied when tcLoadRecords is called. It is reset afterwards.
Example: Sets the alias name to "C1PLC1":
tcSetAlias("C1PLC1")
A second argument can be specified to define replacement rules. For example if a variable is named Ifo and has an alias set to ".${IFO}" using OPC property 8620, it can be exported by tcIoc as H1 using
tcSetAlias("C1PLC1", "IFO=H1,END=C")
Multiple replacement rules are separated by comma.
tcInfoPrefix
Adds a prefix string to all info channel namees. Info channels report on the workings of the tcIoc itself. For example,
tcInfoPrefix(".\${IFO}.Sys.EtherCAT.\${ALIAS}.Info")
will add "H1.Sys.EtherCAT.C1PCL1.Info" to all info records (dollar signs need to be escaped).
tcSetScanRate
Sets the scan rate for the TwinCAT PLC. The first argument is the scan rate in ms for the read or write scanners reading and writing TwinCAT variables. The second number is a multiple which describes the slow down for updating the EPICS read-only channels. The update rate for read/write channels is the same as the TwinCAT scan rate.
Example: Yields a 10ms TwinCAT update rate, and a 50ms EPICS update rate for read-only channels.
tcSetScanRate(10,5)
tcSetAdsAddress
Sets the ADS address manually. The ADS address will typically be available in the tpy file, but it might reflect the network address of the machine that was used to compile the PLC. In cases where the ADS addres is not available, it can be set like
tcSetAdsAddress("tc://10.1.23.156.1.1:0/")
This assumes that the AMS address of the machine where the PLC is running is 10.1.23.156.1.1. If the port number is given as zero, it will default to the one specified in the PLC code.
tcGenerateList
Generates an additional listings when the records are loaded. Multiple tcList commands can be called in series to produce different listing. The first argument is a output file name. The second argument is a set of options. The lists are generated when tcLoadRecords is called. The list commands are reset afterwards.
Example 1: This will generate an autoburt request file:
tcGenerateList("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.req","-lb")
Example 2: This will generate a listing of OPC names:
tcGenerateList("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.opc.txt","-l -rn -yi -cp")
Example 3: This will generate a listing of EPICS names:
tcGenerateList("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.chn.txt","-l")
Example 4: This will generate an EPICS listing without string channels. The available options are listed on page TwinCAT EPICS Options:
tcGenerateList("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.ini","-l -ns")
tcGenerateMacros
Generates ASCII macro lists (aml files) which can be used to generate ADL files for medm. The first argument is a output directory which is used to store the macro files. The second argument is a set of options. The macro files are generated when tcLoadRecords is called. The list commands are reset afterwards.
Example 1: This will generate a macro files for each encountered structure including both fields and error messages. The resulting files are stored in the ADL subdirectory. Error messages require corresponding exp files (see the coding standard, E1200225):
tcGenerateMacros("C:\SlowControls\Target\H1ECATX1\ADL")
Example 2: Generates macro files without error messages.
tcGenerateMacros("C:\SlowControls\Target\H1ECATX1\ADL", "-mf")
tcLoadRecords
Loads a tpy file, then generates and loads the EPICS database. The first argument is the filename to the tpy file. The generated db file will have the same name but with the extension ".db". The second argument is a set of options.
Example: This command will parse the specified tpy file, then generate a db file with the name "C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.db" and the specified options. The available options are listed on page TwinCAT EPICS Options:
tcLoadRecords("C:\SlowControls\Target\H1ECATX1\PLC1\PLC1.tpy","")
The above commands will only be executed before iocInit() is called. Multiple tpy files can be loaded by issuing multiple tcLoadRecords commands. However, tcSetAlias and tcGenerateList need to be specified anew before each tcLoadRecords command. The rate specified with tcSetScanRate will be reused unless a new tcSetScanRate command has been issued.
When generating a db file or a listing, a set of options describing the conversion rules is available. Options can be specified either Windows or Unix style. Meaning, both /ea and -ea will produce the same result.
Channel Processing:
| option | description |
|---|---|
| /eo | Only export variables which are marked by an OPC export directive in the tpy file (default) |
| /ea | Export all variables regardless of the OPC settings in the tpy file |
| --— | --— |
| /ys | String variables are processes (default) |
| /ns | No string variables are processed |
| --— | --— |
| /pa | Process all types (default) |
| /ps | Process only simple types types, e.g., INT, BOOL, DWORD, etc. |
| /pc | Process only complex types, e.g., STRUCT, ARRAY |
Channel Name Conversion:
| option | description |
|---|---|
| /fs 'filename' | Process a substitution file and keep defined or published channels (default) |
| /fi 'filename' | Process a substitution file and keep only found channels |
| /fa 'filename' | Process a substitution file and keep all channels |
| --— | --— |
| /rl | LIGO standard conversion rule (default) |
| /rv | LIGO rules for initial vacuum channel names (version 1.1) |
| /rd | Replace dots with underscores in channel names |
| /rn | Do not apply any special conversion rules |
| --— | --— |
| /cp | Preserve case in EPICS channel names |
| /cu | Force upper case in EPICS channel names (default) |
| /cl | Force lower case in EPICS channel names |
| --— | --— |
| /nd | Eliminate leading dot in channel name (default) |
| /yd | Leave leading dot in channel name |
| --— | --— |
| /yi | Leave array indices in channel names |
| /ni | Replace array brackets with a single leading underscore (default) |
| --— | --— |
| /p 'name' | Include a prefix of 'name' for every channel (defaults to no prefix) |
Split File Support:
| option | description |
|---|---|
| /nsio | Do not split database or listing by record type (default) |
| /ysio | Split database or listing into input only and input/ouput recrods |
| --— | --— |
| /sn 'num' | Split database or listing into files with no more than 'num' records |
| /sn 0 | Does not split database or listing into multiple files (default) |
Database Generation:
| option | description |
|---|---|
| /devopc | Use OPC name in INPUT/OUTPUT field (default) |
| /devtc | Use TwinCAT name in INPUT/OUTPUT fields instead of OPC |
| --— | --— |
| /ss | Use standard strings (stringin/stringout) |
| /sl | Use long strings (sli/slo) |
| /sd | Use long or short strings depending on size (default) |
| --— | --— |
| /is | Use standard 32-bit integers (longin/longout) |
| /il | Use 64-bit integers (int64in/int64out) |
| /id | Use 32 or 64-bit integers depending on size (default) |
List Generation:
| option | description |
|---|---|
| /l | Generate a standard listing, name only (default) |
| /ll | Generate a long listing, name and opc parameters |
| /lb | Generate an autoburt save/restore file |
| /lx | Generate an XML channel list (xsd at LIGO-E2500002) |
Macro Generation:
| option | description |
|---|---|
| /ma | Generate a macro file for each structure describing fields and errors (default) |
| /me | Generate a macro file for each structure describing the error messages |
| /mf | Generate a macro file for each structure describing all fields |
Applicable options are:
| Program/Instruction | Available Options | Enforced Options |
|---|---|---|
| tpyinfo | channel processing | |
| EpicsDbGen | all | |
| tcLoadRecords | channel processing, channel name conversion | -ps -nsio -sn 0 -devtc |
| tcGenerateList | channel processing, channel name conversion, list generation | -ps -nsio -sn 0 |
| tcGenerateMacros | macro generation | |
| infoLoadRecords | channel processing, channel name conversion | -ps -nsio -sn 0 -devtc |
| infoGenerateList | channel processing, channel name conversion, list generation | -ps -nsio -sn 0 |
Several checks on the performance of the IOC have been made to verify that it will be able to reliably handle all ~20,000 Slow Controls channels for extended periods of time.
Test system
Hardware:
Processor: Intel Xeon CPU X5650 Cores: 6 HT Threads: 12 Speed: 2.67GHz Mmeory: 12 GB; 2.99GB usable
Software:
OS: Windows 7 Version: 32-bit operating system TwinCAT: 2.11
This test was performed to see how much data we can read from TwinCAT in one request before overloading the system.
1 channel
1.076ms to read data
TwinCAT System Real Time Usage: was not monitored
1000 channels (~10kB)
1.084ms to read data out in one request
TwinCAT System Real Time Usage: no noticeable change
3,200 channels (~30kB)
1.087ms to read data out in one request
TwinCAT System Real Time Usage: +1-2%
7,500 channels (~70kB)
1.099ms to read data out in one request
TwinCAT System Real Time Usage: +3-4%
15,000 channels (~150kB)
1.121ms to read data out in one request
TwinCAT System Real Time Usage: +4-5%
This test was performed to see how generating individual requests for each channel can overload the TwinCAT system. In this example we specified the memory location for each channel, instead of requesting one large memory region as above. This method proved to be too taxing on the TwinCAT system, so we do not use it in our IOC. Compare to the above performance figures.
1000 channels
1.306ms to get data for all channels
TwinCAT System Real Time Usage: +20%
4000 channels
1.483ms to get data for all channels
TwinCAT System Real Time Usage: +60-80%
It takes ~1.33s to process 1,000,000 records
Thus in a 10ms cycle it can process ~7500 records