node-red-contrib-ax25 1.0.1 → 1.1.0
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/nodes/agwpe-client.js +69 -1
- package/nodes/agwpe-control.html +80 -0
- package/nodes/agwpe-control.js +72 -0
- package/nodes/connect.html +1 -1
- package/nodes/decode.html +1 -1
- package/nodes/disconnect.html +1 -1
- package/nodes/encode.html +1 -1
- package/nodes/monitor-in.html +1 -1
- package/nodes/raw-in.html +1 -1
- package/nodes/raw-out.html +1 -1
- package/nodes/send.html +1 -1
- package/nodes/ui-in.html +1 -1
- package/nodes/ui-out.html +1 -1
- package/package.json +3 -2
- package/local/buffer_compare.json +0 -135
- package/local/debug-d-frame.js +0 -84
- package/local/raw-out-test.json +0 -128
package/nodes/agwpe-client.js
CHANGED
|
@@ -608,7 +608,7 @@ function cleanupActiveSessions(context) {
|
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
function scheduleReconnect(node, context) {
|
|
611
|
-
if (context._closing || !context.reconnect || context._testTransport) {
|
|
611
|
+
if (context._closing || context._manualClose || !context.reconnect || context._testTransport) {
|
|
612
612
|
return;
|
|
613
613
|
}
|
|
614
614
|
if (context._reconnectTimer) {
|
|
@@ -738,8 +738,76 @@ module.exports = function (RED) {
|
|
|
738
738
|
context.reconnectDelay = Number(config.reconnectDelay) || DEFAULT_RECONNECT_DELAY;
|
|
739
739
|
context._testTransport = config._testTransport || null;
|
|
740
740
|
context._closing = false;
|
|
741
|
+
context._manualClose = false;
|
|
741
742
|
context._reconnectTimer = null;
|
|
742
743
|
|
|
744
|
+
node.instance.control = {
|
|
745
|
+
connect: function () {
|
|
746
|
+
if (context._reconnectTimer) {
|
|
747
|
+
clearTimeout(context._reconnectTimer);
|
|
748
|
+
context._reconnectTimer = null;
|
|
749
|
+
}
|
|
750
|
+
connectToTnc(node, context);
|
|
751
|
+
},
|
|
752
|
+
disconnect: function (cb) {
|
|
753
|
+
context._manualClose = true;
|
|
754
|
+
if (context._reconnectTimer) {
|
|
755
|
+
clearTimeout(context._reconnectTimer);
|
|
756
|
+
context._reconnectTimer = null;
|
|
757
|
+
}
|
|
758
|
+
cleanupActiveSessions(context);
|
|
759
|
+
if (context.transport && typeof context.transport.close === "function") {
|
|
760
|
+
context.transport.close(function () {
|
|
761
|
+
context.transport = null;
|
|
762
|
+
context.state = "disconnected";
|
|
763
|
+
node.status({ fill: "grey", shape: "ring", text: "disconnected" });
|
|
764
|
+
context.bus.emit("conn-lifecycle", { event: "transport-closed" });
|
|
765
|
+
context._manualClose = false;
|
|
766
|
+
if (typeof cb === "function") cb();
|
|
767
|
+
});
|
|
768
|
+
} else {
|
|
769
|
+
context.state = "disconnected";
|
|
770
|
+
node.status({ fill: "grey", shape: "ring", text: "disconnected" });
|
|
771
|
+
context._manualClose = false;
|
|
772
|
+
if (typeof cb === "function") cb();
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
setConfig: function (fields) {
|
|
776
|
+
if (fields.host !== undefined) context.host = String(fields.host);
|
|
777
|
+
if (fields.port !== undefined) context.port = Number(fields.port);
|
|
778
|
+
if (fields.callsigns !== undefined) context.callsigns = normalizeCallsigns(fields.callsigns);
|
|
779
|
+
if (fields.username !== undefined || fields.password !== undefined) {
|
|
780
|
+
const username = fields.username !== undefined ? fields.username : (context.auth ? context.auth.username : "");
|
|
781
|
+
const password = fields.password !== undefined ? fields.password : (context.auth ? context.auth.password : "");
|
|
782
|
+
context.auth = (username && password) ? { username, password } : null;
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
getConfig: function () {
|
|
786
|
+
return {
|
|
787
|
+
host: context.host,
|
|
788
|
+
port: context.port,
|
|
789
|
+
callsigns: context.callsigns.slice(),
|
|
790
|
+
username: context.auth ? context.auth.username : "",
|
|
791
|
+
state: context.state
|
|
792
|
+
};
|
|
793
|
+
},
|
|
794
|
+
getStatus: function () {
|
|
795
|
+
return {
|
|
796
|
+
state: context.state,
|
|
797
|
+
monitorEnabled: Boolean(context.monitorEnabled),
|
|
798
|
+
rawEnabled: Boolean(context.rawEnabled),
|
|
799
|
+
sessions: context.registry.list(context.instanceId).map(function (s) {
|
|
800
|
+
return {
|
|
801
|
+
sessionId: s.sessionId,
|
|
802
|
+
source: s.sourceCallsign,
|
|
803
|
+
destination: s.destinationCallsign,
|
|
804
|
+
state: s.state
|
|
805
|
+
};
|
|
806
|
+
})
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
743
811
|
node.status({ fill: "grey", shape: "ring", text: "disconnected" });
|
|
744
812
|
|
|
745
813
|
connectToTnc(node, context);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("agwpe-control", {
|
|
3
|
+
category: "ax25",
|
|
4
|
+
color: "#d4edda",
|
|
5
|
+
defaults: {
|
|
6
|
+
client: { value: "", type: "agwpe-client", required: true },
|
|
7
|
+
name: { value: "" }
|
|
8
|
+
},
|
|
9
|
+
inputs: 1,
|
|
10
|
+
outputs: 1,
|
|
11
|
+
outputLabels: ["result"],
|
|
12
|
+
icon: "font-awesome/fa-cogs",
|
|
13
|
+
label: function () {
|
|
14
|
+
return this.name || "agwpe-control";
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script type="text/html" data-template-name="agwpe-control">
|
|
20
|
+
<div class="form-row">
|
|
21
|
+
<label for="node-input-client"><i class="fa fa-server"></i> Client</label>
|
|
22
|
+
<input type="text" id="node-input-client" />
|
|
23
|
+
</div>
|
|
24
|
+
<div class="form-row">
|
|
25
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
26
|
+
<input type="text" id="node-input-name" />
|
|
27
|
+
</div>
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<script type="text/html" data-help-name="agwpe-control">
|
|
31
|
+
<p>Sends control commands to an <b>agwpe-client</b> config node at runtime.</p>
|
|
32
|
+
|
|
33
|
+
<h3>Input</h3>
|
|
34
|
+
<dl class="message-properties">
|
|
35
|
+
<dt>command <span class="property-type">string</span></dt>
|
|
36
|
+
<dd>
|
|
37
|
+
One of:
|
|
38
|
+
<ul>
|
|
39
|
+
<li><code>"disconnect"</code> — disconnect all active sessions and close the connection to the AGWPE server.</li>
|
|
40
|
+
<li><code>"connect"</code> — connect to the AGWPE server using the current configuration.</li>
|
|
41
|
+
<li><code>"set-config"</code> — update one or more configuration fields. Supply any combination of <code>msg.host</code>, <code>msg.port</code>, <code>msg.callsigns</code>, <code>msg.username</code>, <code>msg.password</code>.</li>
|
|
42
|
+
<li><code>"get-config"</code> — retrieve the current configuration.</li>
|
|
43
|
+
<li><code>"get-status"</code> — retrieve the current runtime status (connection state, mode flags, active sessions).</li>
|
|
44
|
+
</ul>
|
|
45
|
+
</dd>
|
|
46
|
+
<dt class="optional">host <span class="property-type">string</span></dt>
|
|
47
|
+
<dd>Used with <code>set-config</code>. New AGWPE server host.</dd>
|
|
48
|
+
<dt class="optional">port <span class="property-type">number</span></dt>
|
|
49
|
+
<dd>Used with <code>set-config</code>. New AGWPE server port.</dd>
|
|
50
|
+
<dt class="optional">callsigns <span class="property-type">string | array</span></dt>
|
|
51
|
+
<dd>Used with <code>set-config</code>. Callsigns to register. Comma-separated string or array of strings.</dd>
|
|
52
|
+
<dt class="optional">username <span class="property-type">string</span></dt>
|
|
53
|
+
<dd>Used with <code>set-config</code>. AGWPE authentication username.</dd>
|
|
54
|
+
<dt class="optional">password <span class="property-type">string</span></dt>
|
|
55
|
+
<dd>Used with <code>set-config</code>. AGWPE authentication password.</dd>
|
|
56
|
+
</dl>
|
|
57
|
+
|
|
58
|
+
<h3>Output</h3>
|
|
59
|
+
<dl class="message-properties">
|
|
60
|
+
<dt>status <span class="property-type">string</span></dt>
|
|
61
|
+
<dd><code>"ok"</code> on success; <code>"error"</code> on failure.</dd>
|
|
62
|
+
<dt>command <span class="property-type">string</span></dt>
|
|
63
|
+
<dd>Echoes the command that was processed.</dd>
|
|
64
|
+
<dt class="optional">event <span class="property-type">string</span></dt>
|
|
65
|
+
<dd>For <code>disconnect</code>: <code>"disconnected"</code>. For <code>connect</code>: <code>"connecting"</code>.</dd>
|
|
66
|
+
<dt class="optional">config <span class="property-type">object</span></dt>
|
|
67
|
+
<dd>For <code>get-config</code> and <code>set-config</code>: the current configuration object containing <code>host</code>, <code>port</code>, <code>callsigns</code>, <code>username</code>, and <code>state</code>.</dd>
|
|
68
|
+
<dt class="optional">payload <span class="property-type">object</span></dt>
|
|
69
|
+
<dd>For <code>get-status</code>: an object containing <code>state</code> (string), <code>monitorEnabled</code> (boolean), <code>rawEnabled</code> (boolean), and <code>sessions</code> (array of <code>{sessionId, source, destination, state}</code>).</dd>
|
|
70
|
+
<dt class="optional">errorCode <span class="property-type">string</span></dt>
|
|
71
|
+
<dd>When <code>status</code> is <code>"error"</code>: <code>CLIENT_NOT_FOUND</code> or <code>UNKNOWN_COMMAND</code>.</dd>
|
|
72
|
+
<dt class="optional">errorText <span class="property-type">string</span></dt>
|
|
73
|
+
<dd>When <code>status</code> is <code>"error"</code>: human-readable error description.</dd>
|
|
74
|
+
</dl>
|
|
75
|
+
|
|
76
|
+
<h3>Details</h3>
|
|
77
|
+
<p>The <code>connect</code> command triggers an immediate connection attempt; auto-reconnect behaviour is unaffected.</p>
|
|
78
|
+
<p>The <code>disconnect</code> command closes all active sessions and the transport without triggering an auto-reconnect. Use <code>connect</code> to reconnect manually afterwards.</p>
|
|
79
|
+
<p>The <code>set-config</code> command updates runtime configuration only. Changes take effect on the next <code>connect</code>; they are not persisted to the Node-RED flow.</p>
|
|
80
|
+
</script>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { okEnvelope, errorEnvelope } = require("../lib/message-utils");
|
|
4
|
+
|
|
5
|
+
module.exports = function (RED) {
|
|
6
|
+
function AgwpeControlNode(config) {
|
|
7
|
+
RED.nodes.createNode(this, config);
|
|
8
|
+
const node = this;
|
|
9
|
+
|
|
10
|
+
node.status({});
|
|
11
|
+
|
|
12
|
+
node.on("input", function (msg, send, done) {
|
|
13
|
+
const localSend = send || function (m) { node.send(m); };
|
|
14
|
+
const localDone = done || function () {};
|
|
15
|
+
|
|
16
|
+
const configNode = RED.nodes.getNode(config.client);
|
|
17
|
+
if (!configNode || !configNode.instance || !configNode.instance.control) {
|
|
18
|
+
localSend(errorEnvelope("CLIENT_NOT_FOUND", "agwpe-client config node not found", { command: msg.command }));
|
|
19
|
+
localDone();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const control = configNode.instance.control;
|
|
24
|
+
const command = msg.command;
|
|
25
|
+
|
|
26
|
+
if (command === "disconnect") {
|
|
27
|
+
control.disconnect(function () {
|
|
28
|
+
localSend(okEnvelope({ command: "disconnect", event: "disconnected" }));
|
|
29
|
+
localDone();
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (command === "set-config") {
|
|
35
|
+
const fields = {};
|
|
36
|
+
if (msg.host !== undefined) fields.host = msg.host;
|
|
37
|
+
if (msg.port !== undefined) fields.port = msg.port;
|
|
38
|
+
if (msg.callsigns !== undefined) fields.callsigns = msg.callsigns;
|
|
39
|
+
if (msg.username !== undefined) fields.username = msg.username;
|
|
40
|
+
if (msg.password !== undefined) fields.password = msg.password;
|
|
41
|
+
control.setConfig(fields);
|
|
42
|
+
localSend(okEnvelope({ command: "set-config", config: control.getConfig() }));
|
|
43
|
+
localDone();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (command === "connect") {
|
|
48
|
+
control.connect();
|
|
49
|
+
localSend(okEnvelope({ command: "connect", event: "connecting" }));
|
|
50
|
+
localDone();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (command === "get-config") {
|
|
55
|
+
localSend(okEnvelope({ command: "get-config", config: control.getConfig() }));
|
|
56
|
+
localDone();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (command === "get-status") {
|
|
61
|
+
localSend(okEnvelope({ command: "get-status", payload: control.getStatus() }));
|
|
62
|
+
localDone();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
localSend(errorEnvelope("UNKNOWN_COMMAND", "Unknown command: " + command, { command }));
|
|
67
|
+
localDone();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
RED.nodes.registerType("agwpe-control", AgwpeControlNode);
|
|
72
|
+
};
|
package/nodes/connect.html
CHANGED
package/nodes/decode.html
CHANGED
package/nodes/disconnect.html
CHANGED
package/nodes/encode.html
CHANGED
package/nodes/monitor-in.html
CHANGED
package/nodes/raw-in.html
CHANGED
package/nodes/raw-out.html
CHANGED
package/nodes/send.html
CHANGED
package/nodes/ui-in.html
CHANGED
package/nodes/ui-out.html
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-ax25",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Node-RED nodes for AX.25 connectivity and tooling",
|
|
5
5
|
"main": "nodes/agwpe-client.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"raw-in": "nodes/raw-in.js",
|
|
50
50
|
"raw-out": "nodes/raw-out.js",
|
|
51
51
|
"decode": "nodes/decode.js",
|
|
52
|
-
"encode": "nodes/encode.js"
|
|
52
|
+
"encode": "nodes/encode.js",
|
|
53
|
+
"agwpe-control": "nodes/agwpe-control.js"
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "5b244fa604fea472",
|
|
4
|
-
"type": "debug",
|
|
5
|
-
"z": "efec118fdfbb44e1",
|
|
6
|
-
"name": "debug 4",
|
|
7
|
-
"active": false,
|
|
8
|
-
"tosidebar": true,
|
|
9
|
-
"console": false,
|
|
10
|
-
"tostatus": false,
|
|
11
|
-
"complete": "true",
|
|
12
|
-
"targetType": "full",
|
|
13
|
-
"statusVal": "",
|
|
14
|
-
"statusType": "auto",
|
|
15
|
-
"x": 380,
|
|
16
|
-
"y": 480,
|
|
17
|
-
"wires": []
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"id": "21825cd2b6399cdb",
|
|
21
|
-
"type": "raw-in",
|
|
22
|
-
"z": "efec118fdfbb44e1",
|
|
23
|
-
"name": "",
|
|
24
|
-
"x": 170,
|
|
25
|
-
"y": 480,
|
|
26
|
-
"wires": [
|
|
27
|
-
[
|
|
28
|
-
"5b244fa604fea472",
|
|
29
|
-
"7c55133426e0bf75",
|
|
30
|
-
"fa0970eb0abcbd27"
|
|
31
|
-
]
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"id": "7c55133426e0bf75",
|
|
36
|
-
"type": "decode",
|
|
37
|
-
"z": "efec118fdfbb44e1",
|
|
38
|
-
"name": "",
|
|
39
|
-
"x": 380,
|
|
40
|
-
"y": 540,
|
|
41
|
-
"wires": [
|
|
42
|
-
[
|
|
43
|
-
"716c85ad8fa5ab9e",
|
|
44
|
-
"16f45c05ab12108d"
|
|
45
|
-
]
|
|
46
|
-
]
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"id": "665950798e589835",
|
|
50
|
-
"type": "debug",
|
|
51
|
-
"z": "efec118fdfbb44e1",
|
|
52
|
-
"name": "debug 5",
|
|
53
|
-
"active": false,
|
|
54
|
-
"tosidebar": true,
|
|
55
|
-
"console": false,
|
|
56
|
-
"tostatus": false,
|
|
57
|
-
"complete": "true",
|
|
58
|
-
"targetType": "full",
|
|
59
|
-
"statusVal": "",
|
|
60
|
-
"statusType": "auto",
|
|
61
|
-
"x": 740,
|
|
62
|
-
"y": 540,
|
|
63
|
-
"wires": []
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"id": "716c85ad8fa5ab9e",
|
|
67
|
-
"type": "function",
|
|
68
|
-
"z": "efec118fdfbb44e1",
|
|
69
|
-
"name": "function 2",
|
|
70
|
-
"func": "msg.data = String.fromCharCode(...msg.payload);\nreturn msg;",
|
|
71
|
-
"outputs": 1,
|
|
72
|
-
"timeout": 0,
|
|
73
|
-
"noerr": 0,
|
|
74
|
-
"initialize": "",
|
|
75
|
-
"finalize": "",
|
|
76
|
-
"libs": [],
|
|
77
|
-
"x": 560,
|
|
78
|
-
"y": 540,
|
|
79
|
-
"wires": [
|
|
80
|
-
[
|
|
81
|
-
"665950798e589835"
|
|
82
|
-
]
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"id": "16f45c05ab12108d",
|
|
87
|
-
"type": "encode",
|
|
88
|
-
"z": "efec118fdfbb44e1",
|
|
89
|
-
"name": "",
|
|
90
|
-
"x": 560,
|
|
91
|
-
"y": 600,
|
|
92
|
-
"wires": [
|
|
93
|
-
[
|
|
94
|
-
"fa0970eb0abcbd27"
|
|
95
|
-
]
|
|
96
|
-
]
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"id": "d12d061e1569391f",
|
|
100
|
-
"type": "debug",
|
|
101
|
-
"z": "efec118fdfbb44e1",
|
|
102
|
-
"name": "debug 6",
|
|
103
|
-
"active": true,
|
|
104
|
-
"tosidebar": true,
|
|
105
|
-
"console": false,
|
|
106
|
-
"tostatus": false,
|
|
107
|
-
"complete": "true",
|
|
108
|
-
"targetType": "full",
|
|
109
|
-
"statusVal": "",
|
|
110
|
-
"statusType": "auto",
|
|
111
|
-
"x": 760,
|
|
112
|
-
"y": 680,
|
|
113
|
-
"wires": []
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"id": "fa0970eb0abcbd27",
|
|
117
|
-
"type": "function",
|
|
118
|
-
"z": "efec118fdfbb44e1",
|
|
119
|
-
"name": "function 3",
|
|
120
|
-
"func": "if (msg.event === 'raw') {\n flow.set('raw', msg.payload);\n}\n\nif (msg.event === 'encoded') {\n const raw = flow.get('raw');\n const encoded = msg.payload;\n\n // Find differing indices\n const maxLen = Math.max(raw.length, encoded.length);\n const diffs = [];\n for (let i = 0; i < maxLen; i++) {\n if (raw[i] !== encoded[i]) {\n diffs.push({ index: i, raw: raw[i], encoded: encoded[i] });\n }\n }\n\n if (diffs.length > 0) {\n node.warn(`Buffer mismatch!`);\n node.warn(`raw: ${JSON.stringify(raw)}`);\n node.warn(`encoded: ${JSON.stringify(encoded)}`);\n node.warn(`diffs: ${JSON.stringify(diffs)}`);\n\n msg.payload = {\n raw,\n encoded,\n diffs\n };\n return msg;\n }\n}",
|
|
121
|
-
"outputs": 1,
|
|
122
|
-
"timeout": 0,
|
|
123
|
-
"noerr": 0,
|
|
124
|
-
"initialize": "",
|
|
125
|
-
"finalize": "",
|
|
126
|
-
"libs": [],
|
|
127
|
-
"x": 580,
|
|
128
|
-
"y": 680,
|
|
129
|
-
"wires": [
|
|
130
|
-
[
|
|
131
|
-
"d12d061e1569391f"
|
|
132
|
-
]
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
]
|
package/local/debug-d-frame.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const { makeDataFrame } = require("../lib/agwpe-frame-builder");
|
|
3
|
-
const agwpeClientModule = require("../nodes/agwpe-client");
|
|
4
|
-
const connInModule = require("../nodes/conn-in");
|
|
5
|
-
const store = require("../lib/runtime-store");
|
|
6
|
-
const EventEmitter = require("events");
|
|
7
|
-
|
|
8
|
-
const flowData = {};
|
|
9
|
-
function makeRED() {
|
|
10
|
-
const types = {};
|
|
11
|
-
const RED = {
|
|
12
|
-
nodes: {
|
|
13
|
-
createNode(node, config) {
|
|
14
|
-
const ee = new EventEmitter();
|
|
15
|
-
node.on = ee.on.bind(ee);
|
|
16
|
-
node.emit = ee.emit.bind(ee);
|
|
17
|
-
node.status = () => {};
|
|
18
|
-
node._sent = [];
|
|
19
|
-
node._errors = [];
|
|
20
|
-
node.send = m => node._sent.push(m);
|
|
21
|
-
node.error = () => {};
|
|
22
|
-
node.warn = m => console.log("WARN:", m);
|
|
23
|
-
node.log = m => console.log("LOG:", m);
|
|
24
|
-
node.context = () => ({
|
|
25
|
-
flow: {
|
|
26
|
-
get: k => flowData[k] || null,
|
|
27
|
-
set: (k, v) => { flowData[k] = v; }
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
registerType(t, ctor) { types[t] = ctor; }
|
|
32
|
-
},
|
|
33
|
-
_instantiate(type, config) {
|
|
34
|
-
const n = { id: config.id || type, _flowId: config._flowId || "flow-1", _sent: [], _errors: [] };
|
|
35
|
-
types[type].call(n, config);
|
|
36
|
-
return n;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
return RED;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const RED = makeRED();
|
|
43
|
-
agwpeClientModule(RED);
|
|
44
|
-
connInModule(RED);
|
|
45
|
-
|
|
46
|
-
const mockTransport = new EventEmitter();
|
|
47
|
-
mockTransport.sendFrame = (f, cb) => { if (cb) cb(); };
|
|
48
|
-
|
|
49
|
-
const flowId = "flow-1";
|
|
50
|
-
const client = RED._instantiate("agwpe-client", { id: "client-1", _flowId: flowId });
|
|
51
|
-
const connIn = RED._instantiate("conn-in", { id: "conn-in-1", _flowId: flowId });
|
|
52
|
-
|
|
53
|
-
// Open
|
|
54
|
-
client.emit("input", { command: "open", host: "127.0.0.1", port: 8000, callsigns: ["N1CALL"], _testTransport: mockTransport });
|
|
55
|
-
console.log("client sent after open:", JSON.stringify(client._sent.map(m => m.event)));
|
|
56
|
-
|
|
57
|
-
// Connect
|
|
58
|
-
client.emit("input", { command: "connect", source: "N1CALL", destination: "N1CALL-1", sessionId: "s1" });
|
|
59
|
-
console.log("client sent after connect:", JSON.stringify(client._sent.map(m => m.event)));
|
|
60
|
-
console.log("connIn sent after connect:", JSON.stringify(connIn._sent.map(m => m.event)));
|
|
61
|
-
|
|
62
|
-
// Check session in registry
|
|
63
|
-
const ctx = store.getInstance("client-1");
|
|
64
|
-
if (ctx) {
|
|
65
|
-
const sessions = ctx.registry.list("client-1");
|
|
66
|
-
console.log("sessions in registry:", JSON.stringify(sessions.map(s => ({
|
|
67
|
-
sessionId: s.sessionId, state: s.state,
|
|
68
|
-
src: s.sourceCallsign, dst: s.destinationCallsign
|
|
69
|
-
}))));
|
|
70
|
-
console.log("router instances:", Array.from(ctx.router.instances.keys()));
|
|
71
|
-
console.log("bus listeners conn-data:", ctx.bus.listenerCount("conn-data"));
|
|
72
|
-
console.log("bus listeners conn-lifecycle:", ctx.bus.listenerCount("conn-lifecycle"));
|
|
73
|
-
} else {
|
|
74
|
-
console.log("ERROR: no context in store for client-1");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Fire a D frame
|
|
78
|
-
const dFrame = makeDataFrame("N1CALL-1", "N1CALL", Buffer.from("BBS READY>"));
|
|
79
|
-
console.log("\nEmitting D frame on mockTransport...");
|
|
80
|
-
mockTransport.emit("frame", dFrame);
|
|
81
|
-
|
|
82
|
-
console.log("connIn sent after D frame:", JSON.stringify(connIn._sent.map(m => ({
|
|
83
|
-
event: m.event, hasPayload: m.payload != null, payloadType: m.payload ? typeof m.payload : "none"
|
|
84
|
-
}))));
|
package/local/raw-out-test.json
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "1faf64b0a23c2b10",
|
|
4
|
-
"type": "inject",
|
|
5
|
-
"z": "efec118fdfbb44e1",
|
|
6
|
-
"name": "",
|
|
7
|
-
"props": [
|
|
8
|
-
{
|
|
9
|
-
"p": "payload"
|
|
10
|
-
}
|
|
11
|
-
],
|
|
12
|
-
"repeat": "",
|
|
13
|
-
"crontab": "",
|
|
14
|
-
"once": false,
|
|
15
|
-
"onceDelay": 0.1,
|
|
16
|
-
"topic": "",
|
|
17
|
-
"payload": "Test Frame",
|
|
18
|
-
"payloadType": "str",
|
|
19
|
-
"x": 165,
|
|
20
|
-
"y": 540,
|
|
21
|
-
"wires": [
|
|
22
|
-
[
|
|
23
|
-
"e70c9de10439d31f"
|
|
24
|
-
]
|
|
25
|
-
],
|
|
26
|
-
"l": false
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"id": "e70c9de10439d31f",
|
|
30
|
-
"type": "encode",
|
|
31
|
-
"z": "efec118fdfbb44e1",
|
|
32
|
-
"name": "",
|
|
33
|
-
"source": "N1CALL-7",
|
|
34
|
-
"destination": "TEST",
|
|
35
|
-
"digipeaters": "",
|
|
36
|
-
"control": 3,
|
|
37
|
-
"pid": 240,
|
|
38
|
-
"frameType": "U",
|
|
39
|
-
"payload": "",
|
|
40
|
-
"x": 320,
|
|
41
|
-
"y": 540,
|
|
42
|
-
"wires": [
|
|
43
|
-
[
|
|
44
|
-
"c3cecbe84e55b30f",
|
|
45
|
-
"e8d5bbd844110be9",
|
|
46
|
-
"b304e6d9f1e00d48"
|
|
47
|
-
]
|
|
48
|
-
]
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"id": "c3cecbe84e55b30f",
|
|
52
|
-
"type": "raw-out",
|
|
53
|
-
"z": "efec118fdfbb44e1",
|
|
54
|
-
"name": "",
|
|
55
|
-
"x": 500,
|
|
56
|
-
"y": 540,
|
|
57
|
-
"wires": [
|
|
58
|
-
[
|
|
59
|
-
"15ab7cb28d409d6e"
|
|
60
|
-
]
|
|
61
|
-
]
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"id": "15ab7cb28d409d6e",
|
|
65
|
-
"type": "debug",
|
|
66
|
-
"z": "efec118fdfbb44e1",
|
|
67
|
-
"name": "debug 4",
|
|
68
|
-
"active": true,
|
|
69
|
-
"tosidebar": true,
|
|
70
|
-
"console": false,
|
|
71
|
-
"tostatus": false,
|
|
72
|
-
"complete": "true",
|
|
73
|
-
"targetType": "full",
|
|
74
|
-
"statusVal": "",
|
|
75
|
-
"statusType": "auto",
|
|
76
|
-
"x": 680,
|
|
77
|
-
"y": 540,
|
|
78
|
-
"wires": []
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"id": "e8d5bbd844110be9",
|
|
82
|
-
"type": "debug",
|
|
83
|
-
"z": "efec118fdfbb44e1",
|
|
84
|
-
"name": "debug 5",
|
|
85
|
-
"active": false,
|
|
86
|
-
"tosidebar": true,
|
|
87
|
-
"console": false,
|
|
88
|
-
"tostatus": false,
|
|
89
|
-
"complete": "true",
|
|
90
|
-
"targetType": "full",
|
|
91
|
-
"statusVal": "",
|
|
92
|
-
"statusType": "auto",
|
|
93
|
-
"x": 500,
|
|
94
|
-
"y": 480,
|
|
95
|
-
"wires": []
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"id": "b304e6d9f1e00d48",
|
|
99
|
-
"type": "decode",
|
|
100
|
-
"z": "efec118fdfbb44e1",
|
|
101
|
-
"name": "",
|
|
102
|
-
"payloadOutput": "string",
|
|
103
|
-
"x": 500,
|
|
104
|
-
"y": 600,
|
|
105
|
-
"wires": [
|
|
106
|
-
[
|
|
107
|
-
"b4244f4d5601b40f"
|
|
108
|
-
]
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"id": "b4244f4d5601b40f",
|
|
113
|
-
"type": "debug",
|
|
114
|
-
"z": "efec118fdfbb44e1",
|
|
115
|
-
"name": "debug 6",
|
|
116
|
-
"active": true,
|
|
117
|
-
"tosidebar": true,
|
|
118
|
-
"console": false,
|
|
119
|
-
"tostatus": false,
|
|
120
|
-
"complete": "true",
|
|
121
|
-
"targetType": "full",
|
|
122
|
-
"statusVal": "",
|
|
123
|
-
"statusType": "auto",
|
|
124
|
-
"x": 680,
|
|
125
|
-
"y": 600,
|
|
126
|
-
"wires": []
|
|
127
|
-
}
|
|
128
|
-
]
|