UNPKG

st-ethernet-ip

Version:
213 lines (211 loc) 7.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const enip_1 = require("../enip"); const { LOGICAL } = enip_1.CIP.EPATH.segments; const { MessageRouter } = enip_1.CIP; const { MULTIPLE_SERVICE_PACKET } = MessageRouter.services; const deep_equal_1 = __importDefault(require("deep-equal")); class TagGroup { /** * Tag Group Class used for reading and writing multiple tags at once */ constructor() { const pathBuf = Buffer.concat([ LOGICAL.build(LOGICAL.types.ClassID, 0x02), LOGICAL.build(LOGICAL.types.InstanceID, 0x01) // Instance ID (0x01) ]); this.state = { tags: {}, path: pathBuf, timestamp: new Date() }; } /** * Fetches the Number of Tags * * @readonly * @returns Number of tags */ get length() { return Object.keys(this.state.tags).length; } // endregion /** * Adds Tag to Group * * @param tag - Tag to Add to Group */ add(tag) { if (!this.state.tags[tag.instance_id]) this.state.tags[tag.instance_id] = tag; } /** * Removes Tag from Group * * @param tag - Tag to be Removed from Group */ remove(tag) { if (this.state.tags[tag.instance_id]) delete this.state.tags[tag.instance_id]; } /** * Iterable, Allows user to Iterate of each Tag in Group * * @param callback - Accepts Tag Class */ forEach(callback) { for (let key of Object.keys(this.state.tags)) callback(this.state.tags[key]); } /** * Generates Array of Messages to Compile into a Multiple * Service Request * * @returns Array of Read Tag Message Services */ generateReadMessageRequests() { const { tags } = this.state; // Initialize Variables let messages = []; let msgArr = []; let tagIds = []; let messageLength = 0; // Loop Over Tags in List for (let key of Object.keys(tags)) { const tag = tags[key]; // Build Current Message let msg = tag.generateReadMessageRequest(); messageLength += msg.length + 2; tagIds.push(tag.instance_id); msgArr.push(msg); // If Current Message Length is > 350 Bytes then Assemble Message and Move to Next Message if (messageLength >= 300) { let buf = Buffer.alloc(2 + 2 * msgArr.length); buf.writeUInt16LE(msgArr.length, 0); let ptr = 2; let offset = buf.length; for (let i = 0; i < msgArr.length; i++) { buf.writeUInt16LE(offset, ptr); ptr += 2; offset += msgArr[i].length; } buf = Buffer.concat([buf, ...msgArr]); buf = MessageRouter.build(MULTIPLE_SERVICE_PACKET, this.state.path, buf); messages.push({ data: buf, tag_ids: tagIds }); messageLength = 0; msgArr = []; tagIds = []; } } // Assemble and Push Last Message if (msgArr.length > 0) { let buf = Buffer.alloc(2 + 2 * msgArr.length); buf.writeUInt16LE(msgArr.length, 0); let ptr = 2; let offset = buf.length; for (let i = 0; i < msgArr.length; i++) { buf.writeUInt16LE(offset, ptr); ptr += 2; offset += msgArr[i].length; } buf = Buffer.concat([buf, ...msgArr]); buf = MessageRouter.build(MULTIPLE_SERVICE_PACKET, this.state.path, buf); messages.push({ data: buf, tag_ids: tagIds }); } return messages; } /** * Parse Incoming Multi Service Request Messages * * @param responses - response from controller * @param ids - Tag ids */ parseReadMessageResponses(responses, ids) { for (let i = 0; i < ids.length; i++) { if (responses[i].generalStatusCode === 0) this.state.tags[ids[i]].parseReadMessageResponse(responses[i].data); if (responses[i].generalStatusCode === 4) this.state.tags[ids[i]].unknownTag(); } } /** * Generates Array of Messages to Compile into a Multiple * Service Request * * @returns Array of Write Tag Message Services */ generateWriteMessageRequests() { const { tags } = this.state; // Initialize Variables let messages = []; let msgArr = []; let tagIds = []; let messageLength = 0; // Loop Over Tags in List for (let key of Object.keys(tags)) { const tag = tags[key]; if (tag.value !== null && tag.type === "STRUCT") tag.writeObjToValue(); if (tag.value !== null && !(0, deep_equal_1.default)(tag.state.tag.value, tag.controller_value)) { // Build Current Message let msg = tag.generateWriteMessageRequest(); if (tag.type !== "STRUCT") { messageLength += msg.length + 2; tagIds.push(tag.instance_id); msgArr.push(msg); // If Current Message Length is > 350 Bytes then Assemble Message and Move to Next Message if (messageLength >= 350) { let buf = Buffer.alloc(2 + 2 * msgArr.length); buf.writeUInt16LE(msgArr.length, 0); let ptr = 2; let offset = buf.length; for (let i = 0; i < msgArr.length; i++) { buf.writeUInt16LE(offset, ptr); ptr += 2; offset += msgArr[i].length; } buf = Buffer.concat([buf, ...msgArr]); buf = MessageRouter.build(MULTIPLE_SERVICE_PACKET, this.state.path, buf); messages.push({ data: buf, tag: null, tag_ids: tagIds }); messageLength = 0; msgArr = []; tagIds = []; } } else { messages.push({ data: null, tag: tag, tagIds: null }); // Single tag pushed to indicate need to write single STRUCT tag } } } // Assemble and Push Last Message if (msgArr.length > 0) { let buf = Buffer.alloc(2 + 2 * msgArr.length); buf.writeUInt16LE(msgArr.length, 0); let ptr = 2; let offset = buf.length; for (let i = 0; i < msgArr.length; i++) { buf.writeUInt16LE(offset, ptr); ptr += 2; offset += msgArr[i].length; } buf = Buffer.concat([buf, ...msgArr]); buf = MessageRouter.build(MULTIPLE_SERVICE_PACKET, this.state.path, buf); messages.push({ data: buf, tag: null, tag_ids: tagIds }); } return messages; } /** * Parse Incoming Multi Service Request Messages * * @param ids */ parseWriteMessageRequests(ids) { for (let id of ids) { this.state.tags[id].unstageWriteRequest(); } } } exports.default = TagGroup;