st-ethernet-ip
Version:
A simple node interface for Ethernet/IP.
193 lines (192 loc) • 6.54 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tag = exports.extController = void 0;
const net_1 = __importDefault(require("net"));
const controller_1 = __importDefault(require("../controller"));
const events_1 = require("events");
const tag_1 = __importDefault(require("../tag"));
exports.Tag = tag_1.default;
class ControllerManager {
/**
* Controller Manager manages PLC connections and tags. Automatically scans and writes tags that have values changed. Reconnects automatically.
*/
constructor() {
this.controllers = [];
}
/**
* Adds controller to be managed by controller manager
*
* @param ipAddress - controller IP address
* @param slot - Slot number or custom path
* @param rpi - How often to scan tag value in ms
* @param connected - Use connected messaging
* @param retrySP - How long to wait to retry broken connection in ms
* @param opts - custom options for future use
* @returns Extended Controller object
*/
addController(ipAddress, slot = 0, rpi = 100, connected = true, retrySP = 3000, opts = {}) {
const contLength = this.controllers.push(new extController(ipAddress, slot, rpi, connected, retrySP, opts));
return this.controllers[contLength - 1];
}
/**
* Returns all current controller tags
*
* @returns tag values indexed by controller ip address and tag name
*/
getAllValues() {
let allTags = {};
this.controllers.forEach(controller => {
let tags = {};
controller.tags.forEach(tag => {
tags[tag.tag.name] = tag.tag.value;
});
allTags[controller.ipAddress] = tags;
});
return allTags;
}
}
class extController extends events_1.EventEmitter {
/**
* Extended Controller Class To Manage Rebuilding Tags after as disconnect / reconnect event
*
* @param ipAddress - controller IP address
* @param slot - Slot number or custom path
* @param rpi - How often to scan tag value in ms
* @param connected - Use connected messaging
* @param retrySP - How long to wait to retry broken connection in ms
* @param opts - custom options for future use
*/
constructor(ipAddress, slot = 0, rpi = 100, connCom = true, retrySP = 3000, opts = {}) {
super();
this.reconnect = true;
this.ipAddress = ipAddress;
this.slot = slot;
this.opts = opts;
this.rpi = rpi;
this.PLC = null;
this.tags = [];
this.connected = false;
this.conncom = connCom;
this.retryTimeSP = retrySP;
}
/**
* Connect To Controller
*/
connect(reconnect = true) {
this.reconnect = reconnect;
this.PLC = new controller_1.default(this.conncom);
this.PLC.connect(this.ipAddress, this.slot).then(async () => {
this.connected = true;
this.PLC.scan_rate = this.rpi;
this.tags.forEach(tag => {
tag.tag = this.PLC.newTag(tag.tagname, tag.program, true, tag.arrayDims, tag.arraySize);
this.addTagEvents(tag.tag);
});
this.PLC.scan().catch(e => { this.errorHandle(e); });
this.emit("Connected", this);
}).catch((e) => { this.errorHandle(e); });
}
/**
* Add Tag Events to emit from controller
*
* @param tag
*/
addTagEvents(tag) {
tag.on("Changed", (chTag, prevValue) => {
this.emit("TagChanged", tag, prevValue);
});
tag.on("Initialized", () => {
this.emit("TagInit", tag);
});
tag.on("Unknown", () => {
this.emit("TagUnknown", tag);
});
}
/**
* Handle Controller Error during connect or while scanning
*
* @param e - Error emitted
*/
errorHandle(e) {
this.emit("Error", e);
this.connected = false;
this.PLC.destroy();
this.PLC._removeControllerEventHandlers();
this.emit("Disconnected");
if (this.reconnect) {
setTimeout(() => { this.connect(); }, this.retryTimeSP);
}
}
/**
* Add tag to controller scan list.
*
* @param tagname - Tag Name
* @param program - Program Name
* @param arrayDims - Array Dimensions
* @param arraySize - Array Size
* @returns Tag object
*/
addTag(tagname, program = null, arrayDims = 0, arraySize = 0x01) {
let tagItem = this.tags.find(tag => {
return tag.tagname === tagname && tag.program === program;
});
if (tagItem) {
return tagItem.tag;
}
else {
let tag;
if (this.connected) {
tag = this.PLC.newTag(tagname, program, true, arrayDims, arraySize);
this.addTagEvents(tag);
}
this.tags.push({
tagname: tagname,
program: program,
arrayDims: arrayDims,
arraySize: arraySize,
tag: tag
});
return tag;
}
}
/**
* Remove tag from controller scan list.
*
* @param tagname - Tag Name
* @param program - Program Name
*/
removeTag(tagname, program = null) {
tagname = (program) ? tagname.slice(tagname.indexOf(".") + 1) : tagname;
let tagIndex = this.tags.findIndex(tag => {
return tag.tagname === tagname && tag.program === program;
});
if (tagIndex > -1) {
this.PLC.state.subs.remove(this.tags[tagIndex].tag);
this.tags.splice(tagIndex, 1);
}
}
/**
* Disconnect Controller Completely
*
* @returns Promise resolved after disconnect of controller
*/
disconnect() {
return new Promise((resolve) => {
this.connected = false;
this.reconnect = false;
this.PLC.disconnect().then(() => {
this.emit("Disconnected");
resolve();
}).catch(() => {
net_1.default.Socket.prototype.destroy.call(this.PLC);
this.emit("Disconnected");
resolve();
});
});
}
}
exports.extController = extController;
exports.default = ControllerManager;