secs4js 0.3.0 → 0.4.1
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 +84 -0
- package/lib/core/AbstractSecsCommunicator.d.ts +4 -0
- package/lib/core/AbstractSecsCommunicator.d.ts.map +1 -1
- package/lib/core/AbstractSecsCommunicator.js +70 -5
- package/lib/core/AbstractSecsCommunicator.js.map +1 -1
- 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/HsmsActiveCommunicator.d.ts.map +1 -1
- package/lib/hsms/HsmsActiveCommunicator.js +21 -5
- package/lib/hsms/HsmsActiveCommunicator.js.map +1 -1
- package/lib/hsms/HsmsCommunicator.d.ts +1 -0
- package/lib/hsms/HsmsCommunicator.d.ts.map +1 -1
- package/lib/hsms/HsmsCommunicator.js +72 -7
- package/lib/hsms/HsmsCommunicator.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/logging/SecsLogger.d.ts +36 -0
- package/lib/logging/SecsLogger.d.ts.map +1 -0
- package/lib/logging/SecsLogger.js +415 -0
- package/lib/logging/SecsLogger.js.map +1 -0
- package/lib/secs1/Secs1Communicator.d.ts +1 -0
- package/lib/secs1/Secs1Communicator.d.ts.map +1 -1
- package/lib/secs1/Secs1Communicator.js +45 -8
- package/lib/secs1/Secs1Communicator.js.map +1 -1
- package/lib/secs1/Secs1Message.js.map +1 -1
- package/lib/secs1/Secs1MessageBlock.js.map +1 -1
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.d.ts.map +1 -1
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js +13 -2
- package/lib/secs1/Secs1OnTcpIpActiveCommunicator.js.map +1 -1
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.d.ts.map +1 -1
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js +10 -2
- package/lib/secs1/Secs1OnTcpIpPassiveCommunicator.js.map +1 -1
- package/lib/secs1/Secs1SerialCommunicator.js.map +1 -1
- package/lib/sml/SmlParser.js.map +1 -1
- package/package.json +15 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HsmsMessage.js","names":["body: AbstractSecs2Item | null"],"sources":["../../src/hsms/HsmsMessage.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\nimport { HsmsControlType } from \"./enums/HsmsControlType.js\";\nimport { RejectReason } from \"./enums/RejectReason.js\";\nimport { Secs2ItemParser } from \"../core/secs2item/Secs2ItemParser.js\";\n\n/**\n * @description HsmsMessage is the class that represents an HSMS message.\n * @param stream The stream number of the message.\n * @param func The function number of the message.\n * @param wBit The W-Bit of the message.\n * @param body The body of the message.\n * @param systemBytes The system bytes of the message.\n * @param deviceId The device ID of the message.\n * @param pType The P-Type of the message.\n * @param sType The S-Type of the message.\n */\nexport class HsmsMessage 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 pType = 0,\n\t\tpublic readonly sType = 0,\n\t) {\n\t\tsuper(stream, func, wBit, body, systemBytes, deviceId);\n\t}\n\n\t/**\n\t * @description Encodes the message to a buffer (Length + Header + Body).\n\t */\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-1: Session ID (Device ID) or 0xFFFF\n\t\tif (this.isDataMessage()) {\n\t\t\theader.writeUInt16BE(this.deviceId, 0);\n\t\t} else {\n\t\t\theader.writeUInt16BE(0xffff, 0);\n\t\t}\n\n\t\t// Byte 2: Stream / WBit (only for Data)\n\t\t// Byte 3: Function (only for Data)\n\t\tif (this.isDataMessage()) {\n\t\t\tlet b2 = this.stream;\n\t\t\tif (this.wBit) {\n\t\t\t\tb2 |= 0x80;\n\t\t\t}\n\t\t\theader.writeUInt8(b2, 2);\n\t\t\theader.writeUInt8(this.func, 3);\n\t\t} else {\n\t\t\t// For control messages, byte 2 and 3 depend on type\n\t\t\t// But typically 0 unless SelectStatus/RejectReason\n\t\t\t// We will handle specific construction in static methods\n\t\t\t// Here we assume pType and sType are set correctly for Control Messages\n\t\t\t// Wait, P-Type is Byte 4, S-Type is Byte 5.\n\t\t\t// Byte 2 and 3 are 0 for most control messages.\n\t\t\t// EXCEPT Select Response (Byte 3 = Status) and Reject (Byte 3 = Reason).\n\t\t\t// But those are stored in 'sType' or 'func' or similar?\n\t\t\t// In this class, I store pType and sType (Byte 4, 5).\n\t\t\t// Where do I store Byte 2 and 3 for Control Messages?\n\t\t\t// The Python code:\n\t\t\t// Select Response: h10bytes = ... 0x00, select_status ...\n\t\t\t// Reject: ... b2, reject_reason ...\n\n\t\t\t// I should allow passing byte 2 and 3 specifically or handle it via polymorphism.\n\t\t\t// To keep it simple, I will use 'stream' and 'func' to hold Byte 2 and 3 for Control Messages too,\n\t\t\t// even if they don't mean Stream/Function.\n\n\t\t\theader.writeUInt8(this.stream, 2);\n\t\t\theader.writeUInt8(this.func, 3);\n\t\t}\n\n\t\t// Byte 4: P-Type\n\t\theader.writeUInt8(this.pType, 4);\n\n\t\t// Byte 5: S-Type\n\t\theader.writeUInt8(this.sType, 5);\n\n\t\t// Byte 6-9: System Bytes\n\t\theader.writeUInt32BE(this.systemBytes, 6);\n\n\t\tconst length = header.length + bodyBuffer.length;\n\t\tconst lengthBuffer = Buffer.alloc(4);\n\t\tlengthBuffer.writeUInt32BE(length, 0);\n\n\t\treturn Buffer.concat([lengthBuffer, header, bodyBuffer]);\n\t}\n\n\t/**\n\t * @description Checks if the message is a data message.\n\t * @description data message: deviceId not 0xFF 0xFF and header[4] and header[5] are 0.\n\t * @returns True if the message is a data message, false otherwise.\n\t */\n\tisDataMessage(): boolean {\n\t\treturn (this.sType as HsmsControlType) === HsmsControlType.Data;\n\t}\n\n\t/**\n\t * @description Creates an HsmsMessage from a buffer.\n\t * @param buffer The buffer containing the message data.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic fromBuffer(buffer: Buffer): HsmsMessage {\n\t\tif (buffer.length < 14) {\n\t\t\tthrow new Error(\"Buffer too short for HSMS message\");\n\t\t}\n\n\t\t// Bytes 0-3: Length (ignored here, assumed to be correct or handled by framer)\n\t\tconst header = buffer.subarray(4, 14);\n\t\tconst bodyBuffer = buffer.subarray(14);\n\n\t\tconst sessionId = header.readUInt16BE(0);\n\t\tconst byte2 = header.readUInt8(2);\n\t\tconst byte3 = header.readUInt8(3);\n\t\tconst pType = header.readUInt8(4);\n\t\tconst sType = header.readUInt8(5);\n\t\tconst systemBytes = header.readUInt32BE(6);\n\n\t\tlet stream = 0;\n\t\tlet func = 0;\n\t\tlet wBit = false;\n\t\tconst deviceId = sessionId;\n\t\tlet body: AbstractSecs2Item | null = null;\n\n\t\tif ((sType as HsmsControlType) === HsmsControlType.Data) {\n\t\t\tstream = byte2 & 0x7f;\n\t\t\twBit = (byte2 & 0x80) !== 0;\n\t\t\tfunc = byte3;\n\n\t\t\tif (bodyBuffer.length > 0) {\n\t\t\t\tconst result = Secs2ItemParser.fromBuffer(bodyBuffer);\n\t\t\t\tbody = result.item;\n\t\t\t}\n\t\t} else {\n\t\t\t// Control Message\n\t\t\t// Mapping Byte 2/3 to stream/func to preserve data (e.g. Select Status)\n\t\t\tstream = byte2;\n\t\t\tfunc = byte3;\n\t\t\t// deviceId is usually ignored or 0xFFFF, but we keep what we read\n\t\t}\n\n\t\treturn new HsmsMessage(\n\t\t\tstream,\n\t\t\tfunc,\n\t\t\twBit,\n\t\t\tbody,\n\t\t\tsystemBytes,\n\t\t\tdeviceId,\n\t\t\tpType,\n\t\t\tsType,\n\t\t);\n\t}\n\n\t// Factory methods for Control Messages\n\t/**\n\t * @description Creates a Select Request message.\n\t * @param systemBytes The system bytes of the message.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic selectReq(systemBytes: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\t0,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\tsystemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.SelectReq,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Select Response message.\n\t * @param req The Select Request message.\n\t * @param status The status of the response.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic selectRsp(req: HsmsMessage, status: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\tstatus,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\treq.systemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.SelectRsp,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Deselect Request message.\n\t * @param systemBytes The system bytes of the message.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic deselectReq(systemBytes: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\t0,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\tsystemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.DeselectReq,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Deselect Response message.\n\t * @param req The Deselect Request message.\n\t * @param status The status of the response.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic deselectRsp(req: HsmsMessage, status: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\tstatus,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\treq.systemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.DeselectRsp,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Link Test Request message.\n\t * @param systemBytes The system bytes of the message.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic linkTestReq(systemBytes: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\t0,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\tsystemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.LinkTestReq,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Link Test Response message.\n\t * @param req The Link Test Request message.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic linkTestRsp(req: HsmsMessage): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\t0,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\treq.systemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.LinkTestRsp,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Reject Request message.\n\t * @param req The message that triggered the reject.\n\t * @param reason The reason for the reject.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic rejectReq(req: HsmsMessage, reason: RejectReason): HsmsMessage {\n\t\t// Byte 2 (Stream) should be the sType of the rejected message if PType is not supported\n\t\t// But typically it's just mirroring or specific logic.\n\t\t// Python: b2 = h10bytes[4] (PType) if reason == NOT_SUPPORT_TYPE_P else h10bytes[5] (SType)\n\t\tconst b2 = reason === RejectReason.NotSupportTypeP ? req.pType : req.sType;\n\t\treturn new HsmsMessage(\n\t\t\tb2,\n\t\t\treason,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\treq.systemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.RejectReq,\n\t\t);\n\t}\n\n\t/**\n\t * @description Creates a Separate Request message.\n\t * @param systemBytes The system bytes of the message.\n\t * @returns The HsmsMessage object.\n\t */\n\tstatic separateReq(systemBytes: number): HsmsMessage {\n\t\treturn new HsmsMessage(\n\t\t\t0,\n\t\t\t0,\n\t\t\tfalse,\n\t\t\tnull,\n\t\t\tsystemBytes,\n\t\t\t0xffff,\n\t\t\t0,\n\t\t\tHsmsControlType.SeparateReq,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HsmsMessage.js","names":["body: AbstractSecs2Item | null"],"sources":["../../src/hsms/HsmsMessage.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\r\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\r\nimport { HsmsControlType } from \"./enums/HsmsControlType.js\";\r\nimport { RejectReason } from \"./enums/RejectReason.js\";\r\nimport { Secs2ItemParser } from \"../core/secs2item/Secs2ItemParser.js\";\r\n\r\n/**\r\n * @description HsmsMessage is the class that represents an HSMS message.\r\n * @param stream The stream number of the message.\r\n * @param func The function number of the message.\r\n * @param wBit The W-Bit of the message.\r\n * @param body The body of the message.\r\n * @param systemBytes The system bytes of the message.\r\n * @param deviceId The device ID of the message.\r\n * @param pType The P-Type of the message.\r\n * @param sType The S-Type of the message.\r\n */\r\nexport class HsmsMessage extends SecsMessage {\r\n\tconstructor(\r\n\t\tstream: number,\r\n\t\tfunc: number,\r\n\t\twBit: boolean,\r\n\t\tbody: AbstractSecs2Item | null,\r\n\t\tsystemBytes: number,\r\n\t\tdeviceId: number,\r\n\t\tpublic readonly pType = 0,\r\n\t\tpublic readonly sType = 0,\r\n\t) {\r\n\t\tsuper(stream, func, wBit, body, systemBytes, deviceId);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Encodes the message to a buffer (Length + Header + Body).\r\n\t */\r\n\ttoBuffer(): Buffer {\r\n\t\tconst bodyBuffer = this.body ? this.body.toBuffer() : Buffer.alloc(0);\r\n\t\tconst header = Buffer.alloc(10);\r\n\r\n\t\t// Byte 0-1: Session ID (Device ID) or 0xFFFF\r\n\t\tif (this.isDataMessage()) {\r\n\t\t\theader.writeUInt16BE(this.deviceId, 0);\r\n\t\t} else {\r\n\t\t\theader.writeUInt16BE(0xffff, 0);\r\n\t\t}\r\n\r\n\t\t// Byte 2: Stream / WBit (only for Data)\r\n\t\t// Byte 3: Function (only for Data)\r\n\t\tif (this.isDataMessage()) {\r\n\t\t\tlet b2 = this.stream;\r\n\t\t\tif (this.wBit) {\r\n\t\t\t\tb2 |= 0x80;\r\n\t\t\t}\r\n\t\t\theader.writeUInt8(b2, 2);\r\n\t\t\theader.writeUInt8(this.func, 3);\r\n\t\t} else {\r\n\t\t\t// For control messages, byte 2 and 3 depend on type\r\n\t\t\t// But typically 0 unless SelectStatus/RejectReason\r\n\t\t\t// We will handle specific construction in static methods\r\n\t\t\t// Here we assume pType and sType are set correctly for Control Messages\r\n\t\t\t// Wait, P-Type is Byte 4, S-Type is Byte 5.\r\n\t\t\t// Byte 2 and 3 are 0 for most control messages.\r\n\t\t\t// EXCEPT Select Response (Byte 3 = Status) and Reject (Byte 3 = Reason).\r\n\t\t\t// But those are stored in 'sType' or 'func' or similar?\r\n\t\t\t// In this class, I store pType and sType (Byte 4, 5).\r\n\t\t\t// Where do I store Byte 2 and 3 for Control Messages?\r\n\t\t\t// The Python code:\r\n\t\t\t// Select Response: h10bytes = ... 0x00, select_status ...\r\n\t\t\t// Reject: ... b2, reject_reason ...\r\n\r\n\t\t\t// I should allow passing byte 2 and 3 specifically or handle it via polymorphism.\r\n\t\t\t// To keep it simple, I will use 'stream' and 'func' to hold Byte 2 and 3 for Control Messages too,\r\n\t\t\t// even if they don't mean Stream/Function.\r\n\r\n\t\t\theader.writeUInt8(this.stream, 2);\r\n\t\t\theader.writeUInt8(this.func, 3);\r\n\t\t}\r\n\r\n\t\t// Byte 4: P-Type\r\n\t\theader.writeUInt8(this.pType, 4);\r\n\r\n\t\t// Byte 5: S-Type\r\n\t\theader.writeUInt8(this.sType, 5);\r\n\r\n\t\t// Byte 6-9: System Bytes\r\n\t\theader.writeUInt32BE(this.systemBytes, 6);\r\n\r\n\t\tconst length = header.length + bodyBuffer.length;\r\n\t\tconst lengthBuffer = Buffer.alloc(4);\r\n\t\tlengthBuffer.writeUInt32BE(length, 0);\r\n\r\n\t\treturn Buffer.concat([lengthBuffer, header, bodyBuffer]);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Checks if the message is a data message.\r\n\t * @description data message: deviceId not 0xFF 0xFF and header[4] and header[5] are 0.\r\n\t * @returns True if the message is a data message, false otherwise.\r\n\t */\r\n\tisDataMessage(): boolean {\r\n\t\treturn (this.sType as HsmsControlType) === HsmsControlType.Data;\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates an HsmsMessage from a buffer.\r\n\t * @param buffer The buffer containing the message data.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic fromBuffer(buffer: Buffer): HsmsMessage {\r\n\t\tif (buffer.length < 14) {\r\n\t\t\tthrow new Error(\"Buffer too short for HSMS message\");\r\n\t\t}\r\n\r\n\t\t// Bytes 0-3: Length (ignored here, assumed to be correct or handled by framer)\r\n\t\tconst header = buffer.subarray(4, 14);\r\n\t\tconst bodyBuffer = buffer.subarray(14);\r\n\r\n\t\tconst sessionId = header.readUInt16BE(0);\r\n\t\tconst byte2 = header.readUInt8(2);\r\n\t\tconst byte3 = header.readUInt8(3);\r\n\t\tconst pType = header.readUInt8(4);\r\n\t\tconst sType = header.readUInt8(5);\r\n\t\tconst systemBytes = header.readUInt32BE(6);\r\n\r\n\t\tlet stream = 0;\r\n\t\tlet func = 0;\r\n\t\tlet wBit = false;\r\n\t\tconst deviceId = sessionId;\r\n\t\tlet body: AbstractSecs2Item | null = null;\r\n\r\n\t\tif ((sType as HsmsControlType) === HsmsControlType.Data) {\r\n\t\t\tstream = byte2 & 0x7f;\r\n\t\t\twBit = (byte2 & 0x80) !== 0;\r\n\t\t\tfunc = byte3;\r\n\r\n\t\t\tif (bodyBuffer.length > 0) {\r\n\t\t\t\tconst result = Secs2ItemParser.fromBuffer(bodyBuffer);\r\n\t\t\t\tbody = result.item;\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// Control Message\r\n\t\t\t// Mapping Byte 2/3 to stream/func to preserve data (e.g. Select Status)\r\n\t\t\tstream = byte2;\r\n\t\t\tfunc = byte3;\r\n\t\t\t// deviceId is usually ignored or 0xFFFF, but we keep what we read\r\n\t\t}\r\n\r\n\t\treturn new HsmsMessage(\r\n\t\t\tstream,\r\n\t\t\tfunc,\r\n\t\t\twBit,\r\n\t\t\tbody,\r\n\t\t\tsystemBytes,\r\n\t\t\tdeviceId,\r\n\t\t\tpType,\r\n\t\t\tsType,\r\n\t\t);\r\n\t}\r\n\r\n\t// Factory methods for Control Messages\r\n\t/**\r\n\t * @description Creates a Select Request message.\r\n\t * @param systemBytes The system bytes of the message.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic selectReq(systemBytes: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\tsystemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.SelectReq,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Select Response message.\r\n\t * @param req The Select Request message.\r\n\t * @param status The status of the response.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic selectRsp(req: HsmsMessage, status: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\tstatus,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\treq.systemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.SelectRsp,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Deselect Request message.\r\n\t * @param systemBytes The system bytes of the message.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic deselectReq(systemBytes: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\tsystemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.DeselectReq,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Deselect Response message.\r\n\t * @param req The Deselect Request message.\r\n\t * @param status The status of the response.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic deselectRsp(req: HsmsMessage, status: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\tstatus,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\treq.systemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.DeselectRsp,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Link Test Request message.\r\n\t * @param systemBytes The system bytes of the message.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic linkTestReq(systemBytes: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\tsystemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.LinkTestReq,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Link Test Response message.\r\n\t * @param req The Link Test Request message.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic linkTestRsp(req: HsmsMessage): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\treq.systemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.LinkTestRsp,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Reject Request message.\r\n\t * @param req The message that triggered the reject.\r\n\t * @param reason The reason for the reject.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic rejectReq(req: HsmsMessage, reason: RejectReason): HsmsMessage {\r\n\t\t// Byte 2 (Stream) should be the sType of the rejected message if PType is not supported\r\n\t\t// But typically it's just mirroring or specific logic.\r\n\t\t// Python: b2 = h10bytes[4] (PType) if reason == NOT_SUPPORT_TYPE_P else h10bytes[5] (SType)\r\n\t\tconst b2 = reason === RejectReason.NotSupportTypeP ? req.pType : req.sType;\r\n\t\treturn new HsmsMessage(\r\n\t\t\tb2,\r\n\t\t\treason,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\treq.systemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.RejectReq,\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Creates a Separate Request message.\r\n\t * @param systemBytes The system bytes of the message.\r\n\t * @returns The HsmsMessage object.\r\n\t */\r\n\tstatic separateReq(systemBytes: number): HsmsMessage {\r\n\t\treturn new HsmsMessage(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tfalse,\r\n\t\t\tnull,\r\n\t\t\tsystemBytes,\r\n\t\t\t0xffff,\r\n\t\t\t0,\r\n\t\t\tHsmsControlType.SeparateReq,\r\n\t\t);\r\n\t}\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HsmsPassiveCommunicator.js","names":["resolveFirstListen: (() => void) | null","t8Timer: NodeJS.Timeout | null","t7Timer: NodeJS.Timeout | null","msg: HsmsMessage"],"sources":["../../src/hsms/HsmsPassiveCommunicator.ts"],"sourcesContent":["import { Server, Socket, createServer } from \"net\";\nimport {\n\tHsmsCommunicator,\n\tHsmsCommunicatorConfig,\n\tHsmsState,\n} from \"./HsmsCommunicator.js\";\nimport { HsmsMessage } from \"./HsmsMessage.js\";\nimport { HsmsControlType } from \"./enums/HsmsControlType.js\";\nimport { SelectStatus } from \"./enums/SelectStatus.js\";\nimport { RejectReason } from \"./enums/RejectReason.js\";\n\nexport interface HsmsPassiveCommunicatorConfig extends HsmsCommunicatorConfig {\n\ttimeoutRebind?: number;\n}\n\n/**\n * @description HsmsPassiveCommunicator is the class that represents an HSMS passive communicator.\n */\nexport class HsmsPassiveCommunicator extends HsmsCommunicator {\n\tprivate server: Server | null = null;\n\tprivate shouldStop = false;\n\tprivate serverLoopPromise: Promise<void> | null = null;\n\tprivate timeoutRebind = 5;\n\n\tconstructor(config: HsmsPassiveCommunicatorConfig) {\n\t\tsuper(config);\n\t\tif (config.timeoutRebind !== undefined) {\n\t\t\tthis.timeoutRebind = config.timeoutRebind;\n\t\t}\n\t}\n\n\t/**\n\t * @description Opens the passive communicator.\n\t */\n\tasync open(): Promise<void> {\n\t\tif (this.serverLoopPromise) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.shouldStop = false;\n\n\t\tlet resolveFirstListen: (() => void) | null = null;\n\t\tconst firstListen = new Promise<void>((resolve) => {\n\t\t\tresolveFirstListen = resolve;\n\t\t});\n\n\t\tthis.serverLoopPromise = this.runServerLoop(() => {\n\t\t\tresolveFirstListen?.();\n\t\t\tresolveFirstListen = null;\n\t\t}).catch((err: unknown) => {\n\t\t\tthis.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n\t\t});\n\n\t\tawait firstListen;\n\t}\n\n\t/**\n\t * @description Runs the server loop.\n\t * @param onFirstListening The callback function to be called when the first listening is completed.\n\t * @returns A Promise that resolves when the server loop is stopped.\n\t */\n\tprivate async runServerLoop(onFirstListening: () => void): Promise<void> {\n\t\tlet first = true;\n\t\twhile (!this.shouldStop) {\n\t\t\ttry {\n\t\t\t\tawait this.listenOnce(first ? onFirstListening : null);\n\t\t\t} catch (err) {\n\t\t\t\tif (!this.shouldStop) {\n\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\terr instanceof Error ? err : new Error(String(err)),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!this.shouldStop) {\n\t\t\t\t\tawait new Promise((resolve) =>\n\t\t\t\t\t\tsetTimeout(resolve, this.timeoutRebind * 1000),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfirst = false;\n\t\t\tif (this.shouldStop) return;\n\t\t}\n\t}\n\n\t/**\n\t * @description Listens for a single connection.\n\t * @param onListening The callback function to be called when the connection is established.\n\t * @returns A Promise that resolves when the connection is closed.\n\t */\n\tprivate async listenOnce(onListening: (() => void) | null): Promise<void> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst server = createServer((socket) => {\n\t\t\t\tvoid this.handleIncomingSocket(socket);\n\t\t\t});\n\t\t\tthis.server = server;\n\n\t\t\tlet hasConnected = false;\n\t\t\tlet settled = false;\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tserver.removeListener(\"error\", onServerError);\n\t\t\t\tserver.removeListener(\"close\", onServerClose);\n\t\t\t\tthis.removeListener(\"connected\", onConnected);\n\t\t\t\tthis.removeListener(\"disconnected\", onDisconnected);\n\t\t\t};\n\n\t\t\tconst finish = (err?: Error) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tif (this.server === server) {\n\t\t\t\t\tthis.server = null;\n\t\t\t\t}\n\t\t\t\tcleanup();\n\t\t\t\tif (err) reject(err);\n\t\t\t\telse resolve();\n\t\t\t};\n\n\t\t\tconst closeServer = () => {\n\t\t\t\tif (settled) return;\n\t\t\t\tif (!server.listening) {\n\t\t\t\t\tfinish();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tserver.close((err) => {\n\t\t\t\t\tif (err) finish(err);\n\t\t\t\t\telse finish();\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst onServerError = (err: Error) => {\n\t\t\t\tfinish(err);\n\t\t\t};\n\n\t\t\tconst onServerClose = () => {\n\t\t\t\tfinish();\n\t\t\t};\n\n\t\t\tconst onConnected = () => {\n\t\t\t\thasConnected = true;\n\t\t\t};\n\n\t\t\tconst onDisconnected = () => {\n\t\t\t\tif (!hasConnected) return;\n\t\t\t\tcloseServer();\n\t\t\t};\n\n\t\t\tserver.on(\"error\", onServerError);\n\t\t\tserver.on(\"close\", onServerClose);\n\t\t\tthis.on(\"connected\", onConnected);\n\t\t\tthis.on(\"disconnected\", onDisconnected);\n\n\t\t\tserver.listen(this.port, this.ip, () => {\n\t\t\t\tonListening?.();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * @description Handles an incoming socket connection.\n\t * @param socket The socket object representing the incoming connection.\n\t * @returns A Promise that resolves when the socket connection is handled.\n\t */\n\tprivate async handleIncomingSocket(socket: Socket): Promise<void> {\n\t\tsocket.setNoDelay(true);\n\n\t\tif (!this.socket || this.socket.destroyed) {\n\t\t\tconst promoted = await this.handleSocketUntilSelected(socket);\n\t\t\tif (promoted) return;\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.handleSocketUntilSelected(socket);\n\t}\n\n\t/**\n\t * @description Handles a socket connection until it is selected.\n\t * @param socket The socket object representing the connection.\n\t * @returns A Promise that resolves with a boolean indicating whether the socket was promoted to selected.\n\t */\n\tprivate async handleSocketUntilSelected(socket: Socket): Promise<boolean> {\n\t\tlet buffer = Buffer.alloc(0);\n\t\tlet t8Timer: NodeJS.Timeout | null = null;\n\t\tlet t7Timer: NodeJS.Timeout | null = null;\n\n\t\tconst clearT8 = () => {\n\t\t\tif (t8Timer) {\n\t\t\t\tclearTimeout(t8Timer);\n\t\t\t\tt8Timer = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetT8 = () => {\n\t\t\tclearT8();\n\t\t\tif (this.timeoutT8 <= 0) return;\n\t\t\tt8Timer = setTimeout(() => {\n\t\t\t\tt8Timer = null;\n\t\t\t\tif (buffer.length > 0 && !socket.destroyed) {\n\t\t\t\t\tthis.emit(\"error\", new Error(\"T8 Timeout\"));\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t}\n\t\t\t}, this.timeoutT8 * 1000);\n\t\t};\n\n\t\tconst clearT7 = () => {\n\t\t\tif (t7Timer) {\n\t\t\t\tclearTimeout(t7Timer);\n\t\t\t\tt7Timer = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetT7 = () => {\n\t\t\tclearT7();\n\t\t\tif (this.timeoutT7 <= 0) return;\n\t\t\tt7Timer = setTimeout(() => {\n\t\t\t\tt7Timer = null;\n\t\t\t\tif (!socket.destroyed) {\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t}\n\t\t\t}, this.timeoutT7 * 1000);\n\t\t};\n\n\t\tconst sendSocketBuffer = async (buf: Buffer): Promise<void> => {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tsocket.write(buf, (err) => {\n\t\t\t\t\tif (err) reject(err);\n\t\t\t\t\telse resolve();\n\t\t\t\t});\n\t\t\t});\n\t\t};\n\n\t\tconst cleanup = () => {\n\t\t\tclearT8();\n\t\t\tclearT7();\n\t\t};\n\n\t\tconst promoteToSelected = (selectReq: HsmsMessage) => {\n\t\t\tsocket.removeListener(\"data\", onData);\n\t\t\tsocket.removeListener(\"close\", onClose);\n\t\t\tsocket.removeListener(\"end\", onEnd);\n\t\t\tsocket.removeListener(\"error\", onError);\n\t\t\tcleanup();\n\t\t\tthis.handleSocketEvents(socket);\n\t\t\tthis.handleSelectReq(selectReq);\n\t\t};\n\n\t\tconst handlePreSelectedMessage = async (msg: HsmsMessage) => {\n\t\t\tswitch (msg.sType as HsmsControlType) {\n\t\t\t\tcase HsmsControlType.Data: {\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(msg, RejectReason.NotSelected);\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase HsmsControlType.LinkTestReq: {\n\t\t\t\t\tconst rsp = HsmsMessage.linkTestRsp(msg);\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase HsmsControlType.SeparateReq: {\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase HsmsControlType.SelectReq: {\n\t\t\t\t\tif (\n\t\t\t\t\t\tthis.socket &&\n\t\t\t\t\t\t!this.socket.destroyed &&\n\t\t\t\t\t\tthis.state === HsmsState.Selected\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst rsp = HsmsMessage.selectRsp(msg, SelectStatus.AlreadyUsed);\n\t\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpromoteToSelected(msg);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase HsmsControlType.SelectRsp:\n\t\t\t\tcase HsmsControlType.LinkTestRsp: {\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(\n\t\t\t\t\t\tmsg,\n\t\t\t\t\t\tRejectReason.TransactionNotOpen,\n\t\t\t\t\t);\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase HsmsControlType.RejectReq: {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\tconst reason =\n\t\t\t\t\t\tmsg.pType !== 0\n\t\t\t\t\t\t\t? RejectReason.NotSupportTypeP\n\t\t\t\t\t\t\t: RejectReason.NotSupportTypeS;\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(msg, reason);\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tlet processing = false;\n\t\tconst processIncoming = async () => {\n\t\t\tif (processing) return;\n\t\t\tprocessing = true;\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tif (buffer.length < 4) return;\n\t\t\t\t\tconst length = buffer.readUInt32BE(0);\n\t\t\t\t\tif (length < 10) {\n\t\t\t\t\t\tthis.emit(\"error\", new Error(\"Receive message size < 10\"));\n\t\t\t\t\t\tbuffer = Buffer.alloc(0);\n\t\t\t\t\t\tsocket.destroy();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (buffer.length < 4 + length) return;\n\n\t\t\t\t\tconst msgBuffer = buffer.subarray(0, 4 + length);\n\t\t\t\t\tbuffer = buffer.subarray(4 + length);\n\t\t\t\t\tif (buffer.length === 0) clearT8();\n\n\t\t\t\t\tlet msg: HsmsMessage;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmsg = HsmsMessage.fromBuffer(msgBuffer);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\terr instanceof Error ? err : new Error(String(err)),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsocket.destroy();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tresetT7();\n\t\t\t\t\tawait handlePreSelectedMessage(msg);\n\n\t\t\t\t\tif (this.socket === socket && this.state === HsmsState.Selected) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n\t\t\t\tsocket.destroy();\n\t\t\t} finally {\n\t\t\t\tprocessing = false;\n\t\t\t}\n\t\t};\n\n\t\tconst onData = (data: Buffer) => {\n\t\t\tbuffer = Buffer.concat([buffer, data]);\n\t\t\tresetT8();\n\t\t\tvoid processIncoming();\n\t\t};\n\n\t\tconst onClose = () => {\n\t\t\tcleanup();\n\t\t};\n\n\t\tconst onEnd = () => {\n\t\t\tif (!socket.destroyed) {\n\t\t\t\tsocket.destroy();\n\t\t\t}\n\t\t};\n\n\t\tconst onError = (err: Error) => {\n\t\t\tthis.emit(\"error\", err);\n\t\t};\n\n\t\tsocket.on(\"data\", onData);\n\t\tsocket.on(\"close\", onClose);\n\t\tsocket.on(\"end\", onEnd);\n\t\tsocket.on(\"error\", onError);\n\t\tresetT7();\n\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tsocket.once(\"close\", () => resolve());\n\t\t});\n\n\t\treturn this.socket === socket && this.state === HsmsState.Selected;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.shouldStop = true;\n\t\t// Close client connection first\n\t\tif (this.socket) {\n\t\t\tthis.socket.end();\n\t\t\tthis.socket.destroy();\n\t\t\tthis.socket = null;\n\t\t}\n\n\t\t// Stop listening\n\t\tconst server = this.server;\n\t\tif (server) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tserver.close((err) => {\n\t\t\t\t\tthis.server = null;\n\t\t\t\t\tif (err) reject(err);\n\t\t\t\t\telse resolve();\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tif (this.serverLoopPromise) {\n\t\t\tconst loop = this.serverLoopPromise;\n\t\t\tthis.serverLoopPromise = null;\n\t\t\tawait loop;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HsmsPassiveCommunicator.js","names":["resolveFirstListen: (() => void) | null","t8Timer: NodeJS.Timeout | null","t7Timer: NodeJS.Timeout | null","msg: HsmsMessage"],"sources":["../../src/hsms/HsmsPassiveCommunicator.ts"],"sourcesContent":["import { Server, Socket, createServer } from \"net\";\r\nimport {\r\n\tHsmsCommunicator,\r\n\tHsmsCommunicatorConfig,\r\n\tHsmsState,\r\n} from \"./HsmsCommunicator.js\";\r\nimport { HsmsMessage } from \"./HsmsMessage.js\";\r\nimport { HsmsControlType } from \"./enums/HsmsControlType.js\";\r\nimport { SelectStatus } from \"./enums/SelectStatus.js\";\r\nimport { RejectReason } from \"./enums/RejectReason.js\";\r\n\r\nexport interface HsmsPassiveCommunicatorConfig extends HsmsCommunicatorConfig {\r\n\ttimeoutRebind?: number;\r\n}\r\n\r\n/**\r\n * @description HsmsPassiveCommunicator is the class that represents an HSMS passive communicator.\r\n */\r\nexport class HsmsPassiveCommunicator extends HsmsCommunicator {\r\n\tprivate server: Server | null = null;\r\n\tprivate shouldStop = false;\r\n\tprivate serverLoopPromise: Promise<void> | null = null;\r\n\tprivate timeoutRebind = 5;\r\n\r\n\tconstructor(config: HsmsPassiveCommunicatorConfig) {\r\n\t\tsuper(config);\r\n\t\tif (config.timeoutRebind !== undefined) {\r\n\t\t\tthis.timeoutRebind = config.timeoutRebind;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * @description Opens the passive communicator.\r\n\t */\r\n\tasync open(): Promise<void> {\r\n\t\tif (this.serverLoopPromise) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.shouldStop = false;\r\n\r\n\t\tlet resolveFirstListen: (() => void) | null = null;\r\n\t\tconst firstListen = new Promise<void>((resolve) => {\r\n\t\t\tresolveFirstListen = resolve;\r\n\t\t});\r\n\r\n\t\tthis.serverLoopPromise = this.runServerLoop(() => {\r\n\t\t\tresolveFirstListen?.();\r\n\t\t\tresolveFirstListen = null;\r\n\t\t}).catch((err: unknown) => {\r\n\t\t\tthis.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\r\n\t\t});\r\n\r\n\t\tawait firstListen;\r\n\t}\r\n\r\n\t/**\r\n\t * @description Runs the server loop.\r\n\t * @param onFirstListening The callback function to be called when the first listening is completed.\r\n\t * @returns A Promise that resolves when the server loop is stopped.\r\n\t */\r\n\tprivate async runServerLoop(onFirstListening: () => void): Promise<void> {\r\n\t\tlet first = true;\r\n\t\twhile (!this.shouldStop) {\r\n\t\t\ttry {\r\n\t\t\t\tawait this.listenOnce(first ? onFirstListening : null);\r\n\t\t\t} catch (err) {\r\n\t\t\t\tif (!this.shouldStop) {\r\n\t\t\t\t\tthis.emit(\r\n\t\t\t\t\t\t\"error\",\r\n\t\t\t\t\t\terr instanceof Error ? err : new Error(String(err)),\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t\tif (!this.shouldStop) {\r\n\t\t\t\t\tawait new Promise((resolve) =>\r\n\t\t\t\t\t\tsetTimeout(resolve, this.timeoutRebind * 1000),\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfirst = false;\r\n\t\t\tif (this.shouldStop) return;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * @description Listens for a single connection.\r\n\t * @param onListening The callback function to be called when the connection is established.\r\n\t * @returns A Promise that resolves when the connection is closed.\r\n\t */\r\n\tprivate async listenOnce(onListening: (() => void) | null): Promise<void> {\r\n\t\treturn new Promise((resolve, reject) => {\r\n\t\t\tconst server = createServer((socket) => {\r\n\t\t\t\tvoid this.handleIncomingSocket(socket);\r\n\t\t\t});\r\n\t\t\tthis.server = server;\r\n\r\n\t\t\tlet hasConnected = false;\r\n\t\t\tlet settled = false;\r\n\r\n\t\t\tconst cleanup = () => {\r\n\t\t\t\tserver.removeListener(\"error\", onServerError);\r\n\t\t\t\tserver.removeListener(\"close\", onServerClose);\r\n\t\t\t\tthis.removeListener(\"connected\", onConnected);\r\n\t\t\t\tthis.removeListener(\"disconnected\", onDisconnected);\r\n\t\t\t};\r\n\r\n\t\t\tconst finish = (err?: Error) => {\r\n\t\t\t\tif (settled) return;\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tif (this.server === server) {\r\n\t\t\t\t\tthis.server = null;\r\n\t\t\t\t}\r\n\t\t\t\tcleanup();\r\n\t\t\t\tif (err) reject(err);\r\n\t\t\t\telse resolve();\r\n\t\t\t};\r\n\r\n\t\t\tconst closeServer = () => {\r\n\t\t\t\tif (settled) return;\r\n\t\t\t\tif (!server.listening) {\r\n\t\t\t\t\tfinish();\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tserver.close((err) => {\r\n\t\t\t\t\tif (err) finish(err);\r\n\t\t\t\t\telse finish();\r\n\t\t\t\t});\r\n\t\t\t};\r\n\r\n\t\t\tconst onServerError = (err: Error) => {\r\n\t\t\t\tfinish(err);\r\n\t\t\t};\r\n\r\n\t\t\tconst onServerClose = () => {\r\n\t\t\t\tfinish();\r\n\t\t\t};\r\n\r\n\t\t\tconst onConnected = () => {\r\n\t\t\t\thasConnected = true;\r\n\t\t\t};\r\n\r\n\t\t\tconst onDisconnected = () => {\r\n\t\t\t\tif (!hasConnected) return;\r\n\t\t\t\tcloseServer();\r\n\t\t\t};\r\n\r\n\t\t\tserver.on(\"error\", onServerError);\r\n\t\t\tserver.on(\"close\", onServerClose);\r\n\t\t\tthis.on(\"connected\", onConnected);\r\n\t\t\tthis.on(\"disconnected\", onDisconnected);\r\n\r\n\t\t\tserver.listen(this.port, this.ip, () => {\r\n\t\t\t\tonListening?.();\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * @description Handles an incoming socket connection.\r\n\t * @param socket The socket object representing the incoming connection.\r\n\t * @returns A Promise that resolves when the socket connection is handled.\r\n\t */\r\n\tprivate async handleIncomingSocket(socket: Socket): Promise<void> {\r\n\t\tsocket.setNoDelay(true);\r\n\r\n\t\tif (!this.socket || this.socket.destroyed) {\r\n\t\t\tconst promoted = await this.handleSocketUntilSelected(socket);\r\n\t\t\tif (promoted) return;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tawait this.handleSocketUntilSelected(socket);\r\n\t}\r\n\r\n\t/**\r\n\t * @description Handles a socket connection until it is selected.\r\n\t * @param socket The socket object representing the connection.\r\n\t * @returns A Promise that resolves with a boolean indicating whether the socket was promoted to selected.\r\n\t */\r\n\tprivate async handleSocketUntilSelected(socket: Socket): Promise<boolean> {\r\n\t\tlet buffer = Buffer.alloc(0);\r\n\t\tlet t8Timer: NodeJS.Timeout | null = null;\r\n\t\tlet t7Timer: NodeJS.Timeout | null = null;\r\n\r\n\t\tconst clearT8 = () => {\r\n\t\t\tif (t8Timer) {\r\n\t\t\t\tclearTimeout(t8Timer);\r\n\t\t\t\tt8Timer = null;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tconst resetT8 = () => {\r\n\t\t\tclearT8();\r\n\t\t\tif (this.timeoutT8 <= 0) return;\r\n\t\t\tt8Timer = setTimeout(() => {\r\n\t\t\t\tt8Timer = null;\r\n\t\t\t\tif (buffer.length > 0 && !socket.destroyed) {\r\n\t\t\t\t\tthis.emit(\"error\", new Error(\"T8 Timeout\"));\r\n\t\t\t\t\tsocket.destroy();\r\n\t\t\t\t}\r\n\t\t\t}, this.timeoutT8 * 1000);\r\n\t\t};\r\n\r\n\t\tconst clearT7 = () => {\r\n\t\t\tif (t7Timer) {\r\n\t\t\t\tclearTimeout(t7Timer);\r\n\t\t\t\tt7Timer = null;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tconst resetT7 = () => {\r\n\t\t\tclearT7();\r\n\t\t\tif (this.timeoutT7 <= 0) return;\r\n\t\t\tt7Timer = setTimeout(() => {\r\n\t\t\t\tt7Timer = null;\r\n\t\t\t\tif (!socket.destroyed) {\r\n\t\t\t\t\tsocket.destroy();\r\n\t\t\t\t}\r\n\t\t\t}, this.timeoutT7 * 1000);\r\n\t\t};\r\n\r\n\t\tconst sendSocketBuffer = async (buf: Buffer): Promise<void> => {\r\n\t\t\tawait new Promise<void>((resolve, reject) => {\r\n\t\t\t\tsocket.write(buf, (err) => {\r\n\t\t\t\t\tif (err) reject(err);\r\n\t\t\t\t\telse resolve();\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t\t};\r\n\r\n\t\tconst cleanup = () => {\r\n\t\t\tclearT8();\r\n\t\t\tclearT7();\r\n\t\t};\r\n\r\n\t\tconst promoteToSelected = (selectReq: HsmsMessage) => {\r\n\t\t\tsocket.removeListener(\"data\", onData);\r\n\t\t\tsocket.removeListener(\"close\", onClose);\r\n\t\t\tsocket.removeListener(\"end\", onEnd);\r\n\t\t\tsocket.removeListener(\"error\", onError);\r\n\t\t\tcleanup();\r\n\t\t\tthis.handleSocketEvents(socket);\r\n\t\t\tthis.handleSelectReq(selectReq);\r\n\t\t};\r\n\r\n\t\tconst handlePreSelectedMessage = async (msg: HsmsMessage) => {\r\n\t\t\tswitch (msg.sType as HsmsControlType) {\r\n\t\t\t\tcase HsmsControlType.Data: {\r\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(msg, RejectReason.NotSelected);\r\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcase HsmsControlType.LinkTestReq: {\r\n\t\t\t\t\tconst rsp = HsmsMessage.linkTestRsp(msg);\r\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcase HsmsControlType.SeparateReq: {\r\n\t\t\t\t\tsocket.destroy();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcase HsmsControlType.SelectReq: {\r\n\t\t\t\t\tif (\r\n\t\t\t\t\t\tthis.socket &&\r\n\t\t\t\t\t\t!this.socket.destroyed &&\r\n\t\t\t\t\t\tthis.state === HsmsState.Selected\r\n\t\t\t\t\t) {\r\n\t\t\t\t\t\tconst rsp = HsmsMessage.selectRsp(msg, SelectStatus.AlreadyUsed);\r\n\t\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tpromoteToSelected(msg);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcase HsmsControlType.SelectRsp:\r\n\t\t\t\tcase HsmsControlType.LinkTestRsp: {\r\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(\r\n\t\t\t\t\t\tmsg,\r\n\t\t\t\t\t\tRejectReason.TransactionNotOpen,\r\n\t\t\t\t\t);\r\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcase HsmsControlType.RejectReq: {\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tdefault: {\r\n\t\t\t\t\tconst reason =\r\n\t\t\t\t\t\tmsg.pType !== 0\r\n\t\t\t\t\t\t\t? RejectReason.NotSupportTypeP\r\n\t\t\t\t\t\t\t: RejectReason.NotSupportTypeS;\r\n\t\t\t\t\tconst rsp = HsmsMessage.rejectReq(msg, reason);\r\n\t\t\t\t\tawait sendSocketBuffer(rsp.toBuffer());\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tlet processing = false;\r\n\t\tconst processIncoming = async () => {\r\n\t\t\tif (processing) return;\r\n\t\t\tprocessing = true;\r\n\t\t\ttry {\r\n\t\t\t\twhile (true) {\r\n\t\t\t\t\tif (buffer.length < 4) return;\r\n\t\t\t\t\tconst length = buffer.readUInt32BE(0);\r\n\t\t\t\t\tif (length < 10) {\r\n\t\t\t\t\t\tthis.emit(\"error\", new Error(\"Receive message size < 10\"));\r\n\t\t\t\t\t\tbuffer = Buffer.alloc(0);\r\n\t\t\t\t\t\tsocket.destroy();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (buffer.length < 4 + length) return;\r\n\r\n\t\t\t\t\tconst msgBuffer = buffer.subarray(0, 4 + length);\r\n\t\t\t\t\tbuffer = buffer.subarray(4 + length);\r\n\t\t\t\t\tif (buffer.length === 0) clearT8();\r\n\r\n\t\t\t\t\tlet msg: HsmsMessage;\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tmsg = HsmsMessage.fromBuffer(msgBuffer);\r\n\t\t\t\t\t} catch (err) {\r\n\t\t\t\t\t\tthis.emit(\r\n\t\t\t\t\t\t\t\"error\",\r\n\t\t\t\t\t\t\terr instanceof Error ? err : new Error(String(err)),\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t\tsocket.destroy();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tresetT7();\r\n\t\t\t\t\tawait handlePreSelectedMessage(msg);\r\n\r\n\t\t\t\t\tif (this.socket === socket && this.state === HsmsState.Selected) {\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} catch (err) {\r\n\t\t\t\tthis.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\r\n\t\t\t\tsocket.destroy();\r\n\t\t\t} finally {\r\n\t\t\t\tprocessing = false;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tconst onData = (data: Buffer) => {\r\n\t\t\tbuffer = Buffer.concat([buffer, data]);\r\n\t\t\tresetT8();\r\n\t\t\tvoid processIncoming();\r\n\t\t};\r\n\r\n\t\tconst onClose = () => {\r\n\t\t\tcleanup();\r\n\t\t};\r\n\r\n\t\tconst onEnd = () => {\r\n\t\t\tif (!socket.destroyed) {\r\n\t\t\t\tsocket.destroy();\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tconst onError = (err: Error) => {\r\n\t\t\tthis.emit(\"error\", err);\r\n\t\t};\r\n\r\n\t\tsocket.on(\"data\", onData);\r\n\t\tsocket.on(\"close\", onClose);\r\n\t\tsocket.on(\"end\", onEnd);\r\n\t\tsocket.on(\"error\", onError);\r\n\t\tresetT7();\r\n\r\n\t\tawait new Promise<void>((resolve) => {\r\n\t\t\tsocket.once(\"close\", () => resolve());\r\n\t\t});\r\n\r\n\t\treturn this.socket === socket && this.state === HsmsState.Selected;\r\n\t}\r\n\r\n\tasync close(): Promise<void> {\r\n\t\tthis.shouldStop = true;\r\n\t\t// Close client connection first\r\n\t\tif (this.socket) {\r\n\t\t\tthis.socket.end();\r\n\t\t\tthis.socket.destroy();\r\n\t\t\tthis.socket = null;\r\n\t\t}\r\n\r\n\t\t// Stop listening\r\n\t\tconst server = this.server;\r\n\t\tif (server) {\r\n\t\t\tawait new Promise<void>((resolve, reject) => {\r\n\t\t\t\tserver.close((err) => {\r\n\t\t\t\t\tthis.server = null;\r\n\t\t\t\t\tif (err) reject(err);\r\n\t\t\t\t\telse resolve();\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tif (this.serverLoopPromise) {\r\n\t\t\tconst loop = this.serverLoopPromise;\r\n\t\t\tthis.serverLoopPromise = null;\r\n\t\t\tawait loop;\r\n\t\t}\r\n\t}\r\n}\r\n"],"mappings":";;;;;;;;;;;AAkBA,IAAa,0BAAb,cAA6C,iBAAiB;CAC7D,AAAQ,SAAwB;CAChC,AAAQ,aAAa;CACrB,AAAQ,oBAA0C;CAClD,AAAQ,gBAAgB;CAExB,YAAY,QAAuC;AAClD,QAAM,OAAO;AACb,MAAI,OAAO,kBAAkB,OAC5B,MAAK,gBAAgB,OAAO;;;;;CAO9B,MAAM,OAAsB;AAC3B,MAAI,KAAK,kBACR;AAGD,OAAK,aAAa;EAElB,IAAIA,qBAA0C;EAC9C,MAAM,cAAc,IAAI,SAAe,YAAY;AAClD,wBAAqB;IACpB;AAEF,OAAK,oBAAoB,KAAK,oBAAoB;AACjD,yBAAsB;AACtB,wBAAqB;IACpB,CAAC,OAAO,QAAiB;AAC1B,QAAK,KAAK,SAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC;IACtE;AAEF,QAAM;;;;;;;CAQP,MAAc,cAAc,kBAA6C;EACxE,IAAI,QAAQ;AACZ,SAAO,CAAC,KAAK,YAAY;AACxB,OAAI;AACH,UAAM,KAAK,WAAW,QAAQ,mBAAmB,KAAK;YAC9C,KAAK;AACb,QAAI,CAAC,KAAK,WACT,MAAK,KACJ,SACA,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CACnD;AAEF,QAAI,CAAC,KAAK,WACT,OAAM,IAAI,SAAS,YAClB,WAAW,SAAS,KAAK,gBAAgB,IAAK,CAC9C;;AAGH,WAAQ;AACR,OAAI,KAAK,WAAY;;;;;;;;CASvB,MAAc,WAAW,aAAiD;AACzE,SAAO,IAAI,SAAS,SAAS,WAAW;GACvC,MAAM,SAAS,cAAc,WAAW;AACvC,IAAK,KAAK,qBAAqB,OAAO;KACrC;AACF,QAAK,SAAS;GAEd,IAAI,eAAe;GACnB,IAAI,UAAU;GAEd,MAAM,gBAAgB;AACrB,WAAO,eAAe,SAAS,cAAc;AAC7C,WAAO,eAAe,SAAS,cAAc;AAC7C,SAAK,eAAe,aAAa,YAAY;AAC7C,SAAK,eAAe,gBAAgB,eAAe;;GAGpD,MAAM,UAAU,QAAgB;AAC/B,QAAI,QAAS;AACb,cAAU;AACV,QAAI,KAAK,WAAW,OACnB,MAAK,SAAS;AAEf,aAAS;AACT,QAAI,IAAK,QAAO,IAAI;QACf,UAAS;;GAGf,MAAM,oBAAoB;AACzB,QAAI,QAAS;AACb,QAAI,CAAC,OAAO,WAAW;AACtB,aAAQ;AACR;;AAED,WAAO,OAAO,QAAQ;AACrB,SAAI,IAAK,QAAO,IAAI;SACf,SAAQ;MACZ;;GAGH,MAAM,iBAAiB,QAAe;AACrC,WAAO,IAAI;;GAGZ,MAAM,sBAAsB;AAC3B,YAAQ;;GAGT,MAAM,oBAAoB;AACzB,mBAAe;;GAGhB,MAAM,uBAAuB;AAC5B,QAAI,CAAC,aAAc;AACnB,iBAAa;;AAGd,UAAO,GAAG,SAAS,cAAc;AACjC,UAAO,GAAG,SAAS,cAAc;AACjC,QAAK,GAAG,aAAa,YAAY;AACjC,QAAK,GAAG,gBAAgB,eAAe;AAEvC,UAAO,OAAO,KAAK,MAAM,KAAK,UAAU;AACvC,mBAAe;KACd;IACD;;;;;;;CAQH,MAAc,qBAAqB,QAA+B;AACjE,SAAO,WAAW,KAAK;AAEvB,MAAI,CAAC,KAAK,UAAU,KAAK,OAAO,WAAW;AAE1C,OADiB,MAAM,KAAK,0BAA0B,OAAO,CAC/C;AACd;;AAGD,QAAM,KAAK,0BAA0B,OAAO;;;;;;;CAQ7C,MAAc,0BAA0B,QAAkC;EACzE,IAAI,SAAS,OAAO,MAAM,EAAE;EAC5B,IAAIC,UAAiC;EACrC,IAAIC,UAAiC;EAErC,MAAM,gBAAgB;AACrB,OAAI,SAAS;AACZ,iBAAa,QAAQ;AACrB,cAAU;;;EAIZ,MAAM,gBAAgB;AACrB,YAAS;AACT,OAAI,KAAK,aAAa,EAAG;AACzB,aAAU,iBAAiB;AAC1B,cAAU;AACV,QAAI,OAAO,SAAS,KAAK,CAAC,OAAO,WAAW;AAC3C,UAAK,KAAK,yBAAS,IAAI,MAAM,aAAa,CAAC;AAC3C,YAAO,SAAS;;MAEf,KAAK,YAAY,IAAK;;EAG1B,MAAM,gBAAgB;AACrB,OAAI,SAAS;AACZ,iBAAa,QAAQ;AACrB,cAAU;;;EAIZ,MAAM,gBAAgB;AACrB,YAAS;AACT,OAAI,KAAK,aAAa,EAAG;AACzB,aAAU,iBAAiB;AAC1B,cAAU;AACV,QAAI,CAAC,OAAO,UACX,QAAO,SAAS;MAEf,KAAK,YAAY,IAAK;;EAG1B,MAAM,mBAAmB,OAAO,QAA+B;AAC9D,SAAM,IAAI,SAAe,SAAS,WAAW;AAC5C,WAAO,MAAM,MAAM,QAAQ;AAC1B,SAAI,IAAK,QAAO,IAAI;SACf,UAAS;MACb;KACD;;EAGH,MAAM,gBAAgB;AACrB,YAAS;AACT,YAAS;;EAGV,MAAM,qBAAqB,cAA2B;AACrD,UAAO,eAAe,QAAQ,OAAO;AACrC,UAAO,eAAe,SAAS,QAAQ;AACvC,UAAO,eAAe,OAAO,MAAM;AACnC,UAAO,eAAe,SAAS,QAAQ;AACvC,YAAS;AACT,QAAK,mBAAmB,OAAO;AAC/B,QAAK,gBAAgB,UAAU;;EAGhC,MAAM,2BAA2B,OAAO,QAAqB;AAC5D,WAAQ,IAAI,OAAZ;IACC,KAAK,gBAAgB;AAEpB,WAAM,iBADM,YAAY,UAAU,KAAK,aAAa,YAAY,CACrC,UAAU,CAAC;AACtC;IAED,KAAK,gBAAgB;AAEpB,WAAM,iBADM,YAAY,YAAY,IAAI,CACb,UAAU,CAAC;AACtC;IAED,KAAK,gBAAgB;AACpB,YAAO,SAAS;AAChB;IAED,KAAK,gBAAgB;AACpB,SACC,KAAK,UACL,CAAC,KAAK,OAAO,aACb,KAAK,UAAU,UAAU,SAGzB,OAAM,iBADM,YAAY,UAAU,KAAK,aAAa,YAAY,CACrC,UAAU,CAAC;SAEtC,mBAAkB,IAAI;AAEvB;IAED,KAAK,gBAAgB;IACrB,KAAK,gBAAgB;AAKpB,WAAM,iBAJM,YAAY,UACvB,KACA,aAAa,mBACb,CAC0B,UAAU,CAAC;AACtC;IAED,KAAK,gBAAgB,UACpB;IAED,SAAS;KACR,MAAM,SACL,IAAI,UAAU,IACX,aAAa,kBACb,aAAa;AAEjB,WAAM,iBADM,YAAY,UAAU,KAAK,OAAO,CACnB,UAAU,CAAC;AACtC;;;;EAKH,IAAI,aAAa;EACjB,MAAM,kBAAkB,YAAY;AACnC,OAAI,WAAY;AAChB,gBAAa;AACb,OAAI;AACH,WAAO,MAAM;AACZ,SAAI,OAAO,SAAS,EAAG;KACvB,MAAM,SAAS,OAAO,aAAa,EAAE;AACrC,SAAI,SAAS,IAAI;AAChB,WAAK,KAAK,yBAAS,IAAI,MAAM,4BAA4B,CAAC;AAC1D,eAAS,OAAO,MAAM,EAAE;AACxB,aAAO,SAAS;AAChB;;AAED,SAAI,OAAO,SAAS,IAAI,OAAQ;KAEhC,MAAM,YAAY,OAAO,SAAS,GAAG,IAAI,OAAO;AAChD,cAAS,OAAO,SAAS,IAAI,OAAO;AACpC,SAAI,OAAO,WAAW,EAAG,UAAS;KAElC,IAAIC;AACJ,SAAI;AACH,YAAM,YAAY,WAAW,UAAU;cAC/B,KAAK;AACb,WAAK,KACJ,SACA,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CACnD;AACD,aAAO,SAAS;AAChB;;AAGD,cAAS;AACT,WAAM,yBAAyB,IAAI;AAEnC,SAAI,KAAK,WAAW,UAAU,KAAK,UAAU,UAAU,SACtD;;YAGM,KAAK;AACb,SAAK,KAAK,SAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC,CAAC;AACvE,WAAO,SAAS;aACP;AACT,iBAAa;;;EAIf,MAAM,UAAU,SAAiB;AAChC,YAAS,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC;AACtC,YAAS;AACT,GAAK,iBAAiB;;EAGvB,MAAM,gBAAgB;AACrB,YAAS;;EAGV,MAAM,cAAc;AACnB,OAAI,CAAC,OAAO,UACX,QAAO,SAAS;;EAIlB,MAAM,WAAW,QAAe;AAC/B,QAAK,KAAK,SAAS,IAAI;;AAGxB,SAAO,GAAG,QAAQ,OAAO;AACzB,SAAO,GAAG,SAAS,QAAQ;AAC3B,SAAO,GAAG,OAAO,MAAM;AACvB,SAAO,GAAG,SAAS,QAAQ;AAC3B,WAAS;AAET,QAAM,IAAI,SAAe,YAAY;AACpC,UAAO,KAAK,eAAe,SAAS,CAAC;IACpC;AAEF,SAAO,KAAK,WAAW,UAAU,KAAK,UAAU,UAAU;;CAG3D,MAAM,QAAuB;AAC5B,OAAK,aAAa;AAElB,MAAI,KAAK,QAAQ;AAChB,QAAK,OAAO,KAAK;AACjB,QAAK,OAAO,SAAS;AACrB,QAAK,SAAS;;EAIf,MAAM,SAAS,KAAK;AACpB,MAAI,OACH,OAAM,IAAI,SAAe,SAAS,WAAW;AAC5C,UAAO,OAAO,QAAQ;AACrB,SAAK,SAAS;AACd,QAAI,IAAK,QAAO,IAAI;QACf,UAAS;KACb;IACD;AAGH,MAAI,KAAK,mBAAmB;GAC3B,MAAM,OAAO,KAAK;AAClB,QAAK,oBAAoB;AACzB,SAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HsmsControlType.js","names":[],"sources":["../../../src/hsms/enums/HsmsControlType.ts"],"sourcesContent":["export enum HsmsControlType {\n Data = 0,\n SelectReq = 1,\n SelectRsp = 2,\n DeselectReq = 3,\n DeselectRsp = 4,\n LinkTestReq = 5,\n LinkTestRsp = 6,\n RejectReq = 7,\n SeparateReq = 9,\n}\n"],"mappings":";AAAA,IAAY,8DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
1
|
+
{"version":3,"file":"HsmsControlType.js","names":[],"sources":["../../../src/hsms/enums/HsmsControlType.ts"],"sourcesContent":["export enum HsmsControlType {\r\n Data = 0,\r\n SelectReq = 1,\r\n SelectRsp = 2,\r\n DeselectReq = 3,\r\n DeselectRsp = 4,\r\n LinkTestReq = 5,\r\n LinkTestRsp = 6,\r\n RejectReq = 7,\r\n SeparateReq = 9,\r\n}\r\n"],"mappings":";AAAA,IAAY,8DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RejectReason.js","names":[],"sources":["../../../src/hsms/enums/RejectReason.ts"],"sourcesContent":["export enum RejectReason {\n NotSupportTypeS = 1,\n NotSupportTypeP = 2,\n TransactionNotOpen = 3,\n NotSelected = 4,\n Unknown = 0xff,\n}\n"],"mappings":";AAAA,IAAY,wDAAL;AACL;AACA;AACA;AACA;AACA"}
|
|
1
|
+
{"version":3,"file":"RejectReason.js","names":[],"sources":["../../../src/hsms/enums/RejectReason.ts"],"sourcesContent":["export enum RejectReason {\r\n NotSupportTypeS = 1,\r\n NotSupportTypeP = 2,\r\n TransactionNotOpen = 3,\r\n NotSelected = 4,\r\n Unknown = 0xff,\r\n}\r\n"],"mappings":";AAAA,IAAY,wDAAL;AACL;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectStatus.js","names":[],"sources":["../../../src/hsms/enums/SelectStatus.ts"],"sourcesContent":["export enum SelectStatus {\n Success = 0,\n Actived = 1,\n NotReady = 2,\n AlreadyUsed = 3,\n Unknown = 0xff,\n}\n"],"mappings":";AAAA,IAAY,wDAAL;AACL;AACA;AACA;AACA;AACA"}
|
|
1
|
+
{"version":3,"file":"SelectStatus.js","names":[],"sources":["../../../src/hsms/enums/SelectStatus.ts"],"sourcesContent":["export enum SelectStatus {\r\n Success = 0,\r\n Actived = 1,\r\n NotReady = 2,\r\n AlreadyUsed = 3,\r\n Unknown = 0xff,\r\n}\r\n"],"mappings":";AAAA,IAAY,wDAAL;AACL;AACA;AACA;AACA;AACA"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SecsItemType } from "./core/enums/SecsItemType.js";
|
|
2
2
|
import { AbstractSecs2Item } from "./core/secs2item/AbstractSecs2Item.js";
|
|
3
3
|
import { SecsMessage } from "./core/AbstractSecsMessage.js";
|
|
4
|
+
import { SecsLogDirection, SecsLogger, SecsLoggerConfig, SecsLoggerContext } from "./logging/SecsLogger.js";
|
|
4
5
|
import { AbstractSecsCommunicator, SecsCommunicatorConfig, SecsCommunicatorEvents } from "./core/AbstractSecsCommunicator.js";
|
|
5
6
|
import { Secs2ItemAscii } from "./core/secs2item/Secs2ItemAscii.js";
|
|
6
7
|
import { Secs2ItemBinary } from "./core/secs2item/Secs2ItemBinary.js";
|
|
@@ -25,4 +26,4 @@ import { Secs1Communicator, Secs1CommunicatorConfig } from "./secs1/Secs1Communi
|
|
|
25
26
|
import { Secs1OnTcpIpActiveCommunicator, Secs1OnTcpIpActiveCommunicatorConfig } from "./secs1/Secs1OnTcpIpActiveCommunicator.js";
|
|
26
27
|
import { Secs1OnTcpIpPassiveCommunicator, Secs1OnTcpIpPassiveCommunicatorConfig } from "./secs1/Secs1OnTcpIpPassiveCommunicator.js";
|
|
27
28
|
import { Secs1SerialCommunicator, Secs1SerialCommunicatorConfig } from "./secs1/Secs1SerialCommunicator.js";
|
|
28
|
-
export { A, AbstractSecs2Item, AbstractSecsCommunicator, B, BOOLEAN, Clock, ClockType, CommAck, F4, F8, Gem, HsmsActiveCommunicator, HsmsCommunicator, HsmsCommunicatorConfig, HsmsCommunicatorEvents, HsmsControlType, HsmsMessage, HsmsPassiveCommunicator, HsmsPassiveCommunicatorConfig, HsmsState, I1, I2, I4, I8, L, OflAck, OnlAck, RejectReason, Secs1Communicator, Secs1CommunicatorConfig, Secs1Message, Secs1MessageBlock, Secs1OnTcpIpActiveCommunicator, Secs1OnTcpIpActiveCommunicatorConfig, Secs1OnTcpIpPassiveCommunicator, Secs1OnTcpIpPassiveCommunicatorConfig, Secs1SerialCommunicator, Secs1SerialCommunicatorConfig, Secs2ItemAscii, Secs2ItemBinary, Secs2ItemBoolean, Secs2ItemFactory, Secs2ItemList, Secs2ItemNumeric, Secs2ItemParser, SecsCommunicatorConfig, SecsCommunicatorEvents, SecsItemType, SecsMessage, SelectStatus, TiAck, U1, U2, U4, U8 };
|
|
29
|
+
export { A, AbstractSecs2Item, AbstractSecsCommunicator, B, BOOLEAN, Clock, ClockType, CommAck, F4, F8, Gem, HsmsActiveCommunicator, HsmsCommunicator, HsmsCommunicatorConfig, HsmsCommunicatorEvents, HsmsControlType, HsmsMessage, HsmsPassiveCommunicator, HsmsPassiveCommunicatorConfig, HsmsState, I1, I2, I4, I8, L, OflAck, OnlAck, RejectReason, Secs1Communicator, Secs1CommunicatorConfig, Secs1Message, Secs1MessageBlock, Secs1OnTcpIpActiveCommunicator, Secs1OnTcpIpActiveCommunicatorConfig, Secs1OnTcpIpPassiveCommunicator, Secs1OnTcpIpPassiveCommunicatorConfig, Secs1SerialCommunicator, Secs1SerialCommunicatorConfig, Secs2ItemAscii, Secs2ItemBinary, Secs2ItemBoolean, Secs2ItemFactory, Secs2ItemList, Secs2ItemNumeric, Secs2ItemParser, SecsCommunicatorConfig, SecsCommunicatorEvents, SecsItemType, SecsLogDirection, SecsLogger, SecsLoggerConfig, SecsLoggerContext, SecsMessage, SelectStatus, TiAck, U1, U2, U4, U8 };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SecsMessage } from "./core/AbstractSecsMessage.js";
|
|
2
|
+
import { SecsLogger } from "./logging/SecsLogger.js";
|
|
2
3
|
import { AbstractSecsCommunicator } from "./core/AbstractSecsCommunicator.js";
|
|
3
4
|
import { AbstractSecs2Item } from "./core/secs2item/AbstractSecs2Item.js";
|
|
4
5
|
import { SecsItemType } from "./core/enums/SecsItemType.js";
|
|
@@ -26,4 +27,4 @@ import { Secs1SerialCommunicator } from "./secs1/Secs1SerialCommunicator.js";
|
|
|
26
27
|
import { Clock, ClockType } from "./gem/Clock.js";
|
|
27
28
|
import { CommAck, Gem, OflAck, OnlAck, TiAck } from "./gem/Gem.js";
|
|
28
29
|
|
|
29
|
-
export { A, AbstractSecs2Item, AbstractSecsCommunicator, B, BOOLEAN, Clock, ClockType, CommAck, F4, F8, Gem, HsmsActiveCommunicator, HsmsCommunicator, HsmsControlType, HsmsMessage, HsmsPassiveCommunicator, HsmsState, I1, I2, I4, I8, L, OflAck, OnlAck, RejectReason, Secs1Communicator, Secs1Message, Secs1MessageBlock, Secs1OnTcpIpActiveCommunicator, Secs1OnTcpIpPassiveCommunicator, Secs1SerialCommunicator, Secs2ItemAscii, Secs2ItemBinary, Secs2ItemBoolean, Secs2ItemFactory, Secs2ItemList, Secs2ItemNumeric, Secs2ItemParser, SecsItemType, SecsMessage, SelectStatus, TiAck, U1, U2, U4, U8 };
|
|
30
|
+
export { A, AbstractSecs2Item, AbstractSecsCommunicator, B, BOOLEAN, Clock, ClockType, CommAck, F4, F8, Gem, HsmsActiveCommunicator, HsmsCommunicator, HsmsControlType, HsmsMessage, HsmsPassiveCommunicator, HsmsState, I1, I2, I4, I8, L, OflAck, OnlAck, RejectReason, Secs1Communicator, Secs1Message, Secs1MessageBlock, Secs1OnTcpIpActiveCommunicator, Secs1OnTcpIpPassiveCommunicator, Secs1SerialCommunicator, Secs2ItemAscii, Secs2ItemBinary, Secs2ItemBoolean, Secs2ItemFactory, Secs2ItemList, Secs2ItemNumeric, Secs2ItemParser, SecsItemType, SecsLogger, SecsMessage, SelectStatus, TiAck, U1, U2, U4, U8 };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LevelWithSilent, Logger } from "pino";
|
|
2
|
+
|
|
3
|
+
//#region src/logging/SecsLogger.d.ts
|
|
4
|
+
type SecsLogDirection = "Received" | "Sent";
|
|
5
|
+
interface SecsLoggerConfig {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
console?: boolean;
|
|
8
|
+
baseDir?: string;
|
|
9
|
+
retentionDays?: number;
|
|
10
|
+
detailLevel?: LevelWithSilent;
|
|
11
|
+
secs2Level?: LevelWithSilent;
|
|
12
|
+
maxHexBytes?: number;
|
|
13
|
+
}
|
|
14
|
+
interface SecsLoggerContext {
|
|
15
|
+
name: string;
|
|
16
|
+
deviceId: number;
|
|
17
|
+
isEquip: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare class SecsLogger {
|
|
20
|
+
static disabled(): SecsLogger;
|
|
21
|
+
static create(config: SecsLoggerConfig | undefined, ctx: SecsLoggerContext): SecsLogger;
|
|
22
|
+
readonly detail: Logger;
|
|
23
|
+
private readonly secs2;
|
|
24
|
+
private readonly detailStream;
|
|
25
|
+
private readonly secs2Target;
|
|
26
|
+
private readonly secs2Stream;
|
|
27
|
+
private readonly maxHexBytes;
|
|
28
|
+
private constructor();
|
|
29
|
+
logSecs2(direction: SecsLogDirection, sml: string): void;
|
|
30
|
+
logBytes(direction: SecsLogDirection, protocol: string, buffer: Buffer, meta?: Record<string, unknown>): void;
|
|
31
|
+
logState(protocol: string, prev: string, next: string, meta?: Record<string, unknown>): void;
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { SecsLogDirection, SecsLogger, SecsLoggerConfig, SecsLoggerContext };
|
|
36
|
+
//# sourceMappingURL=SecsLogger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecsLogger.d.ts","names":[],"sources":["../../src/logging/SecsLogger.ts"],"sourcesContent":[],"mappings":";;;KAKY,gBAAA;UAEK,gBAAA;EAFL,OAAA,CAAA,EAAA,OAAA;EAEK,OAAA,CAAA,EAAA,OAAA;EAUA,OAAA,CAAA,EAAA,MAAA;EAubJ,aAAU,CAAA,EAAA,MAAA;EACH,WAAA,CAAA,EA7bL,eA6bK;EAKV,UAAA,CAAA,EAjcI,eAicJ;EACH,WAAA,CAAA,EAAA,MAAA;;AA4EkB,UA1gBR,iBAAA,CA0gBQ;EAuBJ,IAAA,EAAA,MAAA;EAQR,QAAA,EAAA,MAAA;EAEH,OAAA,EAAA,OAAA;;AAmBD,cAvII,UAAA,CAuIJ;EAAM,OAAA,QAAA,CAAA,CAAA,EAtIK,UAsIL;wBAjIL,mCACH,oBACH;mBA2EqB;;;;;;;sBAuBJ;sBAQR,4CAEH,eACD;gEAkBA"}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { Transform, Writable } from "stream";
|
|
4
|
+
import pino from "pino";
|
|
5
|
+
|
|
6
|
+
//#region src/logging/SecsLogger.ts
|
|
7
|
+
function formatDate(date) {
|
|
8
|
+
const y = date.getFullYear();
|
|
9
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
10
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
11
|
+
return `${String(y)}-${m}-${d}`;
|
|
12
|
+
}
|
|
13
|
+
function formatDateTime(date) {
|
|
14
|
+
return `${formatDate(date)} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}.${String(date.getMilliseconds()).padStart(3, "0")}`;
|
|
15
|
+
}
|
|
16
|
+
function tryParseYmdDirName(dirName) {
|
|
17
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dirName);
|
|
18
|
+
if (!m) return null;
|
|
19
|
+
const year = Number(m[1]);
|
|
20
|
+
const month = Number(m[2]);
|
|
21
|
+
const day = Number(m[3]);
|
|
22
|
+
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) return null;
|
|
23
|
+
const dt = new Date(year, month - 1, day);
|
|
24
|
+
if (dt.getFullYear() !== year) return null;
|
|
25
|
+
if (dt.getMonth() !== month - 1) return null;
|
|
26
|
+
if (dt.getDate() !== day) return null;
|
|
27
|
+
return dt;
|
|
28
|
+
}
|
|
29
|
+
function normalizeSmlForSingleLine(sml) {
|
|
30
|
+
return sml.trim();
|
|
31
|
+
}
|
|
32
|
+
function bufferToHex(buffer, maxHexBytes) {
|
|
33
|
+
const len = buffer.length;
|
|
34
|
+
const max = Math.max(0, maxHexBytes);
|
|
35
|
+
return `${(len <= max ? buffer : buffer.subarray(0, max)).toString("hex")}${len <= max ? "" : `…(+${String(len - max)} bytes)`}`;
|
|
36
|
+
}
|
|
37
|
+
var PrettyPrintTransformStream = class extends Transform {
|
|
38
|
+
pending = "";
|
|
39
|
+
constructor() {
|
|
40
|
+
super();
|
|
41
|
+
}
|
|
42
|
+
_transform(chunk, _encoding, callback) {
|
|
43
|
+
try {
|
|
44
|
+
const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
45
|
+
this.pending += str;
|
|
46
|
+
this.flush();
|
|
47
|
+
callback();
|
|
48
|
+
} catch (e) {
|
|
49
|
+
callback(e instanceof Error ? e : new Error(String(e)));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
_flush(callback) {
|
|
53
|
+
callback();
|
|
54
|
+
}
|
|
55
|
+
flush() {
|
|
56
|
+
while (true) {
|
|
57
|
+
const idx = this.pending.indexOf("\n");
|
|
58
|
+
if (idx < 0) break;
|
|
59
|
+
const line = this.pending.slice(0, idx);
|
|
60
|
+
this.pending = this.pending.slice(idx + 1);
|
|
61
|
+
if (line.length === 0) continue;
|
|
62
|
+
const obj = JSON.parse(line);
|
|
63
|
+
const formatted = this.formatLine(obj);
|
|
64
|
+
this.push(formatted);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
formatLine(obj) {
|
|
68
|
+
return `${this.formatTime(obj.time)} ${this.formatLevel(obj.level)} ${this.formatExtra(obj)} | ${this.formatMessage(obj)}\n`;
|
|
69
|
+
}
|
|
70
|
+
formatTime(timeValue) {
|
|
71
|
+
if (typeof timeValue === "number") return formatDateTime(new Date(timeValue));
|
|
72
|
+
if (typeof timeValue === "string") return formatDateTime(new Date(timeValue));
|
|
73
|
+
return formatDateTime(/* @__PURE__ */ new Date());
|
|
74
|
+
}
|
|
75
|
+
formatLevel(level) {
|
|
76
|
+
return {
|
|
77
|
+
10: "TRACE",
|
|
78
|
+
20: "DEBUG",
|
|
79
|
+
30: "INFO ",
|
|
80
|
+
40: "WARN ",
|
|
81
|
+
50: "ERROR",
|
|
82
|
+
60: "FATAL"
|
|
83
|
+
}[level] ?? "UNKNOWN";
|
|
84
|
+
}
|
|
85
|
+
formatExtra(obj) {
|
|
86
|
+
const parts = [];
|
|
87
|
+
if (typeof obj.protocol === "string") parts.push(obj.protocol);
|
|
88
|
+
if (typeof obj.dir === "string") parts.push(obj.dir);
|
|
89
|
+
if (typeof obj.prev === "string" && typeof obj.next === "string") parts.push(`${obj.prev} -> ${obj.next}`);
|
|
90
|
+
return parts.join(" ");
|
|
91
|
+
}
|
|
92
|
+
formatMessage(obj) {
|
|
93
|
+
const msgValue = obj.msg;
|
|
94
|
+
if (typeof msgValue === "string" && msgValue.trim().length > 0) return msgValue;
|
|
95
|
+
const excludeKeys = new Set([
|
|
96
|
+
"time",
|
|
97
|
+
"level",
|
|
98
|
+
"msg",
|
|
99
|
+
"name",
|
|
100
|
+
"deviceId",
|
|
101
|
+
"isEquip",
|
|
102
|
+
"protocol",
|
|
103
|
+
"dir",
|
|
104
|
+
"prev",
|
|
105
|
+
"next"
|
|
106
|
+
]);
|
|
107
|
+
const pairs = [];
|
|
108
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
109
|
+
if (excludeKeys.has(key)) continue;
|
|
110
|
+
if (Buffer.isBuffer(value)) pairs.push(`${key}=${value.toString("hex")}`);
|
|
111
|
+
else if (typeof value === "string") pairs.push(`${key}=${value}`);
|
|
112
|
+
else if (typeof value === "number" || typeof value === "boolean") pairs.push(`${key}=${String(value)}`);
|
|
113
|
+
else pairs.push(`${key}=${this.formatValue(value)}`);
|
|
114
|
+
}
|
|
115
|
+
return pairs.join(" ");
|
|
116
|
+
}
|
|
117
|
+
formatValue(value) {
|
|
118
|
+
if (value === null) return "null";
|
|
119
|
+
if (value === void 0) return "undefined";
|
|
120
|
+
if (typeof value === "string") return value;
|
|
121
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
122
|
+
if (Buffer.isBuffer(value)) return value.toString("hex");
|
|
123
|
+
if (typeof value === "object") try {
|
|
124
|
+
return JSON.stringify(value);
|
|
125
|
+
} catch {
|
|
126
|
+
return "[object]";
|
|
127
|
+
}
|
|
128
|
+
return String(value);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var DailyRotatingFileStream = class extends Writable {
|
|
132
|
+
baseDir;
|
|
133
|
+
fileNameForDate;
|
|
134
|
+
retentionDays;
|
|
135
|
+
currentYmd = null;
|
|
136
|
+
currentStream = null;
|
|
137
|
+
cleanupYmd = null;
|
|
138
|
+
pending = "";
|
|
139
|
+
constructor(params) {
|
|
140
|
+
super();
|
|
141
|
+
this.baseDir = params.baseDir;
|
|
142
|
+
this.fileNameForDate = params.fileNameForDate;
|
|
143
|
+
this.retentionDays = params.retentionDays;
|
|
144
|
+
}
|
|
145
|
+
_write(chunk, encoding, callback) {
|
|
146
|
+
try {
|
|
147
|
+
const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
148
|
+
this.pending += str;
|
|
149
|
+
this.flushCompleteLines();
|
|
150
|
+
callback();
|
|
151
|
+
} catch (e) {
|
|
152
|
+
callback(e instanceof Error ? e : new Error(String(e)));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
_final(callback) {
|
|
156
|
+
try {
|
|
157
|
+
if (this.pending.length > 0) {
|
|
158
|
+
this.ensureStreamForNow();
|
|
159
|
+
this.currentStream?.write(this.pending);
|
|
160
|
+
this.pending = "";
|
|
161
|
+
}
|
|
162
|
+
this.currentStream?.end(() => callback());
|
|
163
|
+
this.currentStream = null;
|
|
164
|
+
} catch (e) {
|
|
165
|
+
callback(e instanceof Error ? e : new Error(String(e)));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
flushCompleteLines() {
|
|
169
|
+
while (true) {
|
|
170
|
+
const idx = this.pending.indexOf("\n");
|
|
171
|
+
if (idx < 0) break;
|
|
172
|
+
const line = this.pending.slice(0, idx + 1);
|
|
173
|
+
this.pending = this.pending.slice(idx + 1);
|
|
174
|
+
this.ensureStreamForNow();
|
|
175
|
+
this.currentStream?.write(line);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
ensureStreamForNow() {
|
|
179
|
+
const now = /* @__PURE__ */ new Date();
|
|
180
|
+
const ymd = formatDate(now);
|
|
181
|
+
if (this.currentYmd === ymd && this.currentStream) return;
|
|
182
|
+
if (this.currentStream) {
|
|
183
|
+
this.currentStream.end();
|
|
184
|
+
this.currentStream = null;
|
|
185
|
+
}
|
|
186
|
+
const dir = path.join(this.baseDir, ymd);
|
|
187
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
188
|
+
const filePath = path.join(dir, this.fileNameForDate(ymd));
|
|
189
|
+
this.currentStream = fs.createWriteStream(filePath, { flags: "a" });
|
|
190
|
+
this.currentYmd = ymd;
|
|
191
|
+
if (this.retentionDays > 0 && this.cleanupYmd !== ymd) {
|
|
192
|
+
this.cleanupYmd = ymd;
|
|
193
|
+
queueMicrotask(() => {
|
|
194
|
+
this.cleanupOldDirs(now).catch(() => void 0);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async cleanupOldDirs(now) {
|
|
199
|
+
const retentionDays = this.retentionDays;
|
|
200
|
+
if (retentionDays <= 0) return;
|
|
201
|
+
let entries;
|
|
202
|
+
try {
|
|
203
|
+
entries = await fs.promises.readdir(this.baseDir, { withFileTypes: true });
|
|
204
|
+
} catch {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const cutoff = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
208
|
+
cutoff.setDate(cutoff.getDate() - retentionDays);
|
|
209
|
+
for (const ent of entries) {
|
|
210
|
+
if (!ent.isDirectory()) continue;
|
|
211
|
+
const dirName = ent.name;
|
|
212
|
+
const dirDate = tryParseYmdDirName(dirName);
|
|
213
|
+
if (!dirDate) continue;
|
|
214
|
+
if (dirDate >= cutoff) continue;
|
|
215
|
+
const full = path.join(this.baseDir, dirName);
|
|
216
|
+
try {
|
|
217
|
+
await fs.promises.rm(full, {
|
|
218
|
+
recursive: true,
|
|
219
|
+
force: true
|
|
220
|
+
});
|
|
221
|
+
} catch {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
var Secs2LineTransformStream = class extends Writable {
|
|
228
|
+
target;
|
|
229
|
+
pending = "";
|
|
230
|
+
constructor(target) {
|
|
231
|
+
super();
|
|
232
|
+
this.target = target;
|
|
233
|
+
}
|
|
234
|
+
_write(chunk, encoding, callback) {
|
|
235
|
+
try {
|
|
236
|
+
const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
237
|
+
this.pending += str;
|
|
238
|
+
this.flush();
|
|
239
|
+
callback();
|
|
240
|
+
} catch (e) {
|
|
241
|
+
callback(e instanceof Error ? e : new Error(String(e)));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
_final(callback) {
|
|
245
|
+
try {
|
|
246
|
+
this.flush(true);
|
|
247
|
+
this.target.end(() => callback());
|
|
248
|
+
} catch (e) {
|
|
249
|
+
callback(e instanceof Error ? e : new Error(String(e)));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
flush(flushAll = false) {
|
|
253
|
+
while (true) {
|
|
254
|
+
const idx = this.pending.indexOf("\n");
|
|
255
|
+
if (idx < 0) {
|
|
256
|
+
if (flushAll && this.pending.length > 0) {
|
|
257
|
+
this.handleLine(this.pending);
|
|
258
|
+
this.pending = "";
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const line = this.pending.slice(0, idx);
|
|
263
|
+
this.pending = this.pending.slice(idx + 1);
|
|
264
|
+
if (line.length === 0) continue;
|
|
265
|
+
this.handleLine(line);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
handleLine(jsonLine) {
|
|
269
|
+
let obj;
|
|
270
|
+
try {
|
|
271
|
+
obj = JSON.parse(jsonLine);
|
|
272
|
+
} catch {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (!obj || typeof obj !== "object") return;
|
|
276
|
+
const rec = obj;
|
|
277
|
+
const timeValue = rec.time;
|
|
278
|
+
const dirValue = rec.dir;
|
|
279
|
+
const smlValue = rec.sml;
|
|
280
|
+
const dir = dirValue === "Sent" || dirValue === "Received" ? dirValue : null;
|
|
281
|
+
const sml = typeof smlValue === "string" ? smlValue : null;
|
|
282
|
+
if (!dir || !sml) return;
|
|
283
|
+
let date;
|
|
284
|
+
if (typeof timeValue === "number") date = new Date(timeValue);
|
|
285
|
+
else if (typeof timeValue === "string") date = new Date(timeValue);
|
|
286
|
+
else date = /* @__PURE__ */ new Date();
|
|
287
|
+
const out = `${formatDateTime(date)} ${dir} \n${normalizeSmlForSingleLine(sml)}\n`;
|
|
288
|
+
this.target.write(out);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
var DisabledSecsLogger = class {
|
|
292
|
+
detail;
|
|
293
|
+
secs2;
|
|
294
|
+
constructor() {
|
|
295
|
+
this.detail = pino({ enabled: false });
|
|
296
|
+
this.secs2 = pino({ enabled: false });
|
|
297
|
+
}
|
|
298
|
+
logSecs2(_direction, _sml) {}
|
|
299
|
+
logBytes(_direction, _protocol, _buffer, _meta) {}
|
|
300
|
+
logState(_protocol, _prev, _next, _meta) {}
|
|
301
|
+
close() {}
|
|
302
|
+
};
|
|
303
|
+
var SecsLogger = class SecsLogger {
|
|
304
|
+
static disabled() {
|
|
305
|
+
return new DisabledSecsLogger();
|
|
306
|
+
}
|
|
307
|
+
static create(config, ctx) {
|
|
308
|
+
const enabled = config?.enabled ?? false;
|
|
309
|
+
const consoleEnabled = config?.console ?? false;
|
|
310
|
+
if (!enabled) return SecsLogger.disabled();
|
|
311
|
+
const baseDir = config?.baseDir ? path.resolve(config.baseDir) : path.resolve(process.cwd(), "logs");
|
|
312
|
+
const retentionDays = config?.retentionDays ?? 7;
|
|
313
|
+
const detailLevel = config?.detailLevel ?? "debug";
|
|
314
|
+
const secs2Level = config?.secs2Level ?? "info";
|
|
315
|
+
const detailStream = new DailyRotatingFileStream({
|
|
316
|
+
baseDir,
|
|
317
|
+
fileNameForDate: (ymd) => `${ymd}-DETAIL.log`,
|
|
318
|
+
retentionDays
|
|
319
|
+
});
|
|
320
|
+
const secs2Target = new DailyRotatingFileStream({
|
|
321
|
+
baseDir,
|
|
322
|
+
fileNameForDate: (ymd) => `${ymd}-SECS-II.log`,
|
|
323
|
+
retentionDays
|
|
324
|
+
});
|
|
325
|
+
const secs2Stream = new Secs2LineTransformStream(secs2Target);
|
|
326
|
+
const bindings = {
|
|
327
|
+
name: ctx.name,
|
|
328
|
+
deviceId: ctx.deviceId,
|
|
329
|
+
isEquip: ctx.isEquip
|
|
330
|
+
};
|
|
331
|
+
const detailStreams = [{ stream: detailStream }];
|
|
332
|
+
if (consoleEnabled) {
|
|
333
|
+
const prettyStream = new PrettyPrintTransformStream();
|
|
334
|
+
prettyStream.pipe(process.stdout);
|
|
335
|
+
detailStreams.push({
|
|
336
|
+
stream: prettyStream,
|
|
337
|
+
level: detailLevel
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const detail = pino({
|
|
341
|
+
level: detailLevel,
|
|
342
|
+
base: bindings
|
|
343
|
+
}, pino.multistream(detailStreams));
|
|
344
|
+
const secs2Streams = [{ stream: secs2Stream }];
|
|
345
|
+
if (consoleEnabled) secs2Streams.push({
|
|
346
|
+
stream: process.stdout,
|
|
347
|
+
level: secs2Level
|
|
348
|
+
});
|
|
349
|
+
const secs2 = pino({
|
|
350
|
+
level: secs2Level,
|
|
351
|
+
base: null,
|
|
352
|
+
messageKey: "msg"
|
|
353
|
+
}, pino.multistream(secs2Streams));
|
|
354
|
+
return new SecsLogger({
|
|
355
|
+
config: {
|
|
356
|
+
...config,
|
|
357
|
+
baseDir,
|
|
358
|
+
retentionDays
|
|
359
|
+
},
|
|
360
|
+
detail,
|
|
361
|
+
secs2,
|
|
362
|
+
detailStream,
|
|
363
|
+
secs2Target,
|
|
364
|
+
secs2Stream
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
detail;
|
|
368
|
+
secs2;
|
|
369
|
+
detailStream;
|
|
370
|
+
secs2Target;
|
|
371
|
+
secs2Stream;
|
|
372
|
+
maxHexBytes;
|
|
373
|
+
constructor(params) {
|
|
374
|
+
this.detail = params.detail;
|
|
375
|
+
this.secs2 = params.secs2;
|
|
376
|
+
this.detailStream = params.detailStream;
|
|
377
|
+
this.secs2Target = params.secs2Target;
|
|
378
|
+
this.secs2Stream = params.secs2Stream;
|
|
379
|
+
this.maxHexBytes = params.config.maxHexBytes ?? 64 * 1024;
|
|
380
|
+
}
|
|
381
|
+
logSecs2(direction, sml) {
|
|
382
|
+
this.secs2.info({
|
|
383
|
+
dir: direction,
|
|
384
|
+
sml: normalizeSmlForSingleLine(sml)
|
|
385
|
+
}, "");
|
|
386
|
+
}
|
|
387
|
+
logBytes(direction, protocol, buffer, meta) {
|
|
388
|
+
this.detail.trace({
|
|
389
|
+
protocol,
|
|
390
|
+
dir: direction,
|
|
391
|
+
byteLength: buffer.length,
|
|
392
|
+
hex: bufferToHex(buffer, this.maxHexBytes),
|
|
393
|
+
...meta
|
|
394
|
+
}, "bytes");
|
|
395
|
+
}
|
|
396
|
+
logState(protocol, prev, next, meta) {
|
|
397
|
+
this.detail.info({
|
|
398
|
+
protocol,
|
|
399
|
+
prev,
|
|
400
|
+
next,
|
|
401
|
+
...meta
|
|
402
|
+
}, "state");
|
|
403
|
+
}
|
|
404
|
+
close() {
|
|
405
|
+
this.secs2.flush();
|
|
406
|
+
this.detail.flush();
|
|
407
|
+
this.secs2Stream.end();
|
|
408
|
+
this.secs2Target.end();
|
|
409
|
+
this.detailStream.end();
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
//#endregion
|
|
414
|
+
export { SecsLogger };
|
|
415
|
+
//# sourceMappingURL=SecsLogger.js.map
|