/** * @file dxChannel.js * @module dxChannel * @description * Data communication channel module based on the native dxChannel C library. * This module provides a direct, handle-based interface for various communication channels, * including Serial Port (UART), USB (HID, Keyboard Wedge), and Wiegand. * * It is designed for high-performance, multi-threaded environments, allowing different * channels to be operated in parallel safely. * * @usage * // 1. Import the module * import channel from 'dxChannel.js'; * * // 2. Open a channel (e.g., a serial port) and get its handle * let uart_handle = null; * try { * uart_handle = channel.open(channel.TYPE.UART, "/dev/ttyS2"); * } catch (e) { * console.error("Failed to open UART:", e); * } * * if (uart_handle !== null) { * try { * // 3. Configure the channel * channel.setUartParam(uart_handle, 9600, 8, 'N', 1); * * // 4. Send data * const dataToSend = new Uint8Array([0x01, 0x02, 0x03, 0x04]).buffer; * const success = channel.send(uart_handle, dataToSend); * dxLogger.info("Send successful:", success); * * // 5. Receive data (this is a blocking call) * const receivedData = channel.receive(uart_handle, 4, 1000); // Wait for 4 bytes for up to 1000ms * if (receivedData) { * dxLogger.info("Received data:", receivedData); * } * } catch (e) { * console.error("Channel operation failed:", e); * } finally { * // 6. Close the handle when done * channel.close(uart_handle); * } * } */ import { channel as nativeChannel } from './libvbar-m-dxchannel.so' const channel = {} /** * @readonly * @enum {number} * Channel type enumeration. */ channel.TYPE = Object.freeze({ USBKBW: 1, // USB Keyboard Wedge USBHID: 2, // USB Human Interface Device UART: 3, // Serial Port (UART) WIEGAND: 4 // Wiegand interface }); /** * @readonly * @enum {number} * IO Control command enumeration for `channel.ioctl`. */ channel.IOC_SET_CMD = Object.freeze({ /** Set KBW channel configuration parameters */ CHANNEL_IOC_SET_KBW_CONFIG: 1, /** Set KBW channel upper computer parameters */ CHANNEL_IOC_SET_KBW_UPPER: 2, /** Set KBW online time */ CHANNEL_IOC_SET_KBW_UPTIME: 3, /** Set KBW offline time */ CHANNEL_IOC_SET_KBW_DOWNTIME: 4, /** Set HID channel report length */ CHANNEL_IOC_SET_HID_REPORT_LEN: 5, /** Set UART channel parameters */ CHANNEL_IOC_SET_UART_PARAM: 6, /** Set Wiegand channel working mode */ CHANNEL_IOC_SET_WIEGAND_MODE: 7, /** Set Wiegand channel GPIO configuration */ CHANNEL_IOC_SET_WIEGAND_GPIO: 8, /** Set Wiegand channel delay time */ CHANNEL_IOC_SET_WIEGAND_DELAY: 9, /** Set Wiegand channel logging function */ CHANNEL_IOC_SET_WIEGAND_LOG: 10 }); /** * @readonly * @enum {number} * Wiegand channel working modes. */ channel.WIEGAND_MODE = Object.freeze({ /** Initial value for Wiegand mode */ WIEGAND_MODE_INIT: 0, /** Wiegand 26-bit mode */ WIEGAND_MODE_26: 1, /** Wiegand 34-bit mode */ WIEGAND_MODE_34: 2, /** Wiegand 128-bit mode */ WIEGAND_MODE_128: 3, /** Wiegand 256-bit mode */ WIEGAND_MODE_256: 4, /** Wiegand 2048-bit mode */ WIEGAND_MODE_2048: 5, /** Custom Wiegand mode, max 6400 bits */ WIEGAND_MODE_CUSTOM: 6 }); /** * Opens a communication channel and returns its handle. * @param {number} type - The channel type, from `channel.TYPE` enum. * @param {string} path - The channel device path, e.g., "/dev/ttyS2". Note: The path may vary on different hardware. Please refer to `dxDriver.js` for device-specific constants. * @returns {number} The numeric handle for the opened channel, used in all subsequent operations. * @throws {Error} If the channel fails to open or if parameters are invalid. */ channel.open = function (type, path) { if (type === undefined || type === null) { throw new Error("channel.open: 'type' parameter is required.") } // Validate that the provided type is a valid value from the enum if (!Object.values(channel.TYPE).includes(type)) { throw new Error(`channel.open: invalid 'type' parameter. Please use a value from channel.TYPE.`); } if (path === undefined || path === null) { throw new Error("channel.open: 'path' parameter is required.") } try { const handle_id = nativeChannel.open(type, path); return handle_id; } catch (e) { // The C layer throws an exception on failure. We catch it and re-throw a more user-friendly JS error. throw new Error(`channel.open: failed to open channel with type ${type} and path '${path}'. Reason: ${e.message}`); } } /** * Sends data to a specified channel. * @param {number} handle_id - The channel handle returned by `open()`. * @param {ArrayBuffer} buffer - The binary data to send. * @returns {number} The number of bytes successfully sent, or a negative value on failure. * Note: A successful return only indicates data has been sent from the local buffer, * not that it has been received by the remote end. */ channel.send = function (handle_id, buffer) { if (handle_id === undefined || handle_id === null) { throw new Error("channel.send: 'handle_id' parameter is required.") } if (buffer === undefined || buffer === null) { throw new Error("channel.send: 'buffer' parameter is required.") } return nativeChannel.send(handle_id, buffer); } /** * Receives data from a specified channel. This is a blocking call. * @param {number} handle_id - The channel handle returned by `open()`. * @param {number} size - The number of bytes to receive. * @param {number} [timeout=10] - The maximum time to wait in milliseconds. Defaults to 10. * @returns {Uint8Array|null} A `Uint8Array` containing the received data, or `null` if the operation timed out with no data. */ channel.receive = function (handle_id, size, timeout) { if (handle_id === undefined || handle_id === null) { throw new Error("channel.receive: 'handle_id' parameter is required.") } if (size === undefined || size === null) { throw new Error("channel.receive: 'size' parameter is required.") } if (timeout === undefined || timeout === null) { timeout = 10 } const res = nativeChannel.receive(handle_id, size, timeout) if (res === null) { return null } return new Uint8Array(res) } /** * Performs a special I/O control operation on a specified channel. * @param {number} handle_id - The channel handle returned by `open()`. * @param {number} request - The control command code, from `channel.IOC_SET_CMD` enum. * @param {*} arg - The argument for the request, which can be a number, string, or object. * @returns {boolean} True on success, false on failure. */ channel.ioctl = function (handle_id, request, arg) { if (handle_id === undefined || handle_id === null) { throw new Error("channel.ioctl: 'handle_id' parameter is required.") } // Validate that the provided request is a valid value from the enum if (!Object.values(channel.IOC_SET_CMD).includes(request)) { throw new Error(`channel.ioctl: invalid 'request' parameter. Please use a value from channel.IOC_SET_CMD.`); } return nativeChannel.ioctl(handle_id, request, arg) } /** * Closes a communication channel. * @param {number} handle_id - The channel handle returned by `open()`. * @returns {boolean} Always returns true on success. * @throws {Error} If the handle_id is invalid. */ channel.close = function (handle_id) { if (handle_id === undefined || handle_id === null) { throw new Error("channel.close: 'handle_id' parameter is required.") } return nativeChannel.close(handle_id) } /** * Flushes (clears) the input/output buffers of a specified channel. * @param {number} handle_id - The channel handle returned by `open()`. * @param {number} queue_selector - Which queue to flush. 0: Input, 1: Output, 2: Both. * @returns {boolean} True on success, false on failure. */ channel.flush = function (handle_id, queue_selector) { if (handle_id === undefined || handle_id === null) { throw new Error("channel.flush: 'handle_id' parameter is required.") } if (queue_selector === undefined || queue_selector === null) { throw new Error("channel.flush: 'queue_selector' parameter is required.") } return nativeChannel.flush(handle_id, queue_selector); } /** * Sets the communication parameters for a UART (serial) channel. * @param {number} handle_id - The channel handle returned by `open()`. * @param {number} baudrate - The baud rate, e.g., 9600, 115200. * @param {number} [databits=8] - The number of data bits. * @param {string} [parity='N'] - The parity: 'N' (None), 'O' (Odd), 'E' (Even). * @param {number} [stopbits=1] - The number of stop bits. * @returns {boolean} True on success, false on failure. */ channel.setUartParam = function (handle_id, baudrate, databits, parity, stopbits) { if (baudrate === undefined || baudrate === null) { throw new Error("channel.setUartParam: 'baudrate' parameter is required."); } // Set default values for optional parameters const final_databits = (databits === undefined || databits === null) ? 8 : databits; const final_parity = (parity === undefined || parity === null) ? 'N' : parity; const final_stopbits = (stopbits === undefined || stopbits === null) ? 1 : stopbits; const param_string = `${baudrate}-${final_databits}-${final_parity}-${final_stopbits}`; return channel.ioctl(handle_id, channel.IOC_SET_CMD.CHANNEL_IOC_SET_UART_PARAM, param_string); } /** * @typedef {object} WiegandGpioConfig * @property {number} [busy_time] - Busy time (in microseconds). Defaults to 50 in the C layer. * @property {number} [free_time] - Free time (in microseconds). Defaults to 100 in the C layer. * @property {number} [mode] - Working mode, see `channel.WIEGAND_MODE`. Defaults to 2 (34-bit) in the C layer. * @property {number} [log_level] - Log level. Defaults to 1 in the C layer. * @property {number} [wiegand_d0] - GPIO pin number for D0 data line. Defaults to 4 in the C layer. * @property {number} [wiegand_d1] - GPIO pin number for D1 data line. Defaults to 5 in the C layer. */ /** * Sets the GPIO pins and working parameters for a Wiegand channel. * @param {number} handle_id - The channel handle returned by `open()`. * @param {WiegandGpioConfig} config - Wiegand configuration object. * @returns {boolean} True on success, false on failure. */ channel.setWiegandGpio = function (handle_id, config) { if (config === undefined || config === null) { throw new Error("channel.setWiegandGpio: 'config' object is required."); } // The C layer provides default values, so passing a partial or empty object is safe. return channel.ioctl(handle_id, channel.IOC_SET_CMD.CHANNEL_IOC_SET_WIEGAND_GPIO, config); } export default channel;