secs4js 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +20 -20
- package/README.md +526 -442
- package/lib/core/AbstractSecsMessage.js.map +1 -1
- package/lib/core/enums/HsmsSsControlType.js.map +1 -1
- package/lib/core/enums/RejectReason.js.map +1 -1
- package/lib/core/enums/SecsItemType.js.map +1 -1
- package/lib/core/enums/SelectStatus.js.map +1 -1
- package/lib/core/secs2item/AbstractSecs2Item.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemAscii.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemBinary.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemBoolean.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemFactory.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemList.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemNumeric.js.map +1 -1
- package/lib/core/secs2item/Secs2ItemParser.js.map +1 -1
- package/lib/gem/Clock.js.map +1 -1
- package/lib/gem/Gem.js.map +1 -1
- package/lib/helper/Secs2ItemHelper.js.map +1 -1
- package/lib/hsms/HsmsMessage.js.map +1 -1
- package/lib/hsms/HsmsPassiveCommunicator.js.map +1 -1
- package/lib/hsms/enums/HsmsControlType.js.map +1 -1
- package/lib/hsms/enums/RejectReason.js.map +1 -1
- package/lib/hsms/enums/SelectStatus.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/secs1/Secs1Message.js.map +1 -1
- package/lib/secs1/Secs1MessageBlock.js.map +1 -1
- package/lib/secs1/Secs1SerialCommunicator.js.map +1 -1
- package/lib/sml/SmlParser.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1Message.js","names":["blocks: Secs1MessageBlock[]","body: AbstractSecs2Item | null"],"sources":["../../src/secs1/Secs1Message.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\
|
|
1
|
+
{"version":3,"file":"Secs1Message.js","names":["blocks: Secs1MessageBlock[]","body: AbstractSecs2Item | null"],"sources":["../../src/secs1/Secs1Message.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\nimport { Secs2ItemParser } from \"../core/secs2item/Secs2ItemParser.js\";\nimport { Secs1MessageBlock } from \"./Secs1MessageBlock.js\";\n\nexport class Secs1Message extends SecsMessage {\n\tconstructor(\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\tbody: AbstractSecs2Item | null,\n\t\tsystemBytes: number,\n\t\tdeviceId: number,\n\t\tpublic readonly rBit = false,\n\t) {\n\t\tsuper(stream, func, wBit, body, systemBytes, deviceId);\n\t}\n\n\t/**\n\t * @description Splits the message into SECS-I blocks.\n\t */\n\ttoBlocks(): Secs1MessageBlock[] {\n\t\tconst bodyBuffer = this.body ? this.body.toBuffer() : Buffer.alloc(0);\n\t\tconst blocks: Secs1MessageBlock[] = [];\n\n\t\tlet pos = 0;\n\t\tlet blockNum = 1;\n\n\t\tif (bodyBuffer.length === 0) {\n\t\t\t// Header only block? Or empty body block?\n\t\t\t// SECS-I allows empty body.\n\t\t\tblocks.push(\n\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\tthis.deviceId,\n\t\t\t\t\tthis.stream,\n\t\t\t\t\tthis.func,\n\t\t\t\t\tthis.wBit,\n\t\t\t\t\ttrue, // E-Bit\n\t\t\t\t\tblockNum,\n\t\t\t\t\tthis.systemBytes,\n\t\t\t\t\tBuffer.alloc(0),\n\t\t\t\t\tthis.rBit,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn blocks;\n\t\t}\n\n\t\twhile (pos < bodyBuffer.length) {\n\t\t\tconst remaining = bodyBuffer.length - pos;\n\t\t\tconst chunkSize = remaining > 244 ? 244 : remaining;\n\t\t\tconst chunk = bodyBuffer.subarray(pos, pos + chunkSize);\n\t\t\tconst isLast = pos + chunkSize >= bodyBuffer.length;\n\n\t\t\tblocks.push(\n\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\tthis.deviceId,\n\t\t\t\t\tthis.stream,\n\t\t\t\t\tthis.func,\n\t\t\t\t\tthis.wBit,\n\t\t\t\t\tisLast, // E-Bit\n\t\t\t\t\tblockNum,\n\t\t\t\t\tthis.systemBytes,\n\t\t\t\t\tchunk,\n\t\t\t\t\tthis.rBit,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tpos += chunkSize;\n\t\t\tblockNum++;\n\t\t\tif (blockNum > 0x7fff) {\n\t\t\t\tthrow new Error(\"Block number overflow\");\n\t\t\t}\n\t\t}\n\n\t\treturn blocks;\n\t}\n\n\t/**\n\t * Reassembles a SECS-I message from blocks.\n\t */\n\tstatic fromBlocks(blocks: Secs1MessageBlock[]): Secs1Message {\n\t\tif (blocks.length === 0) {\n\t\t\tthrow new Error(\"No blocks to reassemble\");\n\t\t}\n\n\t\t// Sort by block number just in case (though usually they come in order)\n\t\tblocks.sort((a, b) => a.blockNumber - b.blockNumber);\n\n\t\t// Validate sequence\n\t\tfor (let i = 0; i < blocks.length; i++) {\n\t\t\tif (blocks[i].blockNumber !== i + 1) {\n\t\t\t\tthrow new Error(`Missing block ${i + 1}`);\n\t\t\t}\n\t\t\t// Check if all blocks belong to the same message (SystemBytes, Stream, Func, DeviceID)\n\t\t\tif (i > 0) {\n\t\t\t\tconst prev = blocks[i - 1];\n\t\t\t\tconst curr = blocks[i];\n\t\t\t\tif (\n\t\t\t\t\tprev.systemBytes !== curr.systemBytes ||\n\t\t\t\t\tprev.stream !== curr.stream ||\n\t\t\t\t\tprev.func !== curr.func ||\n\t\t\t\t\tprev.deviceId !== curr.deviceId\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Blocks do not belong to the same message\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!blocks[blocks.length - 1].eBit) {\n\t\t\tthrow new Error(\"Last block missing E-Bit\");\n\t\t}\n\n\t\tconst first = blocks[0];\n\t\tconst bodyBuffers = blocks.map((b) => b.body);\n\t\tconst fullBodyBuffer = Buffer.concat(bodyBuffers);\n\n\t\tlet body: AbstractSecs2Item | null = null;\n\t\tif (fullBodyBuffer.length > 0) {\n\t\t\tconst result = Secs2ItemParser.fromBuffer(fullBodyBuffer);\n\t\t\tbody = result.item;\n\t\t}\n\n\t\treturn new Secs1Message(\n\t\t\tfirst.stream,\n\t\t\tfirst.func,\n\t\t\tfirst.wBit,\n\t\t\tbody,\n\t\t\tfirst.systemBytes,\n\t\t\tfirst.deviceId,\n\t\t\tfirst.rBit,\n\t\t);\n\t}\n\n\t// Override toBuffer to return logical buffer (10-byte header + body)\n\ttoBuffer(): Buffer {\n\t\tconst bodyBuffer = this.body ? this.body.toBuffer() : Buffer.alloc(0);\n\t\tconst header = Buffer.alloc(10);\n\n\t\t// Byte 0: R-Bit + DeviceID MSB\n\t\tlet b0 = (this.deviceId >> 8) & 0x7f;\n\t\tif (this.rBit) b0 |= 0x80;\n\t\theader[0] = b0;\n\n\t\t// Byte 1: DeviceID LSB\n\t\theader[1] = this.deviceId & 0xff;\n\n\t\t// Byte 2: W-Bit + Stream\n\t\tlet b2 = this.stream & 0x7f;\n\t\tif (this.wBit) b2 |= 0x80;\n\t\theader[2] = b2;\n\n\t\t// Byte 3: Function\n\t\theader[3] = this.func & 0xff;\n\n\t\t// Byte 4: E-Bit + BlockNum (Logical 0 or 1? For logical message, maybe 0)\n\t\t// Usually block number is per block. For a \"Message Buffer\", we don't have block number.\n\t\t// We'll leave it 0 or standard.\n\t\theader[4] = 0;\n\t\theader[5] = 0;\n\n\t\t// Byte 6-9: System Bytes\n\t\theader.writeUInt32BE(this.systemBytes, 6);\n\n\t\treturn Buffer.concat([header, bodyBuffer]);\n\t}\n}\n"],"mappings":";;;;;AAKA,IAAa,eAAb,MAAa,qBAAqB,YAAY;CAC7C,YACC,QACA,MACA,MACA,MACA,aACA,UACA,AAAgB,OAAO,OACtB;AACD,QAAM,QAAQ,MAAM,MAAM,MAAM,aAAa,SAAS;EAFtC;;;;;CAQjB,WAAgC;EAC/B,MAAM,aAAa,KAAK,OAAO,KAAK,KAAK,UAAU,GAAG,OAAO,MAAM,EAAE;EACrE,MAAMA,SAA8B,EAAE;EAEtC,IAAI,MAAM;EACV,IAAI,WAAW;AAEf,MAAI,WAAW,WAAW,GAAG;AAG5B,UAAO,KACN,kBAAkB,UACjB,KAAK,UACL,KAAK,QACL,KAAK,MACL,KAAK,MACL,MACA,UACA,KAAK,aACL,OAAO,MAAM,EAAE,EACf,KAAK,KACL,CACD;AACD,UAAO;;AAGR,SAAO,MAAM,WAAW,QAAQ;GAC/B,MAAM,YAAY,WAAW,SAAS;GACtC,MAAM,YAAY,YAAY,MAAM,MAAM;GAC1C,MAAM,QAAQ,WAAW,SAAS,KAAK,MAAM,UAAU;GACvD,MAAM,SAAS,MAAM,aAAa,WAAW;AAE7C,UAAO,KACN,kBAAkB,UACjB,KAAK,UACL,KAAK,QACL,KAAK,MACL,KAAK,MACL,QACA,UACA,KAAK,aACL,OACA,KAAK,KACL,CACD;AAED,UAAO;AACP;AACA,OAAI,WAAW,MACd,OAAM,IAAI,MAAM,wBAAwB;;AAI1C,SAAO;;;;;CAMR,OAAO,WAAW,QAA2C;AAC5D,MAAI,OAAO,WAAW,EACrB,OAAM,IAAI,MAAM,0BAA0B;AAI3C,SAAO,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,YAAY;AAGpD,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACvC,OAAI,OAAO,GAAG,gBAAgB,IAAI,EACjC,OAAM,IAAI,MAAM,iBAAiB,IAAI,IAAI;AAG1C,OAAI,IAAI,GAAG;IACV,MAAM,OAAO,OAAO,IAAI;IACxB,MAAM,OAAO,OAAO;AACpB,QACC,KAAK,gBAAgB,KAAK,eAC1B,KAAK,WAAW,KAAK,UACrB,KAAK,SAAS,KAAK,QACnB,KAAK,aAAa,KAAK,SAEvB,OAAM,IAAI,MAAM,2CAA2C;;;AAK9D,MAAI,CAAC,OAAO,OAAO,SAAS,GAAG,KAC9B,OAAM,IAAI,MAAM,2BAA2B;EAG5C,MAAM,QAAQ,OAAO;EACrB,MAAM,cAAc,OAAO,KAAK,MAAM,EAAE,KAAK;EAC7C,MAAM,iBAAiB,OAAO,OAAO,YAAY;EAEjD,IAAIC,OAAiC;AACrC,MAAI,eAAe,SAAS,EAE3B,QADe,gBAAgB,WAAW,eAAe,CAC3C;AAGf,SAAO,IAAI,aACV,MAAM,QACN,MAAM,MACN,MAAM,MACN,MACA,MAAM,aACN,MAAM,UACN,MAAM,KACN;;CAIF,WAAmB;EAClB,MAAM,aAAa,KAAK,OAAO,KAAK,KAAK,UAAU,GAAG,OAAO,MAAM,EAAE;EACrE,MAAM,SAAS,OAAO,MAAM,GAAG;EAG/B,IAAI,KAAM,KAAK,YAAY,IAAK;AAChC,MAAI,KAAK,KAAM,OAAM;AACrB,SAAO,KAAK;AAGZ,SAAO,KAAK,KAAK,WAAW;EAG5B,IAAI,KAAK,KAAK,SAAS;AACvB,MAAI,KAAK,KAAM,OAAM;AACrB,SAAO,KAAK;AAGZ,SAAO,KAAK,KAAK,OAAO;AAKxB,SAAO,KAAK;AACZ,SAAO,KAAK;AAGZ,SAAO,cAAc,KAAK,aAAa,EAAE;AAEzC,SAAO,OAAO,OAAO,CAAC,QAAQ,WAAW,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1MessageBlock.js","names":["buffer: Buffer"],"sources":["../../src/secs1/Secs1MessageBlock.ts"],"sourcesContent":["export class Secs1MessageBlock {\
|
|
1
|
+
{"version":3,"file":"Secs1MessageBlock.js","names":["buffer: Buffer"],"sources":["../../src/secs1/Secs1MessageBlock.ts"],"sourcesContent":["export class Secs1MessageBlock {\n\t// The full block bytes: [Length(1)] + [Header(10)] + [Body(0-244)] + [Checksum(2)]\n\tconstructor(public readonly buffer: Buffer) {}\n\n\tget length(): number {\n\t\treturn this.buffer[0];\n\t}\n\n\tget header(): Buffer {\n\t\treturn this.buffer.subarray(1, 11);\n\t}\n\n\tget body(): Buffer {\n\t\treturn this.buffer.subarray(11, this.buffer.length - 2);\n\t}\n\n\tget checksum(): number {\n\t\treturn this.buffer.readUInt16BE(this.buffer.length - 2);\n\t}\n\n\t// Parsed fields\n\tget deviceId(): number {\n\t\tconst bs = this.buffer.subarray(1, 3);\n\t\treturn ((bs[0] << 8) & 0x7f00) | bs[1];\n\t}\n\n\tget rBit(): boolean {\n\t\treturn (this.buffer[1] & 0x80) === 0x80;\n\t}\n\n\tget stream(): number {\n\t\treturn this.buffer[3] & 0x7f;\n\t}\n\n\tget wBit(): boolean {\n\t\treturn (this.buffer[3] & 0x80) === 0x80;\n\t}\n\n\tget func(): number {\n\t\treturn this.buffer[4];\n\t}\n\n\tget blockNumber(): number {\n\t\tconst bs = this.buffer.subarray(5, 7);\n\t\treturn ((bs[0] << 8) & 0x7f00) | bs[1];\n\t}\n\n\tget eBit(): boolean {\n\t\treturn (this.buffer[5] & 0x80) === 0x80;\n\t}\n\n\tget systemBytes(): number {\n\t\treturn this.buffer.readUInt32BE(7);\n\t}\n\n\tisValid(): boolean {\n\t\tif (this.buffer.length < 13) return false; // Len(1) + Head(10) + Sum(2)\n\t\tif (this.buffer[0] !== this.buffer.length - 3) return false; // Length byte excludes itself and checksum?\n\t\t// Wait, SECS-I Length Byte is the number of bytes in the block EXCLUSIVE of the length byte itself and the checksum?\n\t\t// No, standard says: \"Length byte is the number of bytes in the block header plus the text.\" (Header + Body).\n\t\t// So Length = 10 + BodyLength.\n\t\t// Total buffer size = 1 (LenByte) + Length + 2 (Checksum).\n\t\t// So Buffer Length = 1 + Length + 2 = Length + 3.\n\t\t// So Length Byte value should be BufferLength - 3.\n\n\t\tconst calculatedSum = this.calculateChecksum();\n\t\treturn calculatedSum === this.checksum;\n\t}\n\n\tprivate calculateChecksum(): number {\n\t\t// Sum of bytes from Header to end of Body (excluding Length byte and Checksum bytes)\n\t\t// i.e., indices 1 to length-3\n\t\tlet sum = 0;\n\t\tfor (let i = 1; i < this.buffer.length - 2; i++) {\n\t\t\tsum += this.buffer[i];\n\t\t}\n\t\treturn sum & 0xffff;\n\t}\n\n\tstatic fromParts(\n\t\tdeviceId: number,\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\teBit: boolean,\n\t\tblockNum: number,\n\t\tsystemBytes: number,\n\t\tbody: Buffer,\n\t\trBit = false,\n\t): Secs1MessageBlock {\n\t\tconst header = Buffer.alloc(10);\n\t\t// Byte 0: R-Bit + DeviceID MSB\n\t\tlet b0 = (deviceId >> 8) & 0x7f;\n\t\tif (rBit) b0 |= 0x80;\n\t\theader[0] = b0;\n\t\t// Byte 1: DeviceID LSB\n\t\theader[1] = deviceId & 0xff;\n\n\t\t// Byte 2: W-Bit + Stream\n\t\tlet b2 = stream & 0x7f;\n\t\tif (wBit) b2 |= 0x80;\n\t\theader[2] = b2;\n\n\t\t// Byte 3: Function\n\t\theader[3] = func & 0xff;\n\n\t\t// Byte 4: E-Bit + BlockNum MSB\n\t\tlet b4 = (blockNum >> 8) & 0x7f;\n\t\tif (eBit) b4 |= 0x80;\n\t\theader[4] = b4;\n\n\t\t// Byte 5: BlockNum LSB\n\t\theader[5] = blockNum & 0xff;\n\n\t\t// Byte 6-9: System Bytes\n\t\theader.writeUInt32BE(systemBytes, 6);\n\n\t\t// Calculate Length\n\t\tconst length = 10 + body.length;\n\t\tif (length > 254) throw new Error(\"Block too large\");\n\n\t\t// Calculate Checksum\n\t\tlet sum = 0;\n\t\tfor (const b of header) sum += b;\n\t\tfor (const b of body) sum += b;\n\t\tconst checksum = sum & 0xffff;\n\n\t\tconst block = Buffer.alloc(1 + 10 + body.length + 2);\n\t\tblock[0] = length;\n\t\theader.copy(block, 1);\n\t\tbody.copy(block, 11);\n\t\tblock.writeUInt16BE(checksum, block.length - 2);\n\n\t\treturn new Secs1MessageBlock(block);\n\t}\n}\n"],"mappings":";AAAA,IAAa,oBAAb,MAAa,kBAAkB;CAE9B,YAAY,AAAgBA,QAAgB;EAAhB;;CAE5B,IAAI,SAAiB;AACpB,SAAO,KAAK,OAAO;;CAGpB,IAAI,SAAiB;AACpB,SAAO,KAAK,OAAO,SAAS,GAAG,GAAG;;CAGnC,IAAI,OAAe;AAClB,SAAO,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,SAAS,EAAE;;CAGxD,IAAI,WAAmB;AACtB,SAAO,KAAK,OAAO,aAAa,KAAK,OAAO,SAAS,EAAE;;CAIxD,IAAI,WAAmB;EACtB,MAAM,KAAK,KAAK,OAAO,SAAS,GAAG,EAAE;AACrC,SAAS,GAAG,MAAM,IAAK,QAAU,GAAG;;CAGrC,IAAI,OAAgB;AACnB,UAAQ,KAAK,OAAO,KAAK,SAAU;;CAGpC,IAAI,SAAiB;AACpB,SAAO,KAAK,OAAO,KAAK;;CAGzB,IAAI,OAAgB;AACnB,UAAQ,KAAK,OAAO,KAAK,SAAU;;CAGpC,IAAI,OAAe;AAClB,SAAO,KAAK,OAAO;;CAGpB,IAAI,cAAsB;EACzB,MAAM,KAAK,KAAK,OAAO,SAAS,GAAG,EAAE;AACrC,SAAS,GAAG,MAAM,IAAK,QAAU,GAAG;;CAGrC,IAAI,OAAgB;AACnB,UAAQ,KAAK,OAAO,KAAK,SAAU;;CAGpC,IAAI,cAAsB;AACzB,SAAO,KAAK,OAAO,aAAa,EAAE;;CAGnC,UAAmB;AAClB,MAAI,KAAK,OAAO,SAAS,GAAI,QAAO;AACpC,MAAI,KAAK,OAAO,OAAO,KAAK,OAAO,SAAS,EAAG,QAAO;AAStD,SADsB,KAAK,mBAAmB,KACrB,KAAK;;CAG/B,AAAQ,oBAA4B;EAGnC,IAAI,MAAM;AACV,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,OAAO,SAAS,GAAG,IAC3C,QAAO,KAAK,OAAO;AAEpB,SAAO,MAAM;;CAGd,OAAO,UACN,UACA,QACA,MACA,MACA,MACA,UACA,aACA,MACA,OAAO,OACa;EACpB,MAAM,SAAS,OAAO,MAAM,GAAG;EAE/B,IAAI,KAAM,YAAY,IAAK;AAC3B,MAAI,KAAM,OAAM;AAChB,SAAO,KAAK;AAEZ,SAAO,KAAK,WAAW;EAGvB,IAAI,KAAK,SAAS;AAClB,MAAI,KAAM,OAAM;AAChB,SAAO,KAAK;AAGZ,SAAO,KAAK,OAAO;EAGnB,IAAI,KAAM,YAAY,IAAK;AAC3B,MAAI,KAAM,OAAM;AAChB,SAAO,KAAK;AAGZ,SAAO,KAAK,WAAW;AAGvB,SAAO,cAAc,aAAa,EAAE;EAGpC,MAAM,SAAS,KAAK,KAAK;AACzB,MAAI,SAAS,IAAK,OAAM,IAAI,MAAM,kBAAkB;EAGpD,IAAI,MAAM;AACV,OAAK,MAAM,KAAK,OAAQ,QAAO;AAC/B,OAAK,MAAM,KAAK,KAAM,QAAO;EAC7B,MAAM,WAAW,MAAM;EAEvB,MAAM,QAAQ,OAAO,MAAM,KAAS,KAAK,SAAS,EAAE;AACpD,QAAM,KAAK;AACX,SAAO,KAAK,OAAO,EAAE;AACrB,OAAK,KAAK,OAAO,GAAG;AACpB,QAAM,cAAc,UAAU,MAAM,SAAS,EAAE;AAE/C,SAAO,IAAI,kBAAkB,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1SerialCommunicator.js","names":[],"sources":["../../src/secs1/Secs1SerialCommunicator.ts"],"sourcesContent":["import { SerialPort } from \"serialport\";\
|
|
1
|
+
{"version":3,"file":"Secs1SerialCommunicator.js","names":[],"sources":["../../src/secs1/Secs1SerialCommunicator.ts"],"sourcesContent":["import { SerialPort } from \"serialport\";\nimport {\n\tSecs1Communicator,\n\tSecs1CommunicatorConfig,\n} from \"./Secs1Communicator.js\";\n\nexport interface Secs1SerialCommunicatorConfig extends Secs1CommunicatorConfig {\n\tpath: string;\n\tbaudRate: number;\n}\n\nexport class Secs1SerialCommunicator extends Secs1Communicator {\n\tprivate port: SerialPort | null = null;\n\n\tpublic path: string;\n\tpublic baudRate: number;\n\n\tconstructor(config: Secs1SerialCommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.path = config.path;\n\t\tthis.baudRate = config.baudRate;\n\t}\n\n\tasync open(): Promise<void> {\n\t\tif (this.port?.isOpen) return;\n\n\t\tconst port = new SerialPort({\n\t\t\tpath: this.path,\n\t\t\tbaudRate: this.baudRate,\n\t\t\tautoOpen: false,\n\t\t});\n\t\tthis.port = port;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst onError = (err: Error) => {\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tport.once(\"error\", onError);\n\t\t\tport.open((err) => {\n\t\t\t\tport.removeListener(\"error\", onError);\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.attachStream(port);\n\t\t\t\tresolve();\n\t\t\t});\n\t\t});\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.stop();\n\n\t\tconst port = this.port;\n\t\tthis.port = null;\n\t\tif (!port) return;\n\n\t\tif (!port.isOpen) {\n\t\t\tport.destroy();\n\t\t\treturn;\n\t\t}\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tport.close((err) => {\n\t\t\t\tif (err) reject(err);\n\t\t\t\telse resolve();\n\t\t\t});\n\t\t});\n\n\t\tport.destroy();\n\t}\n}\n"],"mappings":";;;;AAWA,IAAa,0BAAb,cAA6C,kBAAkB;CAC9D,AAAQ,OAA0B;CAElC,AAAO;CACP,AAAO;CAEP,YAAY,QAAuC;AAClD,QAAM,OAAO;AACb,OAAK,OAAO,OAAO;AACnB,OAAK,WAAW,OAAO;;CAGxB,MAAM,OAAsB;AAC3B,MAAI,KAAK,MAAM,OAAQ;EAEvB,MAAM,OAAO,IAAI,WAAW;GAC3B,MAAM,KAAK;GACX,UAAU,KAAK;GACf,UAAU;GACV,CAAC;AACF,OAAK,OAAO;AAEZ,SAAO,IAAI,SAAS,SAAS,WAAW;GACvC,MAAM,WAAW,QAAe;AAC/B,WAAO,IAAI;;AAGZ,QAAK,KAAK,SAAS,QAAQ;AAC3B,QAAK,MAAM,QAAQ;AAClB,SAAK,eAAe,SAAS,QAAQ;AACrC,QAAI,KAAK;AACR,YAAO,IAAI;AACX;;AAED,SAAK,aAAa,KAAK;AACvB,aAAS;KACR;IACD;;CAGH,MAAM,QAAuB;AAC5B,OAAK,MAAM;EAEX,MAAM,OAAO,KAAK;AAClB,OAAK,OAAO;AACZ,MAAI,CAAC,KAAM;AAEX,MAAI,CAAC,KAAK,QAAQ;AACjB,QAAK,SAAS;AACd;;AAGD,QAAM,IAAI,SAAe,SAAS,WAAW;AAC5C,QAAK,OAAO,QAAQ;AACnB,QAAI,IAAK,QAAO,IAAI;QACf,UAAS;KACb;IACD;AAEF,OAAK,SAAS"}
|
package/lib/sml/SmlParser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmlParser.js","names":["str: string","item: AbstractSecs2Item","items: AbstractSecs2Item[]","buffer: number[]","bools: boolean[]","nums: number[]"],"sources":["../../src/sml/SmlParser.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\r\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\r\nimport { SecsItemType } from \"../core/enums/SecsItemType.js\";\r\nimport { Secs2ItemAscii } from \"../core/secs2item/Secs2ItemAscii.js\";\r\nimport { Secs2ItemList } from \"../core/secs2item/Secs2ItemList.js\";\r\nimport { Secs2ItemBinary } from \"../core/secs2item/Secs2ItemBinary.js\";\r\nimport { Secs2ItemBoolean } from \"../core/secs2item/Secs2ItemBoolean.js\";\r\nimport { Secs2ItemNumeric } from \"../core/secs2item/Secs2ItemNumeric.js\";\r\n\r\n/**\r\n * @description A cursor for SML parsing.\r\n */\r\nclass SmlCursor {\r\n\tconstructor(\r\n\t\tpublic str: string,\r\n\t\tpublic pos = 0,\r\n\t) {}\r\n\r\n\t/**\r\n\t * @description Returns the next character in the string without consuming it.\r\n\t * @returns The next character in the string.\r\n\t */\r\n\tpeek(): string {\r\n\t\treturn this.str[this.pos];\r\n\t}\r\n\r\n\t/**\r\n\t * @description Consumes the next character in the string and returns it.\r\n\t * @returns The consumed character.\r\n\t */\r\n\tconsume(): string {\r\n\t\treturn this.str[this.pos++];\r\n\t}\r\n\r\n\t/**\r\n\t * @description Returns true if the cursor is at the end of the string.\r\n\t * @returns True if the cursor is at the end of the string.\r\n\t */\r\n\teof(): boolean {\r\n\t\treturn this.pos >= this.str.length;\r\n\t}\r\n\r\n\t/**\r\n\t * @description Skips whitespace characters in the string.\r\n\t */\r\n\tskipWs() {\r\n\t\twhile (!this.eof() && /\\s/.test(this.peek())) this.pos++;\r\n\t}\r\n\r\n\t/**\r\n\t * @description Matches the next character in the string with the given character.\r\n\t * @param char The character to match.\r\n\t * @returns True if the next character in the string is the given character.\r\n\t */\r\n\tmatch(char: string): boolean {\r\n\t\tif (this.peek() === char) {\r\n\t\t\tthis.consume();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n/**\r\n * @description A parser for SECS-II SML strings.\r\n */\r\nexport class SmlParser {\r\n\tprivate static readonly SML_REGEX = /^[Ss](\\d+)[Ff](\\d+)\\s*(W?)\\s*(.*)\\.$/s;\r\n\r\n\t/**\r\n\t * @description Parses an SML string into a SecsMessage.\r\n\t * @param sml The SML string to parse.\r\n\t * @returns The parsed SecsMessage.\r\n\t */\r\n\tstatic parse(sml: string): SecsMessage {\r\n\t\tconst trimmed = sml.trim();\r\n\t\tconst match = this.SML_REGEX.exec(trimmed);\r\n\r\n\t\tif (!match) {\r\n\t\t\tthrow new Error(\"Invalid SML format. Must match 'SxFy [W] <Body>.'\");\r\n\t\t}\r\n\r\n\t\tconst stream = parseInt(match[1], 10);\r\n\t\tconst func = parseInt(match[2], 10);\r\n\t\tconst wBit = match[3].toUpperCase() === \"W\";\r\n\t\tconst bodyStr = match[4].trim();\r\n\r\n\t\tconst body = this.parseBody(bodyStr);\r\n\r\n\t\treturn new SecsMessage(stream, func, wBit, body);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Parses an SML body string into a Secs2Item.\r\n\t * @param smlBody The SML body string to parse.\r\n\t * @returns The parsed Secs2Item.\r\n\t */\r\n\tstatic parseBody(smlBody: string): AbstractSecs2Item | null {\r\n\t\tconst trimmed = smlBody.trim();\r\n\t\tif (trimmed.length === 0) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tconst cursor = new SmlCursor(trimmed);\r\n\t\treturn this.parseItem(cursor);\r\n\t}\r\n\r\n\tprivate static parseItem(cursor: SmlCursor): AbstractSecs2Item {\r\n\t\tcursor.skipWs();\r\n\t\tif (cursor.consume() !== \"<\") {\r\n\t\t\tthrow new Error(`Expected '<' at pos ${(cursor.pos - 1).toString()}`);\r\n\t\t}\r\n\r\n\t\tcursor.skipWs();\r\n\t\tconst typeStart = cursor.pos;\r\n\t\twhile (!cursor.eof() && /[A-Z0-9]/.test(cursor.peek().toUpperCase())) {\r\n\t\t\tcursor.consume();\r\n\t\t}\r\n\t\tconst typeStr = cursor.str.substring(typeStart, cursor.pos).toUpperCase();\r\n\r\n\t\t// Skip length [size]\r\n\t\tcursor.skipWs();\r\n\t\tif (cursor.peek() === \"[\") {\r\n\t\t\twhile (!cursor.eof() && cursor.peek() !== \"]\") cursor.consume();\r\n\t\t\tif (cursor.peek() === \"]\") cursor.consume();\r\n\t\t}\r\n\r\n\t\tlet item: AbstractSecs2Item;\r\n\r\n\t\tif (typeStr === \"L\") {\r\n\t\t\tconst items: AbstractSecs2Item[] = [];\r\n\r\n\t\t\twhile (true) {\r\n\t\t\t\tcursor.skipWs();\r\n\t\t\t\tif (cursor.peek() === \">\") {\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t} else if (cursor.peek() === \"<\") {\r\n\t\t\t\t\titems.push(this.parseItem(cursor));\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error(\r\n\t\t\t\t\t\t`Unexpected char in List: '${cursor.peek()}' at ${cursor.pos.toString()}`,\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\titem = new Secs2ItemList(items);\r\n\t\t} else if (typeStr === \"A\") {\r\n\t\t\tcursor.skipWs();\r\n\t\t\t// Expect quoted string\r\n\t\t\tif (cursor.peek() === '\"') {\r\n\t\t\t\tcursor.consume();\r\n\t\t\t\tconst start = cursor.pos;\r\n\t\t\t\twhile (!cursor.eof() && cursor.peek() !== '\"') cursor.consume(); // Handle escaping? SML usually simple.\r\n\t\t\t\tconst val = cursor.str.substring(start, cursor.pos);\r\n\t\t\t\tcursor.consume(); // quote\r\n\t\t\t\titem = new Secs2ItemAscii(val);\r\n\t\t\t} else {\r\n\t\t\t\t// Empty string or unquoted? Standard says quoted.\r\n\t\t\t\t// Assuming empty if > follows immediately\r\n\t\t\t\tif (cursor.peek() === \">\") {\r\n\t\t\t\t\titem = new Secs2ItemAscii(\"\");\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error(\r\n\t\t\t\t\t\t`Expected string value for type A at ${cursor.pos.toString()}`,\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcursor.skipWs();\r\n\t\t\tif (cursor.consume() !== \">\") throw new Error(\"Expected '>' ending A\");\r\n\t\t} else if (typeStr === \"B\") {\r\n\t\t\t// Binary: 0xXX or XX\r\n\t\t\tconst buffer: number[] = [];\r\n\r\n\t\t\twhile (true) {\r\n\t\t\t\tcursor.skipWs();\r\n\t\t\t\tif (cursor.peek() === \">\") {\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\t// Read byte\r\n\t\t\t\t// Handle 0x prefix\r\n\t\t\t\tif (\r\n\t\t\t\t\tcursor.str.startsWith(\"0x\", cursor.pos) ||\r\n\t\t\t\t\tcursor.str.startsWith(\"0X\", cursor.pos)\r\n\t\t\t\t) {\r\n\t\t\t\t\tcursor.pos += 2;\r\n\t\t\t\t}\r\n\t\t\t\tconst byteStr = cursor.str.substring(cursor.pos, cursor.pos + 2);\r\n\t\t\t\tconst byteVal = parseInt(byteStr, 16);\r\n\t\t\t\tif (isNaN(byteVal))\r\n\t\t\t\t\tthrow new Error(`Invalid binary byte at ${cursor.pos.toString()}`);\r\n\t\t\t\tbuffer.push(byteVal);\r\n\t\t\t\tcursor.pos += 2;\r\n\t\t\t}\r\n\t\t\titem = new Secs2ItemBinary(Buffer.from(buffer));\r\n\t\t} else if (typeStr === \"BOOLEAN\") {\r\n\t\t\tconst bools: boolean[] = [];\r\n\r\n\t\t\twhile (true) {\r\n\t\t\t\tcursor.skipWs();\r\n\t\t\t\tif (cursor.peek() === \">\") {\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\t// Read T/True/F/False\r\n\t\t\t\tconst start = cursor.pos;\r\n\t\t\t\twhile (!cursor.eof() && /[A-Z]/.test(cursor.peek().toUpperCase()))\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\tconst val = cursor.str.substring(start, cursor.pos).toUpperCase();\r\n\t\t\t\tif ([\"T\", \"TRUE\"].includes(val)) bools.push(true);\r\n\t\t\t\telse if ([\"F\", \"FALSE\"].includes(val)) bools.push(false);\r\n\t\t\t\telse throw new Error(`Invalid Boolean value ${val}`);\r\n\t\t\t}\r\n\t\t\titem = new Secs2ItemBoolean(bools.length === 1 ? bools[0] : bools);\r\n\t\t} else {\r\n\t\t\t// Numbers\r\n\t\t\tconst nums: number[] = []; // or bigint\r\n\t\t\t// Simplified number parsing\r\n\t\t\tconst isFloat = [\"F4\", \"F8\"].includes(typeStr);\r\n\r\n\t\t\twhile (true) {\r\n\t\t\t\tcursor.skipWs();\r\n\t\t\t\tif (cursor.peek() === \">\") {\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tconst start = cursor.pos;\r\n\t\t\t\twhile (!cursor.eof() && /[0-9.\\-+]/.test(cursor.peek()))\r\n\t\t\t\t\tcursor.consume();\r\n\t\t\t\tconst valStr = cursor.str.substring(start, cursor.pos);\r\n\t\t\t\tconst val = isFloat ? parseFloat(valStr) : Number(valStr); // Use BigInt if needed for U8/I8\r\n\t\t\t\tif (isNaN(val))\r\n\t\t\t\t\tthrow new Error(`Invalid number ${valStr} at ${start.toString()}`);\r\n\t\t\t\tnums.push(val);\r\n\t\t\t}\r\n\r\n\t\t\t// Determine SecsItemType\r\n\t\t\tconst type = SecsItemType[typeStr as keyof typeof SecsItemType];\r\n\r\n\t\t\tif (type === undefined) throw new Error(`Unknown type ${typeStr}`);\r\n\r\n\t\t\titem = new Secs2ItemNumeric(type, nums.length === 1 ? nums[0] : nums);\r\n\t\t}\r\n\r\n\t\treturn item;\r\n\t}\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SmlParser.js","names":["str: string","item: AbstractSecs2Item","items: AbstractSecs2Item[]","buffer: number[]","bools: boolean[]","nums: number[]"],"sources":["../../src/sml/SmlParser.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\nimport { SecsItemType } from \"../core/enums/SecsItemType.js\";\nimport { Secs2ItemAscii } from \"../core/secs2item/Secs2ItemAscii.js\";\nimport { Secs2ItemList } from \"../core/secs2item/Secs2ItemList.js\";\nimport { Secs2ItemBinary } from \"../core/secs2item/Secs2ItemBinary.js\";\nimport { Secs2ItemBoolean } from \"../core/secs2item/Secs2ItemBoolean.js\";\nimport { Secs2ItemNumeric } from \"../core/secs2item/Secs2ItemNumeric.js\";\n\n/**\n * @description A cursor for SML parsing.\n */\nclass SmlCursor {\n\tconstructor(\n\t\tpublic str: string,\n\t\tpublic pos = 0,\n\t) {}\n\n\t/**\n\t * @description Returns the next character in the string without consuming it.\n\t * @returns The next character in the string.\n\t */\n\tpeek(): string {\n\t\treturn this.str[this.pos];\n\t}\n\n\t/**\n\t * @description Consumes the next character in the string and returns it.\n\t * @returns The consumed character.\n\t */\n\tconsume(): string {\n\t\treturn this.str[this.pos++];\n\t}\n\n\t/**\n\t * @description Returns true if the cursor is at the end of the string.\n\t * @returns True if the cursor is at the end of the string.\n\t */\n\teof(): boolean {\n\t\treturn this.pos >= this.str.length;\n\t}\n\n\t/**\n\t * @description Skips whitespace characters in the string.\n\t */\n\tskipWs() {\n\t\twhile (!this.eof() && /\\s/.test(this.peek())) this.pos++;\n\t}\n\n\t/**\n\t * @description Matches the next character in the string with the given character.\n\t * @param char The character to match.\n\t * @returns True if the next character in the string is the given character.\n\t */\n\tmatch(char: string): boolean {\n\t\tif (this.peek() === char) {\n\t\t\tthis.consume();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/**\n * @description A parser for SECS-II SML strings.\n */\nexport class SmlParser {\n\tprivate static readonly SML_REGEX = /^[Ss](\\d+)[Ff](\\d+)\\s*(W?)\\s*(.*)\\.$/s;\n\n\t/**\n\t * @description Parses an SML string into a SecsMessage.\n\t * @param sml The SML string to parse.\n\t * @returns The parsed SecsMessage.\n\t */\n\tstatic parse(sml: string): SecsMessage {\n\t\tconst trimmed = sml.trim();\n\t\tconst match = this.SML_REGEX.exec(trimmed);\n\n\t\tif (!match) {\n\t\t\tthrow new Error(\"Invalid SML format. Must match 'SxFy [W] <Body>.'\");\n\t\t}\n\n\t\tconst stream = parseInt(match[1], 10);\n\t\tconst func = parseInt(match[2], 10);\n\t\tconst wBit = match[3].toUpperCase() === \"W\";\n\t\tconst bodyStr = match[4].trim();\n\n\t\tconst body = this.parseBody(bodyStr);\n\n\t\treturn new SecsMessage(stream, func, wBit, body);\n\t}\n\n\t/**\n\t * @description Parses an SML body string into a Secs2Item.\n\t * @param smlBody The SML body string to parse.\n\t * @returns The parsed Secs2Item.\n\t */\n\tstatic parseBody(smlBody: string): AbstractSecs2Item | null {\n\t\tconst trimmed = smlBody.trim();\n\t\tif (trimmed.length === 0) {\n\t\t\treturn null;\n\t\t}\n\t\tconst cursor = new SmlCursor(trimmed);\n\t\treturn this.parseItem(cursor);\n\t}\n\n\tprivate static parseItem(cursor: SmlCursor): AbstractSecs2Item {\n\t\tcursor.skipWs();\n\t\tif (cursor.consume() !== \"<\") {\n\t\t\tthrow new Error(`Expected '<' at pos ${(cursor.pos - 1).toString()}`);\n\t\t}\n\n\t\tcursor.skipWs();\n\t\tconst typeStart = cursor.pos;\n\t\twhile (!cursor.eof() && /[A-Z0-9]/.test(cursor.peek().toUpperCase())) {\n\t\t\tcursor.consume();\n\t\t}\n\t\tconst typeStr = cursor.str.substring(typeStart, cursor.pos).toUpperCase();\n\n\t\t// Skip length [size]\n\t\tcursor.skipWs();\n\t\tif (cursor.peek() === \"[\") {\n\t\t\twhile (!cursor.eof() && cursor.peek() !== \"]\") cursor.consume();\n\t\t\tif (cursor.peek() === \"]\") cursor.consume();\n\t\t}\n\n\t\tlet item: AbstractSecs2Item;\n\n\t\tif (typeStr === \"L\") {\n\t\t\tconst items: AbstractSecs2Item[] = [];\n\n\t\t\twhile (true) {\n\t\t\t\tcursor.skipWs();\n\t\t\t\tif (cursor.peek() === \">\") {\n\t\t\t\t\tcursor.consume();\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (cursor.peek() === \"<\") {\n\t\t\t\t\titems.push(this.parseItem(cursor));\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unexpected char in List: '${cursor.peek()}' at ${cursor.pos.toString()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\titem = new Secs2ItemList(items);\n\t\t} else if (typeStr === \"A\") {\n\t\t\tcursor.skipWs();\n\t\t\t// Expect quoted string\n\t\t\tif (cursor.peek() === '\"') {\n\t\t\t\tcursor.consume();\n\t\t\t\tconst start = cursor.pos;\n\t\t\t\twhile (!cursor.eof() && cursor.peek() !== '\"') cursor.consume(); // Handle escaping? SML usually simple.\n\t\t\t\tconst val = cursor.str.substring(start, cursor.pos);\n\t\t\t\tcursor.consume(); // quote\n\t\t\t\titem = new Secs2ItemAscii(val);\n\t\t\t} else {\n\t\t\t\t// Empty string or unquoted? Standard says quoted.\n\t\t\t\t// Assuming empty if > follows immediately\n\t\t\t\tif (cursor.peek() === \">\") {\n\t\t\t\t\titem = new Secs2ItemAscii(\"\");\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Expected string value for type A at ${cursor.pos.toString()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor.skipWs();\n\t\t\tif (cursor.consume() !== \">\") throw new Error(\"Expected '>' ending A\");\n\t\t} else if (typeStr === \"B\") {\n\t\t\t// Binary: 0xXX or XX\n\t\t\tconst buffer: number[] = [];\n\n\t\t\twhile (true) {\n\t\t\t\tcursor.skipWs();\n\t\t\t\tif (cursor.peek() === \">\") {\n\t\t\t\t\tcursor.consume();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// Read byte\n\t\t\t\t// Handle 0x prefix\n\t\t\t\tif (\n\t\t\t\t\tcursor.str.startsWith(\"0x\", cursor.pos) ||\n\t\t\t\t\tcursor.str.startsWith(\"0X\", cursor.pos)\n\t\t\t\t) {\n\t\t\t\t\tcursor.pos += 2;\n\t\t\t\t}\n\t\t\t\tconst byteStr = cursor.str.substring(cursor.pos, cursor.pos + 2);\n\t\t\t\tconst byteVal = parseInt(byteStr, 16);\n\t\t\t\tif (isNaN(byteVal))\n\t\t\t\t\tthrow new Error(`Invalid binary byte at ${cursor.pos.toString()}`);\n\t\t\t\tbuffer.push(byteVal);\n\t\t\t\tcursor.pos += 2;\n\t\t\t}\n\t\t\titem = new Secs2ItemBinary(Buffer.from(buffer));\n\t\t} else if (typeStr === \"BOOLEAN\") {\n\t\t\tconst bools: boolean[] = [];\n\n\t\t\twhile (true) {\n\t\t\t\tcursor.skipWs();\n\t\t\t\tif (cursor.peek() === \">\") {\n\t\t\t\t\tcursor.consume();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// Read T/True/F/False\n\t\t\t\tconst start = cursor.pos;\n\t\t\t\twhile (!cursor.eof() && /[A-Z]/.test(cursor.peek().toUpperCase()))\n\t\t\t\t\tcursor.consume();\n\t\t\t\tconst val = cursor.str.substring(start, cursor.pos).toUpperCase();\n\t\t\t\tif ([\"T\", \"TRUE\"].includes(val)) bools.push(true);\n\t\t\t\telse if ([\"F\", \"FALSE\"].includes(val)) bools.push(false);\n\t\t\t\telse throw new Error(`Invalid Boolean value ${val}`);\n\t\t\t}\n\t\t\titem = new Secs2ItemBoolean(bools.length === 1 ? bools[0] : bools);\n\t\t} else {\n\t\t\t// Numbers\n\t\t\tconst nums: number[] = []; // or bigint\n\t\t\t// Simplified number parsing\n\t\t\tconst isFloat = [\"F4\", \"F8\"].includes(typeStr);\n\n\t\t\twhile (true) {\n\t\t\t\tcursor.skipWs();\n\t\t\t\tif (cursor.peek() === \">\") {\n\t\t\t\t\tcursor.consume();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst start = cursor.pos;\n\t\t\t\twhile (!cursor.eof() && /[0-9.\\-+]/.test(cursor.peek()))\n\t\t\t\t\tcursor.consume();\n\t\t\t\tconst valStr = cursor.str.substring(start, cursor.pos);\n\t\t\t\tconst val = isFloat ? parseFloat(valStr) : Number(valStr); // Use BigInt if needed for U8/I8\n\t\t\t\tif (isNaN(val))\n\t\t\t\t\tthrow new Error(`Invalid number ${valStr} at ${start.toString()}`);\n\t\t\t\tnums.push(val);\n\t\t\t}\n\n\t\t\t// Determine SecsItemType\n\t\t\tconst type = SecsItemType[typeStr as keyof typeof SecsItemType];\n\n\t\t\tif (type === undefined) throw new Error(`Unknown type ${typeStr}`);\n\n\t\t\titem = new Secs2ItemNumeric(type, nums.length === 1 ? nums[0] : nums);\n\t\t}\n\n\t\treturn item;\n\t}\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secs4js",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "A simple, efficient, and user-friendly SECS/GEM protocol library implemented in TypeScript.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/LostCat-Qian/secs4js.git"
|
|
8
8
|
},
|
|
9
|
-
"
|
|
9
|
+
"keywords": [
|
|
10
10
|
"secs",
|
|
11
11
|
"secs-i",
|
|
12
12
|
"secs-ii",
|