@hive-command/ethernet-ip v2.5.6-alpha.37
Node Ethernet/IP
A simple and lightweight node based API for interfacing with Rockwell Control/CompactLogix PLCs and Ethernet/IP I/O.
Prerequisites
latest version of NodeJS
Getting Started
Install with npm
npm install st-ethernet-ip --save
The API
How the heck does this thing work anyway? Great question!
The Basics - New And Improved (less mess)
Using Comnnection Manager
//Connection Manager automagically reconnects to controller that have lost connection. Scanner auto initiated.
const {ControllerManager} = require('st-ethernet-ip')
const cm = new ControllerManager();
//addController(ipAddress, slot = 0, rpi = 100, connected = true, retrySP = 3000, opts = {})
const cont = cm.addController('192.168.86.200');
cont.connect();
//addTag(tagname, program = null, arrayDims = 0, arraySize = 0x01)
cont.addTag('TheInteger')
cont.on('TagChanged', (tag, prevValue) => {
console.log(tag.name, ' changed from ', prevValue, ' => ', tag.value)
})
cont.on('error', (e) => {
console.log(e)
})
Arrays / UDTs
const {Controller} = require('st-ethernet-ip')
const PLC = new Controller();
//A taglist and structure templates are automatically retrieved after connected
PLC.connect("192.168.86.200", 0).then(async () => {
// String UDT
const tag2 = PLC.newTag('testString')
// An array of strings with 1 dimension (only 1 currently supported)
const tag3 = PLC.newTag('stringArray', null, true, 1)
// A custom UDT tag
const tag4 = PLC.newTag('bigUDT')
// Automatically determine the size of the array and store in tag
await PLC.getTagArraySize(tag3)
// Read tags
await PLC.readTag(tag2)
await PLC.readTag(tag3)
await PLC.readTag(tag4)
// Display tag as a javascript object
console.log(tag2.value)
console.log(tag3.value)
console.log(tag4.value)
//modify string value
tag2.value = 'Hello World!'
//modify an array of string values
tag3.value[19] = 'The End'
//set value of member of a UDT called 'Struct1' which is an array of booleans
tag4.value.Struct1[3] = true;
//set value of member of a UDT called 'VAR2_STRING' which is an array of another UDT member called 'Group1'
tag4.value.Group1[2].VAR2_STRING = 'What Do You Mean?'
//Write the tag values
await PLC.writeTag(tag2)
await PLC.writeTag(tag3)
await PLC.writeTag(tag4)
}).catch(async e => {
console.log(e)
});
The Basics - Old School
Getting Connected
const { Controller } = require("ethernet-ip");
const PLC = new Controller();
// Controller.connect(IP_ADDR[, SLOT])
// NOTE: SLOT = 0 (default) - 0 if CompactLogix
PLC.connect("192.168.1.1", 0).then(() => {
console.log(PLC.properties);
});
Controller.properties Object
{
name: String, // eg "1756-L83E/B"
serial_number: Number,
slot: Number,
time: Date, // last read controller WallClock datetime
path: Buffer,
version: String, // eg "30.11"
status: Number,
faulted: Boolean, // will be true if any of the below are true
minorRecoverableFault: Boolean,
minorUnrecoverableFault: Boolean,
majorRecoverableFault: Boolean,
majorUnrecoverableFault: Boolean,
io_faulted: Boolean
}
Set the Clock of the Controller
NOTE Controller.prototype.readWallClock
and Controller.prototype.writeWallClock
are experimental features and may not be available on all controllers. 1756-L8 ControlLogix Controllers are currently the only PLCs supporting these features.
Sync Controller WallClock to PC Datetime
const { Controller } = require("ethernet-ip");
const PLC = new Controller();
PLC.connect("192.168.1.1", 0).then(async () => {
// Accepts a JS Date Type
// Controller.writeWallClock([Date])
await PLC.writeWallClock(); // Defaults to 'new Date()'
});
Set Controller WallClock to a Specific Date
const { Controller } = require("ethernet-ip");
const PLC = new Controller();
PLC.connect("192.168.1.1", 0).then(async () => {
const partyLikeIts1999 = new Date('December 17, 1999 03:24:00');
await PLC.writeWallClock(partyLikeIts1999); // Pass a custom Datetime
});
Reading Tags
NOTE: Currently, the Tag
Class only supports Atomic datatypes (SINT, INT, DINT, REAL, BOOL). Not to worry, support for STRING, ARRAY, and UDTs are in the plans and coming soon! =]
Reading Tags Individually
...
const { Controller, Tag } = require("ethernet-ip");
const PLC = new Controller();
// Create Tag Instances
const fooTag = new Tag("contTag"); // Controller Scope Tag
const barTag = new Tag("progTag", "prog"); // Program Scope Tag in PLC Program "prog"
PLC.connect("192.168.1.1", 0).then(async () => {
await PLC.readTag(fooTag);
await PLC.readTag(barTag);
console.log(fooTag.value);
console.log(barTag.value);
});
Additional Tag Name Examples ...
const fooTag = new Tag("Program:prog.progTag"); // Alternative Syntax for Program Scope Tag in PLC Program "prog"
const barTag = new Tag("arrayTag[0]"); // Array Element
const bazTag = new Tag("arrayTag[0,1,2]"); // Multi Dim Array Element
const quxTag = new Tag("integerTag.0"); // SINT, INT, or DINT Bit
const quuxTag = new Tag("udtTag.Member1"); // UDT Tag Atomic Member
const quuzTag = new Tag("boolArray[0]", null, BIT_STRING); // bool array tag MUST have the data type "BIT_STRING" passed in
Reading Tags as a Group
...
const { Controller, Tag, TagGroup } = require("ethernet-ip");
const PLC = new Controller();
const group = new TagGroup();
// Add some tags to group
group.add(new Tag("contTag")); // Controller Scope Tag
group.add(new Tag("progTag", "prog")); // Program Scope Tag in PLC Program "prog"
PLC.connect("192.168.1.1", 0).then(async () => {
await PLC.readTagGroup(group);
// log the values to the console
group.forEach(tag => {
console.log(tag.value);
});
});
Writing Tags
NOTE: You MUST read the tags first or manually provide a valid CIP datatype. The following examples are taking the latter approach.
Writing Tags Individually
...
const { Controller, Tag, EthernetIP } = require("ethernet-ip");
const { DINT, BOOL } = EthernetIP.CIP.DataTypes.Types;
const PLC = new Controller();
// Create Tag Instances
const fooTag = new Tag("contTag", null, DINT); // Controller Scope Tag
const barTag = new Tag("progTag", "prog", BOOL); // Program Scope Tag in PLC Program "prog"
PLC.connect("192.168.1.1", 0).then(async () => {
// First way to write a new value
fooTag.value = 75;
await PLC.writeTag(fooTag);
// Second way to write a new value
await PLC.writeTag(barTag, true);
console.log(fooTag.value);
console.log(barTag.value);
});
Writing Tags as a Group
...
const { Controller, Tag, TagGroup, EthernetIP } = require("ethernet-ip");
const { DINT, BOOL } = EthernetIP.CIP.DataTypes.Types;
const PLC = new Controller();
const group = new TagGroup();
// Create Tag Instances
const fooTag = new Tag("contTag", null, DINT); // Controller Scope Tag
const barTag = new Tag("progTag", "prog", BOOL); // Program Scope Tag in PLC Program "prog"
group.add(fooTag); // Controller Scope Tag
group.add(barTag); // Program Scope Tag in PLC Program "prog"
PLC.connect("192.168.1.1", 0).then(async () => {
// Set new values
fooTag.value = 75;
barTag.value = true;
// Will only write tags whose Tag.controller_tag !== Tag.value
await PLC.writeTagGroup(group);
group.forEach(tag => {
console.log(tag.value);
});
});
Lets Get Fancy
Subscribing to Controller Tags
const { Controller, Tag } = require("ethernet-ip");
const PLC = new Controller();
// Add some tags to group
PLC.subscribe(new Tag("contTag")); // Controller Scope Tag
PLC.subscribe(new Tag("progTag", "prog")); // Program Scope Tag in PLC Program "prog"
PLC.connect("192.168.1.1", 0).then(() => {
// Set Scan Rate of Subscription Group to 50 ms (defaults to 200 ms)
PLC.scan_rate = 50;
// Begin Scanning
PLC.scan();
});
// Catch the Tag "Changed" and "Initialized" Events
PLC.forEach(tag => {
// Called on the First Successful Read from the Controller
tag.on("Initialized", tag => {
console.log("Initialized", tag.value);
});
// Called if Tag.controller_value changes
tag.on("Changed", (tag, oldValue) => {
console.log("Changed:", tag.value);
});
});
Newest Capabilities
New Integrated Tag/Structure into Controller
const {Controller} = require('st-ethernet-ip');
const PLC = new Controller()
PLC.connect('192.168.86.200', 0).then(async () => {
//Display controller tag list
console.log(PLC.tagList)
//Add controller scope atomic tag 'Integer3'
const tag = PLC.newTag('Integer3')
//Add program 'MainProgram2' scope structure tag 'UDT_INST1'
const tag2 = PLC.newTag('UDT_INST1', 'MainProgram2')
//Read each tag
await PLC.readTag(tag)
await PLC.readTag(tag2)
//Display values
console.log(tag.value)
console.log(tag2.value)
//Change structure member 'VAR2_STRING' value
tag2.value.VAR2_STRING = "Hello World!"
//Write new tag value to PLC
await PLC.writeTag(tag2)
//Scan for changes in values in PLC or modified local values and updates at a rate of every 50mS
PLC.scan_rate = 50;
PLC.scan();
PLC.forEach(tag => {
// Called if Tag.controller_value changes
tag.on("Changed", (tag, oldValue) => {
console.log("FROM:", oldValue)
console.log("TO:", tag.value);
});
});
}).catch(err => {
console.log(err)
})
Getting a List of Available Controller Tags and Structure Templates
const { Controller, TagList } = require("st-ethernet-ip");
const PLC = new Controller();
const tagList = new TagList();
PLC.connect("192.168.1.1", 0).then(async () => {
// Get all controller tags and program tags
await PLC.getControllerTagList(tagList)
// Displays all tags
console.log(tagList.tags)
// Displays all templates
console.log(tagList.templates)
// Displays program names
console.log(tagList.programs)
});
TagList.tags[] Object
{
id: Number, // Instance ID
program: String, // Name of program scope of tag. {Null} is controller scope
type {
typeCode: Number, // Data type code
typeName: String, // Data type name
structure: Boolean, // TRUE if is structure.
arrayDims: Number, // Number of dimmensions of array. If not array = 0
reserved: Boolean // TRUE if is reserved
}
}
Reading/Writing LINT - Node.js >= 12.0.0
const { Controller, Tag } = require("st-ethernet-ip");
const PLC = new Controller();
const tag = new Tag('BigInteger1');
PLC.connect("192.168.1.1", 0).then(async () => {
// Read LINT
await PLC.readTag(tag)
// Displays all tags
console.log(tag.value) // output: 123456789n
//Big Number Maths
tag.value = tag.value + 1n // add 1
console.log(tag.value) // output: 123456790n
//Write new Big Number to
await PLC.writeTag(tag)
});
Reading/Writing Strings
const { Controller, Tag, TagList, Structure } = require("st-ethernet-ip");
const PLC = new Controller();
PLC.connect("192.168.1.1", 0).then(async () => {
const tagList = new TagList();
await PLC.getControllerTagList(tagList);
const stringStructure = new Structure('String1', tagList, {Optional Program Name});
await PLC.readTag(stringStructure);
console.log(stringStructure.value);
stringStructure.value = "New String Value";
await PLC.writeTag(stringStructure)
});
Reading/Writing Structures
const { Controller, Tag, TagList, Structure } = require("st-ethernet-ip");
const PLC = new Controller();
PLC.connect("192.168.1.1", 0).then(async () => {
const tagList = new TagList();
await PLC.getControllerTagList(tagList);
//Setup 'Structure1' Tag
const structure1 = new Structure('Structure1', tagList, {Optional Program Name});
//Read value from PLC
await PLC.readTag(structure1);
//Display entire structure
console.log(structure1.value);
//Change value of structure member
structure1.value.Integer1 = 3;
//Write Changes to PLC
await PLC.writeTag(stringStructure)
});
Using unconnected messaging with custom timeout
const { Controller, Tag, TagList, Structure } = require("st-ethernet-ip");
const PLC = new Controller(false, { unconnectedSendTimeout: 5064 });
PLC.connect("192.168.1.1", 0).then(async () => {
const sampleTag = new Tag("sampleTag");
await PLC.readTag(sampleTag);
console.log(sampleTag.value);
});
New Device Browser
Find Devices On The Network
Uses the same method as RsLinx to detect if device is on the network
const { Browser } = require("st-ethernet-ip");
const browser = new Browser();
//When new device is detected
browser.on("New Device", device => {
//Display all device info
console.log(device);
//Display Device IP address
console.log(device.socketAddress.sin_addr);
//Display Device Description
console.log(device.productName)
});
//when device is not detected after x amount of scans
browser.on("Device Disconnected", device => {
// 'device' is the disconnected device
})
New I/O Scanner - ALPHA - NOT FOR PRODUCTION
Read And Write I/O On The Network
Turn your computer into a PLC controller
const { IO } = require("st-ethernet-ip")
const scanner = new IO.Scanner(); // Iinitalize new scanner on default port 2222
// device configuration from manufacturer.
const config = {
configInstance: {
assembly: 100,
size: 0
},
outputInstance: {
assembly: 102,
size: 6
},
inputInstance: {
assembly: 101,
size: 6
}
}
// Add a connection with (device config, rpi, ip_address)
const conn = scanner.addConnection(config, 8, '192.168.86.42')
// After first UDP packet is received
conn.on('connected', () => {
console.log('Connected')
console.log('input data => ', conn.inputData) // Display Input Data Buffer.
console.log('output data => ', conn.outputData) // Display Output Data Buffer.
// Create alias for bits and integers (can be named what ever you want)
conn.addOutputBit(4, 0, 'out0') // Using outputData. Skip 4 bytes, use bit 0 and call it 'out0'
conn.addOutputBit(4, 1, 'out1') // Skip 4 bytes, use bit 1 and call it 'out1'
conn.addOutputInt(2, 'outputValue0') // Skip 2 bytes then use 16bit integer and call it 'value0'
conn.addInputBit(4, 7, 'in7') // Skip 4 bytes then use bit 7 and call it 'in7'
conn.addInputInt(2, 'inputValue0')
// Set values to be written to devices
conn.setValue('out0', true)
conn.setValue('out1', false)
conn.setValue('value0', 1234)
// Read values from device connection
console.log(conn.getValue('in7'))
console.log(conn.getValue('inputValue0'))
})
// Called when UDP packets are not receiving. (Timeout is based on rpi setting)
conn.on('disconnected', () => {
console.log('Disconnected')
})
Demos
- Monitor Tags for Changes Demo
const { Controller, Tag } = require("ethernet-ip");
// Intantiate Controller
const PLC = new Controller();
// Subscribe to Tags
PLC.subscribe(new Tag("TEST_TAG"););
PLC.subscribe(new Tag("TEST", "Prog"););
PLC.subscribe(new Tag("TEST_REAL", "Prog"););
PLC.subscribe(new Tag("TEST_BOOL", "Prog"););
// Connect to PLC at IP, SLOT
PLC.connect("10.1.60.205", 5).then(() => {
const { name } = PLC.properties;
// Log Connected to Console
console.log(`\n\nConnected to PLC ${name}...\n`);
// Begin Scanning Subscription Group
PLC.scan();
});
// Initialize Event Handlers
PLC.forEach(tag => {
tag.on("Changed", (tag, lastValue) => {
console.log(`${tag.name} changed from ${lastValue} -> ${tag.value}`);
});
})
Built With
- NodeJS - The Engine
- javascript - ES2017 - The Language
Contributers
- Jason Serafin - Owner - GitHub Profile
- Canaan Seaton - Forked From Owner - GitHub Profile - Personal Website
- Patrick McDonagh - Collaborator - GitHub Profile
Jeremy Henson - Collaborator - Github Profile
Related Projects
Wanna become a contributor? Here's how!
License
This project is licensed under the MIT License - see the LICENCE file for details
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
11 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago