-
Notifications
You must be signed in to change notification settings - Fork 2
/
port.js
196 lines (177 loc) · 10.1 KB
/
port.js
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
/* Copyright (c) 2019 Parallax Inc., All Rights Reserved. */
// Find Port identifier types
const byCID = "connId"; //Represents numeric Connection ID (cid) type
const byPHID = "phSocket"; //Represents numeric Propeller HTTP Socket ID type
const byPTID = "ptSocket"; //Represents numeric Propeller Telnet Socket ID type
const byMAC = "mac"; //Represents alphanumeric MAC address type
const byPath = "path"; //Represents alphanumeric path (wired/wireless port identifier) type
const byName = "name"; //Represents alphanumeric name (wired/wireless port identifier) type
// Port's max lifetime
const wLife = 2; //Starts at 2; is decremented right away by ageWiredPorts(), leaving a life of 1
const wlLife = 3; //Starts at 3; is decremented right away by ageWirelessPorts(), leaving a life of 2
// Container for attributes of connected ports (wired or wireless)
// See addPort() for a description of the attributes
var ports = [];
// Serial packet handling (for transmissions to browser's terminal)
const serPacketMaxTxTime = 100; // Max wait time before transmitting packet (ms)
const serPacketMax = 1492; // Size of buffer to transmit serial data to browser
const serPacket = {
id : 0,
bufView : null, /*set later to new Uint8Array(new ArrayBuffer(serPacketMax)) since Object.assign() only shallow-copies*/
len : 0,
timer : null,
};
function makePortName(name) {
/* Return friendly port name, excluding leading path.*/
return name.slice(name.lastIndexOf(portDelim[platform])+1);
}
function makeWLPortName(mac) {
/* Return wireless fabricated name in the form 'wx-#######' using last 6 digits of it's MAC address.*/
return 'wx-' + mac.substr(9,16).replace(/\:/g,'');
}
function addPort(alist) {
/* Add new wired or wireless port record (automatically updates existing port if necessary).
alist: [required] one or more attributes of port to add. The only valid attributes for an addPort() operation are (use UpdatePort to adjust more):
path: [required] the string path to the wired serial port, or custom name of wireless port. Can be empty ("") if mac and ip provided,
and a wireless name will be fabricated from the MAC address
mac: [omitted if wired] the wireless port's MAC address
ip: [omitted if wired] the wireless port's IP address*/
function get(attr, src, empty) {
/*Return src.attr (if it exists and is truthy) or returns empty value if not*/
return ((exists(attr, src)) && src[attr]) ? src[attr] : empty;
}
if (!exists("path", alist)) {return}
if (!alist.path) {
//Empty port path? If wireless port details provided, craft port name from mac, else abort (return)
if (get("ip", alist, "") && get("mac", alist, "")) {alist.path = makeWLPortName(alist.mac)} else {return}
}
// Look for existing port (mac used for wireless ports since path/name may have changed since last discovery)
let port = (get("mac", alist, "")) ? findPort(byMAC, alist.mac) : findPort(byPath, alist.path);
if (port) {
// Exists already? Update it's (portPath/Name or iP)
updatePort(port, alist);
} else {
// else, add it as a new port record (all fields included; many with default values to be updated later)
log("Found port: " + alist.path, mDbug);
ports.push({
name : makePortName(alist.path), /*[<>""] Friendly port name; never empty, does not include path*/
path : alist.path, /*[<>""] Wired port path+name, or wireless port's custom name, or fabricated name; never empty*/
connId : get("connId", alist, null), /*[null+] Holds wired serial port's connection id (if open), null (if closed)*/
mac : get("mac", alist, ""), /*[""+] Holds wireless port's MAC address*/
ip : get("ip", alist, ""), /*[""+] Wireless port's IP address; */
new : true, /*[true/false] indicates port is newly-arrived (compared to rest)*/
life : (!get("ip", alist, "")) ? wLife : wlLife, /*[>=0] Initial life value; wired and wireless*/
bSocket : null, /*[null+] Socket to browser (persistent)*/
phSocket : null, /*[null+] Socket to Propeller's HTTP service (not persistent)*/
ptSocket : null, /*[null+] Socket to Propeller's Telnet service (persistent)*/
mode : "none", /*["none"+] Intention of the connection; "none", "debug", or "programming"*/
baud : 0, /*[>=0] Wired port's data rate*/
packet : {}, /*[...] Packet buffer for socket*/
isWired : !Boolean(get("ip", alist, "")), /*[true/false] indicates if port is wired or not*/
isWireless : Boolean(get("ip", alist, "")) /*[true/false] indicates if port is wireless or not*/
});
// Give it its own packet object and buffer
Object.assign(ports[ports.length-1].packet, serPacket);
ports[ports.length-1].packet.bufView = new Uint8Array(new ArrayBuffer(serPacketMax));
}
}
function updatePort(port, alist) {
/* Update port attributes if necessary. Automatically handles special case of baudrate changes.
port: [required] port object to update
alist: [required] one or more attributes of port to update. Unchanging attributes can be omitted. Possible attributes are:
name: the friendly name of the port (not including path). Can be empty ("") and a name will be created from path
path: the string path to the wired serial port, or custom name of wireless port. Can be empty ("") and a wireless name will be fabricated from the MAC address
connId: unique identifier for the wired serial port connection id
ip: the wireless port's IP address
bSocket: active socket to browser to associate with port; may be null
phSocket: active socket to Propeller's HTTP service; may be null
ptSocket: active socket to Propeller's Telnet service; may be null
mode: the current point of the connection; "none", "debug", "programming"
baud: wired serial speed*/
return new Promise(function(resolve, reject) {
function set(attr) {
/*Set existing port.attr = alist.attr if, and only if, alist.attr is defined; otherwise, leave port.attr as-is*/
if (exists(attr, alist)) {port[attr] = alist[attr]}
}
if (exists("path", alist)) {
if (!alist.path) {
// Empty port path? If wireless port, craft path from mac, else abort (reject)
if (port.isWireless && port.mac) {alist.path = makeWLPortName(port.mac)} else {reject(Error("path required!")); return}
}
if (exists("name", alist) && !alist.name) {
// Empty port name? Create it.
alist.name = makePortName(alist.path);
}
}
// log("Updating port '" + port.path + "' with " + alist, mDbug);
// Apply updates (if necessary) as well as special handling
set("name");
set("path");
set("connId");
set("ip");
port.life = (port.isWired) ? wLife : wlLife;
set("bSocket");
set("phSocket");
set("ptSocket");
set("mode");
if (exists("baud", alist)) {
changeBaudrate(port, alist.baud)
.then(function() {resolve()})
.catch(function(e) {reject(e)});
}
})
}
function exists(attr, src) {
/*Returns true if attr exists in src*/
return src.hasOwnProperty(attr);
}
function clearNewPortStatus() {
/* Sets all existing ports to non-new arrival status */
ports.forEach(function(p) {p.new = false})
}
function findPortIdx(type, clue) {
/* Return index of wired or wireless port associated with clue
type / clue pairs must be:
byCID / numeric Connection ID (cid)
byPHID / numeric Propeller HTTP Socket ID
byPTID / numeric Propeller Telnet Socket ID
byMAC / alphanumeric MAC address
byPath / alphanumeric path (wired/wireless port identifier)
byName / alphanumeric name (wired/wireless port identifier)
Returns -1 if not found*/
return ports.findIndex(function(p) {return p[type] === clue});
}
function findPort(type, clue) {
/* Return port record associated with clue. This allows caller to later directly retrieve any member of the record (provided caller safely checks for null)
type / clue pairs must be:
byCID / numeric Connection ID (cid)
byPHID / numeric Propeller HTTP Socket ID
byPTID / numeric Propeller Telnet Socket ID
byMAC / alphanumeric MAC address
byPath / alphanumeric path (wired/wireless port identifier)
byName / alphanumeric name (wired/wireless port identifier)
Returns null if not found*/
let cn = 0;
// Find port record based on scan function
function findConn(scan) {
while (cn < ports.length && !scan()) {cn++}
return cn < ports.length ? ports[cn] : null;
}
// Scan for ID, MAC, path, or name
return findConn(function() {return ports[cn][type] === clue})
}
function deletePort(type, clue) {
/* Delete wired or wireless port associated with clue
type / clue pairs must be:
byCID / numeric Connection ID (cid)
byPHID / numeric Propeller HTTP Socket ID
byPTID / numeric Propeller Telnet Socket ID
byMAC / alphanumeric MAC address
byPath / alphanumeric path (wired/wireless port identifier)
byName / alphanumeric name (wired/wireless port identifier)*/
let idx = findPortIdx(type, clue);
if (idx > -1) {
log("Removed port: " + ports[idx].path, mDbug);
ports.splice(idx, 1);
}
}