UNPKG

modbus-connect

Version:

Modbus RTU over Web Serial and Node.js SerialPort

100 lines (84 loc) 3.98 kB
// function-codes/SGM130/write-device-comment.js const FUNCTION_CODE = 0x15; const MAX_CHANNEL = 255; const MAX_COMMENT_LENGTH = 16; const REQUEST_SIZE = 19; // 3 байта заголовка + 16 данных const RESPONSE_SIZE = 3; const SPACE_CODE = 0; // Оптимизированная таблица символов как Map (быстрый поиск) const CHAR_CODE_MAP = new Map(); [ [' ', 0], ['0', 1], ['1', 2], ['2', 3], ['3', 4], ['4', 5], ['5', 6], ['6', 7], ['7', 8], ['8', 9], ['9', 10], ['A', 11], ['B', 12], ['C', 13], ['D', 14], ['E', 15], ['F', 16], ['G', 17], ['H', 18], ['I', 19], ['J', 20], ['K', 21], ['L', 22], ['M', 23], ['N', 24], ['O', 25], ['P', 26], ['Q', 27], ['R', 28], ['S', 29], ['T', 30], ['U', 31], ['V', 32], ['X', 33], ['Y', 34], ['Z', 35], ['А', 37], ['Б', 38], ['В', 39], ['Г', 40], ['Д', 41], ['Е', 42], ['Ж', 43], ['З', 44], ['И', 45], ['Й', 46], ['К', 47], ['Л', 48], ['М', 49], ['Н', 50], ['О', 51], ['П', 52], ['Р', 53], ['С', 54], ['Т', 55], ['У', 56], ['Ф', 57], ['Х', 58], ['Ц', 59], ['Ч', 60], ['Ш', 61], ['Щ', 62], ['Ъ', 63], ['Ы', 64], ['Ь', 65], ['Э', 66], ['Ю', 67], ['Я', 68] ].forEach(([char, code]) => CHAR_CODE_MAP.set(char, code)); /** * Создаёт PDU-запрос для записи комментария устройства * @param {number} channel - номер канала (0-255) * @param {string} comment - строка комментария (макс. 16 символов) * @returns {Uint8Array} * @throws {Error} При невалидных параметрах */ function buildWriteDeviceCommentRequest(channel, comment) { // Быстрая проверка канала if ((channel | 0) !== channel || channel < 0 || channel > MAX_CHANNEL) { throw new RangeError(`Channel must be 0-${MAX_CHANNEL}`); } if (typeof comment !== 'string') { throw new TypeError('Comment must be a string'); } const trimmed = comment.trim().toUpperCase(); if (trimmed.length > MAX_COMMENT_LENGTH) { throw new Error(`Comment too long: max ${MAX_COMMENT_LENGTH} chars`); } // Создаем буфер сразу нужного размера const pdu = new Uint8Array(REQUEST_SIZE); const view = new DataView(pdu.buffer); // Заполняем заголовок view.setUint8(0, FUNCTION_CODE); view.setUint8(1, channel); view.setUint8(2, MAX_COMMENT_LENGTH); // Кодируем символы и заполняем данные let offset = 3; for (const ch of trimmed) { const code = CHAR_CODE_MAP.get(ch); if (code === undefined) { throw new Error(`Unsupported character: "${ch}"`); } pdu[offset++] = code; } // Заполняем остаток пробелами while (offset < REQUEST_SIZE) { pdu[offset++] = SPACE_CODE; } return pdu; } /** * Парсит ответ устройства на запись комментария * @param {Uint8Array} pdu - PDU ответа * @returns {{ channel: number, length: number }} * @throws {Error} При невалидном ответе */ function parseWriteDeviceCommentResponse(pdu) { if (!(pdu instanceof Uint8Array)) { throw new TypeError('PDU must be Uint8Array'); } if (pdu.length !== RESPONSE_SIZE || pdu[0] !== FUNCTION_CODE) { throw new Error(`Invalid response: expected ${RESPONSE_SIZE} bytes starting with 0x${FUNCTION_CODE.toString(16)}`); } const channel = pdu[1]; const length = pdu[2]; if (length !== MAX_COMMENT_LENGTH) { throw new Error(`Invalid length: expected ${MAX_COMMENT_LENGTH}, got ${length}`); } return { channel, length }; } module.exports = { buildWriteDeviceCommentRequest, parseWriteDeviceCommentResponse };