unetjs 5.1.0 → 6.0.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/dist/cjs/unet.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /* unet.js v5.1.0 2026-03-25T05:10:03.141Z */
1
+ /* unet.js v6.0.0 2026-04-01T02:42:00.135Z */
2
2
 
3
3
  'use strict';
4
4
 
@@ -1912,9 +1912,15 @@ init();
1912
1912
  const DatagramReq$1 = MessageClass('org.arl.unet.DatagramReq');
1913
1913
  const DatagramNtf$1 = MessageClass('org.arl.unet.DatagramNtf');
1914
1914
  const TxFrameReq = MessageClass('org.arl.unet.phy.TxFrameReq', DatagramReq$1);
1915
- const RxFrameNtf$1 = MessageClass('org.arl.unet.phy.RxFrameNtf', DatagramNtf$1);
1915
+ const RxFrameNtf = MessageClass('org.arl.unet.phy.RxFrameNtf', DatagramNtf$1);
1916
1916
  const BasebandSignal = MessageClass('org.arl.unet.bb.BasebandSignal');
1917
1917
 
1918
+ const UnetTopics = {
1919
+ 'PARAMCHANGE' : 'org.arl.unet.Topics.PARAMCHANGE', // Topic for parameter change notification.
1920
+ 'LIFECYCLE' : 'org.arl.unet.Topics.LIFECYCLE', // Topic for agent lifecycle notification.
1921
+ 'DATAGRAM' : 'org.arl.unet.Topics.DATAGRAM', // Topic for incoming datagram notification.
1922
+ };
1923
+
1918
1924
  let UnetServices = {
1919
1925
  'NODE_INFO': 'org.arl.unet.Services.NODE_INFO',
1920
1926
  'ADDRESS_RESOLUTION': 'org.arl.unet.Services.ADDRESS_RESOLUTION',
@@ -1978,6 +1984,7 @@ let UnetMessages = {
1978
1984
  'DatagramNtf' : MessageClass('org.arl.unet.DatagramNtf'),
1979
1985
  'DatagramProgressNtf' : MessageClass('org.arl.unet.DatagramProgressNtf'),
1980
1986
  'DatagramReq' : MessageClass('org.arl.unet.DatagramReq'),
1987
+ 'DatagramTransmissionNtf': MessageClass('org.arl.unet.DatagramTransmissionNtf'),
1981
1988
  'ParamChangeNtf' : MessageClass('org.arl.unet.ParamChangeNtf'),
1982
1989
  'RefuseRsp' : MessageClass('org.arl.unet.RefuseRsp'),
1983
1990
  'FailureNtf' : MessageClass('org.arl.unet.FailureNtf'),
@@ -1991,9 +1998,9 @@ let UnetMessages = {
1991
1998
 
1992
1999
  // phy
1993
2000
  'FecDecodeReq' : MessageClass('org.arl.unet.phy.FecDecodeReq'),
1994
- 'RxSWiG1FrameNtf' : MessageClass('org.arl.unet.phy.RxSWiG1FrameNtf', RxFrameNtf$1),
2001
+ 'RxSWiG1FrameNtf' : MessageClass('org.arl.unet.phy.RxSWiG1FrameNtf', RxFrameNtf),
1995
2002
  'TxSWiG1FrameReq' : MessageClass('org.arl.unet.phy.TxSWiG1FrameReq', TxFrameReq),
1996
- 'RxJanusFrameNtf' : MessageClass('org.arl.unet.phy.RxJanusFrameNtf', RxFrameNtf$1),
2003
+ 'RxJanusFrameNtf' : MessageClass('org.arl.unet.phy.RxJanusFrameNtf', RxFrameNtf),
1997
2004
  'TxJanusFrameReq' : MessageClass('org.arl.unet.phy.TxJanusFrameReq', TxFrameReq),
1998
2005
  'BadFrameNtf' : MessageClass('org.arl.unet.phy.BadFrameNtf'),
1999
2006
  'BadRangeNtf' : MessageClass('org.arl.unet.phy.BadRangeNtf'),
@@ -2047,6 +2054,7 @@ let UnetMessages = {
2047
2054
  'RemoteFileGetReq' : MessageClass('org.arl.unet.remote.RemoteFileGetReq'),
2048
2055
  'RemoteFileNtf' : MessageClass('org.arl.unet.remote.RemoteFileNtf'),
2049
2056
  'RemoteFilePutReq' : MessageClass('org.arl.unet.remote.RemoteFilePutReq'),
2057
+ 'RemoteDeliveryNtf' : MessageClass('org.arl.unet.remote.RemoteDeliveryNtf'),
2050
2058
  'RemoteSuccessNtf' : MessageClass('org.arl.unet.remote.RemoteSuccessNtf'),
2051
2059
  'RemoteTextNtf' : MessageClass('org.arl.unet.remote.RemoteTextNtf'),
2052
2060
  'RemoteTextReq' : MessageClass('org.arl.unet.remote.RemoteTextReq'),
@@ -2101,243 +2109,33 @@ function _initConv(lat){
2101
2109
  return [xScale, yScale];
2102
2110
  }
2103
2111
 
2104
- /**
2105
- * A message which requests the transmission of the datagram from the Unet
2106
- *
2107
- * @typedef {Message} DatagramReq
2108
- * @property {number[]} data - data as an Array of bytes
2109
- * @property {number} from - from/source node address
2110
- * @property {number} to - to/destination node address
2111
- * @property {number} protocol - protocol number to be used to send this Datagram
2112
- * @property {boolean} reliability - true if Datagram should be reliable, false if unreliable
2113
- * @property {number} ttl - time-to-live for the datagram. Time-to-live is advisory, and an agent may choose it ignore it
2114
- */
2115
-
2116
- /**
2117
- * Notification of received datagram message received by the Unet node.
2118
- *
2119
- * @typedef {Message} DatagramNtf
2120
- * @property {number[]} data - data as an Array of bytes
2121
- * @property {number} from - from/source node address
2122
- * @property {number} to - to/destination node address
2123
- * @property {number} protocol - protocol number to be used to send this Datagram
2124
- * @property {number} ttl - time-to-live for the datagram. Time-to-live is advisory, and an agent may choose it ignore it
2125
- */
2126
-
2127
- /**
2128
- * An identifier for an agent or a topic.
2129
- * @external AgentID
2130
- * @see {@link https://org-arl.github.io/fjage/jsdoc/|fjåge.js Documentation}
2131
- */
2132
-
2133
- /**
2134
- * Services supported by fjage agents.
2135
- * @external Services
2136
- * @see {@link https://org-arl.github.io/fjage/jsdoc/|fjåge.js Documentation}
2137
- */
2138
-
2139
- /**
2140
- * An action represented by a message.
2141
- * @external Performative
2142
- * @see {@link https://org-arl.github.io/fjage/jsdoc/|fjåge.js Documentation}
2143
- */
2144
-
2145
- /**
2146
- * Function to creates a unqualified message class based on a fully qualified name.
2147
- * @external MessageClass
2148
- * @see {@link https://org-arl.github.io/fjage/jsdoc/|fjåge.js Documentation}
2149
- */
2150
-
2151
- /**
2152
- * A caching CachingAgentID which caches Agent parameters locally.
2153
- *
2154
- * @class
2155
- * @extends AgentID
2156
- * @param {string | AgentID} name - name of the agent or an AgentID to copy
2157
- * @param {boolean} topic - name of topic
2158
- * @param {Gateway} owner - Gateway owner for this AgentID
2159
- * @param {Boolean} [greedy=true] - greedily fetches and caches all parameters if this Agent
2160
- *
2161
- */
2162
- class CachingAgentID extends AgentID {
2163
-
2164
- constructor(name, topic, owner, greedy=true) {
2165
- if (name instanceof AgentID) {
2166
- super(name.getName(), name.topic, name.owner);
2167
- } else {
2168
- super(name, topic, owner);
2169
- }
2170
- this.greedy = greedy;
2171
- this.cache = {};
2172
- this.specialParams = ['name', 'version'];
2173
- }
2174
-
2175
- /**
2176
- * Sets parameter(s) on the Agent referred to by this AgentID, and caches the parameter(s).
2177
- *
2178
- * @param {(string|string[])} params - parameters name(s) to be set
2179
- * @param {(Object|Object[])} values - parameters value(s) to be set
2180
- * @param {number} [index=-1] - index of parameter(s) to be set
2181
- * @param {number} [timeout=5000] - timeout for the response
2182
- * @returns {Promise<(Object|Object[])>} - a promise which returns the new value(s) of the parameters
2183
- */
2184
- async set(params, values, index=-1, timeout=5000) {
2185
- let s = await super.set(params, values, index, timeout);
2186
- this._updateCache(params, s, index);
2187
- return s;
2188
- }
2189
-
2190
- /**
2191
- * Gets parameter(s) on the Agent referred to by this AgentID, getting them from the cache if possible.
2192
- *
2193
- * @param {(string|string[])} params - parameters name(s) to be fetched
2194
- * @param {number} [index=-1] - index of parameter(s) to be fetched
2195
- * @param {number} [timeout=5000] - timeout for the response
2196
- * @param {number} [maxage=5000] - maximum age of the cached result to retreive
2197
- * @returns {Promise<(Object|Object[])>} - a promise which returns the value(s) of the parameters
2198
- */
2199
- async get(params, index=-1, timeout=5000, maxage=5000) {
2200
- if (this._isCached(params, index, maxage)) return this._getCache(params, index);
2201
- if (this.greedy &&
2202
- !(Array.isArray(params) && [...new Set([...params, ...this.specialParams])].length!=0) &&
2203
- !this.specialParams.includes(params)) {
2204
- let rsp = await super.get(null, index, timeout);
2205
- this._updateCache(null, rsp, index);
2206
- if (!rsp) return Array.isArray(params) ? new Array(params.length).fill(null) : null;
2207
- if (!params) return rsp;
2208
- else if (Array.isArray(params)) {
2209
- return params.map(p => {
2210
- let f = Object.keys(rsp).find(rv => this._toNamed(rv) === p);
2211
- return f ? rsp[f] : null;
2212
- });
2213
- } else {
2214
- let f = Object.keys(rsp).find(rv => this._toNamed(rv) === params);
2215
- return f ? rsp[f] : null;
2216
- }
2217
- } else {
2218
- let r = await super.get(params, index, timeout);
2219
- this._updateCache(params, r, index);
2220
- return r;
2221
- }
2222
- }
2223
-
2224
- _updateCache(params, vals, index) {
2225
- if (vals == null || Array.isArray(vals) && vals.every(v => v == null)) return;
2226
- if (params == null) {
2227
- params = Object.keys(vals);
2228
- vals = Object.values(vals);
2229
- } else if (!Array.isArray(params)) params = [params];
2230
- if (!Array.isArray(vals)) vals = [vals];
2231
- params = params.map(this._toNamed);
2232
- if (this.cache[index.toString()] === undefined) this.cache[index.toString()] = {};
2233
- let c = this.cache[index.toString()];
2234
- for (let i = 0; i < params.length; i++) {
2235
- if (c[params[i]] === undefined) c[params[i]] = {};
2236
- c[params[i]].value = vals[i];
2237
- c[params[i]].ctime = Date.now();
2238
- }
2239
- }
2240
-
2241
- _isCached(params, index, maxage) {
2242
- if (maxage <= 0) return false;
2243
- if (params == null) return false;
2244
- let c = this.cache[index.toString()];
2245
- if (!c) {
2246
- return false;
2247
- }
2248
- if (!Array.isArray(params)) params = [params];
2249
- const rv = params.every(p => {
2250
- p = this._toNamed(p);
2251
- return (p in c) && (Date.now() - c[p].ctime <= maxage);
2252
- });
2253
- return rv;
2254
- }
2255
-
2256
- _getCache(params, index) {
2257
- let c = this.cache[index.toString()];
2258
- if (!c) return null;
2259
- if (!Array.isArray(params)){
2260
- if (params in c) return c[params].value;
2261
- return null;
2262
- }else {
2263
- return params.map(p => p in c ? c[p].value : null);
2264
- }
2265
- }
2266
-
2267
- _toNamed(param) {
2268
- const idx = param.lastIndexOf('.');
2269
- if (idx < 0) return param;
2270
- else return param.slice(idx+1);
2271
- }
2272
-
2273
- }
2274
-
2275
-
2276
- class CachingGateway extends Gateway{
2277
-
2278
- /**
2279
- * Get an AgentID for a given agent name.
2280
- *
2281
- * @param {string} name - name of agent
2282
- * @param {Boolean} [caching=true] - if the AgentID should cache parameters
2283
- * @param {Boolean} [greedy=true] - greedily fetches and caches all parameters if this Agent
2284
- * @returns {AgentID|CachingAgentID} - AgentID for the given name
2285
- */
2286
- agent(name, caching=true, greedy=true) {
2287
- const aid = super.agent(name);
2288
- return caching ? new CachingAgentID(aid, null, null, greedy) : aid;
2289
- }
2290
-
2291
- /**
2292
- * Returns an object representing the named topic.
2293
- *
2294
- * @param {string|AgentID} topic - name of the topic or AgentID
2295
- * @param {string} topic2 - name of the topic if the topic param is an AgentID
2296
- * @param {Boolean} [caching=true] - if the AgentID should cache parameters
2297
- * @param {Boolean} [greedy=true] - greedily fetches and caches all parameters if this Agent
2298
- * @returns {AgentID|CachingAgentID} - object representing the topic
2299
- */
2300
- topic(topic, topic2, caching=true, greedy=true) {
2301
- const aid = super.topic(topic, topic2);
2302
- return caching ? new CachingAgentID(aid, null, null, greedy) : aid;
2303
- }
2304
-
2305
- /**
2306
- * Finds an agent that provides a named service. If multiple agents are registered
2307
- * to provide a given service, any of the agents' id may be returned.
2308
- *
2309
- * @param {string} service - the named service of interest
2310
- * @param {Boolean} [caching=true] - if the AgentID should cache parameters
2311
- * @param {Boolean} [greedy=true] - greedily fetches and caches all parameters if this Agent
2312
- * @returns {Promise<?AgentID|CachingAgentID>} - a promise which returns an agent id for an agent that provides the service when resolved
2313
- */
2314
- async agentForService(service, caching=true, greedy=true) {
2315
- const aid = await super.agentForService(service);
2316
- if (!aid) return aid;
2317
- return caching ? new CachingAgentID(aid, null, null, greedy) : aid;
2318
- }
2319
-
2320
- /**
2321
- * Finds all agents that provides a named service.
2322
- *
2323
- * @param {string} service - the named service of interest
2324
- * @param {Boolean} [caching=true] - if the AgentID should cache parameters
2325
- * @param {Boolean} [greedy=true] - greedily fetches and caches all parameters if this Agent
2326
- * @returns {Promise<?AgentID|CachingAgentID[]>} - a promise which returns an array of all agent ids that provides the service when resolved
2327
- */
2328
- async agentsForService(service, caching=true, greedy=true) {
2329
- const aids = await super.agentsForService(service);
2330
- return caching ? aids.map(a => new CachingAgentID(a, null, null, greedy)) : aids;
2331
- }
2332
- }
2333
-
2112
+ const BROADCAST_ADDR = 0;
2334
2113
  const REQUEST_TIMEOUT = 1000;
2114
+ const NON_BLOCKING = 0;
2115
+ const SEMI_BLOCKING = 1;
2116
+ const BLOCKING = 2;
2335
2117
 
2336
2118
  const AddressResolutionReq = UnetMessages.AddressResolutionReq;
2119
+ const DatagramDeliveryNtf = UnetMessages.DatagramDeliveryNtf;
2120
+ const DatagramFailureNtf = UnetMessages.DatagramFailureNtf;
2337
2121
  const DatagramReq = UnetMessages.DatagramReq;
2338
2122
  const DatagramNtf = UnetMessages.DatagramNtf;
2339
- const RxFrameNtf = UnetMessages.RxFrameNtf;
2123
+ const DatagramTransmissionNtf = UnetMessages.DatagramTransmissionNtf;
2124
+ const RemoteDeliveryNtf = UnetMessages.RemoteDeliveryNtf;
2125
+ const RemoteFailureNtf = UnetMessages.RemoteFailureNtf;
2126
+ const RemoteSuccessNtf = UnetMessages.RemoteSuccessNtf;
2127
+
2128
+ function isReservedProtocol(protocol) {
2129
+ return protocol != Protocol.DATA && (protocol < Protocol.USER || protocol > Protocol.MAX);
2130
+ }
2340
2131
 
2132
+ function hasValue(value) {
2133
+ return value !== undefined && value !== null;
2134
+ }
2135
+
2136
+ function hasFiniteNumber(value) {
2137
+ return typeof value === 'number' && Number.isFinite(value);
2138
+ }
2341
2139
  /**
2342
2140
  * Creates a new UnetSocket to connect to a running Unet instance. This constructor returns a
2343
2141
  * {@link Promise} instead of the constructed UnetSocket object. Use `await` or `.then()` to get
@@ -2358,18 +2156,63 @@ class UnetSocket {
2358
2156
 
2359
2157
  constructor(hostname, port, path='') {
2360
2158
  return (async () => {
2361
- this.gw = new Gateway({
2159
+ this._gw = new Gateway({
2362
2160
  hostname : hostname,
2363
2161
  port : port,
2364
2162
  path : path
2365
2163
  });
2366
- this.localProtocol = -1;
2367
- this.remoteAddress = -1;
2368
- this.remoteProtocol = Protocol.DATA;
2369
- this.timeout = 0;
2370
- this.provider = null;
2371
- const alist = await this.gw.agentsForService(Services.DATAGRAM);
2372
- alist.forEach(a => {this.gw.subscribe(this.gw.topic(a));});
2164
+ this._localProtocol = -1;
2165
+ this._remoteAddress = -1;
2166
+ this._localAddress = -1;
2167
+ this._remoteProtocol = Protocol.DATA;
2168
+ this._timeout = 0;
2169
+ this._serviceProvider = null;
2170
+ this._ttl = NaN;
2171
+ this._priority = null;
2172
+ this._reliability = null;
2173
+ this._route = null;
2174
+ this._mimeType = null;
2175
+ this._remoteRecipient = null;
2176
+ this._mailbox = null;
2177
+ this._messageClass = null;
2178
+ this._sendMode = SEMI_BLOCKING;
2179
+ this._warnedDeprecatedSend = false;
2180
+ this._paramChangeCallbacks = {};
2181
+
2182
+ //for new UnetStack versions (5.2.0 and later)
2183
+ this._gw.subscribe(this._gw.topic(UnetTopics.DATAGRAM));
2184
+
2185
+ //for compatibility with older UnetStack versions (before 5.2.0)
2186
+ const alist = await this._gw.agentsForService(Services.DATAGRAM);
2187
+ alist.forEach(a => {this._gw.subscribe(this._gw.topic(a));});
2188
+
2189
+ // get local nodeinfo agent
2190
+ const nodeinfo = await this._gw.agentForService(Services.NODE_INFO);
2191
+
2192
+ // subscribe to paramchange notifications for onParamChange callbacks
2193
+ this._gw.subscribe(this._gw.topic(UnetTopics.PARAMCHANGE));
2194
+ if (nodeinfo != null) this._gw.subscribe(this._gw.topic(nodeinfo)); // nodeinfo publishes param changes for local node parameters
2195
+ this._gw.addMessageListener(msg => {
2196
+ if (msg instanceof UnetMessages.ParamChangeNtf) {
2197
+ const sender = (msg.sender instanceof AgentID) ? msg.sender.name : msg.sender;
2198
+ if (msg.paramValues != null){
2199
+ for (const p in msg.paramValues) {
2200
+ // if p has dots inside then take the substring after the last dot as the parameter name,
2201
+ // since many times fully qualified parameter names are used in the notifications
2202
+ const paramName = p.includes('.') ? p.substring(p.lastIndexOf('.') + 1) : p;
2203
+ const callback = this._paramChangeCallbacks[sender + '.' + paramName];
2204
+ if (callback != null) callback(msg.paramValues[p]);
2205
+ }
2206
+ }
2207
+ }
2208
+ });
2209
+
2210
+ // get local node address and subscribe to changes in the local node address
2211
+ if (nodeinfo != null) {
2212
+ this.onParamChange(nodeinfo.name, 'address', (addr) => { this._localAddress = addr; });
2213
+ this._localAddress = await nodeinfo.get('address');
2214
+ }
2215
+
2373
2216
  return this;
2374
2217
  })();
2375
2218
  }
@@ -2379,8 +2222,8 @@ class UnetSocket {
2379
2222
  * @returns {void}
2380
2223
  */
2381
2224
  close() {
2382
- this.gw.close();
2383
- this.gw = null;
2225
+ this._gw.close();
2226
+ this._gw = null;
2384
2227
  }
2385
2228
 
2386
2229
  /**
@@ -2388,7 +2231,7 @@ class UnetSocket {
2388
2231
  * @returns {boolean} - true if closed, false if open
2389
2232
  */
2390
2233
  isClosed() {
2391
- return this.gw == null;
2234
+ return this._gw == null;
2392
2235
  }
2393
2236
 
2394
2237
  /**
@@ -2400,7 +2243,7 @@ class UnetSocket {
2400
2243
  */
2401
2244
  bind(protocol) {
2402
2245
  if (protocol == Protocol.DATA || (protocol >= Protocol.USER && protocol <= Protocol.MAX)) {
2403
- this.localProtocol = protocol;
2246
+ this._localProtocol = protocol;
2404
2247
  return true;
2405
2248
  }
2406
2249
  return false;
@@ -2411,13 +2254,13 @@ class UnetSocket {
2411
2254
  * Protocol numbers between Protocol.DATA+1 to Protocol.USER-1 are considered reserved.
2412
2255
  * @returns {void}
2413
2256
  */
2414
- unbind() { this.localProtocol = -1;}
2257
+ unbind() { this._localProtocol = -1;}
2415
2258
 
2416
2259
  /**
2417
2260
  * Checks if a socket is bound.
2418
2261
  * @returns {boolean} - true if bound to a protocol, false if unbound
2419
2262
  */
2420
- isBound() { return this.localProtocol >= 0;}
2263
+ isBound() { return this._localProtocol >= 0;}
2421
2264
 
2422
2265
  /**
2423
2266
  * Sets the default destination address and destination protocol number for datagrams sent
@@ -2433,8 +2276,8 @@ class UnetSocket {
2433
2276
  */
2434
2277
  connect(to, protocol) {
2435
2278
  if (to >= 0 && (protocol == Protocol.DATA || (protocol >= Protocol.USER && protocol <= Protocol.MAX))) {
2436
- this.remoteAddress = to;
2437
- this.remoteProtocol = protocol;
2279
+ this._remoteAddress = to;
2280
+ this._remoteProtocol = protocol;
2438
2281
  return true;
2439
2282
  }
2440
2283
  return false;
@@ -2446,23 +2289,23 @@ class UnetSocket {
2446
2289
  * @returns {void}
2447
2290
  */
2448
2291
  disconnect() {
2449
- this.remoteAddress = -1;
2450
- this.remoteProtocol = 0;
2292
+ this._remoteAddress = -1;
2293
+ this._remoteProtocol = 0;
2451
2294
  }
2452
2295
 
2453
2296
  /**
2454
2297
  * Checks if a socket is connected, i.e., has a default destination address and protocol number.
2455
2298
  * @returns {boolean} - true if connected, false otherwise
2456
2299
  */
2457
- isConnected() { return this.remoteAddress >= 0; }
2300
+ isConnected() { return this._remoteAddress >= 0; }
2458
2301
 
2459
2302
  /**
2460
2303
  * Gets the local node address of the Unet node connected to.
2461
2304
  * @returns {Promise<int>} - local node address, or -1 on error
2462
2305
  */
2463
2306
  async getLocalAddress() {
2464
- if (this.gw == null) return -1;
2465
- const nodeinfo = await this.gw.agentForService(Services.NODE_INFO);
2307
+ if (this._gw == null) return -1;
2308
+ const nodeinfo = await this._gw.agentForService(Services.NODE_INFO);
2466
2309
  if (nodeinfo == null) return -1;
2467
2310
  const addr = await nodeinfo.get('address');
2468
2311
  return addr != null ? addr : -1;
@@ -2470,21 +2313,21 @@ class UnetSocket {
2470
2313
 
2471
2314
  /**
2472
2315
  * Gets the protocol number that the socket is bound to.
2473
- * @returns {number}} - protocol number if socket is bound, -1 otherwise
2316
+ * @returns {number} - protocol number if socket is bound, -1 otherwise
2474
2317
  */
2475
- getLocalProtocol() { return this.localProtocol; }
2318
+ getLocalProtocol() { return this._localProtocol; }
2476
2319
 
2477
2320
  /**
2478
2321
  * Gets the default destination node address for a connected socket.
2479
- * @returns {number}} - default destination node address if connected, -1 otherwise
2322
+ * @returns {number} - default destination node address if connected, -1 otherwise
2480
2323
  */
2481
- getRemoteAddress() { return this.remoteAddress; }
2324
+ getRemoteAddress() { return this._remoteAddress; }
2482
2325
 
2483
2326
  /**
2484
2327
  * Gets the default transmission protocol number.
2485
- * @returns {number}} - default protocol number used to transmit a datagram
2328
+ * @returns {number} - default protocol number used to transmit a datagram
2486
2329
  */
2487
- getRemoteProtocol() { return this.remoteProtocol; }
2330
+ getRemoteProtocol() { return this._remoteProtocol; }
2488
2331
 
2489
2332
  /**
2490
2333
  * Sets the timeout for datagram reception. A timeout of 0 means the
@@ -2496,14 +2339,275 @@ class UnetSocket {
2496
2339
  */
2497
2340
  setTimeout(ms) {
2498
2341
  if (ms < 0) ms = 0;
2499
- this.timeout = ms;
2342
+ this._timeout = ms;
2500
2343
  }
2501
2344
 
2502
2345
  /**
2503
2346
  * Gets the timeout for datagram reception.
2504
2347
  * @returns {number} - timeout in milliseconds
2505
2348
  */
2506
- getTimeout() { return this.timeout; }
2349
+ getTimeout() { return this._timeout; }
2350
+
2351
+ /**
2352
+ * Sets the default time-to-live for datagrams sent using this socket.
2353
+ * TTL is advisory; an agent may choose to ignore it.
2354
+ * @param {number} ttl - time-to-live in seconds
2355
+ * @returns {void}
2356
+ */
2357
+ setTTL(ttl) {
2358
+ this._ttl = ttl;
2359
+ }
2360
+
2361
+ /**
2362
+ * Gets the default time-to-live for datagrams sent using this socket.
2363
+ * @returns {number} - time-to-live in seconds, or NaN if not set
2364
+ */
2365
+ getTTL() {
2366
+ return this._ttl;
2367
+ }
2368
+
2369
+ /**
2370
+ * Sets the default priority for datagrams sent using this socket.
2371
+ * @param {*} priority - priority value; interpretation is provider-dependent
2372
+ * @returns {void}
2373
+ */
2374
+ setPriority(priority) {
2375
+ this._priority = priority;
2376
+ }
2377
+
2378
+ /**
2379
+ * Gets the default priority for datagrams sent using this socket.
2380
+ * @returns {*} - priority value, or null if not set
2381
+ */
2382
+ getPriority() {
2383
+ return this._priority;
2384
+ }
2385
+
2386
+ /**
2387
+ * Sets the default reliability for datagrams sent using this socket.
2388
+ * When set to true, the socket will request reliable delivery and, in
2389
+ * SEMI_BLOCKING mode, will wait for a delivery confirmation before returning.
2390
+ * @param {boolean} reliability - true for reliable delivery, false for unreliable
2391
+ * @returns {void}
2392
+ */
2393
+ setReliability(reliability) {
2394
+ this._reliability = reliability;
2395
+ }
2396
+
2397
+ /**
2398
+ * Gets the default reliability setting for datagrams sent using this socket.
2399
+ * @returns {boolean|null} - true if reliable, false if unreliable, null if not set
2400
+ */
2401
+ getReliability() {
2402
+ return this._reliability;
2403
+ }
2404
+
2405
+ /**
2406
+ * Sets the default route identifier for datagrams sent using this socket.
2407
+ * Route selection is provider-dependent; not all providers support explicit routing.
2408
+ * @param {*} route - route identifier
2409
+ * @returns {void}
2410
+ */
2411
+ setRoute(route) {
2412
+ this._route = route;
2413
+ }
2414
+
2415
+ /**
2416
+ * Gets the default route identifier for datagrams sent using this socket.
2417
+ * @returns {*} - route identifier, or null if not set
2418
+ */
2419
+ getRoute() {
2420
+ return this._route;
2421
+ }
2422
+
2423
+ /**
2424
+ * Sets the default MIME type describing the datagram payload.
2425
+ * This can be used by REMOTE service providers to apply content-aware compression.
2426
+ * @param {string} mimeType - MIME type string (e.g. 'application/json')
2427
+ * @returns {void}
2428
+ */
2429
+ setMimeType(mimeType) {
2430
+ this._mimeType = mimeType;
2431
+ }
2432
+
2433
+ /**
2434
+ * Gets the default MIME type for datagrams sent using this socket.
2435
+ * @returns {string|null} - MIME type string, or null if not set
2436
+ */
2437
+ getMimeType() {
2438
+ return this._mimeType;
2439
+ }
2440
+
2441
+ /**
2442
+ * Sets the default remote recipient for datagrams sent using this socket.
2443
+ * Used by REMOTE service providers to address a specific entity on the remote node.
2444
+ * @param {*} remoteRecipient - remote recipient identifier (e.g. an AgentID or name string)
2445
+ * @returns {void}
2446
+ */
2447
+ setRemoteRecipient(remoteRecipient) {
2448
+ this._remoteRecipient = remoteRecipient;
2449
+ }
2450
+
2451
+ /**
2452
+ * Gets the default remote recipient for datagrams sent using this socket.
2453
+ * @returns {*} - remote recipient identifier, or null if not set
2454
+ */
2455
+ getRemoteRecipient() {
2456
+ return this._remoteRecipient;
2457
+ }
2458
+
2459
+ /**
2460
+ * Sets the default mailbox name for datagrams sent using this socket.
2461
+ * Mailboxes are used by REMOTE service providers to deliver messages
2462
+ * to a named queue on the remote node.
2463
+ * @param {string} mailbox - mailbox name
2464
+ * @returns {void}
2465
+ */
2466
+ setMailbox(mailbox) {
2467
+ this._mailbox = mailbox;
2468
+ }
2469
+
2470
+ /**
2471
+ * Gets the default mailbox name for datagrams sent using this socket.
2472
+ * @returns {string|null} - mailbox name, or null if not set
2473
+ */
2474
+ getMailbox() {
2475
+ return this._mailbox;
2476
+ }
2477
+
2478
+ /**
2479
+ * Sets the default application message class for datagrams sent using this socket.
2480
+ * Used by REMOTE service providers to associate a fully-qualified class name with the payload.
2481
+ * @param {string} messageClass - fully-qualified message class name (e.g. 'org.example.Status')
2482
+ * @returns {void}
2483
+ */
2484
+ setMessageClass(messageClass) {
2485
+ this._messageClass = messageClass;
2486
+ }
2487
+
2488
+ /**
2489
+ * Gets the default application message class for datagrams sent using this socket.
2490
+ * @returns {string|null} - message class name, or null if not set
2491
+ */
2492
+ getMessageClass() {
2493
+ return this._messageClass;
2494
+ }
2495
+
2496
+ /**
2497
+ * Overrides the service provider used to transmit datagrams. When set, provider
2498
+ * auto-selection is bypassed and all sends go through the specified agent.
2499
+ * Pass null to re-enable auto-selection.
2500
+ * @param {AgentID|string|null} provider - agent id, agent name string, or null to clear the override
2501
+ * @returns {void}
2502
+ */
2503
+ setServiceProvider(provider) {
2504
+ if (typeof provider === 'string') provider = this.agent(provider);
2505
+ this._serviceProvider = provider instanceof AgentID || provider == null ? provider : null;
2506
+ }
2507
+
2508
+ /**
2509
+ * Gets the currently configured service provider override.
2510
+ * @returns {AgentID|null} - the override agent id, or null if auto-selection is active
2511
+ */
2512
+ getServiceProvider() {
2513
+ return this._serviceProvider;
2514
+ }
2515
+
2516
+ /**
2517
+ * Sets the send mode that controls how send() behaves after the provider accepts a request:
2518
+ * - `UnetSocket.NON_BLOCKING` (0): returns immediately after the provider agrees.
2519
+ * - `UnetSocket.SEMI_BLOCKING` (1): if reliability is not true, returns after AGREE; otherwise
2520
+ * waits for a delivery confirmation (RemoteDeliveryNtf / DatagramDeliveryNtf) or failure.
2521
+ * - `UnetSocket.BLOCKING` (2): waits for a transmission or delivery notification
2522
+ * (DatagramTransmissionNtf, DatagramDeliveryNtf) before returning.
2523
+ * The default is SEMI_BLOCKING.
2524
+ * @param {number} sendMode - one of UnetSocket.NON_BLOCKING, SEMI_BLOCKING, or BLOCKING
2525
+ * @returns {void}
2526
+ */
2527
+ setSendMode(sendMode) {
2528
+ if ([NON_BLOCKING, SEMI_BLOCKING, BLOCKING].includes(sendMode)) this._sendMode = sendMode;
2529
+ }
2530
+
2531
+ /**
2532
+ * Gets the current send mode.
2533
+ * @returns {number} - current send mode (NON_BLOCKING=0, SEMI_BLOCKING=1, BLOCKING=2)
2534
+ */
2535
+ getSendMode() {
2536
+ return this._sendMode;
2537
+ }
2538
+
2539
+ async _chooseAgentForService(service) {
2540
+ const agents = await this._gw.agentsForService(service);
2541
+ if (agents == null || agents.length === 0) return null;
2542
+ return agents[Math.floor(Math.random() * agents.length)];
2543
+ }
2544
+
2545
+ async _resolveProvider() {
2546
+ if (this._serviceProvider != null) return this._serviceProvider;
2547
+ const serviceOrder = [
2548
+ Services.REMOTE,
2549
+ Services.TRANSPORT,
2550
+ Services.ROUTING,
2551
+ Services.LINK,
2552
+ Services.PHYSICAL,
2553
+ Services.DATAGRAM,
2554
+ ];
2555
+ for (const service of serviceOrder) {
2556
+ const provider = await this._chooseAgentForService(service);
2557
+ if (provider != null) return provider;
2558
+ }
2559
+ return null;
2560
+ }
2561
+
2562
+ _applySocketSettings(req) {
2563
+ if (!hasValue(req.priority) && hasValue(this._priority)) req.priority = this._priority;
2564
+ if (!hasValue(req.reliability) && hasValue(this._reliability)) req.reliability = this._reliability;
2565
+ if ((!hasValue(req.ttl) || Number.isNaN(req.ttl)) && hasFiniteNumber(this._ttl)) req.ttl = this._ttl;
2566
+ if (!hasValue(req.route) && hasValue(this._route)) req.route = this._route;
2567
+ if (!hasValue(req.mimeType) && hasValue(this._mimeType)) req.mimeType = this._mimeType;
2568
+ if (!hasValue(req.remoteRecipient) && hasValue(this._remoteRecipient)) req.remoteRecipient = this._remoteRecipient;
2569
+ if (!hasValue(req.mailbox) && hasValue(this._mailbox)) req.mailbox = this._mailbox;
2570
+ if (!hasValue(req.messageClass) && hasValue(this._messageClass)) req.messageClass = this._messageClass;
2571
+ }
2572
+
2573
+ _effectiveReliability(req) {
2574
+ return hasValue(req.reliability) ? req.reliability : this._reliability;
2575
+ }
2576
+
2577
+ async _waitForNotification(req, types, timeout = REQUEST_TIMEOUT) {
2578
+ return await this._gw.receive(msg => {
2579
+ if (!types.some(type => msg instanceof type)) return false;
2580
+ if (hasValue(msg.inReplyTo) && msg.inReplyTo === req.msgID) return true;
2581
+ return false;
2582
+ }, timeout);
2583
+ }
2584
+
2585
+ async _waitForSemiBlockingOutcome(req) {
2586
+ const notification = await this._waitForNotification(req, [
2587
+ DatagramDeliveryNtf,
2588
+ DatagramFailureNtf,
2589
+ RemoteDeliveryNtf,
2590
+ RemoteSuccessNtf,
2591
+ RemoteFailureNtf,
2592
+ ]);
2593
+ if (notification == null) return false;
2594
+ if (notification instanceof DatagramFailureNtf || notification instanceof RemoteFailureNtf) return false;
2595
+ return true;
2596
+ }
2597
+
2598
+ async _waitForBlockingOutcome(req) {
2599
+ const notification = await this._waitForNotification(req, [
2600
+ DatagramTransmissionNtf,
2601
+ DatagramDeliveryNtf,
2602
+ DatagramFailureNtf,
2603
+ RemoteDeliveryNtf,
2604
+ RemoteSuccessNtf,
2605
+ RemoteFailureNtf,
2606
+ ]);
2607
+ if (notification == null) return false;
2608
+ if (notification instanceof DatagramFailureNtf || notification instanceof RemoteFailureNtf) return false;
2609
+ return true;
2610
+ }
2507
2611
 
2508
2612
  /**
2509
2613
  * Transmits a datagram to the specified node address using the specified protocol.
@@ -2514,32 +2618,43 @@ class UnetSocket {
2514
2618
  * @param {number} protocol - protocol number
2515
2619
  * @returns {Promise<boolean>} - true if the Unet node agreed to send out the Datagram, false otherwise
2516
2620
  */
2517
- async send(data, to=this.remoteAddress, protocol=this.remoteProtocol) {
2518
- if (to < 0 || this.gw == null) return false;
2519
- var req;
2520
- if (Array.isArray(data)){
2621
+ async send(data, to=this._remoteAddress, protocol=this._remoteProtocol) {
2622
+ if (this._gw == null) return false;
2623
+ let req;
2624
+ if (Array.isArray(data)) {
2521
2625
  req = new DatagramReq();
2522
2626
  req.data = data;
2523
2627
  req.to = to;
2524
2628
  req.protocol = protocol;
2525
- } else if (data instanceof DatagramReq){
2629
+ } else if (data instanceof DatagramReq) {
2526
2630
  req = data;
2631
+ if (!this._warnedDeprecatedSend && typeof console !== 'undefined' && typeof console.warn === 'function') {
2632
+ console.warn('UnetSocket.send(DatagramReq) is deprecated; prefer socket metadata setters with send(data, to, protocol).');
2633
+ this._warnedDeprecatedSend = true;
2634
+ }
2527
2635
  } else {
2528
2636
  return false;
2529
2637
  }
2530
- let p = req.protocol;
2531
- if (p != Protocol.DATA && (p < Protocol.USER || p > Protocol.MAX)) return false;
2638
+ if ((!hasValue(req.to) || req.to < 0) && to >= 0) req.to = to;
2639
+ if (!hasValue(req.protocol)) req.protocol = protocol;
2640
+ this._applySocketSettings(req);
2641
+
2642
+ if (!Array.isArray(req.data) || req.to < 0 || isReservedProtocol(req.protocol)) return false;
2532
2643
  if (req.recipient == null) {
2533
- if (this.provider == null) this.provider = await this.gw.agentForService(Services.TRANSPORT);
2534
- if (this.provider == null) this.provider = await this.gw.agentForService(Services.ROUTING);
2535
- if (this.provider == null) this.provider = await this.gw.agentForService(Services.LINK);
2536
- if (this.provider == null) this.provider = await this.gw.agentForService(Services.PHYSICAL);
2537
- if (this.provider == null) this.provider = await this.gw.agentForService(Services.DATAGRAM);
2538
- if (this.provider == null) return false;
2539
- req.recipient = this.provider;
2644
+ req.recipient = await this._resolveProvider();
2645
+ if (req.recipient == null) return false;
2540
2646
  }
2541
- const rsp = await this.gw.request(req, REQUEST_TIMEOUT);
2542
- return (rsp != null && rsp.perf == Performative.AGREE);
2647
+ const rsp = await this._gw.request(req, REQUEST_TIMEOUT);
2648
+ if (rsp == null || rsp.perf != Performative.AGREE) return false;
2649
+
2650
+ if (this._sendMode === NON_BLOCKING) return true;
2651
+
2652
+ if (this._sendMode === SEMI_BLOCKING) {
2653
+ if (this._effectiveReliability(req) !== true) return true;
2654
+ return await this._waitForSemiBlockingOutcome(req);
2655
+ }
2656
+
2657
+ return await this._waitForBlockingOutcome(req);
2543
2658
  }
2544
2659
 
2545
2660
  /**
@@ -2549,22 +2664,23 @@ class UnetSocket {
2549
2664
  * @returns {Promise<?DatagramNtf>} - datagram received by the socket
2550
2665
  */
2551
2666
  async receive() {
2552
- if (this.gw == null) return null;
2553
- return await this.gw.receive(msg => {
2554
- if (msg.__clazz__ != DatagramNtf.__clazz__ && msg.__clazz__ != RxFrameNtf.__clazz__ ) return false;
2667
+ if (this._gw == null) return null;
2668
+ return await this._gw.receive(msg => {
2669
+ if (!(msg instanceof DatagramNtf)) return false;
2555
2670
  let p = msg.protocol;
2671
+ if (msg.to != BROADCAST_ADDR && msg.to != this._localAddress) return false; // Datagram not addressed to this node
2556
2672
  if (p == Protocol.DATA || p >= Protocol.USER) {
2557
- return this.localProtocol < 0 || this.localProtocol == p;
2673
+ return this._localProtocol < 0 || this._localProtocol == p;
2558
2674
  }
2559
2675
  return false;
2560
- }, this.timeout);
2676
+ }, this._timeout);
2561
2677
  }
2562
2678
 
2563
2679
  /**
2564
2680
  * Gets a Gateway to provide low-level access to UnetStack.
2565
2681
  * @returns {Gateway} - underlying fjage Gateway supporting this socket
2566
2682
  */
2567
- getGateway() { return this.gw; }
2683
+ getGateway() { return this._gw; }
2568
2684
 
2569
2685
  /**
2570
2686
  * Gets an AgentID providing a specified service for low-level access to UnetStack
@@ -2572,8 +2688,8 @@ class UnetSocket {
2572
2688
  * @returns {Promise<?AgentID>} - a promise which returns an {@link AgentID} that provides the service when resolved
2573
2689
  */
2574
2690
  async agentForService(svc) {
2575
- if (this.gw == null) return null;
2576
- return await this.gw.agentForService(svc);
2691
+ if (this._gw == null) return null;
2692
+ return await this._gw.agentForService(svc);
2577
2693
  }
2578
2694
 
2579
2695
  /**
@@ -2582,8 +2698,8 @@ class UnetSocket {
2582
2698
  * @returns {Promise<AgentID[]>} - a promise which returns an array of {@link AgentID|AgentIDs} that provides the service when resolved
2583
2699
  */
2584
2700
  async agentsForService(svc) {
2585
- if (this.gw == null) return null;
2586
- return await this.gw.agentsForService(svc);
2701
+ if (this._gw == null) return null;
2702
+ return await this._gw.agentsForService(svc);
2587
2703
  }
2588
2704
 
2589
2705
  /**
@@ -2592,8 +2708,8 @@ class UnetSocket {
2592
2708
  * @returns {AgentID} - AgentID for the given name
2593
2709
  */
2594
2710
  agent(name) {
2595
- if (this.gw == null) return null;
2596
- return this.gw.agent(name);
2711
+ if (this._gw == null) return null;
2712
+ return this._gw.agent(name);
2597
2713
  }
2598
2714
 
2599
2715
  /**
@@ -2607,15 +2723,42 @@ class UnetSocket {
2607
2723
  const req = new AddressResolutionReq(nodeName);
2608
2724
  req.name = nodeName;
2609
2725
  req.recipient = arp;
2610
- const rsp = await this.gw.request(req, REQUEST_TIMEOUT);
2726
+ const rsp = await this._gw.request(req, REQUEST_TIMEOUT);
2611
2727
  if (rsp == null || ! Object.prototype.hasOwnProperty.call(rsp, 'address')) return null;
2612
2728
  return rsp.address;
2613
2729
  }
2730
+
2731
+ /**
2732
+ * Registers a callback function to be called when a parameter value changes for the local node. This
2733
+ * uses the ParamChangeNtf messages published by the Unet node to notify of parameter changes. The callback
2734
+ * function will be called with the new value of the parameter.
2735
+ *
2736
+ * @param {AgentID|string} agentId
2737
+ * @param {string} paramName
2738
+ * @param {function(object): void} callback
2739
+ */
2740
+ onParamChange(agentId, paramName, callback) {
2741
+ if (agentId instanceof AgentID) agentId = agentId.name;
2742
+ this._paramChangeCallbacks[agentId + '.' + paramName] = callback;
2743
+ }
2744
+
2745
+ /**
2746
+ * Removes a callback function registered to be called when a parameter value changes for the local node.
2747
+ *
2748
+ * @param {AgentID|string} agentId
2749
+ * @param {string} paramName
2750
+ */
2751
+ removeParamChange(agentId, paramName) {
2752
+ if (agentId instanceof AgentID) agentId = agentId.name;
2753
+ delete this._paramChangeCallbacks[agentId + '.' + paramName];
2754
+ }
2614
2755
  }
2615
2756
 
2757
+ UnetSocket.NON_BLOCKING = NON_BLOCKING;
2758
+ UnetSocket.SEMI_BLOCKING = SEMI_BLOCKING;
2759
+ UnetSocket.BLOCKING = BLOCKING;
2760
+
2616
2761
  exports.AgentID = AgentID;
2617
- exports.CachingAgentID = CachingAgentID;
2618
- exports.CachingGateway = CachingGateway;
2619
2762
  exports.Gateway = Gateway;
2620
2763
  exports.Message = Message;
2621
2764
  exports.MessageClass = MessageClass;
@@ -2624,5 +2767,6 @@ exports.Protocol = Protocol;
2624
2767
  exports.Services = Services;
2625
2768
  exports.UnetMessages = UnetMessages;
2626
2769
  exports.UnetSocket = UnetSocket;
2770
+ exports.UnetTopics = UnetTopics;
2627
2771
  exports.toGps = toGps;
2628
2772
  exports.toLocal = toLocal;