TransSECS Devices - Scripting for PLC Servers
To write a value to a server in the devices the format is:
/Devices/[DeviceNodeName]_Servers/[ServerName]→[setValue]
For example, to set the integer value 2 to a server called ModbusWord under the ModbusTCP node use:
/Devices/ModbusTCP_Servers/ModbusWord->setIntValue(2);
TransSECS is thread safe and so you can create threads in a script. Here's an example that creates a thread before setting the value of a Modbus server.
var Thread = Java.type("java.lang.Thread"); var Runnable = Java.type("java.lang.Runnable"); // declare our thread this.thread = new Thread(new Runnable(){ run: function () { /Devices/ModbusTCP_Servers/ModbusWord->setIntValue(2); print("printed from a separate thread"); } }); // start our thread this.thread.start();
Set a value to a field in a message and send the message
var TransSecsController = Java.type("com.ergotech.transsecs.secs.TransSecsController"); secsInterface=TransSecsController.findController("PLCTool"); // the name of your project // find the message var message = secsInterface.getMessageBean("MessageName"); // the name of your message message.setSlotNumber(15); // "SlotNumber" is the name you gave to an element in TransSECS when the message "MessageName" was defined. message.sendMessage();
Here's a more concrete example. In an S2F42/S2F50 reject response set the CPName to indicate which parameter is in error:
//function to send a host command reply with an invalid parameter function sendBadParamReply(paramName) { reply = secsInterface.getMessageBean("HostCommandRejectedBadParam"); reply.setCPName(paramName); reply.sendMessage(); } //end of function to send HostCommandRejectedBadParam
A function to set the HCACK on an S2F42/S2F50 message. Note that the HCACK is a little strange. In TransSECS all byte types (SECS format 10) take an array, so here we have to create an array.
//function to send a simple host command reply function sendSimpleReply(hcack) { reply = secsInterface.getMessageBean("HostCommandReply"); reply.setHCACK(Java.to([hcack],"byte[]")); reply.sendMessage(); } //end of function to send HostCommandReply
Register for Connection Status Notification in TransSECS Host Application
In a GEMHost application the TransSECS controller publishes the status of the connection.
To register for this, add a “Startup Trigger” from the “Demo Servers”
Register for the notification in the “script” field. Here the notification is just printed to the display.
var TransSecsController = Java.type("com.ergotech.transsecs.secs.TransSecsController"); var ConnectionStatusListener = Java.type("com.ergotech.transsecs.secs.host.ConnectionStatusListener"); print("Startup Trigger"); host=TransSecsController.findController("GEMHost"); // create the connection status listener var connectionStatusListener = new ConnectionStatusListener() { connectionStatusChanged : function (connectionStatus, comment) { print ("Current Connection Status " + connectionStatus + " Status String \"" + comment + "\""); } }; // add this to the host host.addConnectionStatusListener(connectionStatusListener); print("Connection Status Trigger Registered");
The details of the connection status are described here
Writing a List to PLC Registers
If a message is received from the host contains a list of variable length it's easy to split this list and place it in registers on the PLC.
In TransSECS, give the list a name, here “SlotIDList” and mark it as Publish. Do not set a Device and Tagname.
In a tag-based PLC, such as an Ethernet-IP PLC. You can write to elements of an array tag.
If your device is called “EIPPLC” and you want to write the values to an array “StringArray” using the server “SlotIDList” then the code would look like this:
for (id =0 ; id < incomingValue.size() ; id++ ) { /Devices/EIPPLC_Servers/SlotIDList->setVariableName("StringArray["+id+"]"); // set the element /Devices/EIPPLC_Servers/SlotIDList->setStringValue(incomingValue.get(id).getStringValue()); // send the value } /Devices/EIPPLC_Servers/NumberOfSlotIds->setIntValue(incomingValue.size()); // set the list length
The last line of the script puts the length of the list into a server called “NumberOfSlotIds”. After receipt, the PLC should set the “NumberOfSlotIds” variable to an invalid list length (eg -1, or 100000) so that it will change when the message is next received.
For a register-based PLC, such as a FINs PLC and a device called “FINSPLC” the equivelent code would be:
var baseRegister = 100; var stringLength = 8; for (id =0 ; id < incomingValue.size() ; id++ ) { /Devices/FINSPLC_Servers/SlotIDList->setAddress("" + (baseRegister + stringLength*id)); /Devices/FINSPLC_Servers/SlotIDList->setStringValue(incomingValue.get(id).getStringValue()); } /Devices/FINSPLC_Servers/NumberOfSlotIds->setIntValue(incomingValue.size());
Reading a Variable Length List from a PLC and updating a VID
The most challenging part of reading the list from the PLC is managing the length of the list.
Here we trigger the script on the variable that is the list length (ListVIDLengthFromPLC)
and use that script to populate a VID “ListVID”
The register VIDListArray is used to read the elements of the list individually.
After reading the list from the PLC and setting the VID, the script resets the length variable in the PLC ready for the next update of the variable.
var TransSecsController = Java.type("com.ergotech.transsecs.secs.TransSecsController"); var SecsFormat20 = Java.type("com.ergotech.secs.SecsFormat20"); var SecsFormat00 = Java.type("com.ergotech.secs.SecsFormat00"); var LongValueObject = Java.type("com.ergotech.vib.valueobjects.LongValueObject"); listLength = incomingValue.getIntValue(); //print ("List Length "+ listLength); try { if ( listLength > 0 ) { // will reset this to zero after update print ("List Length "+ listLength); secsInterface=TransSecsController.findController("PLCTool"); gh = secsInterface.getGemHandler(); listValue = new SecsFormat00(); for (id =0 ; id < listLength ; id++ ) { /Devices/NJPLC_Servers/VIDListArray->setVariableName("StringArray[" + id + "]"); /Devices/NJPLC_Servers/VIDListArray->triggerRead(); incomingString = /Devices/NJPLC_Servers/VIDListArray->getStringValue(); listValue.add(new SecsFormat20(incomingString)); } print ("List "+ listValue); gh.setValue("ListVID",listValue); /Devices/NJPLC_Servers/ListVIDLengthFromPLC->setValueObject(new LongValueObject(0)); // reset the length } } catch ( e ) { print ("Error " + e ); } 0; // update the server with a zero...
If the PLC Device supports an Array type, you can use this value directly in the Device Name/Tag Name dropdown. The values from the array will be automatically converted from the type in the array to the SecsFormat type.
For a ModbusArray, the conversion is as follows:
Unsigned Word - SecsFormat52 Signed Word - SecsFormat54 Unsigned Double Word - SecsFormat50 Signed Double Word - SecsFormat50 Float - SecsFormat44 Coil - SecsFormat11 Discrete - SecsFormat11
For an S7 Array the conversions X,B,W,I,D all convert to SecsFormat5. R (real) convert to SecsFormat44 and S (String) to SecsFormat20.
For improved control over the conversion, the SecsFormat00 can be created in a script.
In this example we create a SecsFormat00 containing signed integers - SecsFormat34
var TransSecsController = Java.type("com.ergotech.transsecs.secs.TransSecsController"); var SecsFormat34 = Java.type("com.ergotech.secs.SecsFormat34"); var SecsFormatValueObject = Java.type("com.ergotech.secs.gem.SecsFormatValueObject"); var SecsFormat00 = Java.type("com.ergotech.secs.SecsFormat00"); listValue = new SecsFormat00(); try { secsInterface=TransSecsController.findController("PLCTool"); gh = secsInterface.getGemHandler(); for (id =0 ; id < incomingValue.size() ; id++ ) { listValue.add(new SecsFormat34(incomingValue.get(0).getIntValue())); } print ("List "+ listValue); gh.setValue("ListVID",listValue); } } catch ( e ) { print ("Error " + e ); } new SecsFormatValueObject(listValue);
Reading a File and Storing as Binary Data in the PLC
This example uses the Melsec Binary (MelsecBinary) server. It reads a file from the disk, compresses the file and sends it to the server.
var Deflater = Java.type("java.util.zip.Deflater"); var Inflater = Java.type("java.util.zip.Inflater"); var Files = Java.type("java.nio.file.Files"); var BinaryValueObject = Java.type("com.ergotech.vib.valueobjects.BinaryValueObject"); var LongValueObject = Java.type("com.ergotech.vib.valueobjects.LongValueObject"); var arrayOfBytes = Java.type("byte[]"); recipe = Files.readAllBytes(java.nio.file.Paths.get("DemoPPBody.rcp")); // Compress the bytes output = new arrayOfBytes(2000); compresser = new Deflater(); compresser.setInput(recipe); compresser.finish(); compressedDataLength = compresser.deflate(output); compresser.end(); outArray = new arrayOfBytes(compressedDataLength); java.lang.System.arraycopy(output, 0, outArray, 0, compressedDataLength); // save the length of the compress recipe to the PLC using a server called "CompressedRecipeLength" CompressedRecipeLength->setValueObject(new LongValueObject(compressedDataLength)); new BinaryValueObject(outArray); // will be writen to the server
The inverse of the above code reads the bytes from the PLC and stores them to a file.
var Deflater = Java.type("java.util.zip.Deflater"); var Inflater = Java.type("java.util.zip.Inflater"); var Files = Java.type("java.nio.file.Files"); var BinaryValueObject = Java.type("com.ergotech.vib.valueobjects.BinaryValueObject"); var StringValueObject = Java.type("com.ergotech.vib.valueobjects.StringValueObject"); var arrayOfBytes = Java.type("byte[]"); // read the length of the compress recipe from the PLC using a server called "CompressedRecipeLength" CompressedRecipeLength->triggerRead(); // force an update compressedDataLength = CompressedRecipeLength->getIntValue(); // and read the latest value. java.lang.System.out.println (" Compressed DataLength " + compressedDataLength); // Compress the bytes output = new arrayOfBytes(2000); inflater = new Inflater(); buf = incomingValue.getBinaryValue(); inflater.setInput(buf,0,compressedDataLength); // assumes that the compressed data length is always less than the length read from the server (setLength)) resultLength = inflater.inflate(output); inflater.end(); // trim the array to the size of of the output outArray = new arrayOfBytes(resultLength); java.lang.System.arraycopy(output, 0, outArray, 0, resultLength); Files.write(java.nio.file.Paths.get("DemoPPBody.rcp.out"), outArray);