UNPKG

ethernet-ip

Version:

A feature-complete EtherNet/IP client for Rockwell ControlLogix/CompactLogix PLCs

139 lines 5.45 kB
"use strict"; /** * Struct Codec — decode/encode struct buffers using Template definitions. * * Handles: * - Atomic members at their byte offsets * - Arrays (info field = element count) * - BOOLs mapped to hidden SINT hosts (ZZZZZZZZZZ prefix) * - Nested structs (recursive decode via templateLookup) * - Padding bytes between members (implicit from offsets) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.decodeStruct = decodeStruct; exports.encodeStruct = encodeStruct; const data_types_1 = require("../cip/data-types"); const template_1 = require("../cip/template"); /** * Decode a struct buffer into a JS object using its template. * * @param template - The struct's template definition * @param data - Raw struct bytes (structureSize bytes) * @param templateLookup - Resolver for nested struct templates */ function decodeStruct(template, data, templateLookup) { const result = {}; for (const member of template.members) { // Skip hidden BOOL host members — BOOLs are decoded from their host if ((0, template_1.isBoolHost)(member)) continue; const { name, type, offset, info } = member; if (type.code === data_types_1.CIPDataType.BOOL) { // BOOL: read the host SINT at this offset, extract bit from info result[name] = (data.readUInt8(offset) & (1 << info)) !== 0; } else if (type.isStruct) { result[name] = decodeStructMember(member, data, templateLookup); } else if (info > 0) { // Array: info = element count result[name] = decodeArray(type.code, data, offset, info); } else if ((0, data_types_1.isValidType)(type.code)) { const codec = (0, data_types_1.getCodec)(type.code); result[name] = codec.decode(data, offset); } } return result; } /** * Encode a JS object into a struct buffer using its template. * * @param template - The struct's template definition * @param values - JS object with member values * @param templateLookup - Resolver for nested struct templates */ function encodeStruct(template, values, templateLookup) { const buf = Buffer.alloc(template.attributes.structureSize); for (const member of template.members) { if ((0, template_1.isBoolHost)(member)) continue; const { name, type, offset, info } = member; const val = values[name]; if (val === undefined) continue; if (type.code === data_types_1.CIPDataType.BOOL) { // Set/clear bit in the host SINT const current = buf.readUInt8(offset); buf.writeUInt8(val ? current | (1 << info) : current & ~(1 << info), offset); } else if (type.isStruct) { encodeStructMember(member, val, buf, templateLookup); } else if (info > 0) { encodeArray(type.code, val, buf, offset); } else if ((0, data_types_1.isValidType)(type.code)) { const codec = (0, data_types_1.getCodec)(type.code); codec.encode(val).copy(buf, offset); } } return buf; } function decodeStructMember(member, data, templateLookup) { // STRING special case if (member.type.code === data_types_1.STRING_STRUCT_HANDLE) { const codec = (0, data_types_1.getCodec)(data_types_1.CIPDataType.STRING); return codec.decode(data, member.offset); } const nested = templateLookup(member.type.code); if (!nested) return Buffer.from(data.subarray(member.offset)); if (member.info > 0) { // Array of structs const arr = []; const elemSize = nested.attributes.structureSize; for (let i = 0; i < member.info; i++) { arr.push(decodeStruct(nested, data.subarray(member.offset + i * elemSize), templateLookup)); } return arr; } return decodeStruct(nested, data.subarray(member.offset), templateLookup); } function encodeStructMember(member, val, buf, templateLookup) { if (member.type.code === data_types_1.STRING_STRUCT_HANDLE) { const codec = (0, data_types_1.getCodec)(data_types_1.CIPDataType.STRING); codec.encode(val).copy(buf, member.offset); return; } const nested = templateLookup(member.type.code); if (!nested) return; if (member.info > 0 && Array.isArray(val)) { const elemSize = nested.attributes.structureSize; for (let i = 0; i < val.length; i++) { encodeStruct(nested, val[i], templateLookup).copy(buf, member.offset + i * elemSize); } return; } encodeStruct(nested, val, templateLookup).copy(buf, member.offset); } function decodeArray(typeCode, data, offset, count) { if (!(0, data_types_1.isValidType)(typeCode)) return []; const codec = (0, data_types_1.getCodec)(typeCode); const result = []; for (let i = 0; i < count; i++) { result.push(codec.decode(data, offset + i * codec.size)); } return result; } function encodeArray(typeCode, values, buf, offset) { if (!(0, data_types_1.isValidType)(typeCode)) return; const codec = (0, data_types_1.getCodec)(typeCode); for (let i = 0; i < values.length; i++) { codec.encode(values[i]).copy(buf, offset + i * codec.size); } } //# sourceMappingURL=struct-codec.js.map