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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecsLogger.js","names":["parts: string[]","pairs: string[]","entries: fs.Dirent[]","obj: unknown","date: Date","detailStreams: pino.StreamEntry[]","secs2Streams: pino.StreamEntry[]"],"sources":["../../src/logging/SecsLogger.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { Writable, Transform } from \"stream\";\nimport pino, { type LevelWithSilent, type Logger } from \"pino\";\n\nexport type SecsLogDirection = \"Received\" | \"Sent\";\n\nexport interface SecsLoggerConfig {\n\tenabled?: boolean;\n\tconsole?: boolean;\n\tbaseDir?: string;\n\tretentionDays?: number;\n\tdetailLevel?: LevelWithSilent;\n\tsecs2Level?: LevelWithSilent;\n\tmaxHexBytes?: number;\n}\n\nexport interface SecsLoggerContext {\n\tname: string;\n\tdeviceId: number;\n\tisEquip: boolean;\n}\n\nfunction formatDate(date: Date): string {\n\tconst y = date.getFullYear();\n\tconst m = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst d = String(date.getDate()).padStart(2, \"0\");\n\treturn `${String(y)}-${m}-${d}`;\n}\n\nfunction formatDateTime(date: Date): string {\n\tconst ymd = formatDate(date);\n\tconst hh = String(date.getHours()).padStart(2, \"0\");\n\tconst mm = String(date.getMinutes()).padStart(2, \"0\");\n\tconst ss = String(date.getSeconds()).padStart(2, \"0\");\n\tconst ms = String(date.getMilliseconds()).padStart(3, \"0\");\n\treturn `${ymd} ${hh}:${mm}:${ss}.${ms}`;\n}\n\nfunction tryParseYmdDirName(dirName: string): Date | null {\n\tconst m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dirName);\n\tif (!m) return null;\n\tconst year = Number(m[1]);\n\tconst month = Number(m[2]);\n\tconst day = Number(m[3]);\n\tif (\n\t\t!Number.isFinite(year) ||\n\t\t!Number.isFinite(month) ||\n\t\t!Number.isFinite(day)\n\t)\n\t\treturn null;\n\tconst dt = new Date(year, month - 1, day);\n\tif (dt.getFullYear() !== year) return null;\n\tif (dt.getMonth() !== month - 1) return null;\n\tif (dt.getDate() !== day) return null;\n\treturn dt;\n}\n\nfunction normalizeSmlForSingleLine(sml: string): string {\n\treturn sml.trim();\n}\n\nfunction bufferToHex(buffer: Buffer, maxHexBytes: number): string {\n\tconst len = buffer.length;\n\tconst max = Math.max(0, maxHexBytes);\n\tconst slice = len <= max ? buffer : buffer.subarray(0, max);\n\tconst hex = slice.toString(\"hex\");\n\tconst suffix = len <= max ? \"\" : `…(+${String(len - max)} bytes)`;\n\treturn `${hex}${suffix}`;\n}\n\nclass PrettyPrintTransformStream extends Transform {\n\tprivate pending = \"\";\n\n\tconstructor() {\n\t\tsuper();\n\t}\n\n\toverride _transform(\n\t\tchunk: Buffer | string,\n\t\t_encoding: BufferEncoding,\n\t\tcallback: (error?: Error | null) => void,\n\t): void {\n\t\ttry {\n\t\t\tconst str = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n\t\t\tthis.pending += str;\n\t\t\tthis.flush();\n\t\t\tcallback();\n\t\t} catch (e) {\n\t\t\tcallback(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n\n\toverride _flush(callback: (error?: Error | null) => void): void {\n\t\tcallback();\n\t}\n\n\tprivate flush(): void {\n\t\twhile (true) {\n\t\t\tconst idx = this.pending.indexOf(\"\\n\");\n\t\t\tif (idx < 0) break;\n\t\t\tconst line = this.pending.slice(0, idx);\n\t\t\tthis.pending = this.pending.slice(idx + 1);\n\t\t\tif (line.length === 0) continue;\n\t\t\tconst obj = JSON.parse(line) as Record<string, unknown>;\n\t\t\tconst formatted = this.formatLine(obj);\n\t\t\tthis.push(formatted);\n\t\t}\n\t}\n\n\tprivate formatLine(obj: Record<string, unknown>): string {\n\t\tconst time = this.formatTime(obj.time);\n\t\tconst level = this.formatLevel(obj.level as number);\n\t\tconst extra = this.formatExtra(obj);\n\t\tconst msg = this.formatMessage(obj);\n\n\t\treturn `${time} ${level} ${extra} | ${msg}\\n`;\n\t}\n\n\tprivate formatTime(timeValue: unknown): string {\n\t\tif (typeof timeValue === \"number\") {\n\t\t\treturn formatDateTime(new Date(timeValue));\n\t\t}\n\t\tif (typeof timeValue === \"string\") {\n\t\t\treturn formatDateTime(new Date(timeValue));\n\t\t}\n\t\treturn formatDateTime(new Date());\n\t}\n\n\tprivate formatLevel(level: number): string {\n\t\tconst levels: Record<number, string> = {\n\t\t\t10: \"TRACE\",\n\t\t\t20: \"DEBUG\",\n\t\t\t30: \"INFO \",\n\t\t\t40: \"WARN \",\n\t\t\t50: \"ERROR\",\n\t\t\t60: \"FATAL\",\n\t\t};\n\t\treturn levels[level] ?? \"UNKNOWN\";\n\t}\n\n\tprivate formatExtra(obj: Record<string, unknown>): string {\n\t\tconst parts: string[] = [];\n\n\t\tif (typeof obj.protocol === \"string\") {\n\t\t\tparts.push(obj.protocol);\n\t\t}\n\t\tif (typeof obj.dir === \"string\") {\n\t\t\tparts.push(obj.dir);\n\t\t}\n\t\tif (typeof obj.prev === \"string\" && typeof obj.next === \"string\") {\n\t\t\tparts.push(`${obj.prev} -> ${obj.next}`);\n\t\t}\n\n\t\treturn parts.join(\" \");\n\t}\n\n\tprivate formatMessage(obj: Record<string, unknown>): string {\n\t\tconst msgValue = obj.msg;\n\t\tif (typeof msgValue === \"string\" && msgValue.trim().length > 0) {\n\t\t\treturn msgValue;\n\t\t}\n\n\t\tconst excludeKeys = new Set([\n\t\t\t\"time\",\n\t\t\t\"level\",\n\t\t\t\"msg\",\n\t\t\t\"name\",\n\t\t\t\"deviceId\",\n\t\t\t\"isEquip\",\n\t\t\t\"protocol\",\n\t\t\t\"dir\",\n\t\t\t\"prev\",\n\t\t\t\"next\",\n\t\t]);\n\n\t\tconst pairs: string[] = [];\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tif (excludeKeys.has(key)) continue;\n\t\t\tif (Buffer.isBuffer(value)) {\n\t\t\t\tpairs.push(`${key}=${value.toString(\"hex\")}`);\n\t\t\t} else if (typeof value === \"string\") {\n\t\t\t\tpairs.push(`${key}=${value}`);\n\t\t\t} else if (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\t\t\tpairs.push(`${key}=${String(value)}`);\n\t\t\t} else {\n\t\t\t\tpairs.push(`${key}=${this.formatValue(value)}`);\n\t\t\t}\n\t\t}\n\n\t\treturn pairs.join(\" \");\n\t}\n\n\tprivate formatValue(value: unknown): string {\n\t\tif (value === null) return \"null\";\n\t\tif (value === undefined) return \"undefined\";\n\t\tif (typeof value === \"string\") return value;\n\t\tif (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\t\treturn String(value);\n\t\t}\n\t\tif (Buffer.isBuffer(value)) {\n\t\t\treturn value.toString(\"hex\");\n\t\t}\n\t\tif (typeof value === \"object\") {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(value);\n\t\t\t} catch {\n\t\t\t\treturn \"[object]\";\n\t\t\t}\n\t\t}\n\t\treturn String(value as string | number | bigint | boolean);\n\t}\n}\n\nclass DailyRotatingFileStream extends Writable {\n\tprivate readonly baseDir: string;\n\tprivate readonly fileNameForDate: (ymd: string) => string;\n\tprivate readonly retentionDays: number;\n\tprivate currentYmd: string | null = null;\n\tprivate currentStream: fs.WriteStream | null = null;\n\tprivate cleanupYmd: string | null = null;\n\tprivate pending = \"\";\n\n\tconstructor(params: {\n\t\tbaseDir: string;\n\t\tfileNameForDate: (ymd: string) => string;\n\t\tretentionDays: number;\n\t}) {\n\t\tsuper();\n\t\tthis.baseDir = params.baseDir;\n\t\tthis.fileNameForDate = params.fileNameForDate;\n\t\tthis.retentionDays = params.retentionDays;\n\t}\n\n\toverride _write(\n\t\tchunk: Buffer | string,\n\t\tencoding: BufferEncoding,\n\t\tcallback: (error?: Error | null) => void,\n\t): void {\n\t\ttry {\n\t\t\tconst str = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n\t\t\tthis.pending += str;\n\t\t\tthis.flushCompleteLines();\n\t\t\tcallback();\n\t\t} catch (e) {\n\t\t\tcallback(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n\n\toverride _final(callback: (error?: Error | null) => void): void {\n\t\ttry {\n\t\t\tif (this.pending.length > 0) {\n\t\t\t\tthis.ensureStreamForNow();\n\t\t\t\tthis.currentStream?.write(this.pending);\n\t\t\t\tthis.pending = \"\";\n\t\t\t}\n\t\t\tthis.currentStream?.end(() => callback());\n\t\t\tthis.currentStream = null;\n\t\t} catch (e) {\n\t\t\tcallback(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n\n\tprivate flushCompleteLines() {\n\t\twhile (true) {\n\t\t\tconst idx = this.pending.indexOf(\"\\n\");\n\t\t\tif (idx < 0) break;\n\t\t\tconst line = this.pending.slice(0, idx + 1);\n\t\t\tthis.pending = this.pending.slice(idx + 1);\n\t\t\tthis.ensureStreamForNow();\n\t\t\tthis.currentStream?.write(line);\n\t\t}\n\t}\n\n\tprivate ensureStreamForNow() {\n\t\tconst now = new Date();\n\t\tconst ymd = formatDate(now);\n\t\tif (this.currentYmd === ymd && this.currentStream) return;\n\n\t\tif (this.currentStream) {\n\t\t\tthis.currentStream.end();\n\t\t\tthis.currentStream = null;\n\t\t}\n\n\t\tconst dir = path.join(this.baseDir, ymd);\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t\tconst filePath = path.join(dir, this.fileNameForDate(ymd));\n\t\tthis.currentStream = fs.createWriteStream(filePath, { flags: \"a\" });\n\t\tthis.currentYmd = ymd;\n\n\t\tif (this.retentionDays > 0 && this.cleanupYmd !== ymd) {\n\t\t\tthis.cleanupYmd = ymd;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tthis.cleanupOldDirs(now).catch(() => undefined);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async cleanupOldDirs(now: Date): Promise<void> {\n\t\tconst retentionDays = this.retentionDays;\n\t\tif (retentionDays <= 0) return;\n\n\t\tlet entries: fs.Dirent[];\n\t\ttry {\n\t\t\tentries = await fs.promises.readdir(this.baseDir, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tconst cutoff = new Date(\n\t\t\tnow.getFullYear(),\n\t\t\tnow.getMonth(),\n\t\t\tnow.getDate(),\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\tcutoff.setDate(cutoff.getDate() - retentionDays);\n\n\t\tfor (const ent of entries) {\n\t\t\tif (!ent.isDirectory()) continue;\n\t\t\tconst dirName = ent.name;\n\t\t\tconst dirDate = tryParseYmdDirName(dirName);\n\t\t\tif (!dirDate) continue;\n\t\t\tif (dirDate >= cutoff) continue;\n\t\t\tconst full = path.join(this.baseDir, dirName);\n\t\t\ttry {\n\t\t\t\tawait fs.promises.rm(full, { recursive: true, force: true });\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass Secs2LineTransformStream extends Writable {\n\tprivate readonly target: DailyRotatingFileStream;\n\tprivate pending = \"\";\n\n\tconstructor(target: DailyRotatingFileStream) {\n\t\tsuper();\n\t\tthis.target = target;\n\t}\n\n\toverride _write(\n\t\tchunk: Buffer | string,\n\t\tencoding: BufferEncoding,\n\t\tcallback: (error?: Error | null) => void,\n\t): void {\n\t\ttry {\n\t\t\tconst str = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n\t\t\tthis.pending += str;\n\t\t\tthis.flush();\n\t\t\tcallback();\n\t\t} catch (e) {\n\t\t\tcallback(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n\n\toverride _final(callback: (error?: Error | null) => void): void {\n\t\ttry {\n\t\t\tthis.flush(true);\n\t\t\tthis.target.end(() => callback());\n\t\t} catch (e) {\n\t\t\tcallback(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t}\n\n\tprivate flush(flushAll = false) {\n\t\twhile (true) {\n\t\t\tconst idx = this.pending.indexOf(\"\\n\");\n\t\t\tif (idx < 0) {\n\t\t\t\tif (flushAll && this.pending.length > 0) {\n\t\t\t\t\tthis.handleLine(this.pending);\n\t\t\t\t\tthis.pending = \"\";\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst line = this.pending.slice(0, idx);\n\t\t\tthis.pending = this.pending.slice(idx + 1);\n\t\t\tif (line.length === 0) continue;\n\t\t\tthis.handleLine(line);\n\t\t}\n\t}\n\n\tprivate handleLine(jsonLine: string) {\n\t\tlet obj: unknown;\n\t\ttry {\n\t\t\tobj = JSON.parse(jsonLine);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!obj || typeof obj !== \"object\") return;\n\t\tconst rec = obj as Record<string, unknown>;\n\t\tconst timeValue = rec.time;\n\t\tconst dirValue = rec.dir;\n\t\tconst smlValue = rec.sml;\n\n\t\tconst dir =\n\t\t\tdirValue === \"Sent\" || dirValue === \"Received\" ? dirValue : null;\n\t\tconst sml = typeof smlValue === \"string\" ? smlValue : null;\n\t\tif (!dir || !sml) return;\n\n\t\tlet date: Date;\n\t\tif (typeof timeValue === \"number\") {\n\t\t\tdate = new Date(timeValue);\n\t\t} else if (typeof timeValue === \"string\") {\n\t\t\tdate = new Date(timeValue);\n\t\t} else {\n\t\t\tdate = new Date();\n\t\t}\n\n\t\tconst out = `${formatDateTime(date)} ${dir} \\n${normalizeSmlForSingleLine(sml)}\\n`;\n\t\tthis.target.write(out);\n\t}\n}\n\nclass DisabledSecsLogger {\n\tdetail: Logger;\n\tsecs2: Logger;\n\tconstructor() {\n\t\tthis.detail = pino({ enabled: false });\n\t\tthis.secs2 = pino({ enabled: false });\n\t}\n\n\tlogSecs2(_direction: SecsLogDirection, _sml: string): void {\n\t\treturn;\n\t}\n\n\tlogBytes(\n\t\t_direction: SecsLogDirection,\n\t\t_protocol: string,\n\t\t_buffer: Buffer,\n\t\t_meta?: Record<string, unknown>,\n\t): void {\n\t\treturn;\n\t}\n\n\tlogState(\n\t\t_protocol: string,\n\t\t_prev: string,\n\t\t_next: string,\n\t\t_meta?: Record<string, unknown>,\n\t): void {\n\t\treturn;\n\t}\n\n\tclose(): void {\n\t\treturn;\n\t}\n}\n\nexport class SecsLogger {\n\tstatic disabled(): SecsLogger {\n\t\treturn new DisabledSecsLogger() as unknown as SecsLogger;\n\t}\n\n\tstatic create(\n\t\tconfig: SecsLoggerConfig | undefined,\n\t\tctx: SecsLoggerContext,\n\t): SecsLogger {\n\t\tconst enabled = config?.enabled ?? false;\n\t\tconst consoleEnabled = config?.console ?? false;\n\t\tif (!enabled) return SecsLogger.disabled();\n\n\t\tconst baseDir = config?.baseDir\n\t\t\t? path.resolve(config.baseDir)\n\t\t\t: path.resolve(process.cwd(), \"logs\");\n\t\tconst retentionDays = config?.retentionDays ?? 7;\n\t\tconst detailLevel = config?.detailLevel ?? \"debug\";\n\t\tconst secs2Level = config?.secs2Level ?? \"info\";\n\n\t\tconst detailStream = new DailyRotatingFileStream({\n\t\t\tbaseDir,\n\t\t\tfileNameForDate: (ymd) => `${ymd}-DETAIL.log`,\n\t\t\tretentionDays,\n\t\t});\n\n\t\tconst secs2Target = new DailyRotatingFileStream({\n\t\t\tbaseDir,\n\t\t\tfileNameForDate: (ymd) => `${ymd}-SECS-II.log`,\n\t\t\tretentionDays,\n\t\t});\n\t\tconst secs2Stream = new Secs2LineTransformStream(secs2Target);\n\n\t\tconst bindings = {\n\t\t\tname: ctx.name,\n\t\t\tdeviceId: ctx.deviceId,\n\t\t\tisEquip: ctx.isEquip,\n\t\t};\n\n\t\tconst detailStreams: pino.StreamEntry[] = [\n\t\t\t{ stream: detailStream as pino.DestinationStream },\n\t\t];\n\t\tif (consoleEnabled) {\n\t\t\tconst prettyStream = new PrettyPrintTransformStream();\n\t\t\tprettyStream.pipe(process.stdout);\n\t\t\tdetailStreams.push({\n\t\t\t\tstream: prettyStream,\n\t\t\t\tlevel: detailLevel as pino.Level,\n\t\t\t});\n\t\t}\n\t\tconst detail = pino(\n\t\t\t{ level: detailLevel, base: bindings },\n\t\t\tpino.multistream(detailStreams),\n\t\t);\n\n\t\tconst secs2Streams: pino.StreamEntry[] = [\n\t\t\t{ stream: secs2Stream as pino.DestinationStream },\n\t\t];\n\t\tif (consoleEnabled) {\n\t\t\tsecs2Streams.push({\n\t\t\t\tstream: process.stdout,\n\t\t\t\tlevel: secs2Level as pino.Level,\n\t\t\t});\n\t\t}\n\t\tconst secs2 = pino(\n\t\t\t{\n\t\t\t\tlevel: secs2Level,\n\t\t\t\tbase: null,\n\t\t\t\tmessageKey: \"msg\",\n\t\t\t},\n\t\t\tpino.multistream(secs2Streams),\n\t\t);\n\n\t\treturn new SecsLogger({\n\t\t\tconfig: { ...config, baseDir, retentionDays },\n\t\t\tdetail,\n\t\t\tsecs2,\n\t\t\tdetailStream,\n\t\t\tsecs2Target,\n\t\t\tsecs2Stream,\n\t\t});\n\t}\n\n\tpublic readonly detail: Logger;\n\tprivate readonly secs2: Logger;\n\tprivate readonly detailStream: DailyRotatingFileStream;\n\tprivate readonly secs2Target: DailyRotatingFileStream;\n\tprivate readonly secs2Stream: Secs2LineTransformStream;\n\tprivate readonly maxHexBytes: number;\n\n\tprivate constructor(params: {\n\t\tconfig: SecsLoggerConfig;\n\t\tdetail: Logger;\n\t\tsecs2: Logger;\n\t\tdetailStream: DailyRotatingFileStream;\n\t\tsecs2Target: DailyRotatingFileStream;\n\t\tsecs2Stream: Secs2LineTransformStream;\n\t}) {\n\t\tthis.detail = params.detail;\n\t\tthis.secs2 = params.secs2;\n\t\tthis.detailStream = params.detailStream;\n\t\tthis.secs2Target = params.secs2Target;\n\t\tthis.secs2Stream = params.secs2Stream;\n\t\tthis.maxHexBytes = params.config.maxHexBytes ?? 64 * 1024;\n\t}\n\n\tlogSecs2(direction: SecsLogDirection, sml: string): void {\n\t\tthis.secs2.info(\n\t\t\t{ dir: direction, sml: normalizeSmlForSingleLine(sml) },\n\t\t\t\"\",\n\t\t);\n\t}\n\n\tlogBytes(\n\t\tdirection: SecsLogDirection,\n\t\tprotocol: string,\n\t\tbuffer: Buffer,\n\t\tmeta?: Record<string, unknown>,\n\t): void {\n\t\tthis.detail.trace(\n\t\t\t{\n\t\t\t\tprotocol,\n\t\t\t\tdir: direction,\n\t\t\t\tbyteLength: buffer.length,\n\t\t\t\thex: bufferToHex(buffer, this.maxHexBytes),\n\t\t\t\t...meta,\n\t\t\t},\n\t\t\t\"bytes\",\n\t\t);\n\t}\n\n\tlogState(\n\t\tprotocol: string,\n\t\tprev: string,\n\t\tnext: string,\n\t\tmeta?: Record<string, unknown>,\n\t): void {\n\t\tthis.detail.info({ protocol, prev, next, ...meta }, \"state\");\n\t}\n\n\tclose(): void {\n\t\tthis.secs2.flush();\n\t\tthis.detail.flush();\n\t\tthis.secs2Stream.end();\n\t\tthis.secs2Target.end();\n\t\tthis.detailStream.end();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@ declare abstract class Secs1Communicator extends AbstractSecsCommunicator {
|
|
|
27
27
|
constructor(config: Secs1CommunicatorConfig);
|
|
28
28
|
protected attachStream(stream: Duplex): void;
|
|
29
29
|
protected stop(): void;
|
|
30
|
+
protected sendBufferWithLogs(direction: "Sent" | "Received", protocol: string, buffer: Buffer, meta?: Record<string, unknown>): Promise<void>;
|
|
30
31
|
protected sendBuffer(buffer: Buffer): Promise<void>;
|
|
31
32
|
protected createMessage(stream: number, func: number, wBit: boolean, body: AbstractSecs2Item | null, systemBytes: number): SecsMessage;
|
|
32
33
|
private clearT1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1Communicator.d.ts","names":[],"sources":["../../src/secs1/Secs1Communicator.ts"],"sourcesContent":[],"mappings":";;;;;;UAUiB,uBAAA,SAAgC;;EAAhC,QAAA,CAAA,EAAA,OAAA;AAkBjB;AAImB,uBAJG,iBAAA,SAA0B,wBAAA,CAI7B;EAkBE,KAAA,EAAA,MAAA;EAMW,QAAA,EAAA,OAAA;
|
|
1
|
+
{"version":3,"file":"Secs1Communicator.d.ts","names":[],"sources":["../../src/secs1/Secs1Communicator.ts"],"sourcesContent":[],"mappings":";;;;;;UAUiB,uBAAA,SAAgC;;EAAhC,QAAA,CAAA,EAAA,OAAA;AAkBjB;AAImB,uBAJG,iBAAA,SAA0B,wBAAA,CAI7B;EAkBE,KAAA,EAAA,MAAA;EAMW,QAAA,EAAA,OAAA;EAkDtB,UAAA,MAAA,EA1ES,MA0ET,GAAA,IAAA;EACD,QAAA,KAAA;EACL,QAAA,MAAA;EAYmC,QAAA,SAAA;EAAS,QAAA,aAAA;EAUxC,QAAA,iBAAA;EAEJ,QAAA,UAAA;EAxG4C,QAAA,cAAA;EAAwB,QAAA,gBAAA;;;;;sBAsBnD;iCAMW;;yFAkDtB,eACD,0BACL;+BAYmC,SAAS;6EAUxC,gDAEJ"}
|
|
@@ -40,7 +40,9 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
40
40
|
this.stream = stream;
|
|
41
41
|
this.resetState();
|
|
42
42
|
this.buffer = Buffer.alloc(0);
|
|
43
|
+
this.logger.logState("SECS1", "NotConnected", "Connected");
|
|
43
44
|
stream.on("data", (data) => {
|
|
45
|
+
this.logger.logBytes("Received", "SECS1", data, { chunkLength: data.length });
|
|
44
46
|
this.buffer = Buffer.concat([this.buffer, data]);
|
|
45
47
|
if (this.state === CommState.WAIT_BLOCK_LENGTH || this.state === CommState.WAIT_BLOCK_DATA) this.startT1();
|
|
46
48
|
this.processBuffer();
|
|
@@ -49,6 +51,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
49
51
|
this.rejectAllTransactions(/* @__PURE__ */ new Error("Stream closed"));
|
|
50
52
|
this.stream = null;
|
|
51
53
|
this.emit("disconnected");
|
|
54
|
+
this.logger.logState("SECS1", "Connected", "NotConnected");
|
|
52
55
|
this.stopAllTimers();
|
|
53
56
|
this.resetState();
|
|
54
57
|
});
|
|
@@ -65,6 +68,12 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
65
68
|
this.stopAllTimers();
|
|
66
69
|
this.resetState();
|
|
67
70
|
}
|
|
71
|
+
async sendBufferWithLogs(direction, protocol, buffer, meta) {
|
|
72
|
+
await super.sendBufferWithLogs(direction, protocol === "SECS" ? "SECS1" : protocol, buffer, {
|
|
73
|
+
commState: CommState[this.state],
|
|
74
|
+
...meta
|
|
75
|
+
});
|
|
76
|
+
}
|
|
68
77
|
sendBuffer(buffer) {
|
|
69
78
|
this.sendQueue.push(buffer);
|
|
70
79
|
process.nextTick(() => this.processSendQueue());
|
|
@@ -120,13 +129,13 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
120
129
|
}, timeout);
|
|
121
130
|
}
|
|
122
131
|
handleT1Timeout() {
|
|
123
|
-
|
|
132
|
+
this.logger.detail.warn({ protocol: "SECS1" }, "t1 timeout");
|
|
124
133
|
this.t1Timer = null;
|
|
125
134
|
this.emit("error", /* @__PURE__ */ new Error("T1 Timeout"));
|
|
126
135
|
this.resetState();
|
|
127
136
|
}
|
|
128
137
|
handleT2Timeout() {
|
|
129
|
-
|
|
138
|
+
this.logger.detail.warn({ protocol: "SECS1" }, "t2 timeout");
|
|
130
139
|
this.t2Timer = null;
|
|
131
140
|
if (this.state === CommState.WAIT_EOT) {
|
|
132
141
|
this.retryCount++;
|
|
@@ -134,7 +143,11 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
134
143
|
this.emit("error", /* @__PURE__ */ new Error("Retry limit exceeded waiting for EOT"));
|
|
135
144
|
this.resetState();
|
|
136
145
|
} else {
|
|
137
|
-
|
|
146
|
+
this.logger.detail.info({
|
|
147
|
+
protocol: "SECS1",
|
|
148
|
+
retryCount: this.retryCount,
|
|
149
|
+
retry: this.retry
|
|
150
|
+
}, "retrying ENQ");
|
|
138
151
|
this.sendByte(ENQ);
|
|
139
152
|
this.startT2();
|
|
140
153
|
}
|
|
@@ -146,7 +159,11 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
146
159
|
this.emit("error", /* @__PURE__ */ new Error("Retry limit exceeded waiting for ACK"));
|
|
147
160
|
this.resetState();
|
|
148
161
|
} else {
|
|
149
|
-
|
|
162
|
+
this.logger.detail.info({
|
|
163
|
+
protocol: "SECS1",
|
|
164
|
+
retryCount: this.retryCount,
|
|
165
|
+
retry: this.retry
|
|
166
|
+
}, "retrying block");
|
|
150
167
|
this.sendCurrentBlock();
|
|
151
168
|
}
|
|
152
169
|
return;
|
|
@@ -154,7 +171,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
154
171
|
this.resetState();
|
|
155
172
|
}
|
|
156
173
|
handleT4Timeout() {
|
|
157
|
-
|
|
174
|
+
this.logger.detail.warn({ protocol: "SECS1" }, "t4 timeout");
|
|
158
175
|
this.t4Timer = null;
|
|
159
176
|
this.resetState();
|
|
160
177
|
}
|
|
@@ -167,7 +184,11 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
167
184
|
}
|
|
168
185
|
sendByte(byte) {
|
|
169
186
|
const stream = this.stream;
|
|
170
|
-
if (stream && !stream.destroyed)
|
|
187
|
+
if (stream && !stream.destroyed) {
|
|
188
|
+
const buf = Buffer.from([byte]);
|
|
189
|
+
this.logger.logBytes("Sent", "SECS1", buf);
|
|
190
|
+
stream.write(buf);
|
|
191
|
+
}
|
|
171
192
|
}
|
|
172
193
|
processBuffer() {
|
|
173
194
|
while (this.buffer.length > 0) switch (this.state) {
|
|
@@ -175,6 +196,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
175
196
|
const byte = this.buffer[0];
|
|
176
197
|
this.buffer = this.buffer.subarray(1);
|
|
177
198
|
if (byte === ENQ) {
|
|
199
|
+
this.logger.detail.debug({ protocol: "SECS1" }, "rx ENQ");
|
|
178
200
|
this.sendByte(EOT);
|
|
179
201
|
this.state = CommState.WAIT_BLOCK_LENGTH;
|
|
180
202
|
this.receivedBlocks = [];
|
|
@@ -187,11 +209,13 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
187
209
|
const byte = this.buffer[0];
|
|
188
210
|
this.buffer = this.buffer.subarray(1);
|
|
189
211
|
if (byte === EOT) {
|
|
212
|
+
this.logger.detail.debug({ protocol: "SECS1" }, "rx EOT");
|
|
190
213
|
this.clearT2();
|
|
191
214
|
this.currentBlockIndex = 0;
|
|
192
215
|
this.sendCurrentBlock();
|
|
193
216
|
} else if (byte === ENQ) {
|
|
194
217
|
if (!this.isMaster) {
|
|
218
|
+
this.logger.detail.debug({ protocol: "SECS1" }, "rx ENQ while waiting EOT");
|
|
195
219
|
this.clearT2();
|
|
196
220
|
this.sendByte(EOT);
|
|
197
221
|
this.state = CommState.WAIT_BLOCK_LENGTH;
|
|
@@ -206,6 +230,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
206
230
|
const byte = this.buffer[0];
|
|
207
231
|
this.buffer = this.buffer.subarray(1);
|
|
208
232
|
if (byte === ACK) {
|
|
233
|
+
this.logger.detail.debug({ protocol: "SECS1" }, "rx ACK");
|
|
209
234
|
this.clearT2();
|
|
210
235
|
if (this.currentBlocks[this.currentBlockIndex].eBit) {
|
|
211
236
|
this.currentBlocks = [];
|
|
@@ -217,6 +242,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
217
242
|
this.sendCurrentBlock();
|
|
218
243
|
}
|
|
219
244
|
} else if (byte === NAK) {
|
|
245
|
+
this.logger.detail.warn({ protocol: "SECS1" }, "rx NAK");
|
|
220
246
|
this.clearT2();
|
|
221
247
|
this.handleT2Timeout();
|
|
222
248
|
}
|
|
@@ -247,7 +273,11 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
247
273
|
blockData.copy(fullBlockBuffer, 1);
|
|
248
274
|
const block = new Secs1MessageBlock(fullBlockBuffer);
|
|
249
275
|
if (block.isValid()) if (block.blockNumber !== this.expectedBlockNum) {
|
|
250
|
-
|
|
276
|
+
this.logger.detail.warn({
|
|
277
|
+
protocol: "SECS1",
|
|
278
|
+
expected: this.expectedBlockNum,
|
|
279
|
+
got: block.blockNumber
|
|
280
|
+
}, "wrong block number");
|
|
251
281
|
this.sendByte(NAK);
|
|
252
282
|
this.state = CommState.IDLE;
|
|
253
283
|
this.resetState();
|
|
@@ -270,7 +300,7 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
270
300
|
}
|
|
271
301
|
}
|
|
272
302
|
else {
|
|
273
|
-
|
|
303
|
+
this.logger.detail.warn({ protocol: "SECS1" }, "invalid checksum");
|
|
274
304
|
this.sendByte(NAK);
|
|
275
305
|
this.state = CommState.IDLE;
|
|
276
306
|
this.resetState();
|
|
@@ -285,6 +315,13 @@ var Secs1Communicator = class extends AbstractSecsCommunicator {
|
|
|
285
315
|
const stream = this.stream;
|
|
286
316
|
const block = this.currentBlocks[this.currentBlockIndex];
|
|
287
317
|
if (stream && !stream.destroyed) {
|
|
318
|
+
this.logger.logBytes("Sent", "SECS1", block.buffer, {
|
|
319
|
+
blockNumber: block.blockNumber,
|
|
320
|
+
eBit: block.eBit,
|
|
321
|
+
systemBytes: block.systemBytes,
|
|
322
|
+
stream: block.stream,
|
|
323
|
+
func: block.func
|
|
324
|
+
});
|
|
288
325
|
stream.write(block.buffer);
|
|
289
326
|
this.state = CommState.WAIT_ACK;
|
|
290
327
|
this.startT2();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1Communicator.js","names":[],"sources":["../../src/secs1/Secs1Communicator.ts"],"sourcesContent":["import { Duplex } from \"stream\";\nimport {\n\tAbstractSecsCommunicator,\n\tSecsCommunicatorConfig,\n} from \"../core/AbstractSecsCommunicator.js\";\nimport { SecsMessage } from \"../core/AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\nimport { Secs1Message } from \"./Secs1Message.js\";\nimport { Secs1MessageBlock } from \"./Secs1MessageBlock.js\";\n\nexport interface Secs1CommunicatorConfig extends SecsCommunicatorConfig {\n\tretry?: number;\n\tisMaster?: boolean;\n}\n\nconst ENQ = 0x05;\nconst EOT = 0x04;\nconst ACK = 0x06;\nconst NAK = 0x15;\n\nenum CommState {\n\tIDLE,\n\tWAIT_EOT,\n\tWAIT_ACK,\n\tWAIT_BLOCK_LENGTH,\n\tWAIT_BLOCK_DATA,\n}\n\nexport abstract class Secs1Communicator extends AbstractSecsCommunicator {\n\tpublic retry: number;\n\tpublic isMaster: boolean;\n\n\tprotected stream: Duplex | null = null;\n\n\tprivate state: CommState = CommState.IDLE;\n\tprivate buffer: Buffer = Buffer.alloc(0);\n\n\tprivate sendQueue: Buffer[] = [];\n\tprivate currentBlocks: Secs1MessageBlock[] = [];\n\tprivate currentBlockIndex = 0;\n\tprivate retryCount = 0;\n\n\tprivate receivedBlocks: Secs1MessageBlock[] = [];\n\tprivate expectedBlockNum = 1;\n\tprivate currentBlockLength = 0;\n\n\tprivate t1Timer: NodeJS.Timeout | null = null;\n\tprivate t2Timer: NodeJS.Timeout | null = null;\n\tprivate t4Timer: NodeJS.Timeout | null = null;\n\n\tconstructor(config: Secs1CommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.retry = config.retry ?? 3;\n\t\tthis.isMaster = config.isMaster ?? false;\n\t}\n\n\tprotected attachStream(stream: Duplex) {\n\t\tthis.stream = stream;\n\t\tthis.resetState();\n\t\tthis.buffer = Buffer.alloc(0);\n\n\t\tstream.on(\"data\", (data: Buffer) => {\n\t\t\tthis.buffer = Buffer.concat([this.buffer, data]);\n\t\t\tif (\n\t\t\t\tthis.state === CommState.WAIT_BLOCK_LENGTH ||\n\t\t\t\tthis.state === CommState.WAIT_BLOCK_DATA\n\t\t\t) {\n\t\t\t\tthis.startT1();\n\t\t\t}\n\t\t\tthis.processBuffer();\n\t\t});\n\n\t\tstream.on(\"close\", () => {\n\t\t\tthis.rejectAllTransactions(new Error(\"Stream closed\"));\n\t\t\tthis.stream = null;\n\t\t\tthis.emit(\"disconnected\");\n\t\t\tthis.stopAllTimers();\n\t\t\tthis.resetState();\n\t\t});\n\n\t\tstream.on(\"error\", (err: Error) => {\n\t\t\tthis.emit(\"error\", err);\n\t\t});\n\n\t\tthis.emit(\"connected\");\n\t\tthis.processSendQueue();\n\t}\n\n\tprotected stop() {\n\t\tconst stream = this.stream;\n\t\tif (stream && !stream.destroyed) {\n\t\t\tstream.destroy();\n\t\t}\n\t\tthis.stream = null;\n\t\tthis.stopAllTimers();\n\t\tthis.resetState();\n\t}\n\n\tprotected override sendBuffer(buffer: Buffer): Promise<void> {\n\t\tthis.sendQueue.push(buffer);\n\t\tprocess.nextTick(() => this.processSendQueue());\n\t\treturn Promise.resolve();\n\t}\n\n\tprotected override createMessage(\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\tbody: AbstractSecs2Item | null,\n\t\tsystemBytes: number,\n\t): SecsMessage {\n\t\treturn new Secs1Message(\n\t\t\tstream,\n\t\t\tfunc,\n\t\t\twBit,\n\t\t\tbody,\n\t\t\tsystemBytes,\n\t\t\tthis.deviceId,\n\t\t);\n\t}\n\n\tprivate clearT1() {\n\t\tif (this.t1Timer) {\n\t\t\tclearTimeout(this.t1Timer);\n\t\t\tthis.t1Timer = null;\n\t\t}\n\t}\n\n\tprivate clearT2() {\n\t\tif (this.t2Timer) {\n\t\t\tclearTimeout(this.t2Timer);\n\t\t\tthis.t2Timer = null;\n\t\t}\n\t}\n\n\tprivate clearT4() {\n\t\tif (this.t4Timer) {\n\t\t\tclearTimeout(this.t4Timer);\n\t\t\tthis.t4Timer = null;\n\t\t}\n\t}\n\n\tprivate stopAllTimers() {\n\t\tthis.clearT1();\n\t\tthis.clearT2();\n\t\tthis.clearT4();\n\t}\n\n\tprivate startT1() {\n\t\tthis.clearT1();\n\t\tif (this.timeoutT1 <= 0) return;\n\t\tthis.t1Timer = setTimeout(() => {\n\t\t\tthis.handleT1Timeout();\n\t\t}, this.timeoutT1 * 1000);\n\t}\n\n\tprivate startT2() {\n\t\tthis.clearT2();\n\t\tif (this.timeoutT2 <= 0) return;\n\t\tconst timeout = this.timeoutT2 * 1000;\n\t\tthis.t2Timer = setTimeout(() => {\n\t\t\tthis.handleT2Timeout();\n\t\t}, timeout);\n\t}\n\n\tprivate startT4() {\n\t\tthis.clearT4();\n\t\tif (this.timeoutT4 <= 0) return;\n\t\tconst timeout = this.timeoutT4 * 1000;\n\t\tthis.t4Timer = setTimeout(() => {\n\t\t\tthis.handleT4Timeout();\n\t\t}, timeout);\n\t}\n\n\tprivate handleT1Timeout() {\n\t\tconsole.warn(\"T1 Timeout\");\n\t\tthis.t1Timer = null;\n\t\tthis.emit(\"error\", new Error(\"T1 Timeout\"));\n\t\tthis.resetState();\n\t}\n\n\tprivate handleT2Timeout() {\n\t\tconsole.warn(\"T2 Timeout\");\n\t\tthis.t2Timer = null;\n\t\tif (this.state === CommState.WAIT_EOT) {\n\t\t\tthis.retryCount++;\n\t\t\tif (this.retryCount > this.retry) {\n\t\t\t\tthis.emit(\"error\", new Error(\"Retry limit exceeded waiting for EOT\"));\n\t\t\t\tthis.resetState();\n\t\t\t} else {\n\t\t\t\tconsole.log(`Retrying ENQ (${this.retryCount}/${this.retry})`);\n\t\t\t\tthis.sendByte(ENQ);\n\t\t\t\tthis.startT2();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.state === CommState.WAIT_ACK) {\n\t\t\tthis.retryCount++;\n\t\t\tif (this.retryCount > this.retry) {\n\t\t\t\tthis.emit(\"error\", new Error(\"Retry limit exceeded waiting for ACK\"));\n\t\t\t\tthis.resetState();\n\t\t\t} else {\n\t\t\t\tconsole.log(`Retrying Block (${this.retryCount}/${this.retry})`);\n\t\t\t\tthis.sendCurrentBlock();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.resetState();\n\t}\n\n\tprivate handleT4Timeout() {\n\t\tconsole.warn(\"T4 Timeout (Inter-block)\");\n\t\tthis.t4Timer = null;\n\t\tthis.resetState();\n\t}\n\n\tprivate resetState() {\n\t\tthis.state = CommState.IDLE;\n\t\tthis.receivedBlocks = [];\n\t\tthis.expectedBlockNum = 1;\n\t\tthis.currentBlockLength = 0;\n\t\tthis.stopAllTimers();\n\t}\n\n\tprivate sendByte(byte: number) {\n\t\tconst stream = this.stream;\n\t\tif (stream && !stream.destroyed) {\n\t\t\tstream.write(Buffer.from([byte]));\n\t\t}\n\t}\n\n\tprivate processBuffer() {\n\t\twhile (this.buffer.length > 0) {\n\t\t\tswitch (this.state) {\n\t\t\t\tcase CommState.IDLE: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === ENQ) {\n\t\t\t\t\t\tthis.sendByte(EOT);\n\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\tthis.receivedBlocks = [];\n\t\t\t\t\t\tthis.expectedBlockNum = 1;\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_EOT: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === EOT) {\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tthis.currentBlockIndex = 0;\n\t\t\t\t\t\tthis.sendCurrentBlock();\n\t\t\t\t\t} else if (byte === ENQ) {\n\t\t\t\t\t\tif (!this.isMaster) {\n\t\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\t\tthis.sendByte(EOT);\n\t\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\t\tthis.receivedBlocks = [];\n\t\t\t\t\t\t\tthis.expectedBlockNum = 1;\n\t\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_ACK: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === ACK) {\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tconst currentBlock = this.currentBlocks[this.currentBlockIndex];\n\t\t\t\t\t\tif (currentBlock.eBit) {\n\t\t\t\t\t\t\tthis.currentBlocks = [];\n\t\t\t\t\t\t\tthis.retryCount = 0;\n\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\tthis.processSendQueue();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.currentBlockIndex++;\n\t\t\t\t\t\t\tthis.sendCurrentBlock();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (byte === NAK) {\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tthis.handleT2Timeout();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_BLOCK_LENGTH: {\n\t\t\t\t\tif (this.t4Timer) {\n\t\t\t\t\t\tthis.clearT4();\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tconst len = this.buffer[0];\n\t\t\t\t\tif (len < 10) {\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.currentBlockLength = len;\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_DATA;\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_BLOCK_DATA: {\n\t\t\t\t\tif (this.buffer.length >= this.currentBlockLength + 2) {\n\t\t\t\t\t\tconst blockData = this.buffer.subarray(\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tthis.currentBlockLength + 2,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(this.currentBlockLength + 2);\n\t\t\t\t\t\tthis.stopAllTimers();\n\n\t\t\t\t\t\tconst fullBlockBuffer = Buffer.alloc(1 + blockData.length);\n\t\t\t\t\t\tfullBlockBuffer[0] = this.currentBlockLength;\n\t\t\t\t\t\tblockData.copy(fullBlockBuffer, 1);\n\n\t\t\t\t\t\tconst block = new Secs1MessageBlock(fullBlockBuffer);\n\t\t\t\t\t\tif (block.isValid()) {\n\t\t\t\t\t\t\tif (block.blockNumber !== this.expectedBlockNum) {\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`Wrong Block Number. Expected ${this.expectedBlockNum}, got ${block.blockNumber}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tthis.sendByte(NAK);\n\t\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.sendByte(ACK);\n\t\t\t\t\t\t\t\tthis.receivedBlocks.push(block);\n\t\t\t\t\t\t\t\tif (block.eBit) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tconst msg = Secs1Message.fromBlocks(this.receivedBlocks);\n\t\t\t\t\t\t\t\t\t\tthis.handleMessage(msg);\n\t\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t\t\t\t\te instanceof Error ? e : new Error(String(e)),\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t\t\t\tthis.processSendQueue();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthis.expectedBlockNum++;\n\t\t\t\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\t\t\t\tthis.startT4();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.warn(\"Invalid Checksum\");\n\t\t\t\t\t\t\tthis.sendByte(NAK);\n\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate sendCurrentBlock() {\n\t\tif (this.currentBlockIndex >= this.currentBlocks.length) return;\n\t\tconst stream = this.stream;\n\t\tconst block = this.currentBlocks[this.currentBlockIndex];\n\t\tif (stream && !stream.destroyed) {\n\t\t\tstream.write(block.buffer);\n\t\t\tthis.state = CommState.WAIT_ACK;\n\t\t\tthis.startT2();\n\t\t}\n\t}\n\n\tprivate processSendQueue() {\n\t\tif (this.state !== CommState.IDLE) return;\n\t\tconst stream = this.stream;\n\t\tif (!stream || stream.destroyed) return;\n\t\tif (this.sendQueue.length === 0) return;\n\n\t\tconst buffer = this.sendQueue.shift();\n\t\tif (!buffer) return;\n\n\t\ttry {\n\t\t\tconst rBit = (buffer[0] & 0x80) === 0x80;\n\t\t\tconst deviceId = ((buffer[0] & 0x7f) << 8) | buffer[1];\n\t\t\tconst streamId = buffer[2] & 0x7f;\n\t\t\tconst wBit = (buffer[2] & 0x80) === 0x80;\n\t\t\tconst func = buffer[3];\n\t\t\tconst systemBytes = buffer.readUInt32BE(6);\n\t\t\tconst bodyBuffer = buffer.subarray(10);\n\n\t\t\tthis.currentBlocks = [];\n\t\t\tlet pos = 0;\n\t\t\tlet blockNum = 1;\n\n\t\t\tif (bodyBuffer.length === 0) {\n\t\t\t\tthis.currentBlocks.push(\n\t\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\t\tdeviceId,\n\t\t\t\t\t\tstreamId,\n\t\t\t\t\t\tfunc,\n\t\t\t\t\t\twBit,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t\tblockNum,\n\t\t\t\t\t\tsystemBytes,\n\t\t\t\t\t\tBuffer.alloc(0),\n\t\t\t\t\t\trBit,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\twhile (pos < bodyBuffer.length) {\n\t\t\t\t\tconst remaining = bodyBuffer.length - pos;\n\t\t\t\t\tconst chunkSize = remaining > 244 ? 244 : remaining;\n\t\t\t\t\tconst chunk = bodyBuffer.subarray(pos, pos + chunkSize);\n\t\t\t\t\tconst isLast = pos + chunkSize >= bodyBuffer.length;\n\n\t\t\t\t\tthis.currentBlocks.push(\n\t\t\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\t\t\tdeviceId,\n\t\t\t\t\t\t\tstreamId,\n\t\t\t\t\t\t\tfunc,\n\t\t\t\t\t\t\twBit,\n\t\t\t\t\t\t\tisLast,\n\t\t\t\t\t\t\tblockNum,\n\t\t\t\t\t\t\tsystemBytes,\n\t\t\t\t\t\t\tchunk,\n\t\t\t\t\t\t\trBit,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tpos += chunkSize;\n\t\t\t\t\tblockNum++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.currentBlockIndex = 0;\n\t\t\tthis.retryCount = 0;\n\n\t\t\tthis.sendByte(ENQ);\n\t\t\tthis.state = CommState.WAIT_EOT;\n\t\t\tthis.startT2();\n\t\t} catch (e) {\n\t\t\tthis.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n\t\t\tthis.processSendQueue();\n\t\t}\n\t}\n\n\tprivate rejectAllTransactions(err: Error) {\n\t\tfor (const [systemBytes, tx] of this._transactions) {\n\t\t\tclearTimeout(tx.timer);\n\t\t\ttx.reject(err);\n\t\t\tthis._transactions.delete(systemBytes);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Secs1Communicator.js","names":[],"sources":["../../src/secs1/Secs1Communicator.ts"],"sourcesContent":["import { Duplex } from \"stream\";\nimport {\n\tAbstractSecsCommunicator,\n\tSecsCommunicatorConfig,\n} from \"../core/AbstractSecsCommunicator.js\";\nimport { SecsMessage } from \"../core/AbstractSecsMessage.js\";\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\nimport { Secs1Message } from \"./Secs1Message.js\";\nimport { Secs1MessageBlock } from \"./Secs1MessageBlock.js\";\n\nexport interface Secs1CommunicatorConfig extends SecsCommunicatorConfig {\n\tretry?: number;\n\tisMaster?: boolean;\n}\n\nconst ENQ = 0x05;\nconst EOT = 0x04;\nconst ACK = 0x06;\nconst NAK = 0x15;\n\nenum CommState {\n\tIDLE,\n\tWAIT_EOT,\n\tWAIT_ACK,\n\tWAIT_BLOCK_LENGTH,\n\tWAIT_BLOCK_DATA,\n}\n\nexport abstract class Secs1Communicator extends AbstractSecsCommunicator {\n\tpublic retry: number;\n\tpublic isMaster: boolean;\n\n\tprotected stream: Duplex | null = null;\n\n\tprivate state: CommState = CommState.IDLE;\n\tprivate buffer: Buffer = Buffer.alloc(0);\n\n\tprivate sendQueue: Buffer[] = [];\n\tprivate currentBlocks: Secs1MessageBlock[] = [];\n\tprivate currentBlockIndex = 0;\n\tprivate retryCount = 0;\n\n\tprivate receivedBlocks: Secs1MessageBlock[] = [];\n\tprivate expectedBlockNum = 1;\n\tprivate currentBlockLength = 0;\n\n\tprivate t1Timer: NodeJS.Timeout | null = null;\n\tprivate t2Timer: NodeJS.Timeout | null = null;\n\tprivate t4Timer: NodeJS.Timeout | null = null;\n\n\tconstructor(config: Secs1CommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.retry = config.retry ?? 3;\n\t\tthis.isMaster = config.isMaster ?? false;\n\t}\n\n\tprotected attachStream(stream: Duplex) {\n\t\tthis.stream = stream;\n\t\tthis.resetState();\n\t\tthis.buffer = Buffer.alloc(0);\n\t\tthis.logger.logState(\"SECS1\", \"NotConnected\", \"Connected\");\n\n\t\tstream.on(\"data\", (data: Buffer) => {\n\t\t\tthis.logger.logBytes(\"Received\", \"SECS1\", data, {\n\t\t\t\tchunkLength: data.length,\n\t\t\t});\n\t\t\tthis.buffer = Buffer.concat([this.buffer, data]);\n\t\t\tif (\n\t\t\t\tthis.state === CommState.WAIT_BLOCK_LENGTH ||\n\t\t\t\tthis.state === CommState.WAIT_BLOCK_DATA\n\t\t\t) {\n\t\t\t\tthis.startT1();\n\t\t\t}\n\t\t\tthis.processBuffer();\n\t\t});\n\n\t\tstream.on(\"close\", () => {\n\t\t\tthis.rejectAllTransactions(new Error(\"Stream closed\"));\n\t\t\tthis.stream = null;\n\t\t\tthis.emit(\"disconnected\");\n\t\t\tthis.logger.logState(\"SECS1\", \"Connected\", \"NotConnected\");\n\t\t\tthis.stopAllTimers();\n\t\t\tthis.resetState();\n\t\t});\n\n\t\tstream.on(\"error\", (err: Error) => {\n\t\t\tthis.emit(\"error\", err);\n\t\t});\n\n\t\tthis.emit(\"connected\");\n\t\tthis.processSendQueue();\n\t}\n\n\tprotected stop() {\n\t\tconst stream = this.stream;\n\t\tif (stream && !stream.destroyed) {\n\t\t\tstream.destroy();\n\t\t}\n\t\tthis.stream = null;\n\t\tthis.stopAllTimers();\n\t\tthis.resetState();\n\t}\n\n\tprotected override async sendBufferWithLogs(\n\t\tdirection: \"Sent\" | \"Received\",\n\t\tprotocol: string,\n\t\tbuffer: Buffer,\n\t\tmeta?: Record<string, unknown>,\n\t): Promise<void> {\n\t\tawait super.sendBufferWithLogs(\n\t\t\tdirection,\n\t\t\tprotocol === \"SECS\" ? \"SECS1\" : protocol,\n\t\t\tbuffer,\n\t\t\t{\n\t\t\t\tcommState: CommState[this.state],\n\t\t\t\t...meta,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override sendBuffer(buffer: Buffer): Promise<void> {\n\t\tthis.sendQueue.push(buffer);\n\t\tprocess.nextTick(() => this.processSendQueue());\n\t\treturn Promise.resolve();\n\t}\n\n\tprotected override createMessage(\n\t\tstream: number,\n\t\tfunc: number,\n\t\twBit: boolean,\n\t\tbody: AbstractSecs2Item | null,\n\t\tsystemBytes: number,\n\t): SecsMessage {\n\t\treturn new Secs1Message(\n\t\t\tstream,\n\t\t\tfunc,\n\t\t\twBit,\n\t\t\tbody,\n\t\t\tsystemBytes,\n\t\t\tthis.deviceId,\n\t\t);\n\t}\n\n\tprivate clearT1() {\n\t\tif (this.t1Timer) {\n\t\t\tclearTimeout(this.t1Timer);\n\t\t\tthis.t1Timer = null;\n\t\t}\n\t}\n\n\tprivate clearT2() {\n\t\tif (this.t2Timer) {\n\t\t\tclearTimeout(this.t2Timer);\n\t\t\tthis.t2Timer = null;\n\t\t}\n\t}\n\n\tprivate clearT4() {\n\t\tif (this.t4Timer) {\n\t\t\tclearTimeout(this.t4Timer);\n\t\t\tthis.t4Timer = null;\n\t\t}\n\t}\n\n\tprivate stopAllTimers() {\n\t\tthis.clearT1();\n\t\tthis.clearT2();\n\t\tthis.clearT4();\n\t}\n\n\tprivate startT1() {\n\t\tthis.clearT1();\n\t\tif (this.timeoutT1 <= 0) return;\n\t\tthis.t1Timer = setTimeout(() => {\n\t\t\tthis.handleT1Timeout();\n\t\t}, this.timeoutT1 * 1000);\n\t}\n\n\tprivate startT2() {\n\t\tthis.clearT2();\n\t\tif (this.timeoutT2 <= 0) return;\n\t\tconst timeout = this.timeoutT2 * 1000;\n\t\tthis.t2Timer = setTimeout(() => {\n\t\t\tthis.handleT2Timeout();\n\t\t}, timeout);\n\t}\n\n\tprivate startT4() {\n\t\tthis.clearT4();\n\t\tif (this.timeoutT4 <= 0) return;\n\t\tconst timeout = this.timeoutT4 * 1000;\n\t\tthis.t4Timer = setTimeout(() => {\n\t\t\tthis.handleT4Timeout();\n\t\t}, timeout);\n\t}\n\n\tprivate handleT1Timeout() {\n\t\tthis.logger.detail.warn({ protocol: \"SECS1\" }, \"t1 timeout\");\n\t\tthis.t1Timer = null;\n\t\tthis.emit(\"error\", new Error(\"T1 Timeout\"));\n\t\tthis.resetState();\n\t}\n\n\tprivate handleT2Timeout() {\n\t\tthis.logger.detail.warn({ protocol: \"SECS1\" }, \"t2 timeout\");\n\t\tthis.t2Timer = null;\n\t\tif (this.state === CommState.WAIT_EOT) {\n\t\t\tthis.retryCount++;\n\t\t\tif (this.retryCount > this.retry) {\n\t\t\t\tthis.emit(\"error\", new Error(\"Retry limit exceeded waiting for EOT\"));\n\t\t\t\tthis.resetState();\n\t\t\t} else {\n\t\t\t\tthis.logger.detail.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\tretryCount: this.retryCount,\n\t\t\t\t\t\tretry: this.retry,\n\t\t\t\t\t},\n\t\t\t\t\t\"retrying ENQ\",\n\t\t\t\t);\n\t\t\t\tthis.sendByte(ENQ);\n\t\t\t\tthis.startT2();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.state === CommState.WAIT_ACK) {\n\t\t\tthis.retryCount++;\n\t\t\tif (this.retryCount > this.retry) {\n\t\t\t\tthis.emit(\"error\", new Error(\"Retry limit exceeded waiting for ACK\"));\n\t\t\t\tthis.resetState();\n\t\t\t} else {\n\t\t\t\tthis.logger.detail.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\tretryCount: this.retryCount,\n\t\t\t\t\t\tretry: this.retry,\n\t\t\t\t\t},\n\t\t\t\t\t\"retrying block\",\n\t\t\t\t);\n\t\t\t\tthis.sendCurrentBlock();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.resetState();\n\t}\n\n\tprivate handleT4Timeout() {\n\t\tthis.logger.detail.warn({ protocol: \"SECS1\" }, \"t4 timeout\");\n\t\tthis.t4Timer = null;\n\t\tthis.resetState();\n\t}\n\n\tprivate resetState() {\n\t\tthis.state = CommState.IDLE;\n\t\tthis.receivedBlocks = [];\n\t\tthis.expectedBlockNum = 1;\n\t\tthis.currentBlockLength = 0;\n\t\tthis.stopAllTimers();\n\t}\n\n\tprivate sendByte(byte: number) {\n\t\tconst stream = this.stream;\n\t\tif (stream && !stream.destroyed) {\n\t\t\tconst buf = Buffer.from([byte]);\n\t\t\tthis.logger.logBytes(\"Sent\", \"SECS1\", buf);\n\t\t\tstream.write(buf);\n\t\t}\n\t}\n\n\tprivate processBuffer() {\n\t\twhile (this.buffer.length > 0) {\n\t\t\tswitch (this.state) {\n\t\t\t\tcase CommState.IDLE: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === ENQ) {\n\t\t\t\t\t\tthis.logger.detail.debug({ protocol: \"SECS1\" }, \"rx ENQ\");\n\t\t\t\t\t\tthis.sendByte(EOT);\n\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\tthis.receivedBlocks = [];\n\t\t\t\t\t\tthis.expectedBlockNum = 1;\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_EOT: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === EOT) {\n\t\t\t\t\t\tthis.logger.detail.debug({ protocol: \"SECS1\" }, \"rx EOT\");\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tthis.currentBlockIndex = 0;\n\t\t\t\t\t\tthis.sendCurrentBlock();\n\t\t\t\t\t} else if (byte === ENQ) {\n\t\t\t\t\t\tif (!this.isMaster) {\n\t\t\t\t\t\t\tthis.logger.detail.debug(\n\t\t\t\t\t\t\t\t{ protocol: \"SECS1\" },\n\t\t\t\t\t\t\t\t\"rx ENQ while waiting EOT\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\t\tthis.sendByte(EOT);\n\t\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\t\tthis.receivedBlocks = [];\n\t\t\t\t\t\t\tthis.expectedBlockNum = 1;\n\t\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_ACK: {\n\t\t\t\t\tconst byte = this.buffer[0];\n\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\tif (byte === ACK) {\n\t\t\t\t\t\tthis.logger.detail.debug({ protocol: \"SECS1\" }, \"rx ACK\");\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tconst currentBlock = this.currentBlocks[this.currentBlockIndex];\n\t\t\t\t\t\tif (currentBlock.eBit) {\n\t\t\t\t\t\t\tthis.currentBlocks = [];\n\t\t\t\t\t\t\tthis.retryCount = 0;\n\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\tthis.processSendQueue();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.currentBlockIndex++;\n\t\t\t\t\t\t\tthis.sendCurrentBlock();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (byte === NAK) {\n\t\t\t\t\t\tthis.logger.detail.warn({ protocol: \"SECS1\" }, \"rx NAK\");\n\t\t\t\t\t\tthis.clearT2();\n\t\t\t\t\t\tthis.handleT2Timeout();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_BLOCK_LENGTH: {\n\t\t\t\t\tif (this.t4Timer) {\n\t\t\t\t\t\tthis.clearT4();\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tconst len = this.buffer[0];\n\t\t\t\t\tif (len < 10) {\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.currentBlockLength = len;\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(1);\n\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_DATA;\n\t\t\t\t\t\tthis.startT1();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CommState.WAIT_BLOCK_DATA: {\n\t\t\t\t\tif (this.buffer.length >= this.currentBlockLength + 2) {\n\t\t\t\t\t\tconst blockData = this.buffer.subarray(\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tthis.currentBlockLength + 2,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.buffer = this.buffer.subarray(this.currentBlockLength + 2);\n\t\t\t\t\t\tthis.stopAllTimers();\n\n\t\t\t\t\t\tconst fullBlockBuffer = Buffer.alloc(1 + blockData.length);\n\t\t\t\t\t\tfullBlockBuffer[0] = this.currentBlockLength;\n\t\t\t\t\t\tblockData.copy(fullBlockBuffer, 1);\n\n\t\t\t\t\t\tconst block = new Secs1MessageBlock(fullBlockBuffer);\n\t\t\t\t\t\tif (block.isValid()) {\n\t\t\t\t\t\t\tif (block.blockNumber !== this.expectedBlockNum) {\n\t\t\t\t\t\t\t\tthis.logger.detail.warn(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\t\t\t\t\texpected: this.expectedBlockNum,\n\t\t\t\t\t\t\t\t\t\tgot: block.blockNumber,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"wrong block number\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tthis.sendByte(NAK);\n\t\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.sendByte(ACK);\n\t\t\t\t\t\t\t\tthis.receivedBlocks.push(block);\n\t\t\t\t\t\t\t\tif (block.eBit) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tconst msg = Secs1Message.fromBlocks(this.receivedBlocks);\n\t\t\t\t\t\t\t\t\t\tthis.handleMessage(msg);\n\t\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\t\tthis.emit(\n\t\t\t\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t\t\t\t\te instanceof Error ? e : new Error(String(e)),\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t\t\t\tthis.processSendQueue();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthis.expectedBlockNum++;\n\t\t\t\t\t\t\t\t\tthis.state = CommState.WAIT_BLOCK_LENGTH;\n\t\t\t\t\t\t\t\t\tthis.startT4();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.logger.detail.warn(\n\t\t\t\t\t\t\t\t{ protocol: \"SECS1\" },\n\t\t\t\t\t\t\t\t\"invalid checksum\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthis.sendByte(NAK);\n\t\t\t\t\t\t\tthis.state = CommState.IDLE;\n\t\t\t\t\t\t\tthis.resetState();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate sendCurrentBlock() {\n\t\tif (this.currentBlockIndex >= this.currentBlocks.length) return;\n\t\tconst stream = this.stream;\n\t\tconst block = this.currentBlocks[this.currentBlockIndex];\n\t\tif (stream && !stream.destroyed) {\n\t\t\tthis.logger.logBytes(\"Sent\", \"SECS1\", block.buffer, {\n\t\t\t\tblockNumber: block.blockNumber,\n\t\t\t\teBit: block.eBit,\n\t\t\t\tsystemBytes: block.systemBytes,\n\t\t\t\tstream: block.stream,\n\t\t\t\tfunc: block.func,\n\t\t\t});\n\t\t\tstream.write(block.buffer);\n\t\t\tthis.state = CommState.WAIT_ACK;\n\t\t\tthis.startT2();\n\t\t}\n\t}\n\n\tprivate processSendQueue() {\n\t\tif (this.state !== CommState.IDLE) return;\n\t\tconst stream = this.stream;\n\t\tif (!stream || stream.destroyed) return;\n\t\tif (this.sendQueue.length === 0) return;\n\n\t\tconst buffer = this.sendQueue.shift();\n\t\tif (!buffer) return;\n\n\t\ttry {\n\t\t\tconst rBit = (buffer[0] & 0x80) === 0x80;\n\t\t\tconst deviceId = ((buffer[0] & 0x7f) << 8) | buffer[1];\n\t\t\tconst streamId = buffer[2] & 0x7f;\n\t\t\tconst wBit = (buffer[2] & 0x80) === 0x80;\n\t\t\tconst func = buffer[3];\n\t\t\tconst systemBytes = buffer.readUInt32BE(6);\n\t\t\tconst bodyBuffer = buffer.subarray(10);\n\n\t\t\tthis.currentBlocks = [];\n\t\t\tlet pos = 0;\n\t\t\tlet blockNum = 1;\n\n\t\t\tif (bodyBuffer.length === 0) {\n\t\t\t\tthis.currentBlocks.push(\n\t\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\t\tdeviceId,\n\t\t\t\t\t\tstreamId,\n\t\t\t\t\t\tfunc,\n\t\t\t\t\t\twBit,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t\tblockNum,\n\t\t\t\t\t\tsystemBytes,\n\t\t\t\t\t\tBuffer.alloc(0),\n\t\t\t\t\t\trBit,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\twhile (pos < bodyBuffer.length) {\n\t\t\t\t\tconst remaining = bodyBuffer.length - pos;\n\t\t\t\t\tconst chunkSize = remaining > 244 ? 244 : remaining;\n\t\t\t\t\tconst chunk = bodyBuffer.subarray(pos, pos + chunkSize);\n\t\t\t\t\tconst isLast = pos + chunkSize >= bodyBuffer.length;\n\n\t\t\t\t\tthis.currentBlocks.push(\n\t\t\t\t\t\tSecs1MessageBlock.fromParts(\n\t\t\t\t\t\t\tdeviceId,\n\t\t\t\t\t\t\tstreamId,\n\t\t\t\t\t\t\tfunc,\n\t\t\t\t\t\t\twBit,\n\t\t\t\t\t\t\tisLast,\n\t\t\t\t\t\t\tblockNum,\n\t\t\t\t\t\t\tsystemBytes,\n\t\t\t\t\t\t\tchunk,\n\t\t\t\t\t\t\trBit,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tpos += chunkSize;\n\t\t\t\t\tblockNum++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.currentBlockIndex = 0;\n\t\t\tthis.retryCount = 0;\n\n\t\t\tthis.sendByte(ENQ);\n\t\t\tthis.state = CommState.WAIT_EOT;\n\t\t\tthis.startT2();\n\t\t} catch (e) {\n\t\t\tthis.emit(\"error\", e instanceof Error ? e : new Error(String(e)));\n\t\t\tthis.processSendQueue();\n\t\t}\n\t}\n\n\tprivate rejectAllTransactions(err: Error) {\n\t\tfor (const [systemBytes, tx] of this._transactions) {\n\t\t\tclearTimeout(tx.timer);\n\t\t\ttx.reject(err);\n\t\t\tthis._transactions.delete(systemBytes);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -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\";\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
|
+
{"version":3,"file":"Secs1Message.js","names":["blocks: Secs1MessageBlock[]","body: AbstractSecs2Item | null"],"sources":["../../src/secs1/Secs1Message.ts"],"sourcesContent":["import { SecsMessage } from \"../core/AbstractSecsMessage.js\";\r\nimport { AbstractSecs2Item } from \"../core/secs2item/AbstractSecs2Item.js\";\r\nimport { Secs2ItemParser } from \"../core/secs2item/Secs2ItemParser.js\";\r\nimport { Secs1MessageBlock } from \"./Secs1MessageBlock.js\";\r\n\r\nexport class Secs1Message 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 rBit = false,\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 Splits the message into SECS-I blocks.\r\n\t */\r\n\ttoBlocks(): Secs1MessageBlock[] {\r\n\t\tconst bodyBuffer = this.body ? this.body.toBuffer() : Buffer.alloc(0);\r\n\t\tconst blocks: Secs1MessageBlock[] = [];\r\n\r\n\t\tlet pos = 0;\r\n\t\tlet blockNum = 1;\r\n\r\n\t\tif (bodyBuffer.length === 0) {\r\n\t\t\t// Header only block? Or empty body block?\r\n\t\t\t// SECS-I allows empty body.\r\n\t\t\tblocks.push(\r\n\t\t\t\tSecs1MessageBlock.fromParts(\r\n\t\t\t\t\tthis.deviceId,\r\n\t\t\t\t\tthis.stream,\r\n\t\t\t\t\tthis.func,\r\n\t\t\t\t\tthis.wBit,\r\n\t\t\t\t\ttrue, // E-Bit\r\n\t\t\t\t\tblockNum,\r\n\t\t\t\t\tthis.systemBytes,\r\n\t\t\t\t\tBuffer.alloc(0),\r\n\t\t\t\t\tthis.rBit,\r\n\t\t\t\t),\r\n\t\t\t);\r\n\t\t\treturn blocks;\r\n\t\t}\r\n\r\n\t\twhile (pos < bodyBuffer.length) {\r\n\t\t\tconst remaining = bodyBuffer.length - pos;\r\n\t\t\tconst chunkSize = remaining > 244 ? 244 : remaining;\r\n\t\t\tconst chunk = bodyBuffer.subarray(pos, pos + chunkSize);\r\n\t\t\tconst isLast = pos + chunkSize >= bodyBuffer.length;\r\n\r\n\t\t\tblocks.push(\r\n\t\t\t\tSecs1MessageBlock.fromParts(\r\n\t\t\t\t\tthis.deviceId,\r\n\t\t\t\t\tthis.stream,\r\n\t\t\t\t\tthis.func,\r\n\t\t\t\t\tthis.wBit,\r\n\t\t\t\t\tisLast, // E-Bit\r\n\t\t\t\t\tblockNum,\r\n\t\t\t\t\tthis.systemBytes,\r\n\t\t\t\t\tchunk,\r\n\t\t\t\t\tthis.rBit,\r\n\t\t\t\t),\r\n\t\t\t);\r\n\r\n\t\t\tpos += chunkSize;\r\n\t\t\tblockNum++;\r\n\t\t\tif (blockNum > 0x7fff) {\r\n\t\t\t\tthrow new Error(\"Block number overflow\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn blocks;\r\n\t}\r\n\r\n\t/**\r\n\t * Reassembles a SECS-I message from blocks.\r\n\t */\r\n\tstatic fromBlocks(blocks: Secs1MessageBlock[]): Secs1Message {\r\n\t\tif (blocks.length === 0) {\r\n\t\t\tthrow new Error(\"No blocks to reassemble\");\r\n\t\t}\r\n\r\n\t\t// Sort by block number just in case (though usually they come in order)\r\n\t\tblocks.sort((a, b) => a.blockNumber - b.blockNumber);\r\n\r\n\t\t// Validate sequence\r\n\t\tfor (let i = 0; i < blocks.length; i++) {\r\n\t\t\tif (blocks[i].blockNumber !== i + 1) {\r\n\t\t\t\tthrow new Error(`Missing block ${i + 1}`);\r\n\t\t\t}\r\n\t\t\t// Check if all blocks belong to the same message (SystemBytes, Stream, Func, DeviceID)\r\n\t\t\tif (i > 0) {\r\n\t\t\t\tconst prev = blocks[i - 1];\r\n\t\t\t\tconst curr = blocks[i];\r\n\t\t\t\tif (\r\n\t\t\t\t\tprev.systemBytes !== curr.systemBytes ||\r\n\t\t\t\t\tprev.stream !== curr.stream ||\r\n\t\t\t\t\tprev.func !== curr.func ||\r\n\t\t\t\t\tprev.deviceId !== curr.deviceId\r\n\t\t\t\t) {\r\n\t\t\t\t\tthrow new Error(\"Blocks do not belong to the same message\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!blocks[blocks.length - 1].eBit) {\r\n\t\t\tthrow new Error(\"Last block missing E-Bit\");\r\n\t\t}\r\n\r\n\t\tconst first = blocks[0];\r\n\t\tconst bodyBuffers = blocks.map((b) => b.body);\r\n\t\tconst fullBodyBuffer = Buffer.concat(bodyBuffers);\r\n\r\n\t\tlet body: AbstractSecs2Item | null = null;\r\n\t\tif (fullBodyBuffer.length > 0) {\r\n\t\t\tconst result = Secs2ItemParser.fromBuffer(fullBodyBuffer);\r\n\t\t\tbody = result.item;\r\n\t\t}\r\n\r\n\t\treturn new Secs1Message(\r\n\t\t\tfirst.stream,\r\n\t\t\tfirst.func,\r\n\t\t\tfirst.wBit,\r\n\t\t\tbody,\r\n\t\t\tfirst.systemBytes,\r\n\t\t\tfirst.deviceId,\r\n\t\t\tfirst.rBit,\r\n\t\t);\r\n\t}\r\n\r\n\t// Override toBuffer to return logical buffer (10-byte header + body)\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: R-Bit + DeviceID MSB\r\n\t\tlet b0 = (this.deviceId >> 8) & 0x7f;\r\n\t\tif (this.rBit) b0 |= 0x80;\r\n\t\theader[0] = b0;\r\n\r\n\t\t// Byte 1: DeviceID LSB\r\n\t\theader[1] = this.deviceId & 0xff;\r\n\r\n\t\t// Byte 2: W-Bit + Stream\r\n\t\tlet b2 = this.stream & 0x7f;\r\n\t\tif (this.wBit) b2 |= 0x80;\r\n\t\theader[2] = b2;\r\n\r\n\t\t// Byte 3: Function\r\n\t\theader[3] = this.func & 0xff;\r\n\r\n\t\t// Byte 4: E-Bit + BlockNum (Logical 0 or 1? For logical message, maybe 0)\r\n\t\t// Usually block number is per block. For a \"Message Buffer\", we don't have block number.\r\n\t\t// We'll leave it 0 or standard.\r\n\t\theader[4] = 0;\r\n\t\theader[5] = 0;\r\n\r\n\t\t// Byte 6-9: System Bytes\r\n\t\theader.writeUInt32BE(this.systemBytes, 6);\r\n\r\n\t\treturn Buffer.concat([header, bodyBuffer]);\r\n\t}\r\n}\r\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 {\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
|
+
{"version":3,"file":"Secs1MessageBlock.js","names":["buffer: Buffer"],"sources":["../../src/secs1/Secs1MessageBlock.ts"],"sourcesContent":["export class Secs1MessageBlock {\r\n\t// The full block bytes: [Length(1)] + [Header(10)] + [Body(0-244)] + [Checksum(2)]\r\n\tconstructor(public readonly buffer: Buffer) {}\r\n\r\n\tget length(): number {\r\n\t\treturn this.buffer[0];\r\n\t}\r\n\r\n\tget header(): Buffer {\r\n\t\treturn this.buffer.subarray(1, 11);\r\n\t}\r\n\r\n\tget body(): Buffer {\r\n\t\treturn this.buffer.subarray(11, this.buffer.length - 2);\r\n\t}\r\n\r\n\tget checksum(): number {\r\n\t\treturn this.buffer.readUInt16BE(this.buffer.length - 2);\r\n\t}\r\n\r\n\t// Parsed fields\r\n\tget deviceId(): number {\r\n\t\tconst bs = this.buffer.subarray(1, 3);\r\n\t\treturn ((bs[0] << 8) & 0x7f00) | bs[1];\r\n\t}\r\n\r\n\tget rBit(): boolean {\r\n\t\treturn (this.buffer[1] & 0x80) === 0x80;\r\n\t}\r\n\r\n\tget stream(): number {\r\n\t\treturn this.buffer[3] & 0x7f;\r\n\t}\r\n\r\n\tget wBit(): boolean {\r\n\t\treturn (this.buffer[3] & 0x80) === 0x80;\r\n\t}\r\n\r\n\tget func(): number {\r\n\t\treturn this.buffer[4];\r\n\t}\r\n\r\n\tget blockNumber(): number {\r\n\t\tconst bs = this.buffer.subarray(5, 7);\r\n\t\treturn ((bs[0] << 8) & 0x7f00) | bs[1];\r\n\t}\r\n\r\n\tget eBit(): boolean {\r\n\t\treturn (this.buffer[5] & 0x80) === 0x80;\r\n\t}\r\n\r\n\tget systemBytes(): number {\r\n\t\treturn this.buffer.readUInt32BE(7);\r\n\t}\r\n\r\n\tisValid(): boolean {\r\n\t\tif (this.buffer.length < 13) return false; // Len(1) + Head(10) + Sum(2)\r\n\t\tif (this.buffer[0] !== this.buffer.length - 3) return false; // Length byte excludes itself and checksum?\r\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?\r\n\t\t// No, standard says: \"Length byte is the number of bytes in the block header plus the text.\" (Header + Body).\r\n\t\t// So Length = 10 + BodyLength.\r\n\t\t// Total buffer size = 1 (LenByte) + Length + 2 (Checksum).\r\n\t\t// So Buffer Length = 1 + Length + 2 = Length + 3.\r\n\t\t// So Length Byte value should be BufferLength - 3.\r\n\r\n\t\tconst calculatedSum = this.calculateChecksum();\r\n\t\treturn calculatedSum === this.checksum;\r\n\t}\r\n\r\n\tprivate calculateChecksum(): number {\r\n\t\t// Sum of bytes from Header to end of Body (excluding Length byte and Checksum bytes)\r\n\t\t// i.e., indices 1 to length-3\r\n\t\tlet sum = 0;\r\n\t\tfor (let i = 1; i < this.buffer.length - 2; i++) {\r\n\t\t\tsum += this.buffer[i];\r\n\t\t}\r\n\t\treturn sum & 0xffff;\r\n\t}\r\n\r\n\tstatic fromParts(\r\n\t\tdeviceId: number,\r\n\t\tstream: number,\r\n\t\tfunc: number,\r\n\t\twBit: boolean,\r\n\t\teBit: boolean,\r\n\t\tblockNum: number,\r\n\t\tsystemBytes: number,\r\n\t\tbody: Buffer,\r\n\t\trBit = false,\r\n\t): Secs1MessageBlock {\r\n\t\tconst header = Buffer.alloc(10);\r\n\t\t// Byte 0: R-Bit + DeviceID MSB\r\n\t\tlet b0 = (deviceId >> 8) & 0x7f;\r\n\t\tif (rBit) b0 |= 0x80;\r\n\t\theader[0] = b0;\r\n\t\t// Byte 1: DeviceID LSB\r\n\t\theader[1] = deviceId & 0xff;\r\n\r\n\t\t// Byte 2: W-Bit + Stream\r\n\t\tlet b2 = stream & 0x7f;\r\n\t\tif (wBit) b2 |= 0x80;\r\n\t\theader[2] = b2;\r\n\r\n\t\t// Byte 3: Function\r\n\t\theader[3] = func & 0xff;\r\n\r\n\t\t// Byte 4: E-Bit + BlockNum MSB\r\n\t\tlet b4 = (blockNum >> 8) & 0x7f;\r\n\t\tif (eBit) b4 |= 0x80;\r\n\t\theader[4] = b4;\r\n\r\n\t\t// Byte 5: BlockNum LSB\r\n\t\theader[5] = blockNum & 0xff;\r\n\r\n\t\t// Byte 6-9: System Bytes\r\n\t\theader.writeUInt32BE(systemBytes, 6);\r\n\r\n\t\t// Calculate Length\r\n\t\tconst length = 10 + body.length;\r\n\t\tif (length > 254) throw new Error(\"Block too large\");\r\n\r\n\t\t// Calculate Checksum\r\n\t\tlet sum = 0;\r\n\t\tfor (const b of header) sum += b;\r\n\t\tfor (const b of body) sum += b;\r\n\t\tconst checksum = sum & 0xffff;\r\n\r\n\t\tconst block = Buffer.alloc(1 + 10 + body.length + 2);\r\n\t\tblock[0] = length;\r\n\t\theader.copy(block, 1);\r\n\t\tbody.copy(block, 11);\r\n\t\tblock.writeUInt16BE(checksum, block.length - 2);\r\n\r\n\t\treturn new Secs1MessageBlock(block);\r\n\t}\r\n}\r\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":"Secs1OnTcpIpActiveCommunicator.d.ts","names":[],"sources":["../../src/secs1/Secs1OnTcpIpActiveCommunicator.ts"],"sourcesContent":[],"mappings":";;;UAMiB,oCAAA,SAA6C;;EAA7C,IAAA,EAAA,MAAA;AAKjB;AASqB,cATR,8BAAA,SAAuC,iBAAA,CAS/B;
|
|
1
|
+
{"version":3,"file":"Secs1OnTcpIpActiveCommunicator.d.ts","names":[],"sources":["../../src/secs1/Secs1OnTcpIpActiveCommunicator.ts"],"sourcesContent":[],"mappings":";;;UAMiB,oCAAA,SAA6C;;EAA7C,IAAA,EAAA,MAAA;AAKjB;AASqB,cATR,8BAAA,SAAuC,iBAAA,CAS/B;EAoBN,EAAA,EAAA,MAAA;EAsEC,IAAA,EAAA,MAAA;EAnGoC,QAAA,UAAA;EAAiB,QAAA,cAAA;;;sBAShD;UAoBN;;;WAsEC"}
|
|
@@ -15,7 +15,12 @@ var Secs1OnTcpIpActiveCommunicator = class extends Secs1Communicator {
|
|
|
15
15
|
this.port = config.port;
|
|
16
16
|
this.on("disconnected", () => {
|
|
17
17
|
if (!this.shouldStop) {
|
|
18
|
-
|
|
18
|
+
this.logger.detail.warn({
|
|
19
|
+
protocol: "SECS1",
|
|
20
|
+
ip: this.ip,
|
|
21
|
+
port: this.port,
|
|
22
|
+
timeoutT5: this.timeoutT5
|
|
23
|
+
}, "connection lost; scheduling reconnect");
|
|
19
24
|
this.scheduleReconnect();
|
|
20
25
|
}
|
|
21
26
|
});
|
|
@@ -38,7 +43,13 @@ var Secs1OnTcpIpActiveCommunicator = class extends Secs1Communicator {
|
|
|
38
43
|
this.pendingSocket = socket;
|
|
39
44
|
const onError = (err) => {
|
|
40
45
|
socket.destroy();
|
|
41
|
-
|
|
46
|
+
this.logger.detail.warn({
|
|
47
|
+
protocol: "SECS1",
|
|
48
|
+
ip: this.ip,
|
|
49
|
+
port: this.port,
|
|
50
|
+
timeoutT5: this.timeoutT5,
|
|
51
|
+
err
|
|
52
|
+
}, "connection failed; scheduling reconnect");
|
|
42
53
|
if (!this.shouldStop) this.scheduleReconnect();
|
|
43
54
|
};
|
|
44
55
|
socket.once("error", onError);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1OnTcpIpActiveCommunicator.js","names":[],"sources":["../../src/secs1/Secs1OnTcpIpActiveCommunicator.ts"],"sourcesContent":["import { Socket } from \"net\";\nimport {\n\tSecs1Communicator,\n\tSecs1CommunicatorConfig,\n} from \"./Secs1Communicator.js\";\n\nexport interface Secs1OnTcpIpActiveCommunicatorConfig extends Secs1CommunicatorConfig {\n\tip: string;\n\tport: number;\n}\n\nexport class Secs1OnTcpIpActiveCommunicator extends Secs1Communicator {\n\tpublic ip: string;\n\tpublic port: number;\n\n\tprivate shouldStop = false;\n\tprivate reconnectTimer: NodeJS.Timeout | null = null;\n\tprivate connectionPromiseResolver: (() => void) | null = null;\n\tprivate pendingSocket: Socket | null = null;\n\n\tconstructor(config: Secs1OnTcpIpActiveCommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.ip = config.ip;\n\t\tthis.port = config.port;\n\t\tthis.on(\"disconnected\", () => {\n\t\t\tif (!this.shouldStop) {\n\t\t\t\
|
|
1
|
+
{"version":3,"file":"Secs1OnTcpIpActiveCommunicator.js","names":[],"sources":["../../src/secs1/Secs1OnTcpIpActiveCommunicator.ts"],"sourcesContent":["import { Socket } from \"net\";\nimport {\n\tSecs1Communicator,\n\tSecs1CommunicatorConfig,\n} from \"./Secs1Communicator.js\";\n\nexport interface Secs1OnTcpIpActiveCommunicatorConfig extends Secs1CommunicatorConfig {\n\tip: string;\n\tport: number;\n}\n\nexport class Secs1OnTcpIpActiveCommunicator extends Secs1Communicator {\n\tpublic ip: string;\n\tpublic port: number;\n\n\tprivate shouldStop = false;\n\tprivate reconnectTimer: NodeJS.Timeout | null = null;\n\tprivate connectionPromiseResolver: (() => void) | null = null;\n\tprivate pendingSocket: Socket | null = null;\n\n\tconstructor(config: Secs1OnTcpIpActiveCommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.ip = config.ip;\n\t\tthis.port = config.port;\n\t\tthis.on(\"disconnected\", () => {\n\t\t\tif (!this.shouldStop) {\n\t\t\t\tthis.logger.detail.warn(\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\tip: this.ip,\n\t\t\t\t\t\tport: this.port,\n\t\t\t\t\t\ttimeoutT5: this.timeoutT5,\n\t\t\t\t\t},\n\t\t\t\t\t\"connection lost; scheduling reconnect\",\n\t\t\t\t);\n\t\t\t\tthis.scheduleReconnect();\n\t\t\t}\n\t\t});\n\t}\n\n\tasync open(): Promise<void> {\n\t\tif (this.stream && !this.stream.destroyed) return;\n\t\tif (this.pendingSocket && !this.pendingSocket.destroyed) return;\n\n\t\tthis.shouldStop = false;\n\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.connectionPromiseResolver = resolve;\n\t\t\tthis.connect();\n\t\t});\n\t}\n\n\tprivate connect() {\n\t\tif (this.shouldStop) return;\n\t\tif (this.stream && !this.stream.destroyed) return;\n\t\tif (this.pendingSocket && !this.pendingSocket.destroyed) return;\n\n\t\tconst socket = new Socket();\n\t\tsocket.setNoDelay(true);\n\t\tthis.pendingSocket = socket;\n\n\t\tconst onError = (err: Error) => {\n\t\t\tsocket.destroy();\n\t\t\tthis.logger.detail.warn(\n\t\t\t\t{\n\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\tip: this.ip,\n\t\t\t\t\tport: this.port,\n\t\t\t\t\ttimeoutT5: this.timeoutT5,\n\t\t\t\t\terr,\n\t\t\t\t},\n\t\t\t\t\"connection failed; scheduling reconnect\",\n\t\t\t);\n\t\t\tif (!this.shouldStop) {\n\t\t\t\tthis.scheduleReconnect();\n\t\t\t}\n\t\t};\n\n\t\tsocket.once(\"error\", onError);\n\n\t\tsocket.once(\"close\", () => {\n\t\t\tif (this.pendingSocket === socket) {\n\t\t\t\tthis.pendingSocket = null;\n\t\t\t}\n\t\t});\n\n\t\tsocket.connect(this.port, this.ip, () => {\n\t\t\tsocket.removeListener(\"error\", onError);\n\t\t\tif (this.pendingSocket === socket) {\n\t\t\t\tthis.pendingSocket = null;\n\t\t\t}\n\t\t\tthis.attachStream(socket);\n\n\t\t\tif (this.connectionPromiseResolver) {\n\t\t\t\tthis.connectionPromiseResolver();\n\t\t\t\tthis.connectionPromiseResolver = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate scheduleReconnect() {\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t}\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\tthis.reconnectTimer = null;\n\t\t\tthis.connect();\n\t\t}, this.timeoutT5 * 1000);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.shouldStop = true;\n\t\tif (this.reconnectTimer) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = null;\n\t\t}\n\t\tif (this.pendingSocket) {\n\t\t\tthis.pendingSocket.destroy();\n\t\t\tthis.pendingSocket = null;\n\t\t}\n\t\tthis.stop();\n\t\tawait Promise.resolve();\n\t}\n}\n"],"mappings":";;;;AAWA,IAAa,iCAAb,cAAoD,kBAAkB;CACrE,AAAO;CACP,AAAO;CAEP,AAAQ,aAAa;CACrB,AAAQ,iBAAwC;CAChD,AAAQ,4BAAiD;CACzD,AAAQ,gBAA+B;CAEvC,YAAY,QAA8C;AACzD,QAAM,OAAO;AACb,OAAK,KAAK,OAAO;AACjB,OAAK,OAAO,OAAO;AACnB,OAAK,GAAG,sBAAsB;AAC7B,OAAI,CAAC,KAAK,YAAY;AACrB,SAAK,OAAO,OAAO,KAClB;KACC,UAAU;KACV,IAAI,KAAK;KACT,MAAM,KAAK;KACX,WAAW,KAAK;KAChB,EACD,wCACA;AACD,SAAK,mBAAmB;;IAExB;;CAGH,MAAM,OAAsB;AAC3B,MAAI,KAAK,UAAU,CAAC,KAAK,OAAO,UAAW;AAC3C,MAAI,KAAK,iBAAiB,CAAC,KAAK,cAAc,UAAW;AAEzD,OAAK,aAAa;AAElB,SAAO,IAAI,SAAS,YAAY;AAC/B,QAAK,4BAA4B;AACjC,QAAK,SAAS;IACb;;CAGH,AAAQ,UAAU;AACjB,MAAI,KAAK,WAAY;AACrB,MAAI,KAAK,UAAU,CAAC,KAAK,OAAO,UAAW;AAC3C,MAAI,KAAK,iBAAiB,CAAC,KAAK,cAAc,UAAW;EAEzD,MAAM,SAAS,IAAI,QAAQ;AAC3B,SAAO,WAAW,KAAK;AACvB,OAAK,gBAAgB;EAErB,MAAM,WAAW,QAAe;AAC/B,UAAO,SAAS;AAChB,QAAK,OAAO,OAAO,KAClB;IACC,UAAU;IACV,IAAI,KAAK;IACT,MAAM,KAAK;IACX,WAAW,KAAK;IAChB;IACA,EACD,0CACA;AACD,OAAI,CAAC,KAAK,WACT,MAAK,mBAAmB;;AAI1B,SAAO,KAAK,SAAS,QAAQ;AAE7B,SAAO,KAAK,eAAe;AAC1B,OAAI,KAAK,kBAAkB,OAC1B,MAAK,gBAAgB;IAErB;AAEF,SAAO,QAAQ,KAAK,MAAM,KAAK,UAAU;AACxC,UAAO,eAAe,SAAS,QAAQ;AACvC,OAAI,KAAK,kBAAkB,OAC1B,MAAK,gBAAgB;AAEtB,QAAK,aAAa,OAAO;AAEzB,OAAI,KAAK,2BAA2B;AACnC,SAAK,2BAA2B;AAChC,SAAK,4BAA4B;;IAEjC;;CAGH,AAAQ,oBAAoB;AAC3B,MAAI,KAAK,eACR,cAAa,KAAK,eAAe;AAElC,OAAK,iBAAiB,iBAAiB;AACtC,QAAK,iBAAiB;AACtB,QAAK,SAAS;KACZ,KAAK,YAAY,IAAK;;CAG1B,MAAM,QAAuB;AAC5B,OAAK,aAAa;AAClB,MAAI,KAAK,gBAAgB;AACxB,gBAAa,KAAK,eAAe;AACjC,QAAK,iBAAiB;;AAEvB,MAAI,KAAK,eAAe;AACvB,QAAK,cAAc,SAAS;AAC5B,QAAK,gBAAgB;;AAEtB,OAAK,MAAM;AACX,QAAM,QAAQ,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1OnTcpIpPassiveCommunicator.d.ts","names":[],"sources":["../../src/secs1/Secs1OnTcpIpPassiveCommunicator.ts"],"sourcesContent":[],"mappings":";;;UAMiB,qCAAA,SAA8C;;EAA9C,IAAA,EAAA,MAAA;AAKjB;AAMqB,cANR,+BAAA,SAAwC,iBAAA,CAMhC;EAMN,EAAA,EAAA,MAAA;
|
|
1
|
+
{"version":3,"file":"Secs1OnTcpIpPassiveCommunicator.d.ts","names":[],"sources":["../../src/secs1/Secs1OnTcpIpPassiveCommunicator.ts"],"sourcesContent":[],"mappings":";;;UAMiB,qCAAA,SAA8C;;EAA9C,IAAA,EAAA,MAAA;AAKjB;AAMqB,cANR,+BAAA,SAAwC,iBAAA,CAMhC;EAMN,EAAA,EAAA,MAAA;EAsCL,IAAA,EAAA,MAAA;EAlD2C,QAAA,MAAA;EAAiB,WAAA,CAAA,MAAA,EAMjD,qCANiD;UAYvD;WAsCL"}
|
|
@@ -16,11 +16,19 @@ var Secs1OnTcpIpPassiveCommunicator = class extends Secs1Communicator {
|
|
|
16
16
|
return new Promise((resolve, reject) => {
|
|
17
17
|
this.server = createServer((socket) => {
|
|
18
18
|
if (this.stream && !this.stream.destroyed) {
|
|
19
|
-
|
|
19
|
+
this.logger.detail.warn({
|
|
20
|
+
protocol: "SECS1",
|
|
21
|
+
remoteAddress: socket.remoteAddress,
|
|
22
|
+
remotePort: socket.remotePort
|
|
23
|
+
}, "rejecting new connection (single session)");
|
|
20
24
|
socket.destroy();
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
23
|
-
|
|
27
|
+
this.logger.detail.info({
|
|
28
|
+
protocol: "SECS1",
|
|
29
|
+
remoteAddress: socket.remoteAddress,
|
|
30
|
+
remotePort: socket.remotePort
|
|
31
|
+
}, "accepted connection");
|
|
24
32
|
this.handleIncomingSocket(socket);
|
|
25
33
|
});
|
|
26
34
|
this.server.on("error", (err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secs1OnTcpIpPassiveCommunicator.js","names":[],"sources":["../../src/secs1/Secs1OnTcpIpPassiveCommunicator.ts"],"sourcesContent":["import { Server, Socket, createServer } from \"net\";\nimport {\n\tSecs1Communicator,\n\tSecs1CommunicatorConfig,\n} from \"./Secs1Communicator.js\";\n\nexport interface Secs1OnTcpIpPassiveCommunicatorConfig extends Secs1CommunicatorConfig {\n\tip: string;\n\tport: number;\n}\n\nexport class Secs1OnTcpIpPassiveCommunicator extends Secs1Communicator {\n\tpublic ip: string;\n\tpublic port: number;\n\n\tprivate server: Server | null = null;\n\n\tconstructor(config: Secs1OnTcpIpPassiveCommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.ip = config.ip;\n\t\tthis.port = config.port;\n\t}\n\n\tasync open(): Promise<void> {\n\t\tif (this.server) return;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.server = createServer((socket) => {\n\t\t\t\tif (this.stream && !this.stream.destroyed) {\n\t\t\t\t\
|
|
1
|
+
{"version":3,"file":"Secs1OnTcpIpPassiveCommunicator.js","names":[],"sources":["../../src/secs1/Secs1OnTcpIpPassiveCommunicator.ts"],"sourcesContent":["import { Server, Socket, createServer } from \"net\";\nimport {\n\tSecs1Communicator,\n\tSecs1CommunicatorConfig,\n} from \"./Secs1Communicator.js\";\n\nexport interface Secs1OnTcpIpPassiveCommunicatorConfig extends Secs1CommunicatorConfig {\n\tip: string;\n\tport: number;\n}\n\nexport class Secs1OnTcpIpPassiveCommunicator extends Secs1Communicator {\n\tpublic ip: string;\n\tpublic port: number;\n\n\tprivate server: Server | null = null;\n\n\tconstructor(config: Secs1OnTcpIpPassiveCommunicatorConfig) {\n\t\tsuper(config);\n\t\tthis.ip = config.ip;\n\t\tthis.port = config.port;\n\t}\n\n\tasync open(): Promise<void> {\n\t\tif (this.server) return;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.server = createServer((socket) => {\n\t\t\t\tif (this.stream && !this.stream.destroyed) {\n\t\t\t\t\tthis.logger.detail.warn(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\t\tremoteAddress: socket.remoteAddress,\n\t\t\t\t\t\t\tremotePort: socket.remotePort,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"rejecting new connection (single session)\",\n\t\t\t\t\t);\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.logger.detail.info(\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocol: \"SECS1\",\n\t\t\t\t\t\tremoteAddress: socket.remoteAddress,\n\t\t\t\t\t\tremotePort: socket.remotePort,\n\t\t\t\t\t},\n\t\t\t\t\t\"accepted connection\",\n\t\t\t\t);\n\t\t\t\tthis.handleIncomingSocket(socket);\n\t\t\t});\n\n\t\t\tthis.server.on(\"error\", (err) => {\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\tthis.server.listen(this.port, this.ip, () => {\n\t\t\t\tresolve();\n\t\t\t});\n\t\t});\n\t}\n\n\tclose(): Promise<void> {\n\t\tthis.stop();\n\t\tif (this.server) {\n\t\t\tthis.server.close();\n\t\t\tthis.server = null;\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\tprivate handleIncomingSocket(socket: Socket) {\n\t\tthis.attachStream(socket);\n\t}\n}\n"],"mappings":";;;;AAWA,IAAa,kCAAb,cAAqD,kBAAkB;CACtE,AAAO;CACP,AAAO;CAEP,AAAQ,SAAwB;CAEhC,YAAY,QAA+C;AAC1D,QAAM,OAAO;AACb,OAAK,KAAK,OAAO;AACjB,OAAK,OAAO,OAAO;;CAGpB,MAAM,OAAsB;AAC3B,MAAI,KAAK,OAAQ;AAEjB,SAAO,IAAI,SAAS,SAAS,WAAW;AACvC,QAAK,SAAS,cAAc,WAAW;AACtC,QAAI,KAAK,UAAU,CAAC,KAAK,OAAO,WAAW;AAC1C,UAAK,OAAO,OAAO,KAClB;MACC,UAAU;MACV,eAAe,OAAO;MACtB,YAAY,OAAO;MACnB,EACD,4CACA;AACD,YAAO,SAAS;AAChB;;AAED,SAAK,OAAO,OAAO,KAClB;KACC,UAAU;KACV,eAAe,OAAO;KACtB,YAAY,OAAO;KACnB,EACD,sBACA;AACD,SAAK,qBAAqB,OAAO;KAChC;AAEF,QAAK,OAAO,GAAG,UAAU,QAAQ;AAChC,WAAO,IAAI;KACV;AAEF,QAAK,OAAO,OAAO,KAAK,MAAM,KAAK,UAAU;AAC5C,aAAS;KACR;IACD;;CAGH,QAAuB;AACtB,OAAK,MAAM;AACX,MAAI,KAAK,QAAQ;AAChB,QAAK,OAAO,OAAO;AACnB,QAAK,SAAS;;AAEf,SAAO,QAAQ,SAAS;;CAGzB,AAAQ,qBAAqB,QAAgB;AAC5C,OAAK,aAAa,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
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"}
|
|
1
|
+
{"version":3,"file":"Secs1SerialCommunicator.js","names":[],"sources":["../../src/secs1/Secs1SerialCommunicator.ts"],"sourcesContent":["import { SerialPort } from \"serialport\";\r\nimport {\r\n\tSecs1Communicator,\r\n\tSecs1CommunicatorConfig,\r\n} from \"./Secs1Communicator.js\";\r\n\r\nexport interface Secs1SerialCommunicatorConfig extends Secs1CommunicatorConfig {\r\n\tpath: string;\r\n\tbaudRate: number;\r\n}\r\n\r\nexport class Secs1SerialCommunicator extends Secs1Communicator {\r\n\tprivate port: SerialPort | null = null;\r\n\r\n\tpublic path: string;\r\n\tpublic baudRate: number;\r\n\r\n\tconstructor(config: Secs1SerialCommunicatorConfig) {\r\n\t\tsuper(config);\r\n\t\tthis.path = config.path;\r\n\t\tthis.baudRate = config.baudRate;\r\n\t}\r\n\r\n\tasync open(): Promise<void> {\r\n\t\tif (this.port?.isOpen) return;\r\n\r\n\t\tconst port = new SerialPort({\r\n\t\t\tpath: this.path,\r\n\t\t\tbaudRate: this.baudRate,\r\n\t\t\tautoOpen: false,\r\n\t\t});\r\n\t\tthis.port = port;\r\n\r\n\t\treturn new Promise((resolve, reject) => {\r\n\t\t\tconst onError = (err: Error) => {\r\n\t\t\t\treject(err);\r\n\t\t\t};\r\n\r\n\t\t\tport.once(\"error\", onError);\r\n\t\t\tport.open((err) => {\r\n\t\t\t\tport.removeListener(\"error\", onError);\r\n\t\t\t\tif (err) {\r\n\t\t\t\t\treject(err);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tthis.attachStream(port);\r\n\t\t\t\tresolve();\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n\r\n\tasync close(): Promise<void> {\r\n\t\tthis.stop();\r\n\r\n\t\tconst port = this.port;\r\n\t\tthis.port = null;\r\n\t\tif (!port) return;\r\n\r\n\t\tif (!port.isOpen) {\r\n\t\t\tport.destroy();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tawait new Promise<void>((resolve, reject) => {\r\n\t\t\tport.close((err) => {\r\n\t\t\t\tif (err) reject(err);\r\n\t\t\t\telse resolve();\r\n\t\t\t});\r\n\t\t});\r\n\r\n\t\tport.destroy();\r\n\t}\r\n}\r\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\";\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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"}
|
|
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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"}
|