sen-ether-client 0.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/API.md +239 -0
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/bin/node-sen-probe.js +426 -0
- package/bin/node-sen-scan.js +77 -0
- package/index.js +75 -0
- package/lib/bus.js +740 -0
- package/lib/client.js +634 -0
- package/lib/codec.js +501 -0
- package/lib/crc32.js +26 -0
- package/lib/discovery.js +439 -0
- package/lib/hash32.js +40 -0
- package/lib/protocol/generated.js +157 -0
- package/lib/sen.js +1346 -0
- package/lib/values.js +421 -0
- package/package.json +31 -0
- package/resources/protocol/ether/discovery.stl +19 -0
- package/resources/protocol/ether/runtime.stl +40 -0
- package/resources/protocol/kernel/basic_types.stl +274 -0
- package/resources/protocol/kernel/bus_protocol.stl +198 -0
- package/resources/protocol/kernel/type_specs.stl +554 -0
- package/resources/protocol/protocol.json +15 -0
- package/scripts/generate-protocol.mjs +111 -0
package/API.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# sen-ether-client API
|
|
2
|
+
|
|
3
|
+
Public import:
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { Sen, SenInterest, SenRemoteObject } from 'sen-ether-client';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Compatibility
|
|
10
|
+
|
|
11
|
+
`sen-ether-client@0.1.x` supports:
|
|
12
|
+
|
|
13
|
+
- kernel protocol `9`
|
|
14
|
+
- ether protocol `2`
|
|
15
|
+
|
|
16
|
+
These protocol versions are checked during the SEN handshake. A different
|
|
17
|
+
kernel or ether protocol should be treated as unsupported unless `sen-ether-client`
|
|
18
|
+
explicitly adds support for it.
|
|
19
|
+
|
|
20
|
+
The protocol STL files are included in `resources/protocol` as the source for
|
|
21
|
+
the codec. The SEN release noted in that folder is informational; it is not a
|
|
22
|
+
compatibility check.
|
|
23
|
+
|
|
24
|
+
The generated protocol module is loaded at runtime; STL parsing is a maintenance
|
|
25
|
+
step, not part of connection or message decoding.
|
|
26
|
+
|
|
27
|
+
## Sen
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const sen = await Sen.connect();
|
|
31
|
+
|
|
32
|
+
// with explicit options:
|
|
33
|
+
const sen = new Sen(options);
|
|
34
|
+
await sen.connect(options);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Connection options:
|
|
38
|
+
|
|
39
|
+
- `interfaceAddress`: local interface address or interface name for multicast
|
|
40
|
+
discovery.
|
|
41
|
+
- `tcpHub`: optional SEN TCP discovery hub as `host:port`. If omitted,
|
|
42
|
+
multicast discovery is used.
|
|
43
|
+
- `session`: optional SEN session name. If omitted, `Sen` can use queries for
|
|
44
|
+
different sessions and connects to each one on demand.
|
|
45
|
+
- `timeout`: discovery and operation timeout in ms.
|
|
46
|
+
- `discoverySettleMs`: discovery settle time after the first process is found.
|
|
47
|
+
Defaults to `100`.
|
|
48
|
+
- `reconnect`: whether to reconnect and restart interests.
|
|
49
|
+
- `reconnectDelayMs`: delay between reconnect attempts.
|
|
50
|
+
- `maxReconnectAttempts`: maximum reconnect attempts.
|
|
51
|
+
- `participantReadyTimeoutMs`: short non-fatal grace timeout for bus
|
|
52
|
+
participant acknowledgements. Defaults to `1000`.
|
|
53
|
+
- `socketKeepAlive`: enable TCP keepalive. Defaults to `true`.
|
|
54
|
+
- `socketIdleTimeoutMs`: optional TCP idle timeout. Defaults to `0` because
|
|
55
|
+
valid SEN connections can be quiet on TCP while bus data flows separately.
|
|
56
|
+
|
|
57
|
+
`Sen.connect()` uses multicast discovery. `sen-ether-client` reads this SEN environment
|
|
58
|
+
variable as its multicast default:
|
|
59
|
+
|
|
60
|
+
- `SEN_ETHER_DISCOVERY_PORT`
|
|
61
|
+
|
|
62
|
+
Multicast group, bind address and interface selection are explicit `sen-ether-client`
|
|
63
|
+
options, not SEN environment variables.
|
|
64
|
+
|
|
65
|
+
Preferred multi-session usage:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
const sen = await Sen.connect();
|
|
69
|
+
|
|
70
|
+
const hmi = await sen.interest('SELECT * FROM hmi.diagnostics');
|
|
71
|
+
const world = await sen.interest('SELECT * FROM world1.environment');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
TCP discovery hub usage:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const sen = await Sen.connect({ tcpHub: '127.0.0.1:65222' });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Explicit single-session usage is still supported:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const hmi = await Sen.connect({ session: 'hmi' });
|
|
84
|
+
await hmi.interest('SELECT * FROM hmi.diagnostics');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If a SEN bus name itself contains dots and is not a session-qualified bus, pass
|
|
88
|
+
it explicitly. This is useful for standalone scenarios that run in one Ether
|
|
89
|
+
session but publish on a bus such as `scenario.environment`:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
const scenario = await Sen.connect({
|
|
93
|
+
session: 'scenario'
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const objects = await scenario.interest('SELECT * FROM scenario.environment', {
|
|
97
|
+
bus: 'scenario.environment',
|
|
98
|
+
forceBus: true
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Main methods:
|
|
103
|
+
|
|
104
|
+
- `await sen.connect(options)`
|
|
105
|
+
- `await sen.interest(query, options)`
|
|
106
|
+
- `await sen.session(name)`
|
|
107
|
+
- `sen.listSessions()`
|
|
108
|
+
- `sen.listBuses(options)`
|
|
109
|
+
- `await sen.bus(name, options)`
|
|
110
|
+
- `sen.objects()`
|
|
111
|
+
- `sen.getObject(selector)`
|
|
112
|
+
- `await sen.waitForObject(selector, options)`
|
|
113
|
+
- `await sen.close()`
|
|
114
|
+
|
|
115
|
+
Session and bus navigation:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const sen = await Sen.connect();
|
|
119
|
+
|
|
120
|
+
for (const sessionName of sen.listSessions()) {
|
|
121
|
+
const session = await sen.session(sessionName);
|
|
122
|
+
console.log(sessionName, session.listBuses());
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const diagnostics = await sen.session('hmi').then(hmi => hmi.bus('diagnostics'));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Main events:
|
|
129
|
+
|
|
130
|
+
- `connect`
|
|
131
|
+
- `close`
|
|
132
|
+
- `reconnecting`
|
|
133
|
+
- `reconnect`
|
|
134
|
+
- `reconnectError`
|
|
135
|
+
- `warning`
|
|
136
|
+
- `object`
|
|
137
|
+
- `remove`
|
|
138
|
+
- `change`
|
|
139
|
+
- `event`
|
|
140
|
+
|
|
141
|
+
## SenInterest
|
|
142
|
+
|
|
143
|
+
Returned by `await sen.interest(query)`.
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
const interest = await sen.interest('SELECT * FROM hmi.diagnostics');
|
|
147
|
+
const object = await interest.waitFor('EtherProbe');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Main methods:
|
|
151
|
+
|
|
152
|
+
- `interest.objects()`
|
|
153
|
+
- `interest.get(selector)`
|
|
154
|
+
- `await interest.waitFor(selector, options)`
|
|
155
|
+
- `interest.close()`
|
|
156
|
+
|
|
157
|
+
Main events:
|
|
158
|
+
|
|
159
|
+
- `object`
|
|
160
|
+
- `remove`
|
|
161
|
+
- `change`
|
|
162
|
+
- `changes`
|
|
163
|
+
- `event`
|
|
164
|
+
- `stale`
|
|
165
|
+
- `restart`
|
|
166
|
+
|
|
167
|
+
For browser gateways or high-frequency telemetry, request only the properties
|
|
168
|
+
you need and emit batches instead of one JS event per property update:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
const tracks = await sen.interest('SELECT hmi.tactical.BaseTrack FROM hmi.loadtest', {
|
|
172
|
+
properties: ['latitude', 'longitude', 'altitude', 'trackHeading'],
|
|
173
|
+
changeMode: 'batch',
|
|
174
|
+
batchIntervalMs: 16,
|
|
175
|
+
batchMaxSize: 1000,
|
|
176
|
+
maxQueuedChanges: 10000,
|
|
177
|
+
backpressure: 'drop-oldest',
|
|
178
|
+
coalesce: true
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
tracks.on('changes', ({ changes, dropped }) => {
|
|
182
|
+
// Send one compact WebSocket frame to the browser.
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`changeMode: 'individual'` is the default and preserves the traditional
|
|
187
|
+
`change`/`change:<property>` events. `changeMode: 'both'` emits both forms.
|
|
188
|
+
|
|
189
|
+
## SenRemoteObject
|
|
190
|
+
|
|
191
|
+
Returned by `interest.waitFor(...)`, `interest.getObject(...)`, or
|
|
192
|
+
`sen.getObject(...)`.
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
console.log(await object.get('label'));
|
|
196
|
+
await object.set('label', 'from-js');
|
|
197
|
+
console.log(await object.call('ping', ['hello']));
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Main properties:
|
|
201
|
+
|
|
202
|
+
- `id`
|
|
203
|
+
- `name`
|
|
204
|
+
- `className`
|
|
205
|
+
- `snapshot`
|
|
206
|
+
- `timestampNs`: latest SEN source timestamp as a nanosecond `BigInt`
|
|
207
|
+
- `propertyTimestamps`: `Map<string, bigint>` with the latest known timestamp per property
|
|
208
|
+
|
|
209
|
+
Main methods:
|
|
210
|
+
|
|
211
|
+
- `object.matches(selector)`
|
|
212
|
+
- `await object.waitForType(options)`
|
|
213
|
+
- `await object.get(property)`
|
|
214
|
+
- `object.getPropertyTimestamp(property)`
|
|
215
|
+
- `await object.set(property, value)`
|
|
216
|
+
- `await object.call(method, args)`
|
|
217
|
+
|
|
218
|
+
Main events:
|
|
219
|
+
|
|
220
|
+
- `change`
|
|
221
|
+
- `change:<property>`
|
|
222
|
+
- SEN runtime event names emitted by the remote object.
|
|
223
|
+
- `stale`
|
|
224
|
+
|
|
225
|
+
`change.timestampNs` is also a nanosecond `BigInt`. This keeps SEN's original
|
|
226
|
+
64-bit timestamp precision. Convert it explicitly at JSON boundaries:
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
tracks.on('change', ({ object, name, value, timestampNs }) => {
|
|
230
|
+
websocket.send(JSON.stringify({
|
|
231
|
+
object: object.name,
|
|
232
|
+
name,
|
|
233
|
+
value,
|
|
234
|
+
timestampNs: timestampNs?.toString()
|
|
235
|
+
}));
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Low-level protocol modules are intentionally not public API.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcos Pérez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# sen-ether-client
|
|
2
|
+
|
|
3
|
+
JavaScript client for SEN from Node.js.
|
|
4
|
+
|
|
5
|
+
[SEN](https://github.com/airbus/sen) is a general-purpose, distributed,
|
|
6
|
+
object-oriented system for applications that demand high modularity and rich
|
|
7
|
+
communication.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { Sen } from 'sen-ether-client';
|
|
11
|
+
|
|
12
|
+
const sen = await Sen.connect();
|
|
13
|
+
|
|
14
|
+
const diagnostics = await sen.interest('SELECT * FROM hmi.diagnostics');
|
|
15
|
+
const probe = await diagnostics.waitFor('EtherProbe');
|
|
16
|
+
|
|
17
|
+
probe.on('change:label', ({ value }) => {
|
|
18
|
+
console.log('label changed:', value);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(await probe.get('label'));
|
|
22
|
+
await probe.set('label', 'from-js');
|
|
23
|
+
console.log(await probe.call('ping', ['hello']));
|
|
24
|
+
|
|
25
|
+
await sen.close();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install sen-ether-client
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Compatibility
|
|
35
|
+
|
|
36
|
+
`sen-ether-client` speaks SEN ether directly, so compatibility is tied to SEN protocol
|
|
37
|
+
versions, not to a specific SEN release name.
|
|
38
|
+
|
|
39
|
+
| sen-ether-client | Kernel protocol | Ether protocol |
|
|
40
|
+
| --- | ---: | ---: |
|
|
41
|
+
| 0.1.x | 9 | 2 |
|
|
42
|
+
|
|
43
|
+
If a remote kernel reports another kernel or ether protocol version during the
|
|
44
|
+
SEN handshake, `sen-ether-client` treats it as incompatible until that protocol version
|
|
45
|
+
is explicitly supported.
|
|
46
|
+
|
|
47
|
+
The protocol STL files used to maintain this codec are shipped in
|
|
48
|
+
`resources/protocol`. The source SEN release recorded there is informational;
|
|
49
|
+
runtime compatibility is checked only with the kernel and ether protocol
|
|
50
|
+
numbers announced by the remote process.
|
|
51
|
+
|
|
52
|
+
The runtime imports generated protocol constants from those STL files, so the
|
|
53
|
+
hot path does not parse STL while receiving updates.
|
|
54
|
+
|
|
55
|
+
## Connect
|
|
56
|
+
|
|
57
|
+
By default, `sen-ether-client` uses SEN ether multicast discovery and connects to the
|
|
58
|
+
visible SEN processes:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
const sen = await Sen.connect();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If your SEN ether discovery port is configured through SEN's environment,
|
|
65
|
+
`sen-ether-client` reads the same variable:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export SEN_ETHER_DISCOVERY_PORT=60543
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For machines with more than one network interface, select the multicast
|
|
72
|
+
interface explicitly with either its IPv4 address or interface name:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const sen = await Sen.connect({ interfaceAddress: 'enp0s25' });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If your setup uses a SEN TCP discovery hub instead of multicast, pass it
|
|
79
|
+
explicitly:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
const sen = await Sen.connect({
|
|
83
|
+
tcpHub: '127.0.0.1:65222'
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`sen-ether-client` can work with several SEN sessions from the same client. The session
|
|
88
|
+
is inferred from the query:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
const hmi = await sen.interest('SELECT * FROM hmi.diagnostics');
|
|
92
|
+
const world = await sen.interest('SELECT * FROM world1.environment');
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can also navigate explicitly through sessions and buses:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
const sen = await Sen.connect();
|
|
99
|
+
|
|
100
|
+
console.log(sen.listSessions());
|
|
101
|
+
|
|
102
|
+
const hmi = await sen.session('hmi');
|
|
103
|
+
console.log(hmi.listBuses());
|
|
104
|
+
|
|
105
|
+
const diagnostics = await hmi.bus('diagnostics');
|
|
106
|
+
const probe = await diagnostics.waitFor('EtherProbe');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
You can also connect to one explicit session:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const hmi = await Sen.connect({
|
|
113
|
+
session: 'hmi'
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const diagnostics = await hmi.interest('SELECT * FROM hmi.diagnostics');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Interests
|
|
120
|
+
|
|
121
|
+
Create an interest with a normal SEN query:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
const tracks = await sen.interest('SELECT * FROM world1.environment');
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Listen for objects and changes:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
tracks.on('object', object => {
|
|
131
|
+
console.log(object.name, object.className);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
tracks.on('change', ({ object, name, value }) => {
|
|
135
|
+
console.log(object.name, name, value);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
For browser gateways or high-frequency telemetry, batch changes and decode only
|
|
140
|
+
the properties needed by the UI:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
const tracks = await sen.interest('SELECT hmi.tactical.BaseTrack FROM hmi.loadtest', {
|
|
144
|
+
properties: ['latitude', 'longitude', 'altitude', 'trackHeading'],
|
|
145
|
+
changeMode: 'batch',
|
|
146
|
+
coalesce: true
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
tracks.on('changes', ({ changes }) => {
|
|
150
|
+
websocket.send(JSON.stringify(changes.map(({ object, name, value, timestampNs }) => ({
|
|
151
|
+
object: object.name,
|
|
152
|
+
name,
|
|
153
|
+
value,
|
|
154
|
+
timestampNs: timestampNs?.toString()
|
|
155
|
+
}))));
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Get an object by name, id, class name, or predicate:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
const aircraft = await tracks.waitFor('blue-air-1');
|
|
163
|
+
|
|
164
|
+
const firstAircraft = await tracks.waitFor(
|
|
165
|
+
object => object.className === 'rpr.Aircraft'
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Objects
|
|
170
|
+
|
|
171
|
+
Read and write properties:
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
const label = await probe.get('label');
|
|
175
|
+
await probe.set('label', 'ready');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Call methods:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
const result = await probe.call('ping', ['hello']);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Subscribe to property changes:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
probe.on('change:label', ({ value, previous, timestampNs }) => {
|
|
188
|
+
console.log(previous, '->', value, timestampNs);
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
SEN timestamps are exposed as nanosecond `BigInt` values (`timestampNs`) so the
|
|
193
|
+
64-bit source timestamp is not rounded by JavaScript numbers.
|
|
194
|
+
|
|
195
|
+
Subscribe to SEN runtime events:
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
probe.on('probeEvent', event => {
|
|
199
|
+
console.log(event.args);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## CLI
|
|
204
|
+
|
|
205
|
+
List visible SEN processes:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
npx sen-ether-scan --tcp-hub 127.0.0.1:65222 --timeout 3000
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Probe a bus:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npx sen-ether-probe \
|
|
215
|
+
--tcp-hub 127.0.0.1:65222 \
|
|
216
|
+
--bus hmi.diagnostics
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## API
|
|
220
|
+
|
|
221
|
+
The public import is:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
import { Sen, SenInterest, SenRemoteObject } from 'sen-ether-client';
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
See [API.md](./API.md) for the complete public interface.
|