/**
|
* @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;
|