Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

nodepccc

plcpeople1.1kMIT0.1.17

Routine to communicate with Allen-Bradley PLCs

Allen, Bradley, PLC, Micrologix, SLC, CompactLogix, ControlLogix, PCCC, Ethernet/IP

readme

nodePCCC

nodePCCC is a library that allows communication to certain Allen-Bradley PLCs - The SLC 500 series, Micrologix and ControlLogix/CompactLogix PLCs using PCCC embedded in Ethernet/IP. This is not an official implementation of Ethernet/IP. This is not affiliated with or supported by Allen-Bradley in any way. CompactLogix, ControlLogix, SLC 500, 1761-NET-ENI are trademarks of Allen-Bradley.

WARNING - This is BETA CODE and you need to be aware that WRONG VALUES could be written to WRONG LOCATIONS. Fully test everything you do. In situations where writing to a random area of memory within the PLC could cost you money, back up your data and test this really well. If this could injure someone or worse, consider other software.

To use this on a SLC, you must have a SLC 5/05 with Ser A FRN 5 or later, or Ser C FRN 3 or later. Earlier versions will not work. You may also use a 1761-NET-ENI module with a 5/03 and other processors supported by the ENI module. This may work using an ENI module connected to a CompactLogix, although this combination hasn't been tested either. It has been tested only on direct connection to newer SLC 5/05 CPUs, a 5/03 CPU with a 1761-NET-ENI module, a couple of CompactLogix and a much earlier version was tested on a ControlLogix. Try at your own risk with other combinations. Using this with a PLC5 will likely require a different DF1 command with different formatting. Consult the DF1 manual (google it) for more details. (The manual suggests reading is more likely to work than writing but we don't have access to a PLC-5 and can't test it.) In any case, PLC-5-specific commands could certainly be added.

If you are using with a ControlLogix and possibly some CompactLogix, you likely need to specify the {routing: [0x01,0x00,0x01,0x00]} option (meaning path length 1 (2 bytes), backplane port 1 of the ENBT module, slot 0 of the 1756 backplane). You can experiment with other paths to make requests from a SLC 5/04 over DH+, for example, but it may not work - we have not tried this.

On a CompactLogix or ControlLogix, you must go to the "Tools" menu in Logix 5000 and "Map PLC5/SLC messages" to map an array of values (only Float/Int/DINT have been tested) to a "file number" and then request that file number preferably with the corresponding type. So if you create a variable called THEINTEGER with type INT[10] then map it to file 7 and download, you can request N7:0 to get the first element, and so on.

Note that it is currently not possible to write to bits above 15 (most-significant word) in a long integer as the PCCC read-modify-write command appears to not support this. You must write the entire DINT or use bits within an INT.

It is optimized in two ways - it sorts a large number of items being requested from the PLC and decides what overall data areas to request. It does not yet group multiple small requests together in a single packet, which is apparently possible. It does, however, send 2 packets at once, for speed, and this number could potentially be increased. So a request for 100 different bits, all close (but not necessarily completely contiguous) will be grouped in one single request to the PLC, with no additional direction from the user. Its optimizations are not likely tuned as well as some commercial OPC servers, however.

nodePCCC manages reconnects for you. So if the connection is lost because the PLC is powered down or disconnected, you can continue to request data with no other action necessary. "Bad" values are returned, and eventually the connection will be automatically restored.

nodePCCC is written entirely in Javascript, so no compiler installation is necessary on Windows, and deployment on other platforms (ARM, etc) should be no problem.

This was developed using Wireshark to help with packet format. Allen Bradley's own documentation was helpful as well, such as the "DF1 manual".

To get started:

npm install nodepccc

Example usage:

var nodepccc = require('nodepccc');
var conn = new nodepccc;
var doneReading = false;
var doneWriting = false;

conn.initiateConnection({port: 44818, host: '192.168.8.106' /* , routing: [0x01,0x00,0x01,0x00] */}, connected);
// Either uncomment the routing or uncomment this next line for ControlLogix/CompactLogix or if otherwise using routing    
// First 0x01, 0x00 = 1 word in the path, second 0x01, 0x00 = Port 0x01 (backplane port of Ethernet module), 0x00 = PLC is in slot 0 in chassis.   

function connected(err) {
    if (typeof(err) !== "undefined") {
        // We have an error.  Maybe the PLC is not reachable.  
        console.log(err);
        process.exit();
    }
    conn.setTranslationCB(tagLookup);
    conn.addItems(['TEST1', 'TEST4']);
    conn.addItems('TEST1');
//    conn.removeItems(['TEST2', 'TEST3']);  // Demo of "removeItems".  
//    conn.writeItems(['TEST5', 'TEST6'], [ 867.5309, 9 ], valuesWritten);  // You can write an array of items like this if you want.  
    conn.writeItems('TEST7', [ 666, 777 ], valuesWritten);  // You can write a single array item too.  
    conn.readAllItems(valuesReady);    
}

function valuesReady(anythingBad, values) {
    if (anythingBad) { console.log("SOMETHING WENT WRONG READING VALUES!!!!"); }
    console.log(values);
// alternative syntax        console.log("Value is " + conn.findItem('TEST1').value + " quality is " + conn.findItem('TEST1').quality);
    doneReading = true;
    if (doneWriting) { process.exit(); }
}

function valuesWritten(anythingBad) {
    if (anythingBad) { console.log("SOMETHING WENT WRONG WRITING VALUES!!!!"); }
    console.log("Done writing.");
    doneWriting = true;
    if (doneReading) { process.exit(); }
}

// This is a very simple "tag lookup" callback function that would eventually be replaced with either a database findOne(), or a large array in memory.  
// Note that the return value is a controller absolute address and datatype specifier.  
// If you want to use absolute addresses only, you can do that too.  
function tagLookup(tag) {
    switch (tag) {
    case 'TEST1':
        return 'N7:0';                // Integer
    case 'TEST2':
        return 'B3:0/0';            // Bit
    case 'TEST3':
        return 'B3/17';                // Same as B3:1/1
    case 'TEST4':
        return 'F8:0,20';              // Yes this is an array...  20 real numbers.  
    case 'TEST5':
        return 'F8:1';                // Single real.  
    case 'TEST6':
        return 'F8:2';                // Another single real.  
    case 'TEST7':
        return 'N7:1,2';            // A couple of integers in an array      
    case 'TEST8':
        return 'O:5/1';                // Direct output      
    case 'TEST9':
        return 'ST18:0';
    default:
        return undefined;
    }
}

This returns some diagnostic output as well as the following:

{ TEST1: 30724,
  TEST4: 
   [ 867530.875,
     1,
     97.0999984741211,
     2.9000000953674316,
     97,
     0,
     0,
     19,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0 ] }

API

nodepccc.initiateConnection(params, callback)

Connects to a PLC.

params should be an object with the following keys:

  • port (normally specify 44818)
  • host (address)
  • routing (array of characters specifying path length, path, etc. Most common is [0x01, 0x00, 0x01, 0x00] for ControlLogix.)

callback(err) will be executed on success or failure. err is either an error object, or undefined on successful connection.

nodepccc.dropConnection(callback)

Disconnects from a PLC.

This simply terminates the TCP connection. It does NOT do an Ethernet/IP disconnect at this time. The callback is called upon completion of the TCP close.

nodepccc.setTranslationCB(translator)

Sets a callback for name - address translation.

This is optional - you can choose to use "addItem" etc with absolute addresses.

If you use it, translator should be a function that takes a string as an argument, and returns a string in the following format: <type specifier><file number - I assumed 1, O assumed 0, S assumed 2>:<element>[</bit> or </DN, /EN, /TT> or <.ACC, .PRE>],array length

Examples:

  • F8:30
  • F8:0,10 - array of 10 floating point numbers
  • N7:12
  • L9:1 - long integer is MicroLogix/ControlLogix/CompactLogix only
  • N7:12/1 - second bit in the word
  • B3:6/6
  • T4:6.ACC - timer accumulator - read/write
  • C5:1.PRE - counter preset - read/write
  • T4:0,20 - array of timers - will return an array of objects representing 20 timers - READ ONLY
  • R6:0.LEN - control length - read/write
  • R6:0 - control structure - will return a JS object - READ ONLY
  • ST18:0,2 - Array of strings
  • NST34:0 - String that has been copied with the COP instruction to an integer data type in a Control/CompactLogix PLC

Note that some values are not supported in an array - timer presets and accumulators are an example, but entire timers are fine for READ ONLY.

Note that strings are supported, but in Control/CompactLogix you must first COP the string to an integer that is mapped to a PLC5/SLC file. You can read and write from this and COP it back to a string as needed.

In the example above, an object is declared and the translator references that object. It could just as reference a file or database. In any case, it allows cleaner Javascript code to be written that refers to a name instead of an absolute address.

nodepccc.addItems(items)

Adds items to the internal read polling list.

items can be a string or an array of strings.

nodepccc.removeItems(items)

Removes items to the internal read polling list.

items can be a string or an array of strings.

nodepccc.writeItems(items, values)

Writes items to the PLC using the corresponding values.

items can be a string or an array of strings. If items is a single string, values should then be a single item (or an array if items is an array item). If items is an array of strings, values must be an array.

You should monitor the return value - if it is non-zero, the write will not be processed as there is already one it progress, and the callback will not be called.

nodepccc.readAllItems(callback)

Reads the internal polling list and calls callback when done.

callback(err, values) is called with two arguments - a boolean indicating if ANY of the items have "bad quality", and values, an object containing the values being read as keys and their value (from the PLC) as the value.

nodepccc.findItem(item)

Returns the item object being searched for (by iterating through the array of items), or undefined if it isn't found in the item list. This allows accessing item.value and item.quality.

changelog

Change Log

All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.

[0.1.17] - 2019-11-09

Changed

  • Zero the parallel job counter when connection established

[0.1.16] - 2019-09-14

Changed

  • More changes to reset for Node Red compatibility

[0.1.15] - 2019-09-08

Changed

  • Modified read/write timeout again for improved reliability on reconnect

[0.1.14] - 2019-08-21

Changed

  • Modified read/write timeout to do a reset

[0.1.13] - 2019-02-11

Fixed

  • Bug in prepareReadPacket related to packet length fixed (Thanks to Lucasrsv1)
  • Bad quality details added to writeDoneCallback (Thanks to Lucasrsv1)

[0.1.12] - 2018-12-17

Fixed

  • Ghost write issue (Thanks to Lucasrsv1)

[0.1.11] - 2018-11-20

Fixed

  • Compile issue.

[0.1.10] - 2018-11-20

Fixed

  • 26: Fixed issue with not indicating bad quality on write

  • Fixed issue writing any values with an offset within the file > 254.
  • Fixed issues reconnecting when writing
  • Better indication when a write is requested and it will be lost
  • Better support of NSTRING arrays
  • Better support of long arrays of all types

[0.1.9] - 2018-10-24

Fixed

  • 25: Fixed issue reading any values with an offset within the file > 254.

[0.1.8] - 2018-05-31

Fixed

  • 24: Fixed node throwing error on failed write due to loss of communication with PLC.

[0.1.7] - 2017-10-05

Fixed

  • 15: Fixed node throwing with ECONNRESET because socket was not properly cleaned up

Added

  • NodePCCC accepts an object with debug and silent options for controlling verbosity
  • Removed dependency to underscore

[0.1.6] - 2017-02-27

Fixed

  • Fixed incorrect information in README section that talks about path/routing
  • Fixed the possibility of not returning from readAllItems if PLC initiates a disconnect

[0.1.5] - 2016-11-13

Fixed

  • Bug fix for reading/writing bit arrays (thanks to Jotan)
  • Bug fix for arrays of bits in long integers
  • Improvements and documentation for NSTRING data type to read strings from Control/CompactLogix
  • DropConnection improvements thanks to dom-white
  • Fixed a bug writing odd length strings

Added

  • Experimental NString datatype support to allow reading strings from Control/CompactLogix that does not support ST files

[0.1.4] - 2016-01-13

Fixed

  • Odd-length string bug fix (thanks to Julien Ledun).
  • MIT license mentioned in package.json

Added

  • Experimental NString datatype support to allow reading strings from Control/CompactLogix that does not support ST files

[0.1.3] - 2015-07-12

Fixed

  • Log error writing array of TIMER/COUNTER/CONTROL that has never been supported.
  • Return a JS object with TIMER/COUNTER/CONTROL data (.PRE, .ACC etc) when an entire timer or counter or control is requested without a subelement or when an array is requested.
  • Fix for timer/counter .PRE not working at all when requested individually

Added

  • String datatype support
  • Control (R) structure datatype support