import { COTP_CR, COTP_CC, COTP_DR, COTP_DT, COTP_DT_EOT, COTP_SRC_TSAP, COTP_DST_TSAP, COTP_TPDU_SIZE, } from './constants.js'; const DEFAULT_TPDU_SIZE = 1024; export function computeRemoteTSAP(rack: number, slot: number): number { return ((rack & 0xFF) << 8) | (slot & 0xFF); } export function computeLocalTSAP(connType: number, connNum: number): number { const base = connType === 0x01 ? 0x0100 : connType === 0x02 ? 0x0200 : 0x0300; return base + (connNum & 0xFF); } export function buildCR(localTSAP: number, remoteTSAP: number, tpduSize = DEFAULT_TPDU_SIZE): Buffer { // CR PDU: LI(1) + TPDU_Code(1) + DstRef(2) + SrcRef(2) + Class(1) + Params const srcParam = Buffer.from([COTP_SRC_TSAP, 0x02, (localTSAP >> 8) & 0xFF, localTSAP & 0xFF]); const dstParam = Buffer.from([COTP_DST_TSAP, 0x02, (remoteTSAP >> 8) & 0xFF, remoteTSAP & 0xFF]); const sizeParam = Buffer.from([COTP_TPDU_SIZE, 0x01, tpduSizeCode(tpduSize)]); const paramsLen = srcParam.length + dstParam.length + sizeParam.length; const li = 6 + paramsLen; // 6 = DstRef(2)+SrcRef(2)+Class(1)+TPDU_Code counted after LI const buf = Buffer.alloc(1 + 1 + 2 + 2 + 1 + paramsLen); let offset = 0; buf[offset++] = li; buf[offset++] = COTP_CR; buf.writeUInt16BE(0x0001, offset); offset += 2; // DstRef buf.writeUInt16BE(0x0001, offset); offset += 2; // SrcRef buf[offset++] = 0x00; // Class srcParam.copy(buf, offset); offset += srcParam.length; dstParam.copy(buf, offset); offset += dstParam.length; sizeParam.copy(buf, offset); return buf; } export function buildCC(): Buffer { // CC PDU: LI(1) + TPDU_Code(1) + DstRef(2) + SrcRef(2) + Class(1) + Params (same as CR) const srcParam = Buffer.from([COTP_SRC_TSAP, 0x02, 0x01, 0x00]); const dstParam = Buffer.from([COTP_DST_TSAP, 0x02, 0x01, 0x00]); const sizeParam = Buffer.from([COTP_TPDU_SIZE, 0x01, 0x0A]); // 1024 const paramsLen = srcParam.length + dstParam.length + sizeParam.length; const li = 6 + paramsLen; const buf = Buffer.alloc(1 + 1 + 2 + 2 + 1 + paramsLen); let offset = 0; buf[offset++] = li; buf[offset++] = COTP_CC; buf.writeUInt16BE(0x0001, offset); offset += 2; // DstRef buf.writeUInt16BE(0x0001, offset); offset += 2; // SrcRef buf[offset++] = 0x00; // Class srcParam.copy(buf, offset); offset += srcParam.length; dstParam.copy(buf, offset); offset += dstParam.length; sizeParam.copy(buf, offset); return buf; } export function buildDR(): Buffer { const buf = Buffer.alloc(7); buf[0] = 6; // LI buf[1] = COTP_DR; buf.writeUInt16BE(0x0001, 2); // DstRef buf.writeUInt16BE(0x0001, 4); // SrcRef buf[6] = 0x00; // Reason return buf; } export function buildDT(payload: Buffer, eot = true): Buffer { // DT Class 0: LI(1) + TPDU_Code(1) + TPDU_NR(1) + Payload // LI = 2 means 2 bytes follow after LI byte (TPDU_Code + TPDU_NR) const li = 2; const buf = Buffer.alloc(1 + li + payload.length); buf[0] = li; buf[1] = eot ? (COTP_DT | COTP_DT_EOT) : COTP_DT; buf[2] = 0x80; // TPDU-NR: highest bit set, sequence = 0 payload.copy(buf, 3); return buf; } export function isCR(data: Buffer): boolean { return data.length > 1 && data[1] === COTP_CR; } export function isCC(data: Buffer): boolean { return data.length > 1 && data[1] === COTP_CC; } export function isDR(data: Buffer): boolean { return data.length > 1 && data[1] === COTP_DR; } export function isDT(data: Buffer): boolean { return data.length > 1 && (data[1] & 0xF0) === COTP_DT; } export function isDTEot(data: Buffer): boolean { return isDT(data) && (data[1] & COTP_DT_EOT) !== 0; } export function extractDTPayload(data: Buffer): Buffer { const headerLen = data[0] + 1; // LI + 1 byte for LI itself return data.subarray(headerLen); } function tpduSizeCode(size: number): number { if (size <= 128) return 0x07; if (size <= 256) return 0x08; if (size <= 512) return 0x09; if (size <= 1024) return 0x0A; if (size <= 2048) return 0x0B; if (size <= 4096) return 0x0C; if (size <= 8192) return 0x0D; if (size <= 16384) return 0x0E; return 0x0F; }