lgq
3 天以前 081f12a52906abe6c2d139fdc144135978681009
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/**
 * @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;