device-communication-node 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/package.json +28 -0
- package/src/device/channel/CommChannel.js +73 -0
- package/src/device/channel/SerialChannel.js +112 -0
- package/src/device/channel/TcpChannel.js +84 -0
- package/src/device/channel/TcpServerChannel.js +149 -0
- package/src/device/channel/UdpChannel.js +96 -0
- package/src/device/core/CommDispatcher.js +387 -0
- package/src/device/core/DeviceCore.js +236 -0
- package/src/device/core/commDispatcherManager.js +105 -0
- package/src/device/core/dispatchers/SerialDispatcher.js +77 -0
- package/src/device/core/dispatchers/TcpDispatcher.js +72 -0
- package/src/device/core/dispatchers/TcpServerDispatcher.js +99 -0
- package/src/device/core/dispatchers/UdpDispatcher.js +77 -0
- package/src/device/core/factories/serialFactory.js +32 -0
- package/src/device/core/factories/tcpFactory.js +24 -0
- package/src/device/core/factories/tcpserverFactory.js +61 -0
- package/src/device/core/factories/udpFactory.js +47 -0
- package/src/device/model/InteractionPattern.js +16 -0
- package/src/device/model/SchedulingStrategy.js +13 -0
- package/src/device/model/Task.js +155 -0
- package/src/device/utils/ByteUtils.js +62 -0
- package/src/device/utils/CheckUtils.js +57 -0
- package/src/device/utils/HexUtils.js +100 -0
- package/src/device/utils/NetworkUtils.js +50 -0
- package/src/drivers/test/DeviceTest.js +49 -0
- package/src/index.js +14 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const serialFactory = require('./factories/serialFactory')
|
|
2
|
+
const tcpFactory = require('./factories/tcpFactory')
|
|
3
|
+
const udpFactory = require('./factories/udpFactory')
|
|
4
|
+
const tcpserverFactory = require('./factories/tcpserverFactory')
|
|
5
|
+
/**
|
|
6
|
+
* 调度器工厂 Map
|
|
7
|
+
*/
|
|
8
|
+
const factoryMap = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* 调度器 Map
|
|
11
|
+
*/
|
|
12
|
+
const dispatcherMap = new Map();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 通信调度器管理(全局单例工具模块)
|
|
16
|
+
* * 支持的通信类型及地址格式规范:
|
|
17
|
+
* - "serial" : 串口号@波特率 (例: "COM1@9600")
|
|
18
|
+
* - "tcp" : IP地址:端口号 (例: "192.168.1.100:8080")
|
|
19
|
+
* - "udp" : IP地址:端口号 (例: "192.168.1.100:8080")
|
|
20
|
+
* - "tcpserver" : IP地址:端口号 或 端口号 (例:"0.0.0.0:8080") (例:"8080")
|
|
21
|
+
*/
|
|
22
|
+
const CommDispatcherManager = {
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 初始化注册原生工厂
|
|
26
|
+
*/
|
|
27
|
+
init() {
|
|
28
|
+
this.registerFactory(serialFactory)
|
|
29
|
+
this.registerFactory(tcpFactory)
|
|
30
|
+
this.registerFactory(udpFactory)
|
|
31
|
+
this.registerFactory(tcpserverFactory)
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/** 动态注册新工厂 */
|
|
35
|
+
registerFactory(factory) {
|
|
36
|
+
if (factory && factory.getCommType) {
|
|
37
|
+
factoryMap.set(factory.getCommType().toLowerCase(), factory);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 仅创建不保存
|
|
43
|
+
* @param {*} commType
|
|
44
|
+
* @param {*} commAddress
|
|
45
|
+
* @returns
|
|
46
|
+
*/
|
|
47
|
+
create(commType, commAddress) {
|
|
48
|
+
if (!commType) throw new Error("通信类型不能为空");
|
|
49
|
+
|
|
50
|
+
const factory = factoryMap.get(commType.toLowerCase());
|
|
51
|
+
if (!factory) throw new Error(`不支持的通信类型: ${commType}`);
|
|
52
|
+
|
|
53
|
+
// 执行特定校验并创建
|
|
54
|
+
factory.validate(commAddress);
|
|
55
|
+
return factory.create(commAddress);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 获取或创建
|
|
60
|
+
* @param {*} commType
|
|
61
|
+
* @param {*} commAddress
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
getOrCreate(commType, commAddress) {
|
|
65
|
+
const key = this.buildKey(commType, commAddress);
|
|
66
|
+
if (!dispatcherMap.has(key)) {
|
|
67
|
+
const dispatcher = this.create(commType, commAddress);
|
|
68
|
+
dispatcherMap.set(key, dispatcher);
|
|
69
|
+
}
|
|
70
|
+
return dispatcherMap.get(key);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 移除并释放资源
|
|
75
|
+
* @param {*} commType
|
|
76
|
+
* @param {*} commAddress
|
|
77
|
+
*/
|
|
78
|
+
async remove(commType, commAddress) {
|
|
79
|
+
const key = this.buildKey(commType, commAddress);
|
|
80
|
+
const dispatcher = dispatcherMap.get(key);
|
|
81
|
+
if (dispatcher) {
|
|
82
|
+
dispatcherMap.delete(key);
|
|
83
|
+
try {
|
|
84
|
+
await dispatcher.dispose();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(`[CommDispatcherManager] 关闭通道失败 [${key}]:`, err.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* 构建 key
|
|
92
|
+
* @param {*} commType
|
|
93
|
+
* @param {*} commAddress
|
|
94
|
+
* @returns
|
|
95
|
+
*/
|
|
96
|
+
buildKey(commType, commAddress) {
|
|
97
|
+
return `${commType.toLowerCase().trim()}:${commAddress.trim()}`;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// 执行初始化
|
|
102
|
+
CommDispatcherManager.init();
|
|
103
|
+
|
|
104
|
+
// 单例
|
|
105
|
+
module.exports = CommDispatcherManager;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const CommDispatcher = require('../CommDispatcher');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 串口调度器适配器
|
|
5
|
+
*/
|
|
6
|
+
class SerialDispatcher extends CommDispatcher {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {SerialChannel} serialChannel 串口通道实例
|
|
10
|
+
*/
|
|
11
|
+
constructor(serialChannel) {
|
|
12
|
+
super(serialChannel);
|
|
13
|
+
|
|
14
|
+
this.serialChannel = serialChannel;
|
|
15
|
+
|
|
16
|
+
// 串口接收事件
|
|
17
|
+
this.serialChannel.onReceive = (port, data) => {
|
|
18
|
+
this.channelReceiveEvent(data);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 获取连接字符串
|
|
24
|
+
*/
|
|
25
|
+
getConnectionString() {
|
|
26
|
+
const config = this.serialChannel.getConfig();
|
|
27
|
+
|
|
28
|
+
return `${config.path}@${config.baudRate}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 连接是否打开
|
|
33
|
+
*/
|
|
34
|
+
isOpen() {
|
|
35
|
+
return this.serialChannel.getIsOpen();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 打开连接
|
|
40
|
+
*/
|
|
41
|
+
async open() {
|
|
42
|
+
await this.serialChannel.open();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 关闭连接
|
|
47
|
+
*/
|
|
48
|
+
async close() {
|
|
49
|
+
await this.serialChannel.close();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 执行底层写入
|
|
54
|
+
* @param {Task} task
|
|
55
|
+
*/
|
|
56
|
+
async write(task) {
|
|
57
|
+
await this.serialChannel.send(task.writeBytes);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 获取字符集
|
|
62
|
+
*/
|
|
63
|
+
getCharset() {
|
|
64
|
+
return this.serialChannel.charset || 'utf8';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 串口数据接收事件
|
|
69
|
+
* @param {Buffer} data
|
|
70
|
+
*/
|
|
71
|
+
channelReceiveEvent(data) {
|
|
72
|
+
|
|
73
|
+
this.receive(data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = SerialDispatcher;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const CommDispatcher = require('../CommDispatcher');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TCP 调度器适配器
|
|
5
|
+
*/
|
|
6
|
+
class TcpDispatcher extends CommDispatcher {
|
|
7
|
+
/**
|
|
8
|
+
* @param {TcpChannel} tcpChannel TCP 通道实例
|
|
9
|
+
*/
|
|
10
|
+
constructor(tcpChannel) {
|
|
11
|
+
super(tcpChannel);
|
|
12
|
+
|
|
13
|
+
this.tcpChannel = tcpChannel;
|
|
14
|
+
|
|
15
|
+
this.tcpChannel.onReceive = (socket, data) => {
|
|
16
|
+
this.channelReceiveEvent(data);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取连接字符串
|
|
22
|
+
*/
|
|
23
|
+
getConnectionString() {
|
|
24
|
+
return `${this.tcpChannel.host}:${this.tcpChannel.port}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 连接是否打开
|
|
29
|
+
*/
|
|
30
|
+
isOpen() {
|
|
31
|
+
return this.tcpChannel.getIsOpen();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 打开连接
|
|
36
|
+
*/
|
|
37
|
+
async open() {
|
|
38
|
+
await this.tcpChannel.open();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 关闭连接
|
|
43
|
+
*/
|
|
44
|
+
async close() {
|
|
45
|
+
await this.tcpChannel.close();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 执行底层写入
|
|
50
|
+
* @param {Task} task
|
|
51
|
+
*/
|
|
52
|
+
async write(task) {
|
|
53
|
+
await this.tcpChannel.send(task.writeBytes);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 获取编码格式
|
|
58
|
+
*/
|
|
59
|
+
getCharset() {
|
|
60
|
+
return this.tcpChannel.charset || 'utf8';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* TCP 数据接收事件(流式)
|
|
65
|
+
* @param {Buffer} data
|
|
66
|
+
*/
|
|
67
|
+
channelReceiveEvent(data) {
|
|
68
|
+
this.receive(data);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = TcpDispatcher;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const CommDispatcher = require('../CommDispatcher');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TCP Server 调度器适配器(支持多客户端管理与广播/定向发送)
|
|
5
|
+
*/
|
|
6
|
+
class TcpServerDispatcher extends CommDispatcher {
|
|
7
|
+
/**
|
|
8
|
+
* @param {TcpServerChannel} tcpServerChannel TCP 服务端通道实例
|
|
9
|
+
*/
|
|
10
|
+
constructor(tcpServerChannel) {
|
|
11
|
+
super(tcpServerChannel);
|
|
12
|
+
|
|
13
|
+
this.tcpServerChannel = tcpServerChannel;
|
|
14
|
+
|
|
15
|
+
// 核心改动 1:TCP Server 的 onReceive 会多带一个 socket 参数
|
|
16
|
+
this.tcpServerChannel.onReceive = (socket, data) => {
|
|
17
|
+
this.channelReceiveEvent(socket, data);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 获取连接字符串(服务端自身监听的地址)
|
|
23
|
+
*/
|
|
24
|
+
getConnectionString() {
|
|
25
|
+
return `${this.tcpServerChannel.host}:${this.tcpServerChannel.port}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 获取某个具体客户端连接的唯一标识
|
|
30
|
+
* @param {net.Socket} socket
|
|
31
|
+
*/
|
|
32
|
+
getClientConnectionString(socket) {
|
|
33
|
+
if (!socket || socket.destroyed) return 'Unknown';
|
|
34
|
+
return `${socket.remoteAddress}:${socket.remotePort}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 服务端是否打开
|
|
39
|
+
*/
|
|
40
|
+
isOpen() {
|
|
41
|
+
return this.tcpServerChannel.getIsOpen();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 开启服务端监听
|
|
46
|
+
*/
|
|
47
|
+
async open() {
|
|
48
|
+
await this.tcpServerChannel.open();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 关闭服务端并断开所有客户端
|
|
53
|
+
*/
|
|
54
|
+
async close() {
|
|
55
|
+
await this.tcpServerChannel.close();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 执行底层写入
|
|
60
|
+
* @param {Task} task 任务对象
|
|
61
|
+
*/
|
|
62
|
+
async write(task) {
|
|
63
|
+
const targetSocket = task.socket || task.context?.socket;
|
|
64
|
+
|
|
65
|
+
if (targetSocket) {
|
|
66
|
+
// 定向发送给特定客户端
|
|
67
|
+
await this.tcpServerChannel.send(task.writeBytes, targetSocket);
|
|
68
|
+
} else {
|
|
69
|
+
// 未指定则默认广播给所有客户端
|
|
70
|
+
await this.tcpServerChannel.send(task.writeBytes);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取编码格式
|
|
76
|
+
*/
|
|
77
|
+
getCharset() {
|
|
78
|
+
return this.tcpServerChannel.charset || 'utf8';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* TCP 数据接收事件
|
|
83
|
+
* @param {net.Socket} socket 触发数据的客户端套接字
|
|
84
|
+
* @param {Buffer} data
|
|
85
|
+
*/
|
|
86
|
+
channelReceiveEvent(socket, data) {
|
|
87
|
+
// 核心改动 3:如果基类的 this.receive(data) 无法携带上下文,
|
|
88
|
+
// 建议检查底层 CommDispatcher 是否支持附加信息。
|
|
89
|
+
// 如果支持,可以将 socket 注入到解析链路中,方便业务层回包时知道该发给谁:
|
|
90
|
+
if (typeof this.receiveWithContext === 'function') {
|
|
91
|
+
this.receiveWithContext(data, { socket });
|
|
92
|
+
} else {
|
|
93
|
+
// 降级使用标准设计,但通常需要在 task 层面能够绑定源 socket
|
|
94
|
+
this.receive(data);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = TcpServerDispatcher;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const CommDispatcher = require('../CommDispatcher');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UDP 调度器适配器
|
|
5
|
+
*/
|
|
6
|
+
class UdpDispatcher extends CommDispatcher {
|
|
7
|
+
/**
|
|
8
|
+
* @param {UdpChannel} udpChannel UDP 通道实例
|
|
9
|
+
*/
|
|
10
|
+
constructor(udpChannel) {
|
|
11
|
+
super(udpChannel);
|
|
12
|
+
|
|
13
|
+
this.udpChannel = udpChannel;
|
|
14
|
+
|
|
15
|
+
this.udpChannel.onReceive = (rinfo, data) => {
|
|
16
|
+
this.channelReceiveEvent(rinfo, data);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取连接字符串
|
|
22
|
+
*/
|
|
23
|
+
getConnectionString() {
|
|
24
|
+
return `${this.udpChannel.localPort}:${this.udpChannel.remoteHost}:${this.udpChannel.remotePort}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 连接是否打开
|
|
29
|
+
*/
|
|
30
|
+
isOpen() {
|
|
31
|
+
return this.udpChannel.getIsOpen();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 打开连接
|
|
36
|
+
*/
|
|
37
|
+
async open() {
|
|
38
|
+
await this.udpChannel.open();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 关闭连接
|
|
43
|
+
*/
|
|
44
|
+
async close() {
|
|
45
|
+
await this.udpChannel.close();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 执行底层写入
|
|
50
|
+
* @param {Task} task
|
|
51
|
+
*/
|
|
52
|
+
async write(task) {
|
|
53
|
+
// 调用 UdpChannel 的 send 方法
|
|
54
|
+
await this.udpChannel.send(task.writeBytes);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 获取编码格式
|
|
59
|
+
*/
|
|
60
|
+
getCharset() {
|
|
61
|
+
// 优先使用通道定义的编码,没有则默认 utf8
|
|
62
|
+
return this.udpChannel.charset || 'utf8';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 处理通道接收到的原始数据
|
|
67
|
+
* @param {Object} rinfo 远程信息 (address, port, family, size)
|
|
68
|
+
* @param {Buffer} data 原始数据
|
|
69
|
+
*/
|
|
70
|
+
channelReceiveEvent(rinfo, data) {
|
|
71
|
+
// 调用基类 CommDispatcher 的 receive 方法进行帧匹配和逻辑分发
|
|
72
|
+
// data 在 Node UDP 中已经是 Buffer 对象
|
|
73
|
+
this.receive(data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = UdpDispatcher;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const SerialDispatcher = require('../dispatchers/SerialDispatcher');
|
|
2
|
+
const SerialChannel = require('../../channel/SerialChannel');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
getCommType() {
|
|
6
|
+
return "serial";
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
validate(commAddress) {
|
|
10
|
+
if (!commAddress) throw new Error("地址不能为空");
|
|
11
|
+
|
|
12
|
+
const parts = commAddress.split("@");
|
|
13
|
+
if (parts.length !== 2) throw new Error("地址格式必须为路径:波特率(例如 COM1@9600)");
|
|
14
|
+
|
|
15
|
+
const [path, baudRateStr] = parts;
|
|
16
|
+
if (!path) throw new Error("串口路径不能为空");
|
|
17
|
+
|
|
18
|
+
const baudRate = parseInt(baudRateStr, 10);
|
|
19
|
+
if (isNaN(baudRate) || baudRate <= 0) {
|
|
20
|
+
throw new Error(`无效的波特率: ${baudRateStr}`);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create(commAddress) {
|
|
25
|
+
this.validate(commAddress);
|
|
26
|
+
|
|
27
|
+
const [path, baudRateStr] = commAddress.split(":");
|
|
28
|
+
const baudRate = parseInt(baudRateStr, 10);
|
|
29
|
+
|
|
30
|
+
return new SerialDispatcher(new SerialChannel(path, baudRate));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const TcpDispatcher = require('../dispatchers/TcpDispatcher');
|
|
2
|
+
const TcpChannel = require('../../channel/TcpChannel');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
getCommType() {
|
|
6
|
+
return "tcp";
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
validate(commAddress) {
|
|
10
|
+
if (!commAddress) throw new Error("地址不能为空");
|
|
11
|
+
const parts = commAddress.split(":");
|
|
12
|
+
if (parts.length !== 2) throw new Error("地址格式必须为 ip:port");
|
|
13
|
+
|
|
14
|
+
const port = parseInt(parts[1], 10);
|
|
15
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
16
|
+
throw new Error(`无效端口: ${parts[1]}`);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
create(commAddress) {
|
|
21
|
+
const [ip, port] = commAddress.split(":");
|
|
22
|
+
return new TcpDispatcher(new TcpChannel(ip, parseInt(port, 10)));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const TcpServerDispatcher = require('../dispatchers/TcpServerDispatcher');
|
|
2
|
+
const TcpServerChannel = require('../../channel/TcpServerChannel');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
/**
|
|
6
|
+
* 获取通信类型标识
|
|
7
|
+
*/
|
|
8
|
+
getCommType() {
|
|
9
|
+
return "tcpserver";
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 验证通信地址格式
|
|
14
|
+
* 支持两种格式: "ip:port" (如 0.0.0.0:9000) 或 单独的 "port" (如 9000)
|
|
15
|
+
*/
|
|
16
|
+
validate(commAddress) {
|
|
17
|
+
if (!commAddress) throw new Error("地址不能为空");
|
|
18
|
+
|
|
19
|
+
const parts = commAddress.split(":");
|
|
20
|
+
let portStr = "";
|
|
21
|
+
|
|
22
|
+
if (parts.length === 1) {
|
|
23
|
+
portStr = parts[0];
|
|
24
|
+
} else if (parts.length === 2) {
|
|
25
|
+
portStr = parts[1];
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error("地址格式错误,应为 port 或 ip:port");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const port = parseInt(portStr, 10);
|
|
31
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
32
|
+
throw new Error(`无效端口: ${portStr}`);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 创建 TcpServer 调度器适配器实例
|
|
38
|
+
*/
|
|
39
|
+
create(commAddress) {
|
|
40
|
+
this.validate(commAddress);
|
|
41
|
+
|
|
42
|
+
const parts = commAddress.split(":");
|
|
43
|
+
let host = '0.0.0.0';
|
|
44
|
+
let port = 9000;
|
|
45
|
+
|
|
46
|
+
if (parts.length === 1) {
|
|
47
|
+
// 仅提供了端口号
|
|
48
|
+
port = parseInt(parts[0], 10);
|
|
49
|
+
} else {
|
|
50
|
+
// 提供了 ip 和 端口号
|
|
51
|
+
host = parts[0];
|
|
52
|
+
port = parseInt(parts[1], 10);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 实例化服务端通道
|
|
56
|
+
const channel = new TcpServerChannel(port, host);
|
|
57
|
+
|
|
58
|
+
// 返回服务端调度器
|
|
59
|
+
return new TcpServerDispatcher(channel);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const UdpDispatcher = require('../dispatchers/UdpDispatcher');
|
|
2
|
+
const UdpChannel = require('../../channel/UdpChannel');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
getCommType() {
|
|
6
|
+
return "udp";
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
validate(commAddress) {
|
|
10
|
+
if (!commAddress) {
|
|
11
|
+
throw new Error("地址不能为空");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const parts = commAddress.split(":");
|
|
15
|
+
if (parts.length !== 3) {
|
|
16
|
+
throw new Error("地址格式必须为 localPort:ip:remotePort");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const localPort = parseInt(parts[0], 10);
|
|
20
|
+
const ip = parts[1];
|
|
21
|
+
const remotePort = parseInt(parts[2], 10);
|
|
22
|
+
|
|
23
|
+
if (isNaN(localPort) || localPort < 0 || localPort > 65535) {
|
|
24
|
+
throw new Error(`无效本地端口: ${parts[0]}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!ip) {
|
|
28
|
+
throw new Error("远程IP不能为空");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isNaN(remotePort) || remotePort < 1 || remotePort > 65535) {
|
|
32
|
+
throw new Error(`无效远程端口: ${parts[2]}`);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
create(commAddress) {
|
|
37
|
+
const [localPort, remoteHost, remotePort] = commAddress.split(":");
|
|
38
|
+
|
|
39
|
+
return new UdpDispatcher(
|
|
40
|
+
new UdpChannel(
|
|
41
|
+
remoteHost,
|
|
42
|
+
parseInt(remotePort, 10),
|
|
43
|
+
parseInt(localPort, 10)
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 任务交互模式 (Interaction Pattern)
|
|
3
|
+
* 决定了单个发送任务的生命周期和行为
|
|
4
|
+
*/
|
|
5
|
+
const InteractionPattern = Object.freeze({
|
|
6
|
+
/**
|
|
7
|
+
* [请求-响应] 发送并等待设备应答
|
|
8
|
+
*/
|
|
9
|
+
WAIT_RESPONSE: 'WAIT_RESPONSE',
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* [单向发送] 触发即忘,只发不管
|
|
13
|
+
*/
|
|
14
|
+
FIRE_AND_FORGET: 'FIRE_AND_FORGET'
|
|
15
|
+
});
|
|
16
|
+
module.exports = InteractionPattern
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 队列调度策略 (Scheduling Strategy)
|
|
3
|
+
* 决定了任务在队列中如何排序和出队
|
|
4
|
+
*/
|
|
5
|
+
const SchedulingStrategy = Object.freeze({
|
|
6
|
+
/** 顺序调度 (先进先出 FIFO) */
|
|
7
|
+
FIFO: 'FIFO',
|
|
8
|
+
|
|
9
|
+
/** 优先级调度 (按 Priority 字段排序) */
|
|
10
|
+
PRIORITY: 'PRIORITY'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = SchedulingStrategy
|