@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 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
- The following assumes that [NodeJS](https://nodejs.org) is already installed. This module was developed using Node v12.18.4 for Raspberry Pi OS (Debian GNU/Linux) 10.6.
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\cs-socketcan');
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. The CAN adapter is set up before the `open` method resolves, so once it is complete, the CAN interface is ready for use.
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. This module is not part of the Raspberry Pi OS (formerly Raspbian) Linux distribution, but can be built from source and installed separately.
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 `eslint`.
149
- - Confirm that unit tests pass, noting the exceptions described in the next section.
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
- In order to run unit tests (`npm test`), at least one PCAN-USB device must be connected to the computer.
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
- The `pingpong` test is currently configured to use `cs-socketcan` for one adapter, and `can-usb-com` for the other. These must be connected via a properly terminated bus.
182
+ ### Releasing
154
183
 
155
- Do not run any tests on a bus with active traffic, since receiving unexpected CAN packets may confuse the tests.
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 shell = require('shelljs');
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, reject) => {
46
+ return new Promise((resolve) => {
38
47
  if (process.platform != "linux") {
39
48
  resolve([]);
40
- } else {
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
- .catch((err) => {
59
- throw err;
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
- resolve();
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
- .catch((err) => {
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.port === undefined) {
128
- reject(new Error("CAN port is undefined"));
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
- // The CAN-USB-COM interface expects a .buf property that is
131
- // a <Buffer>, while the underlying 'socketcan' module expects
132
- // a .data property that is a <Buffer>. Since we may be looping this back
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
- if (msg.buf.length <= 8) {
144
- // Write message to bus
145
-
146
- try {
147
- this.can.send(msg);
148
- } catch(err) {
149
- reject(err);
150
- }
151
-
152
- // If loopback is enabled, push message back into receive stream
153
- if (this.options.loopback) {
154
- this.push(msg);
155
- }
156
-
157
- this.emit('write', msg);
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
- .catch((err) => {
166
- throw err;
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
- return this.status;
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['buf'] = Buffer.from(msg['buf']);
196
- //delete msg['data'];
203
+ msg.buf = Buffer.from(msg.buf);
197
204
  } else if (msg.data) {
198
- msg['buf'] = Buffer.from(msg['data']);
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; we don't need to do anything
207
- _write() {}
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.2",
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
- "@csllc/j1939": "^1.0.0",
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
- "can-usb-com": "^4.0.0",
20
- "chai": "^4.2.0",
22
+ "@eslint/js": "^10.0.0",
23
+ "chai": "^6.0.0",
21
24
  "chai-events": "0.0.3",
22
- "eslint-plugin-mocha": "^8.0.0",
23
- "mocha": "^8.1.3"
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
- "gypfile": true
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
33
40
  }
@@ -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
-
@@ -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
-