@csllc/cs-socketcan 0.0.2 → 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/LICENSE +21 -0
- package/README.md +52 -10
- package/index.js +103 -93
- package/package.json +20 -13
- package/examples/pingpong.js +0 -206
- package/examples/send.js +0 -50
- package/examples/test.js +0 -29
- package/test/basic.test.js +0 -375
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2026 Control Solutions LLC
|
|
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
CHANGED
|
@@ -10,17 +10,24 @@ This is also compatible with Kvaser CAN adapters that are supported by the `kvas
|
|
|
10
10
|
|
|
11
11
|
## Getting Started
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This module requires Linux and NodeJS 22.22.2 or later (or 24.15.0 or later).
|
|
14
|
+
|
|
15
|
+
It depends on the [`socketcan`](https://github.com/sebi2k1/node-can) module, which is compiled from source when it is installed, so the native build tools must be installed first. On Debian or Raspberry Pi OS:
|
|
16
|
+
```
|
|
17
|
+
sudo apt install build-essential python3
|
|
18
|
+
```
|
|
14
19
|
|
|
15
20
|
To install this module, run:
|
|
16
21
|
```
|
|
17
22
|
npm install @csllc/cs-socketcan
|
|
18
23
|
```
|
|
19
24
|
|
|
25
|
+
The CAN interface must be configured and brought up before it is opened; see [Configuration](#configuration).
|
|
26
|
+
|
|
20
27
|
The following is a sample script that opens the CAN port, writes a single message to the bus, and listens for and prints received messages from the bus.
|
|
21
28
|
|
|
22
29
|
```js
|
|
23
|
-
const Can = require('@csllc
|
|
30
|
+
const Can = require('@csllc/cs-socketcan');
|
|
24
31
|
|
|
25
32
|
let board = new Can({
|
|
26
33
|
canRate: 250000,
|
|
@@ -59,14 +66,21 @@ JavaScript examples are available in the `examples` folder.
|
|
|
59
66
|
|
|
60
67
|
## Configuration
|
|
61
68
|
|
|
62
|
-
The constructor accepts an object that specifies the desired configuration.
|
|
69
|
+
The constructor accepts an object that specifies the desired configuration. Once the `open` method resolves, the CAN interface is ready for use.
|
|
70
|
+
|
|
71
|
+
The bit rate is a property of the Linux network interface, and setting it needs root privileges, so this module does not set it; the `canRate` option is accepted for compatibility with `can-usb-com` but is not used. Configure the interface beforehand, for example:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
sudo ip link set can0 type can bitrate 250000
|
|
75
|
+
sudo ip link set up can0
|
|
76
|
+
```
|
|
63
77
|
|
|
64
78
|
The default options are shown in the following example. Any of them may be omitted from the constructor's option object to use their default value.
|
|
65
79
|
|
|
66
80
|
```js
|
|
67
81
|
let can = new Can({
|
|
68
82
|
|
|
69
|
-
// bit rate on the CAN bus
|
|
83
|
+
// bit rate on the CAN bus (not used; see above)
|
|
70
84
|
canRate: 250000,
|
|
71
85
|
|
|
72
86
|
// useful for testing, each sent packet is also received
|
|
@@ -127,7 +141,7 @@ Refer to the `socketcan` and `node-can` documentation for more information on th
|
|
|
127
141
|
|
|
128
142
|
## Using Kvaser Adapters on Rasbperry Pi
|
|
129
143
|
|
|
130
|
-
SocketCAN works with Kvaser CAN adapters that are supported by the `kvaser_usb` Linux kernel module.
|
|
144
|
+
SocketCAN works with Kvaser CAN adapters that are supported by the `kvaser_usb` Linux kernel module. Older Raspberry Pi OS (formerly Raspbian) kernels did not include this module; check whether yours does with `modinfo kvaser_usb`. If it's missing, it can be built from source and installed separately.
|
|
131
145
|
|
|
132
146
|
The whole build and install process can be performed by running the script:
|
|
133
147
|
|
|
@@ -145,16 +159,44 @@ The script can be run with a mounted external drive as its working directory if
|
|
|
145
159
|
|
|
146
160
|
Before updating or modifying this package, please
|
|
147
161
|
|
|
148
|
-
- Lint any changes using `
|
|
149
|
-
- Confirm that unit tests pass
|
|
162
|
+
- Lint any changes using `npm run lint`.
|
|
163
|
+
- Confirm that unit tests pass (`npm test`).
|
|
164
|
+
|
|
165
|
+
The unit tests need a CAN network interface that is up. No hardware is needed: a virtual CAN interface works, and is what the release workflow uses:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
sudo modprobe vcan
|
|
169
|
+
sudo ip link add dev vcan0 type vcan
|
|
170
|
+
sudo ip link set up vcan0
|
|
171
|
+
CAN_INTERFACE=vcan0 npm test
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
If `CAN_INTERFACE` is not set, the tests use the first CAN interface found. To test with real hardware, set it to that interface (e.g. `CAN_INTERFACE=can0`). Do not run the tests on a bus with active traffic, since receiving unexpected CAN packets may confuse the tests.
|
|
175
|
+
|
|
176
|
+
The `pingpong` example (`examples/pingpong.js`) uses `@csllc/cs-socketcan` for one adapter, and `@csllc/can-usb-com` for the other. These must be connected via a properly terminated bus. It also needs `@csllc/can-usb-com` and `@csllc/j1939`, which are not dev dependencies of this package; install them with `npm install --no-save @csllc/can-usb-com @csllc/j1939`.
|
|
177
|
+
|
|
178
|
+
## Distribution
|
|
150
179
|
|
|
151
|
-
|
|
180
|
+
The module is plain JavaScript. Its only native code is in the `socketcan` dependency, which is a Node-API addon compiled with `node-gyp` when it is installed.
|
|
152
181
|
|
|
153
|
-
|
|
182
|
+
### Releasing
|
|
154
183
|
|
|
155
|
-
|
|
184
|
+
Releases are published by the GitHub Actions workflow in `.github/workflows/release.yml`, which runs when a version tag is pushed:
|
|
185
|
+
|
|
186
|
+
1. Optionally, run the tests locally against real hardware (the CI runners only have virtual CAN interfaces)
|
|
187
|
+
1. `npm version patch` (or `minor` / `major`), which updates `package.json` and creates the tag `vX.Y.Z`
|
|
188
|
+
1. `git push --follow-tags`
|
|
189
|
+
|
|
190
|
+
The workflow lints the code and runs the tests against a virtual CAN interface on Linux x64 and arm64 with NodeJS 22 and 24. It then publishes the package to npm. Publishing uses npm trusted publishing, so no npm token is stored in GitHub; the package's trusted publisher on npmjs.com must name this repository and the workflow file `release.yml`.
|
|
156
191
|
|
|
157
192
|
## Functionality Limitations
|
|
158
193
|
|
|
159
194
|
- No hardware loopback support - loopback is implemented in this module by pushing messages back into the stream in the `write()` method
|
|
160
195
|
- This module does not implement message filtering
|
|
196
|
+
- This module does not set the bit rate (see [Configuration](#configuration))
|
|
197
|
+
|
|
198
|
+
## Licensing and Dependencies
|
|
199
|
+
|
|
200
|
+
This project is licensed under the MIT license - see the [LICENSE](LICENSE) file for details.
|
|
201
|
+
|
|
202
|
+
It uses [socketcan](https://github.com/sebi2k1/node-can), which is also MIT licensed.
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const socketcan = require('socketcan');
|
|
4
|
-
const
|
|
4
|
+
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const { Duplex } = require('stream');
|
|
@@ -10,8 +10,17 @@ const { Duplex } = require('stream');
|
|
|
10
10
|
// Default configuration used unless overridden by caller
|
|
11
11
|
const DEFAULT_OPTIONS = {
|
|
12
12
|
canRate: 250000,
|
|
13
|
+
loopback: false,
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
// Network interfaces are listed in sysfs. We can't use os.networkInterfaces()
|
|
17
|
+
// because it only returns interfaces that have been assigned an IP address.
|
|
18
|
+
const SYSFS_NET = '/sys/class/net';
|
|
19
|
+
|
|
20
|
+
// Interface type for CAN, defined in Linux source at
|
|
21
|
+
// include/uapi/linux/if_arp.h
|
|
22
|
+
const ARPHRD_CAN = 280;
|
|
23
|
+
|
|
15
24
|
|
|
16
25
|
module.exports = class Socketcan extends Duplex {
|
|
17
26
|
|
|
@@ -28,78 +37,76 @@ module.exports = class Socketcan extends Duplex {
|
|
|
28
37
|
|
|
29
38
|
// Sets (or re-sets) the configuration options
|
|
30
39
|
setOptions(options) {
|
|
31
|
-
// Save for later use
|
|
32
|
-
this.options = Object.assign(DEFAULT_OPTIONS, options);
|
|
40
|
+
// Save for later use. Copy the defaults so that they aren't changed.
|
|
41
|
+
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
// Returns a promise that resolves to a list of available CAN ports
|
|
36
45
|
list() {
|
|
37
|
-
return new Promise((resolve
|
|
46
|
+
return new Promise((resolve) => {
|
|
38
47
|
if (process.platform != "linux") {
|
|
39
48
|
resolve([]);
|
|
40
|
-
|
|
41
|
-
// We can't use os.networkInterfaces() because it only returns
|
|
42
|
-
// interfaces that have been assigned an IP address. Instead, we look
|
|
43
|
-
// directly in sysfs.
|
|
44
|
-
let sysfsInterfaces = '/sys/class/net';
|
|
45
|
-
let channels = [];
|
|
46
|
-
|
|
47
|
-
let interfaces = shell.ls(sysfsInterfaces);
|
|
48
|
-
interfaces.forEach((iface) => {
|
|
49
|
-
let type = Number(shell.cat(path.join(sysfsInterfaces, iface, "type")));
|
|
50
|
-
if (type == 280) { // CAN, defined in Linux source at include/uapi/linux/if_arp.h
|
|
51
|
-
channels.push({ path: iface });
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
resolve(channels);
|
|
49
|
+
return;
|
|
56
50
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
|
|
52
|
+
let channels = [];
|
|
53
|
+
|
|
54
|
+
fs.readdirSync(SYSFS_NET).forEach((iface) => {
|
|
55
|
+
let type;
|
|
56
|
+
try {
|
|
57
|
+
type = Number(fs.readFileSync(path.join(SYSFS_NET, iface, 'type'), 'utf8'));
|
|
58
|
+
} catch {
|
|
59
|
+
// The interface may have disappeared since the directory was read
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (type === ARPHRD_CAN) {
|
|
63
|
+
channels.push({ path: iface });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
resolve(channels);
|
|
60
68
|
});
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
open(port) {
|
|
64
72
|
return new Promise((resolve, reject) => {
|
|
65
73
|
// Initialize the CAN port (aka "channel")
|
|
74
|
+
|
|
66
75
|
try {
|
|
67
76
|
// Create the channel
|
|
68
77
|
this.can = socketcan.createRawChannel(port, this.enableTimestamps);
|
|
69
|
-
|
|
78
|
+
|
|
79
|
+
// Data receive event handler
|
|
80
|
+
this.can.addListener("onMessage", this._onData.bind(this));
|
|
81
|
+
|
|
70
82
|
// Start the bus
|
|
71
83
|
this.can.start();
|
|
72
84
|
|
|
73
85
|
this.port = port;
|
|
74
86
|
this._configure();
|
|
75
87
|
this.isReady = true;
|
|
88
|
+
|
|
89
|
+
// Close event handler
|
|
90
|
+
this.on('close', () => {
|
|
91
|
+
this.close();
|
|
92
|
+
});
|
|
93
|
+
|
|
76
94
|
this.emit('open');
|
|
77
95
|
|
|
78
96
|
resolve();
|
|
79
97
|
} catch(err) {
|
|
98
|
+
this.can = undefined;
|
|
80
99
|
this.emit('error', err); // emit() instead of throw, matching CAN-USB-COM behavior
|
|
81
100
|
reject(err);
|
|
82
101
|
}
|
|
83
|
-
|
|
84
|
-
})
|
|
85
|
-
.then(() => {
|
|
86
|
-
// Close event handler
|
|
87
|
-
this.on('close', () => {
|
|
88
|
-
this.close();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Data receive event handler
|
|
92
|
-
//this.can.addListener("onMessage", this._onData.bind(this));
|
|
93
|
-
})
|
|
94
|
-
.catch((err) => {
|
|
95
|
-
throw err;
|
|
96
102
|
});
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
close() {
|
|
100
106
|
return new Promise((resolve, reject) => {
|
|
101
107
|
if (this.port === undefined) {
|
|
102
|
-
reject(new Error("CAN port is undefined"))
|
|
108
|
+
reject(new Error("CAN port is undefined"));
|
|
109
|
+
return;
|
|
103
110
|
}
|
|
104
111
|
// Remove listeners
|
|
105
112
|
this.removeAllListeners();
|
|
@@ -109,74 +116,70 @@ module.exports = class Socketcan extends Duplex {
|
|
|
109
116
|
}
|
|
110
117
|
// Close stream
|
|
111
118
|
this.emit('close');
|
|
112
|
-
|
|
113
|
-
})
|
|
114
|
-
.then(() => {
|
|
119
|
+
|
|
115
120
|
this.can = undefined;
|
|
116
121
|
this.port = undefined;
|
|
117
122
|
this.isReady = false;
|
|
118
123
|
this.push(null);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
throw err;
|
|
124
|
+
|
|
125
|
+
resolve();
|
|
122
126
|
});
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
write(msg) {
|
|
126
130
|
return new Promise((resolve, reject) => {
|
|
127
|
-
if (this.
|
|
128
|
-
reject(new Error("CAN port is
|
|
131
|
+
if (!this.isOpen()) {
|
|
132
|
+
reject(new Error("CAN port is not open"));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The CAN-USB-COM interface expects a .buf property that is
|
|
137
|
+
// a <Buffer>, while the underlying 'socketcan' module expects
|
|
138
|
+
// a .data property that is a <Buffer>. Since we may be looping this back
|
|
139
|
+
// into the stream -and- writing this message to the bus, just duplicate
|
|
140
|
+
// whichever property does exist in the msg object.
|
|
141
|
+
if (msg.buf) {
|
|
142
|
+
msg.buf = Buffer.from(msg.buf);
|
|
143
|
+
msg.data = Buffer.from(msg.buf);
|
|
144
|
+
} else if (msg.data) {
|
|
145
|
+
msg.buf = Buffer.from(msg.data);
|
|
146
|
+
msg.data = Buffer.from(msg.data);
|
|
129
147
|
} else {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// into the stream -and- writing this message to the bus, just duplicate
|
|
134
|
-
// whichever property does exist in the msg object.
|
|
135
|
-
if (msg.buf) {
|
|
136
|
-
msg['buf'] = Buffer.from(msg['buf']);
|
|
137
|
-
msg['data'] = Buffer.from(msg['buf']);
|
|
138
|
-
} else if (msg.data) {
|
|
139
|
-
msg['buf'] = Buffer.from(msg['data']);
|
|
140
|
-
msg['data'] = Buffer.from(msg['data']);
|
|
141
|
-
}
|
|
148
|
+
msg.buf = Buffer.alloc(0);
|
|
149
|
+
msg.data = Buffer.alloc(0);
|
|
150
|
+
}
|
|
142
151
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
resolve();
|
|
160
|
-
} else {
|
|
161
|
-
reject(new Error("Tried to send invalid CAN data"));
|
|
162
|
-
}
|
|
152
|
+
if (msg.buf.length > 8) {
|
|
153
|
+
reject(new Error("Tried to send invalid CAN data"));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Write message to bus
|
|
158
|
+
try {
|
|
159
|
+
this.can.send(msg);
|
|
160
|
+
} catch(err) {
|
|
161
|
+
reject(err);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// If loopback is enabled, push message back into receive stream
|
|
166
|
+
if (this.options.loopback) {
|
|
167
|
+
this.push(msg);
|
|
163
168
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
|
|
170
|
+
this.emit('write', msg);
|
|
171
|
+
|
|
172
|
+
resolve();
|
|
167
173
|
});
|
|
168
174
|
}
|
|
169
|
-
|
|
175
|
+
|
|
170
176
|
status() {
|
|
171
177
|
return new Promise((resolve, reject) => {
|
|
172
|
-
if (this.port === undefined) {
|
|
178
|
+
if (this.port === undefined || this.port === null) {
|
|
173
179
|
reject(new Error("CAN port is undefined"));
|
|
174
180
|
} else {
|
|
175
|
-
|
|
181
|
+
resolve({ port: this.port, open: !!this.isOpen() });
|
|
176
182
|
}
|
|
177
|
-
})
|
|
178
|
-
.catch((err) => {
|
|
179
|
-
throw err;
|
|
180
183
|
});
|
|
181
184
|
}
|
|
182
185
|
|
|
@@ -191,11 +194,15 @@ module.exports = class Socketcan extends Duplex {
|
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
_onData(msg) {
|
|
197
|
+
// The socketcan module only sets ext and rtr when they are true; always
|
|
198
|
+
// set them, to match can-usb-com
|
|
199
|
+
msg.ext = !!msg.ext;
|
|
200
|
+
msg.rtr = !!msg.rtr;
|
|
201
|
+
|
|
194
202
|
if (msg.buf) {
|
|
195
|
-
msg
|
|
196
|
-
//delete msg['data'];
|
|
203
|
+
msg.buf = Buffer.from(msg.buf);
|
|
197
204
|
} else if (msg.data) {
|
|
198
|
-
msg
|
|
205
|
+
msg.buf = Buffer.from(msg.data);
|
|
199
206
|
}
|
|
200
207
|
this.emit('data', msg);
|
|
201
208
|
}
|
|
@@ -203,12 +210,15 @@ module.exports = class Socketcan extends Duplex {
|
|
|
203
210
|
// Required for a readable stream; we don't need to do anything
|
|
204
211
|
_read() {}
|
|
205
212
|
|
|
206
|
-
// Required for a writable stream
|
|
207
|
-
|
|
213
|
+
// Required for a writable stream. write() is overridden above, so this is
|
|
214
|
+
// only reached through the stream internals (e.g. end(chunk)).
|
|
215
|
+
_write(chunk, encoding, cb) {
|
|
216
|
+
this.write(chunk).then(() => cb(), cb);
|
|
217
|
+
}
|
|
208
218
|
|
|
209
219
|
_configure() {
|
|
210
220
|
|
|
211
221
|
return;
|
|
212
222
|
}
|
|
213
223
|
|
|
214
|
-
}
|
|
224
|
+
};
|
package/package.json
CHANGED
|
@@ -1,33 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csllc/cs-socketcan",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Socketcan module for NodeJS",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"tools/rpi-kvaser-usb.sh"
|
|
9
|
+
],
|
|
6
10
|
"license": "MIT",
|
|
7
11
|
"author": "cwalden@controls.com",
|
|
8
12
|
"os": [
|
|
9
13
|
"linux"
|
|
10
14
|
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
|
17
|
+
},
|
|
11
18
|
"dependencies": {
|
|
12
|
-
"
|
|
13
|
-
"eslint": "^7.10.0",
|
|
14
|
-
"shelljs": "^0.8.4",
|
|
15
|
-
"socketcan": "^2.8.0",
|
|
16
|
-
"stream": "0.0.2"
|
|
19
|
+
"socketcan": "^4.3.2"
|
|
17
20
|
},
|
|
18
21
|
"devDependencies": {
|
|
19
|
-
"
|
|
20
|
-
"chai": "^
|
|
22
|
+
"@eslint/js": "^10.0.0",
|
|
23
|
+
"chai": "^6.0.0",
|
|
21
24
|
"chai-events": "0.0.3",
|
|
22
|
-
"eslint
|
|
23
|
-
"
|
|
25
|
+
"eslint": "^10.0.0",
|
|
26
|
+
"globals": "^16.0.0",
|
|
27
|
+
"mocha": "^11.0.0"
|
|
24
28
|
},
|
|
25
29
|
"scripts": {
|
|
26
|
-
"test": "mocha test"
|
|
30
|
+
"test": "mocha test",
|
|
31
|
+
"lint": "eslint ."
|
|
27
32
|
},
|
|
28
33
|
"repository": {
|
|
29
34
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/csllc/cs-socketcan.git"
|
|
35
|
+
"url": "git+https://github.com/csllc/cs-socketcan.git"
|
|
31
36
|
},
|
|
32
|
-
"
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
33
40
|
}
|
package/examples/pingpong.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Implements a pingpong test using 2 CAN-USB-COM modules
|
|
3
|
-
*
|
|
4
|
-
* Note you must
|
|
5
|
-
* npm install can-usb-com
|
|
6
|
-
* in order for this to work.to
|
|
7
|
-
*
|
|
8
|
-
* The two devices must be connected together and properly terminated (so that
|
|
9
|
-
* CANBUS communication is physically possible).physically
|
|
10
|
-
*
|
|
11
|
-
* This script opens both devices, and each device sends a message to the other.
|
|
12
|
-
* When a device receives a message from the other, it immediately sends it back.it
|
|
13
|
-
*
|
|
14
|
-
* We use the 'priority' field of the message ID to keep track of who originated
|
|
15
|
-
* the message.
|
|
16
|
-
*
|
|
17
|
-
* So the two devices continually send bi-directional messages to each other.
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const Socketcan = require('..');
|
|
22
|
-
const CanUSBCOM = require('can-usb-com');
|
|
23
|
-
|
|
24
|
-
const J1939 = require('@csllc/j1939');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
let can1 = new Socketcan({ // Device 1
|
|
28
|
-
canRate: 250000,
|
|
29
|
-
//canRate: 1000000,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
filters: [{
|
|
33
|
-
ext: true,
|
|
34
|
-
|
|
35
|
-
// make sure to include any possible incoming messages we might get
|
|
36
|
-
id: '08000000 1CFFFFFF'
|
|
37
|
-
}]
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
let can2 = new CanUSBCOM({ // Device 2
|
|
42
|
-
baudRate: 460800,
|
|
43
|
-
canRate: 250000,
|
|
44
|
-
//canRate: 250000,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
filters: [{
|
|
48
|
-
ext: true,
|
|
49
|
-
|
|
50
|
-
// make sure to include any possible incoming messages we might get
|
|
51
|
-
id: '08000000 1CFFFFFF'
|
|
52
|
-
}]
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// A class to hold the state information for the pinging
|
|
58
|
-
class Pinger extends J1939 {
|
|
59
|
-
|
|
60
|
-
constructor(bus, options) {
|
|
61
|
-
super(bus, options)
|
|
62
|
-
|
|
63
|
-
this.priority = options.priority;
|
|
64
|
-
this.fill = options.fill;
|
|
65
|
-
|
|
66
|
-
this.msgSize = 1;
|
|
67
|
-
|
|
68
|
-
// bus.on('data', (msg) => {
|
|
69
|
-
// console.log(msg.id.toString(16), msg.buf);
|
|
70
|
-
// });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
start(target) {
|
|
74
|
-
|
|
75
|
-
this.target = target;
|
|
76
|
-
|
|
77
|
-
this.on('data', this.onMsg.bind(this));
|
|
78
|
-
|
|
79
|
-
this.sendNext();
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
sendNext() {
|
|
85
|
-
|
|
86
|
-
console.log(this.address + ' sending ' + this.msgSize + ' bytes');
|
|
87
|
-
|
|
88
|
-
this.write({
|
|
89
|
-
pgn: 0xEF00,
|
|
90
|
-
dst: this.target,
|
|
91
|
-
priority: this.priority,
|
|
92
|
-
buf: Buffer.alloc(this.msgSize).fill(this.fill)
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
onMsg(msg) {
|
|
98
|
-
|
|
99
|
-
let me = this;
|
|
100
|
-
|
|
101
|
-
if (msg.pgn === 0xEF00) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// If it originated from us, this is a response so go to the next message
|
|
105
|
-
// size
|
|
106
|
-
if (msg.buf[0] === me.fill) {
|
|
107
|
-
|
|
108
|
-
console.log(this.address + ' Received ' + msg.buf.length + ' byte response from ' + msg.src);
|
|
109
|
-
|
|
110
|
-
me.msgSize++;
|
|
111
|
-
if (me.msgSize > 1785) {
|
|
112
|
-
me.msgSize = 1;
|
|
113
|
-
process.exit(0);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
me.sendNext();
|
|
117
|
-
} else {
|
|
118
|
-
// it's from the other guy, just send it back
|
|
119
|
-
this.write({
|
|
120
|
-
pgn: msg.pgn,
|
|
121
|
-
dst: msg.src,
|
|
122
|
-
priority: msg.pri,
|
|
123
|
-
buf: msg.buf
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// The two instances of pingers
|
|
132
|
-
|
|
133
|
-
let device1 = new Pinger(can1, { //PCAN-USB
|
|
134
|
-
address: 0x80,
|
|
135
|
-
priority: 4,
|
|
136
|
-
fill: 0x55,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
let device2 = new Pinger(can2, { //CAN-USB-COM
|
|
140
|
-
address: 0x81,
|
|
141
|
-
priority: 5,
|
|
142
|
-
fill: 0xAA,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Find the device COM ports
|
|
149
|
-
can1.list()
|
|
150
|
-
.then((ports) => {
|
|
151
|
-
// open the last ports. When successful, the J1939 instances will
|
|
152
|
-
// emit 'open'
|
|
153
|
-
return Promise.all([
|
|
154
|
-
can1.open(ports[0].path),
|
|
155
|
-
]);
|
|
156
|
-
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
.catch((err) => {
|
|
160
|
-
|
|
161
|
-
console.error(err);
|
|
162
|
-
process.exit(-1);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
can2.list()
|
|
166
|
-
.then((ports) => {
|
|
167
|
-
// open the last ports. When successful, the J1939 instances will
|
|
168
|
-
// emit 'open'
|
|
169
|
-
return Promise.all([
|
|
170
|
-
can2.open(ports[0].path),
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
.catch((err) => {
|
|
176
|
-
|
|
177
|
-
console.error(err);
|
|
178
|
-
process.exit(-1);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
device1.on('open', (address) => {
|
|
183
|
-
|
|
184
|
-
console.log('Device 1 ready with address ', address);
|
|
185
|
-
|
|
186
|
-
// if both are ready, start
|
|
187
|
-
if (device2.isReady()) {
|
|
188
|
-
console.log("Device 2 appears to be ready.");
|
|
189
|
-
device1.start(device2.address);
|
|
190
|
-
device2.start(device1.address);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
device2.on('open', (address) => {
|
|
196
|
-
|
|
197
|
-
console.log('Device 2 ready with address ', address);
|
|
198
|
-
// if both are ready, start
|
|
199
|
-
if (device1.isReady()) {
|
|
200
|
-
console.log("starting device 1...")
|
|
201
|
-
device1.start(device2.address);
|
|
202
|
-
console.log("starting device 2...");
|
|
203
|
-
device2.start(device1.address);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
});
|
package/examples/send.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Example script that opens the CAN connection and
|
|
2
|
-
// sends a message.
|
|
3
|
-
// The example also sets up a filter to catch a response message
|
|
4
|
-
// (obviously you need a compatible remote device on the bus in order to get a
|
|
5
|
-
// response )
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const Can = require('..');
|
|
9
|
-
|
|
10
|
-
let board = new Can({
|
|
11
|
-
canRate: 250000,
|
|
12
|
-
filters: [ { ext: true, id: '10EF0000 10EFFFFF' } ]
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// Look for compatible CAN adapters
|
|
17
|
-
board.list()
|
|
18
|
-
.then(function(ports) {
|
|
19
|
-
|
|
20
|
-
// got a list of the ports, try to open the last one which is likely
|
|
21
|
-
// the USB cable
|
|
22
|
-
ports = ports.slice(-1);
|
|
23
|
-
|
|
24
|
-
board.on('write', function(msg) {
|
|
25
|
-
console.log('Write: ', msg);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Event handler for each incoming message
|
|
29
|
-
board.on('data', function(msg) {
|
|
30
|
-
console.log('Msg: ', msg.id.toString(16), msg.buf);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Open the COM port and initialize the USBCAN device...
|
|
34
|
-
console.log('opening ', ports[0].path);
|
|
35
|
-
return board.open(ports[0].path);
|
|
36
|
-
|
|
37
|
-
})
|
|
38
|
-
.then(function() {
|
|
39
|
-
|
|
40
|
-
// send a command
|
|
41
|
-
board.write({ id: 0x10EF8001, ext: true, buf: Buffer.from([0x45, 0x00, 0x00, 0x04, 0x00, 0x00]) });
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
})
|
|
45
|
-
.catch(function(err) {
|
|
46
|
-
// Something went wrong...
|
|
47
|
-
console.error(err);
|
|
48
|
-
board.close();
|
|
49
|
-
process.exit(-1);
|
|
50
|
-
});
|
package/examples/test.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const Can = require('..');
|
|
2
|
-
|
|
3
|
-
let can = new Can({
|
|
4
|
-
canRate: 250000,
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
can.list()
|
|
8
|
-
.then((ports) => {
|
|
9
|
-
console.log(ports);
|
|
10
|
-
return can.open(ports[0].path)
|
|
11
|
-
.catch((err) => {
|
|
12
|
-
console.error(err);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
can.on('data', function(msg) {
|
|
19
|
-
console.log(JSON.stringify(msg, (key, value) => {
|
|
20
|
-
if ((typeof value === 'number') && (key == "id")) {
|
|
21
|
-
return value.toString(16).toUpperCase().padStart(8, '0');
|
|
22
|
-
} else if (typeof value === 'number') {
|
|
23
|
-
return value.toString(16).toUpperCase().padStart(2, '0');
|
|
24
|
-
} else {
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
}));
|
|
28
|
-
});
|
|
29
|
-
|
package/test/basic.test.js
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test of basic functionality
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const CsSocketcan = require('..');
|
|
7
|
-
const chai = require('chai');
|
|
8
|
-
const expect = chai.expect;
|
|
9
|
-
const should = chai.should();
|
|
10
|
-
chai.use(require('chai-events'));
|
|
11
|
-
|
|
12
|
-
const { Duplex } = require('stream');
|
|
13
|
-
|
|
14
|
-
const CAN_OPTIONS = {
|
|
15
|
-
canRate: 250000,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Define a simple stream to send packet(s)
|
|
20
|
-
class SendStream extends Duplex {
|
|
21
|
-
|
|
22
|
-
constructor() {
|
|
23
|
-
super({ objectMode: true });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
_read() {}
|
|
27
|
-
|
|
28
|
-
_write(chunk, encoding, cb) {
|
|
29
|
-
// increase the ID by one, just for giggles
|
|
30
|
-
chunk.id++;
|
|
31
|
-
|
|
32
|
-
this.push(chunk);
|
|
33
|
-
cb();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Define a simple stream to receive packets
|
|
39
|
-
class ReceiveStream extends Duplex {
|
|
40
|
-
|
|
41
|
-
constructor() {
|
|
42
|
-
super({ objectMode: true });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
_read() {}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
_write(chunk, encoding, cb) {
|
|
49
|
-
// reduce the ID by one, just for giggles
|
|
50
|
-
chunk.id--;
|
|
51
|
-
|
|
52
|
-
this.push(chunk);
|
|
53
|
-
cb();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
describe('List available devices', () => {
|
|
60
|
-
|
|
61
|
-
it('should be at least one device', async () => {
|
|
62
|
-
|
|
63
|
-
let can = new CsSocketcan(CAN_OPTIONS);
|
|
64
|
-
|
|
65
|
-
let result = await can.list();
|
|
66
|
-
|
|
67
|
-
expect(result).to.be.an('array');
|
|
68
|
-
expect(result.length).to.be.gt(0);
|
|
69
|
-
|
|
70
|
-
result.forEach((port) => {
|
|
71
|
-
|
|
72
|
-
expect(port).to.be.an('object');
|
|
73
|
-
expect(port.path).to.exist;
|
|
74
|
-
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// after all tests in this block
|
|
78
|
-
after(async () => {
|
|
79
|
-
|
|
80
|
-
await can.close();
|
|
81
|
-
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
describe('Send a Message', () => {
|
|
89
|
-
|
|
90
|
-
let can = null;
|
|
91
|
-
|
|
92
|
-
// before all tests in this block, get an open port
|
|
93
|
-
before(async () => {
|
|
94
|
-
|
|
95
|
-
// set and keep for later tests
|
|
96
|
-
can = new CsSocketcan(CAN_OPTIONS);
|
|
97
|
-
|
|
98
|
-
let result = await can.list();
|
|
99
|
-
|
|
100
|
-
let p = can.should.emit('open');
|
|
101
|
-
|
|
102
|
-
can.open(result[0].path);
|
|
103
|
-
|
|
104
|
-
return p;
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('should send a message with standard identifier', async () => {
|
|
108
|
-
|
|
109
|
-
let p = can.should.not.emit('error');
|
|
110
|
-
|
|
111
|
-
// mainly we are checking that the write does not throw or error
|
|
112
|
-
|
|
113
|
-
can.write({
|
|
114
|
-
id: 0x100,
|
|
115
|
-
ext: false,
|
|
116
|
-
buf: []
|
|
117
|
-
});
|
|
118
|
-
can.write({
|
|
119
|
-
id: 0x101,
|
|
120
|
-
ext: false,
|
|
121
|
-
buf: [1]
|
|
122
|
-
});
|
|
123
|
-
can.write({
|
|
124
|
-
id: 0x102,
|
|
125
|
-
ext: false,
|
|
126
|
-
buf: [1, 2]
|
|
127
|
-
});
|
|
128
|
-
can.write({
|
|
129
|
-
id: 0x103,
|
|
130
|
-
ext: false,
|
|
131
|
-
buf: [1, 2, 3]
|
|
132
|
-
});
|
|
133
|
-
can.write({
|
|
134
|
-
id: 0x104,
|
|
135
|
-
ext: false,
|
|
136
|
-
buf: [1, 2, 3, 4, 5]
|
|
137
|
-
});
|
|
138
|
-
can.write({
|
|
139
|
-
id: 0x105,
|
|
140
|
-
ext: false,
|
|
141
|
-
buf: [1, 2, 3, 4, 5, 6]
|
|
142
|
-
});
|
|
143
|
-
can.write({
|
|
144
|
-
id: 0x106,
|
|
145
|
-
ext: false,
|
|
146
|
-
buf: [1, 2, 3, 4, 5, 6, 7]
|
|
147
|
-
});
|
|
148
|
-
can.write({
|
|
149
|
-
id: 0x107,
|
|
150
|
-
ext: false,
|
|
151
|
-
buf: [1, 2, 3, 4, 5, 6, 7, 8]
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return p;
|
|
155
|
-
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should emit a write event', async () => {
|
|
159
|
-
|
|
160
|
-
let p = can.should.emit('write');
|
|
161
|
-
|
|
162
|
-
can.write({
|
|
163
|
-
id: 0x100,
|
|
164
|
-
ext: false,
|
|
165
|
-
buf: []
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
return p;
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// after all tests in this block
|
|
173
|
-
after(async () => {
|
|
174
|
-
|
|
175
|
-
await can.close();
|
|
176
|
-
|
|
177
|
-
})
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
describe('Receive a Message', () => {
|
|
182
|
-
|
|
183
|
-
let can = null;
|
|
184
|
-
|
|
185
|
-
// before all tests in this block, get an open port
|
|
186
|
-
before(async () => {
|
|
187
|
-
|
|
188
|
-
// set and keep for later tests
|
|
189
|
-
can = new CsSocketcan(CAN_OPTIONS);
|
|
190
|
-
|
|
191
|
-
let result = await can.list();
|
|
192
|
-
|
|
193
|
-
let p = can.should.emit('open');
|
|
194
|
-
|
|
195
|
-
can.open(result[0].path);
|
|
196
|
-
|
|
197
|
-
return p;
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
it('should receive an extended message', (done) => {
|
|
202
|
-
|
|
203
|
-
can.once('data', (msg) => {
|
|
204
|
-
|
|
205
|
-
expect(msg).to.be.an('object');
|
|
206
|
-
|
|
207
|
-
expect(msg.id).to.exist;
|
|
208
|
-
expect(msg.ext).to.exist;
|
|
209
|
-
expect(msg.buf).to.exist;
|
|
210
|
-
expect(msg.id).to.be.eq(0x10EF8081);
|
|
211
|
-
expect(msg.ext).to.be.eq(true);
|
|
212
|
-
expect(msg.buf).to.deep.eq([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
213
|
-
|
|
214
|
-
done();
|
|
215
|
-
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// here we simulate a receipt of a message thru the serial port
|
|
219
|
-
// we could also send a packet and loop it back
|
|
220
|
-
//can._onData(Buffer.from(':X10EF8081N0102030405060708;'));
|
|
221
|
-
can.push({id: 0x10EF8081, ext: true, buf: [1, 2, 3, 4, 5, 6, 7, 8]});
|
|
222
|
-
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should receive a standard message', (done) => {
|
|
226
|
-
|
|
227
|
-
can.once('data', (msg) => {
|
|
228
|
-
|
|
229
|
-
expect(msg).to.be.an('object');
|
|
230
|
-
|
|
231
|
-
expect(msg.id).to.exist;
|
|
232
|
-
expect(msg.ext).to.exist;
|
|
233
|
-
expect(msg.buf).to.exist;
|
|
234
|
-
expect(msg.id).to.be.eq(0x100);
|
|
235
|
-
expect(msg.ext).to.be.eq(false);
|
|
236
|
-
expect(msg.buf).to.deep.eq([8, 7, 6, 5, 4, 3, 2, 1]);
|
|
237
|
-
|
|
238
|
-
done();
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// here we simulate a receipt of a message thru the serial port
|
|
243
|
-
// we could also send a packet and loop it back
|
|
244
|
-
//can._onData(Buffer.from(':S100N0807060504030201;'));
|
|
245
|
-
can.push({id: 0x100, ext: false, buf: [8, 7, 6, 5, 4, 3, 2, 1]});
|
|
246
|
-
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// after all tests in this block
|
|
250
|
-
after(async () => {
|
|
251
|
-
|
|
252
|
-
await can.close();
|
|
253
|
-
|
|
254
|
-
})
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
describe('Loopback Mode', () => {
|
|
260
|
-
|
|
261
|
-
let can = null;
|
|
262
|
-
|
|
263
|
-
// before all tests in this block, get an open port
|
|
264
|
-
before(async () => {
|
|
265
|
-
|
|
266
|
-
// set and keep for later tests
|
|
267
|
-
can = new CsSocketcan(Object.assign(CAN_OPTIONS, { loopback: true }));
|
|
268
|
-
|
|
269
|
-
let result = await can.list();
|
|
270
|
-
|
|
271
|
-
let p = can.should.emit('open');
|
|
272
|
-
|
|
273
|
-
can.open(result[0].path);
|
|
274
|
-
|
|
275
|
-
return p;
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
it('should loop back a packet', (done) => {
|
|
280
|
-
|
|
281
|
-
// because we opened the port in loopback mode, the same
|
|
282
|
-
// message shoud come out
|
|
283
|
-
can.once('data', (msg) => {
|
|
284
|
-
|
|
285
|
-
expect(msg).to.be.an('object');
|
|
286
|
-
|
|
287
|
-
expect(msg.id).to.exist;
|
|
288
|
-
expect(msg.ext).to.exist;
|
|
289
|
-
expect(msg.buf).to.exist;
|
|
290
|
-
expect(msg.buf).to.be.instanceof(Buffer)
|
|
291
|
-
expect(msg.id).to.be.eq(0x100);
|
|
292
|
-
expect(msg.ext).to.be.eq(false);
|
|
293
|
-
expect(msg.buf).to.deep.eq(Buffer.from([]));
|
|
294
|
-
|
|
295
|
-
done();
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
can.write({
|
|
299
|
-
id: 0x100,
|
|
300
|
-
ext: false,
|
|
301
|
-
buf: []
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// after all tests in this block
|
|
307
|
-
after(async () => {
|
|
308
|
-
|
|
309
|
-
await can.close();
|
|
310
|
-
|
|
311
|
-
})
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
describe('Implement a standard stream interface', () => {
|
|
316
|
-
|
|
317
|
-
let can = null;
|
|
318
|
-
|
|
319
|
-
let sender = new SendStream();
|
|
320
|
-
let receiver = new ReceiveStream();
|
|
321
|
-
|
|
322
|
-
// before all tests in this block, get an open port
|
|
323
|
-
before(async () => {
|
|
324
|
-
|
|
325
|
-
// set and keep for later tests
|
|
326
|
-
can = new CsSocketcan(CAN_OPTIONS);
|
|
327
|
-
|
|
328
|
-
can.pipe(receiver);
|
|
329
|
-
sender.pipe(can);
|
|
330
|
-
|
|
331
|
-
let result = await can.list();
|
|
332
|
-
|
|
333
|
-
let p = can.should.emit('open');
|
|
334
|
-
|
|
335
|
-
can.open(result[0].path);
|
|
336
|
-
|
|
337
|
-
return p;
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
it('should send a packet', async () => {
|
|
342
|
-
|
|
343
|
-
let p = can.should.emit('write');
|
|
344
|
-
|
|
345
|
-
sender.write({
|
|
346
|
-
id: 0x100,
|
|
347
|
-
ext: false,
|
|
348
|
-
buf: [1, 2, 3, 4]
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
return p;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('should receive a message', async () => {
|
|
357
|
-
|
|
358
|
-
let p = receiver.should.emit('data');
|
|
359
|
-
|
|
360
|
-
// here we simulate a receipt of a message thru the serial port
|
|
361
|
-
|
|
362
|
-
can._onData(Buffer.from(':S100N0102030405060708;'));
|
|
363
|
-
|
|
364
|
-
return p;
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// after all tests in this block
|
|
368
|
-
after(async () => {
|
|
369
|
-
|
|
370
|
-
await can.close();
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
})
|
|
374
|
-
});
|
|
375
|
-
|