ads-client 1.14.1 → 1.14.3
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/CHANGELOG.md +32 -1
- package/README.md +20 -5
- package/package.json +2 -1
- package/src/ads-client.js +43 -39
package/CHANGELOG.md
CHANGED
|
@@ -4,13 +4,44 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.14.3] - 23.09.2023
|
|
8
|
+
### Changed
|
|
9
|
+
- Bug fix: Some TwinCAT 2 devices (such as BK9050) do not send data length if answering with error code
|
|
10
|
+
- This caused `RangeError: Index out of range` exception as there wasn't enough bytes received
|
|
11
|
+
- See [issue #116](https://github.com/jisotalo/ads-client/issues/116)
|
|
12
|
+
- Bug fix: If using older Node.js versions such as 8.x, connection lost could have caused unhandled exception
|
|
13
|
+
- Reason was `catch {}` which isn't supported in old versions
|
|
14
|
+
- See [issue #116](https://github.com/jisotalo/ads-client/issues/116)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Updated readme to include information about TypeScript types
|
|
18
|
+
- Thanks to [Christian Rishøj](https://github.com/crishoj)
|
|
19
|
+
- Updated readme with FAQ about TwinCAT 2 low-end devices
|
|
20
|
+
- Updated readme about v2 development
|
|
21
|
+
- Added option to run tests with usermode runtime AmsNetId (`192.168.4.1.1.1`) instead of localhost (`npm run test-um`)
|
|
22
|
+
|
|
23
|
+
## [1.14.2] - 02.05.2023
|
|
24
|
+
### Changed
|
|
25
|
+
- Bug fix: `ADS_DATA_TYPE_FLAGS` (`dataType.flags`) were parsed incorrectly.
|
|
26
|
+
- See [issue #109](https://github.com/jisotalo/ads-client/issues/109)
|
|
27
|
+
- Thank you [Michael Croes](https://github.com/mycroes) for contribution!
|
|
28
|
+
- Bug fix: If calling `unsubcribeAll()` when client is already unsubcribing and disconnecting, an error might be thrown
|
|
29
|
+
- See [issue #103](https://github.com/jisotalo/ads-client/issues/103#issuecomment-1450640160)
|
|
30
|
+
- Bug fix: Check that subscription callback exists before calling it
|
|
31
|
+
- Fixes unecessary error if receiving notification data (for subscription) but it's being unsubscribed at the same time
|
|
32
|
+
- Bump json5 from 2.2.1 to 2.2.3
|
|
33
|
+
- See [pull request 101](https://github.com/jisotalo/ads-client/pull/101)
|
|
34
|
+
- Not used in production code, only when running tests
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- New test for [issue #103](https://github.com/jisotalo/ads-client/issues/103)
|
|
38
|
+
|
|
7
39
|
## [1.14.1] - 13.09.2022
|
|
8
40
|
### Changed
|
|
9
41
|
- Bug fix: Connecting to local router failed with ECONNREFUSED error on Node.js version 17 and newer
|
|
10
42
|
- See [https://github.com/nodejs/node/issues/40702](https://github.com/nodejs/node/issues/40702)
|
|
11
43
|
- Fixed by using `127.0.0.1` instead of `localhost`
|
|
12
44
|
|
|
13
|
-
|
|
14
45
|
## [1.14.0] - 23.07.2022
|
|
15
46
|
### Added
|
|
16
47
|
- Created end-to-end testing for the library using Jest
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
[](https://www.npmjs.org/package/ads-client)
|
|
5
|
-
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
6
6
|
[](https://github.com/jisotalo/ads-client)
|
|
7
7
|
[](https://choosealicense.com/licenses/mit/)
|
|
8
8
|
|
|
@@ -10,16 +10,19 @@ Beckhoff TwinCAT ADS client library for Node.js (unofficial). Connects to Beckho
|
|
|
10
10
|
|
|
11
11
|
Coded from scratch using [TwinCAT ADS specification](https://infosys.beckhoff.com/content/1033/tc3_ads_intro/116157835.html?id=124964102706356243) and [Beckhoff.TwinCAT.Ads nuget package](https://www.nuget.org/packages/Beckhoff.TwinCAT.Ads/5.0.0-preview6). Inspiration from similar projects like [node-ads](https://www.npmjs.com/package/node-ads), [beckhoff-js](https://www.npmjs.com/package/beckhoff-js) and [iecstruct](https://www.npmjs.com/package/iecstruct).
|
|
12
12
|
|
|
13
|
-
There is automatically created documentation available at https://jisotalo.
|
|
13
|
+
There is automatically created documentation available at https://jisotalo.fi/ads-client/
|
|
14
14
|
|
|
15
15
|
# Project status
|
|
16
16
|
This project is currently "ready". It's maintained actively and used in projects by the author and others (also lot's of commercial projects)
|
|
17
17
|
|
|
18
18
|
Bugs are fixed if found and new features can be added. Please let me know if you have any ideas!
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
If you want to support my work, you can do it using PayPal. I can provide you support in exchange.
|
|
21
21
|
|
|
22
|
-
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
23
|
+
|
|
24
|
+
## Version 2
|
|
25
|
+
Version 2 is under development in [`v2-dev`](https://github.com/jisotalo/ads-client/tree/v2-dev) branch. It's written in TypeScript (including all types!) and will also be more optimized. At the moment basic functions *might* work but it's not ready for production use.
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
# Using Node-RED?
|
|
@@ -101,6 +104,13 @@ Install the [npm package](https://www.npmjs.com/package/ads-client) using npm co
|
|
|
101
104
|
npm i ads-client
|
|
102
105
|
```
|
|
103
106
|
|
|
107
|
+
If you are using TypeScript, install unofficial types using npm command (thanks [Christian Rishøj](https://github.com/crishoj)):
|
|
108
|
+
```bash
|
|
109
|
+
npm install --save @types/ads-client
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
*Note: Version 2 under development will be written in 100% TypeScript*
|
|
113
|
+
|
|
104
114
|
Include the module in your code
|
|
105
115
|
```js
|
|
106
116
|
const ads = require('ads-client')
|
|
@@ -1764,12 +1774,17 @@ Solution:
|
|
|
1764
1774
|
* When closing application, first unsubscribe from all notifications using `unsubscribeAll()`
|
|
1765
1775
|
* Use router instead of direct connection, see https://github.com/jisotalo/ads-client/issues/85#issuecomment-1193098519
|
|
1766
1776
|
|
|
1777
|
+
### Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.)
|
|
1778
|
+
* You can only use raw commands (such as `readRaw()`, `writeRaw()`, `subscribeRaw()`) as these devices provide no symbols
|
|
1779
|
+
* See [issue 114](https://github.com/jisotalo/ads-client/issues/114) and [issue 116](https://github.com/jisotalo/ads-client/issues/116) for starters
|
|
1780
|
+
|
|
1781
|
+
|
|
1767
1782
|
# Automatic testing
|
|
1768
1783
|
Since version 1.14.0 the library has automatic testing using Jest. Idea is to run the tests before updates to make sure everything works OK (this should have been done much earlier...)
|
|
1769
1784
|
|
|
1770
1785
|
Separate PLC project is required for testing, see https://github.com/jisotalo/ads-client-test-plc-project for more project and more info.
|
|
1771
1786
|
|
|
1772
|
-
Tests are run with command `npm test` (not in npm version, please clone this repository).
|
|
1787
|
+
Tests are run with command `npm test` or `npm run test-um` (usermode runtime) (not in npm version, please clone this repository).
|
|
1773
1788
|
|
|
1774
1789
|
# Documentation
|
|
1775
1790
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ads-client",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.3",
|
|
4
4
|
"description": "Beckhoff TwinCAT ADS client library for Node.js (unofficial). Connects to Beckhoff TwinCAT automation systems using ADS protocol.",
|
|
5
5
|
"main": "./src/ads-client.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest --runInBand",
|
|
8
|
+
"test-um": "set ADS_CLIENT_TEST_AMS=192.168.4.1.1.1 && jest --runInBand",
|
|
8
9
|
"generate-docs": "jsdoc ./src/ads-client.js ./README.md -c ./jsdoc-conf.json -d ./docs/ -t ./node_modules/docdash/"
|
|
9
10
|
},
|
|
10
11
|
"keywords": [
|
package/src/ads-client.js
CHANGED
|
@@ -894,7 +894,7 @@ class Client extends EventEmitter {
|
|
|
894
894
|
} catch (err) {
|
|
895
895
|
return reject(new ClientException(this, 'readSymbol()', `Reading symbol ${variableName} failed: Reading data type failed`, err))
|
|
896
896
|
}
|
|
897
|
-
|
|
897
|
+
|
|
898
898
|
//4. Parse the data to javascript object
|
|
899
899
|
let data = {}
|
|
900
900
|
try {
|
|
@@ -1224,7 +1224,11 @@ class Client extends EventEmitter {
|
|
|
1224
1224
|
unSubCount++
|
|
1225
1225
|
|
|
1226
1226
|
} catch (err) {
|
|
1227
|
-
|
|
1227
|
+
if (this._internals.activeSubscriptions[sub] && this._internals.activeSubscriptions[sub].target !== undefined) {
|
|
1228
|
+
debug(`unsubscribeAll(): Unsubscribing from notification ${JSON.stringify(this._internals.activeSubscriptions[sub].target)} failed`)
|
|
1229
|
+
} else {
|
|
1230
|
+
debug(`unsubscribeAll(): Unsubscribing from notification with handle ${sub} failed`)
|
|
1231
|
+
}
|
|
1228
1232
|
|
|
1229
1233
|
firstError = new ClientException(this, 'unsubscribeAll()', err)
|
|
1230
1234
|
}
|
|
@@ -3654,7 +3658,7 @@ function _reconnect(forceDisconnect = false, isReconnecting = false) {
|
|
|
3654
3658
|
|
|
3655
3659
|
debug(`_reconnect(): Connection and some subscriptions reinitialized. Connection is back.`)
|
|
3656
3660
|
})
|
|
3657
|
-
|
|
3661
|
+
|
|
3658
3662
|
this.emit('reconnect')
|
|
3659
3663
|
|
|
3660
3664
|
resolve(res)
|
|
@@ -3941,7 +3945,9 @@ async function _onConnectionLost(socketFailure = false) {
|
|
|
3941
3945
|
_console.call(this, 'WARNING: Connection was lost and setting autoReconnect=false. Quiting.')
|
|
3942
3946
|
try {
|
|
3943
3947
|
await this.disconnect(true)
|
|
3944
|
-
} catch {
|
|
3948
|
+
} catch (err) {
|
|
3949
|
+
debugD(`_onConnectionLost(): Error during disconnecting. Quiting.`)
|
|
3950
|
+
}
|
|
3945
3951
|
|
|
3946
3952
|
return
|
|
3947
3953
|
}
|
|
@@ -3961,7 +3967,7 @@ async function _onConnectionLost(socketFailure = false) {
|
|
|
3961
3967
|
//Try to reconnect
|
|
3962
3968
|
_reconnect.call(this, socketFailure, true)
|
|
3963
3969
|
.then(res => {
|
|
3964
|
-
|
|
3970
|
+
|
|
3965
3971
|
//Success -> remove timer
|
|
3966
3972
|
_clearTimer(this._internals.reconnectionTimer)
|
|
3967
3973
|
|
|
@@ -3970,7 +3976,7 @@ async function _onConnectionLost(socketFailure = false) {
|
|
|
3970
3976
|
//Reconnecting failed
|
|
3971
3977
|
if (firstTime)
|
|
3972
3978
|
_console.call(this, `WARNING: Reconnecting failed. Keeping trying in the background every ${this.settings.reconnectInterval} ms...`)
|
|
3973
|
-
|
|
3979
|
+
|
|
3974
3980
|
//If this is still a valid timer, start over again
|
|
3975
3981
|
if (this._internals.reconnectionTimer.id === timerId) {
|
|
3976
3982
|
//Creating a new timer with the same id
|
|
@@ -4014,7 +4020,7 @@ function _clearTimer(timerObject) {
|
|
|
4014
4020
|
//Increasing timer id
|
|
4015
4021
|
timerObject.id = timerObject.id < Number.MAX_SAFE_INTEGER ? timerObject.id + 1 : 0;
|
|
4016
4022
|
}
|
|
4017
|
-
|
|
4023
|
+
|
|
4018
4024
|
|
|
4019
4025
|
|
|
4020
4026
|
|
|
@@ -4290,7 +4296,7 @@ function _systemManagerStatePoller() {
|
|
|
4290
4296
|
let oldState = this.metaData.systemManagerState
|
|
4291
4297
|
|
|
4292
4298
|
//If the timer has changed, quit here
|
|
4293
|
-
if (this._internals.systemManagerStatePoller.id !== timerId){
|
|
4299
|
+
if (this._internals.systemManagerStatePoller.id !== timerId) {
|
|
4294
4300
|
return
|
|
4295
4301
|
}
|
|
4296
4302
|
|
|
@@ -4349,7 +4355,7 @@ function _systemManagerStatePoller() {
|
|
|
4349
4355
|
() => poller(this._internals.systemManagerStatePoller.id),
|
|
4350
4356
|
this.settings.checkStateInterval
|
|
4351
4357
|
)
|
|
4352
|
-
|
|
4358
|
+
|
|
4353
4359
|
}
|
|
4354
4360
|
|
|
4355
4361
|
|
|
@@ -4722,32 +4728,32 @@ async function _parseDataType(data) {
|
|
|
4722
4728
|
dataType.adsDataTypeStr = ADS.ADS_DATA_TYPES.toString(dataType.adsDataType)
|
|
4723
4729
|
pos += 4
|
|
4724
4730
|
|
|
4725
|
-
//
|
|
4726
|
-
dataType.flags = data.
|
|
4731
|
+
//28..31 Flags (AdsDataTypeFlags)
|
|
4732
|
+
dataType.flags = data.readUInt32LE(pos)
|
|
4727
4733
|
dataType.flagsStr = ADS.ADS_DATA_TYPE_FLAGS.toStringArray(dataType.flags)
|
|
4728
4734
|
pos += 4
|
|
4729
4735
|
|
|
4730
|
-
//
|
|
4736
|
+
//32..33 Name length
|
|
4731
4737
|
dataType.nameLength = data.readUInt16LE(pos)
|
|
4732
4738
|
pos += 2
|
|
4733
4739
|
|
|
4734
|
-
//
|
|
4740
|
+
//34..35 Type length
|
|
4735
4741
|
dataType.typeLength = data.readUInt16LE(pos)
|
|
4736
4742
|
pos += 2
|
|
4737
4743
|
|
|
4738
|
-
//
|
|
4744
|
+
//36..37 Comment length
|
|
4739
4745
|
dataType.commentLength = data.readUInt16LE(pos)
|
|
4740
4746
|
pos += 2
|
|
4741
4747
|
|
|
4742
|
-
//
|
|
4748
|
+
//38..39 Array dimension
|
|
4743
4749
|
dataType.arrayDimension = data.readUInt16LE(pos)
|
|
4744
4750
|
pos += 2
|
|
4745
4751
|
|
|
4746
|
-
//41
|
|
4752
|
+
//40..41 Subitem count
|
|
4747
4753
|
dataType.subItemCount = data.readUInt16LE(pos)
|
|
4748
4754
|
pos += 2
|
|
4749
4755
|
|
|
4750
|
-
//
|
|
4756
|
+
//42.... Name
|
|
4751
4757
|
dataType.name = _trimPlcString(iconv.decode(data.slice(pos, pos + dataType.nameLength + 1), 'cp1252'))
|
|
4752
4758
|
pos += dataType.nameLength + 1
|
|
4753
4759
|
|
|
@@ -5237,7 +5243,7 @@ function _parseJsObjectToBuffer(value, dataType, objectPathStr = '', isArraySubI
|
|
|
5237
5243
|
//Struct or array subitem - Go through each subitem
|
|
5238
5244
|
if ((dataType.arrayData.length === 0 || isArraySubItem) && dataType.subItems.length > 0) {
|
|
5239
5245
|
buffer = Buffer.alloc(dataType.size)
|
|
5240
|
-
|
|
5246
|
+
|
|
5241
5247
|
for (const subItem of dataType.subItems) {
|
|
5242
5248
|
//Try the find the subitem from javascript object
|
|
5243
5249
|
let key = null
|
|
@@ -6036,11 +6042,16 @@ function _parseAdsData(packet, data) {
|
|
|
6036
6042
|
case ADS.ADS_COMMAND.ReadWrite:
|
|
6037
6043
|
case ADS.ADS_COMMAND.Read:
|
|
6038
6044
|
|
|
6039
|
-
|
|
6040
6045
|
//0..3 Ads error number
|
|
6041
6046
|
ads.errorCode = data.readUInt32LE(pos)
|
|
6042
6047
|
pos += 4
|
|
6043
6048
|
|
|
6049
|
+
if (data.byteLength <= 4) {
|
|
6050
|
+
ads.dataLength = 0
|
|
6051
|
+
ads.data = Buffer.alloc(0)
|
|
6052
|
+
break
|
|
6053
|
+
}
|
|
6054
|
+
|
|
6044
6055
|
//4..7 Data length (bytes)
|
|
6045
6056
|
ads.dataLength = data.readUInt32LE(pos)
|
|
6046
6057
|
pos += 4
|
|
@@ -6051,8 +6062,6 @@ function _parseAdsData(packet, data) {
|
|
|
6051
6062
|
|
|
6052
6063
|
break
|
|
6053
6064
|
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
6065
|
//-------------- Write ---------------
|
|
6057
6066
|
case ADS.ADS_COMMAND.Write:
|
|
6058
6067
|
|
|
@@ -6062,8 +6071,6 @@ function _parseAdsData(packet, data) {
|
|
|
6062
6071
|
|
|
6063
6072
|
break
|
|
6064
6073
|
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
6074
|
//-------------- Device info ---------------
|
|
6068
6075
|
case ADS.ADS_COMMAND.ReadDeviceInfo:
|
|
6069
6076
|
|
|
@@ -6073,6 +6080,10 @@ function _parseAdsData(packet, data) {
|
|
|
6073
6080
|
|
|
6074
6081
|
ads.data = {}
|
|
6075
6082
|
|
|
6083
|
+
if (data.byteLength <= 4) {
|
|
6084
|
+
break
|
|
6085
|
+
}
|
|
6086
|
+
|
|
6076
6087
|
//4 Major version
|
|
6077
6088
|
ads.data.majorVersion = data.readUInt8(pos)
|
|
6078
6089
|
pos += 1
|
|
@@ -6090,10 +6101,6 @@ function _parseAdsData(packet, data) {
|
|
|
6090
6101
|
|
|
6091
6102
|
break
|
|
6092
6103
|
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
6104
|
//-------------- Device status ---------------
|
|
6098
6105
|
case ADS.ADS_COMMAND.ReadState:
|
|
6099
6106
|
|
|
@@ -6103,6 +6110,10 @@ function _parseAdsData(packet, data) {
|
|
|
6103
6110
|
|
|
6104
6111
|
ads.data = {}
|
|
6105
6112
|
|
|
6113
|
+
if (data.byteLength <= 4) {
|
|
6114
|
+
break
|
|
6115
|
+
}
|
|
6116
|
+
|
|
6106
6117
|
//4..5 ADS state
|
|
6107
6118
|
ads.data.adsState = data.readUInt16LE(pos)
|
|
6108
6119
|
ads.data.adsStateStr = ADS.ADS_STATE.toString(ads.data.adsState)
|
|
@@ -6114,9 +6125,6 @@ function _parseAdsData(packet, data) {
|
|
|
6114
6125
|
|
|
6115
6126
|
break
|
|
6116
6127
|
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
6128
|
//-------------- Add notification ---------------
|
|
6121
6129
|
case ADS.ADS_COMMAND.AddNotification:
|
|
6122
6130
|
|
|
@@ -6126,15 +6134,16 @@ function _parseAdsData(packet, data) {
|
|
|
6126
6134
|
|
|
6127
6135
|
ads.data = {}
|
|
6128
6136
|
|
|
6137
|
+
if (data.byteLength <= 4) {
|
|
6138
|
+
break
|
|
6139
|
+
}
|
|
6140
|
+
|
|
6129
6141
|
//4..7 Notification handle
|
|
6130
6142
|
ads.data.notificationHandle = data.readUInt32LE(pos)
|
|
6131
6143
|
pos += 4
|
|
6132
6144
|
|
|
6133
6145
|
break
|
|
6134
6146
|
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
6147
|
//-------------- Delete notification ---------------
|
|
6139
6148
|
case ADS.ADS_COMMAND.DeleteNotification:
|
|
6140
6149
|
|
|
@@ -6144,8 +6153,6 @@ function _parseAdsData(packet, data) {
|
|
|
6144
6153
|
|
|
6145
6154
|
break
|
|
6146
6155
|
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
6156
|
//-------------- Notification ---------------
|
|
6150
6157
|
case ADS.ADS_COMMAND.Notification:
|
|
6151
6158
|
|
|
@@ -6153,8 +6160,6 @@ function _parseAdsData(packet, data) {
|
|
|
6153
6160
|
|
|
6154
6161
|
break
|
|
6155
6162
|
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
6163
|
//-------------- WriteControl ---------------
|
|
6159
6164
|
case ADS.ADS_COMMAND.WriteControl:
|
|
6160
6165
|
|
|
@@ -6164,7 +6169,6 @@ function _parseAdsData(packet, data) {
|
|
|
6164
6169
|
|
|
6165
6170
|
break
|
|
6166
6171
|
|
|
6167
|
-
|
|
6168
6172
|
default:
|
|
6169
6173
|
//Unknown command, return a custom error
|
|
6170
6174
|
debug(`_parseAdsResponse: Unknown ads command in response: ${packet.ams.adsCommand}`)
|
|
@@ -6327,7 +6331,7 @@ async function _onAdsCommandReceived(packet) {
|
|
|
6327
6331
|
parsedValue.timeStamp = stamp.timeStamp
|
|
6328
6332
|
|
|
6329
6333
|
//Then lets call the users callback
|
|
6330
|
-
sub.callback(
|
|
6334
|
+
sub.callback && sub.callback(
|
|
6331
6335
|
parsedValue,
|
|
6332
6336
|
sub
|
|
6333
6337
|
)
|