diff --git a/CHANGELOG.md b/CHANGELOG.md index d16b386..44058a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.14.3] - 23.09.2023 +### Changed +- Bug fix: Some TwinCAT 2 devices (such as BK9050) do not send data length if answering with error code + - This caused `RangeError: Index out of range` exception as there wasn't enough bytes received + - See [issue #116](https://github.com/jisotalo/ads-client/issues/116) +- Bug fix: If using older Node.js versions such as 8.x, connection lost could have caused unhandled exception + - Reason was `catch {}` which isn't supported in old versions + - See [issue #116](https://github.com/jisotalo/ads-client/issues/116) + +### Added +- Updated readme to include information about TypeScript types + - Thanks to [Christian Rishøj](https://github.com/crishoj) +- Updated readme with FAQ about TwinCAT 2 low-end devices +- Updated readme about v2 development +- Added option to run tests with usermode runtime AmsNetId (`192.168.4.1.1.1`) instead of localhost (`npm run test-um`) + ## [1.14.2] - 02.05.2023 ### Changed - Bug fix: `ADS_DATA_TYPE_FLAGS` (`dataType.flags`) were parsed incorrectly. diff --git a/README.md b/README.md index 7a6d212..81f69d9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![npm version](https://img.shields.io/npm/v/ads-client)](https://www.npmjs.org/package/ads-client) -[![Donate](https://img.shields.io/badge/Donate-PayPal-yellow)](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR) +[![Donate](https://img.shields.io/badge/Support-PayPal-yellow)](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR) [![GitHub](https://img.shields.io/badge/View%20on-GitHub-brightgreen)](https://github.com/jisotalo/ads-client) [![License](https://img.shields.io/github/license/jisotalo/ads-client)](https://choosealicense.com/licenses/mit/) @@ -10,16 +10,19 @@ Beckhoff TwinCAT ADS client library for Node.js (unofficial). Connects to Beckho 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). -There is automatically created documentation available at https://jisotalo.github.io/ads-client/ +There is automatically created documentation available at https://jisotalo.fi/ads-client/ # Project status This project is currently "ready". It's maintained actively and used in projects by the author and others (also lot's of commercial projects) Bugs are fixed if found and new features can be added. Please let me know if you have any ideas! -And if you want you can buy me a beer using PayPal :) +If you want to support my work, you can do it using PayPal. I can provide you support in exchange. -[![Donate](https://img.shields.io/badge/Donate%20a%20beer!-PayPal-yellow)](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR) +[![Donate](https://img.shields.io/badge/Support%20my%20work!-PayPal-yellow)](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR) + +## Version 2 +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. # Using Node-RED? @@ -101,6 +104,13 @@ Install the [npm package](https://www.npmjs.com/package/ads-client) using npm co npm i ads-client ``` +If you are using TypeScript, install unofficial types using npm command (thanks [Christian Rishøj](https://github.com/crishoj)): +```bash +npm install --save @types/ads-client +``` + +*Note: Version 2 under development will be written in 100% TypeScript* + Include the module in your code ```js const ads = require('ads-client') @@ -1764,12 +1774,17 @@ Solution: * When closing application, first unsubscribe from all notifications using `unsubscribeAll()` * Use router instead of direct connection, see https://github.com/jisotalo/ads-client/issues/85#issuecomment-1193098519 +### Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.) +* You can only use raw commands (such as `readRaw()`, `writeRaw()`, `subscribeRaw()`) as these devices provide no symbols +* See [issue 114](https://github.com/jisotalo/ads-client/issues/114) and [issue 116](https://github.com/jisotalo/ads-client/issues/116) for starters + + # Automatic testing 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...) Separate PLC project is required for testing, see https://github.com/jisotalo/ads-client-test-plc-project for more project and more info. -Tests are run with command `npm test` (not in npm version, please clone this repository). +Tests are run with command `npm test` or `npm run test-um` (usermode runtime) (not in npm version, please clone this repository). # Documentation diff --git a/docs/-_LibraryInternals.html b/docs/-_LibraryInternals.html index ee9b20a..966cb7b 100644 --- a/docs/-_LibraryInternals.html +++ b/docs/-_LibraryInternals.html @@ -466,7 +466,7 @@

(static)
Source:
@@ -554,7 +554,7 @@

(static)
Source:
@@ -1963,7 +1963,7 @@

(static) Source:
@@ -2127,7 +2127,7 @@

(stati
Source:
@@ -2801,7 +2801,7 @@

(a
Source:
@@ -2888,7 +2888,7 @@

(async
Source:
@@ -3107,7 +3107,7 @@

(asy
Source:
@@ -3238,7 +3238,7 @@

(static) Source:
@@ -3554,7 +3554,7 @@

(static) Source:
@@ -3712,7 +3712,7 @@

(static)
Source:
@@ -3870,7 +3870,7 @@

(async, s
Source:
@@ -4006,7 +4006,7 @@

(async, stati
Source:
@@ -4164,7 +4164,7 @@

(stat
Source:
@@ -4449,7 +4449,7 @@

(stati
Source:
@@ -4631,7 +4631,7 @@

(stati
Source:
@@ -4879,7 +4879,7 @@

(stati
Source:
@@ -5060,7 +5060,7 @@

(static) Source:
@@ -5218,7 +5218,7 @@

(static) <
Source:
@@ -5380,7 +5380,7 @@

(static) Source:
@@ -5878,7 +5878,7 @@

(asyn
Source:
@@ -5994,7 +5994,7 @@

(
Source:
@@ -6401,7 +6401,7 @@

(static) Source:
@@ -6489,7 +6489,7 @@

(static) Source:
@@ -6621,7 +6621,7 @@

Source:
@@ -6708,7 +6708,7 @@

(static) _
Source:
@@ -6919,7 +6919,7 @@

Source:
@@ -7007,7 +7007,7 @@

(s
Source:
@@ -7366,7 +7366,7 @@

(st
Source:
@@ -7481,7 +7481,7 @@

subscriptionSettings

Source:
@@ -7725,7 +7725,7 @@
Properties:

- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/docs/Client.html b/docs/Client.html index 96931af..19f338e 100644 --- a/docs/Client.html +++ b/docs/Client.html @@ -9027,7 +9027,7 @@
Returns:

- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/docs/ClientException.html b/docs/ClientException.html index f117c44..c3197ab 100644 --- a/docs/ClientException.html +++ b/docs/ClientException.html @@ -857,7 +857,7 @@
Type:

- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/docs/ads-client.js.html b/docs/ads-client.js.html index 423b1e9..0660ea0 100644 --- a/docs/ads-client.js.html +++ b/docs/ads-client.js.html @@ -941,7 +941,7 @@

ads-client.js

} catch (err) { return reject(new ClientException(this, 'readSymbol()', `Reading symbol ${variableName} failed: Reading data type failed`, err)) } - + //4. Parse the data to javascript object let data = {} try { @@ -3705,7 +3705,7 @@

ads-client.js

debug(`_reconnect(): Connection and some subscriptions reinitialized. Connection is back.`) }) - + this.emit('reconnect') resolve(res) @@ -3992,7 +3992,9 @@

ads-client.js

_console.call(this, 'WARNING: Connection was lost and setting autoReconnect=false. Quiting.') try { await this.disconnect(true) - } catch { } + } catch (err) { + debugD(`_onConnectionLost(): Error during disconnecting. Quiting.`) + } return } @@ -4012,7 +4014,7 @@

ads-client.js

//Try to reconnect _reconnect.call(this, socketFailure, true) .then(res => { - + //Success -> remove timer _clearTimer(this._internals.reconnectionTimer) @@ -4021,7 +4023,7 @@

ads-client.js

//Reconnecting failed if (firstTime) _console.call(this, `WARNING: Reconnecting failed. Keeping trying in the background every ${this.settings.reconnectInterval} ms...`) - + //If this is still a valid timer, start over again if (this._internals.reconnectionTimer.id === timerId) { //Creating a new timer with the same id @@ -4065,7 +4067,7 @@

ads-client.js

//Increasing timer id timerObject.id = timerObject.id < Number.MAX_SAFE_INTEGER ? timerObject.id + 1 : 0; } - + @@ -4341,7 +4343,7 @@

ads-client.js

let oldState = this.metaData.systemManagerState //If the timer has changed, quit here - if (this._internals.systemManagerStatePoller.id !== timerId){ + if (this._internals.systemManagerStatePoller.id !== timerId) { return } @@ -4400,7 +4402,7 @@

ads-client.js

() => poller(this._internals.systemManagerStatePoller.id), this.settings.checkStateInterval ) - + } @@ -5288,7 +5290,7 @@

ads-client.js

//Struct or array subitem - Go through each subitem if ((dataType.arrayData.length === 0 || isArraySubItem) && dataType.subItems.length > 0) { buffer = Buffer.alloc(dataType.size) - + for (const subItem of dataType.subItems) { //Try the find the subitem from javascript object let key = null @@ -6087,11 +6089,16 @@

ads-client.js

case ADS.ADS_COMMAND.ReadWrite: case ADS.ADS_COMMAND.Read: - //0..3 Ads error number ads.errorCode = data.readUInt32LE(pos) pos += 4 + if (data.byteLength <= 4) { + ads.dataLength = 0 + ads.data = Buffer.alloc(0) + break + } + //4..7 Data length (bytes) ads.dataLength = data.readUInt32LE(pos) pos += 4 @@ -6102,8 +6109,6 @@

ads-client.js

break - - //-------------- Write --------------- case ADS.ADS_COMMAND.Write: @@ -6113,8 +6118,6 @@

ads-client.js

break - - //-------------- Device info --------------- case ADS.ADS_COMMAND.ReadDeviceInfo: @@ -6124,6 +6127,10 @@

ads-client.js

ads.data = {} + if (data.byteLength <= 4) { + break + } + //4 Major version ads.data.majorVersion = data.readUInt8(pos) pos += 1 @@ -6141,10 +6148,6 @@

ads-client.js

break - - - - //-------------- Device status --------------- case ADS.ADS_COMMAND.ReadState: @@ -6154,6 +6157,10 @@

ads-client.js

ads.data = {} + if (data.byteLength <= 4) { + break + } + //4..5 ADS state ads.data.adsState = data.readUInt16LE(pos) ads.data.adsStateStr = ADS.ADS_STATE.toString(ads.data.adsState) @@ -6165,9 +6172,6 @@

ads-client.js

break - - - //-------------- Add notification --------------- case ADS.ADS_COMMAND.AddNotification: @@ -6177,15 +6181,16 @@

ads-client.js

ads.data = {} + if (data.byteLength <= 4) { + break + } + //4..7 Notification handle ads.data.notificationHandle = data.readUInt32LE(pos) pos += 4 break - - - //-------------- Delete notification --------------- case ADS.ADS_COMMAND.DeleteNotification: @@ -6195,8 +6200,6 @@

ads-client.js

break - - //-------------- Notification --------------- case ADS.ADS_COMMAND.Notification: @@ -6204,8 +6207,6 @@

ads-client.js

break - - //-------------- WriteControl --------------- case ADS.ADS_COMMAND.WriteControl: @@ -6215,7 +6216,6 @@

ads-client.js

break - default: //Unknown command, return a custom error debug(`_parseAdsResponse: Unknown ads command in response: ${packet.ams.adsCommand}`) @@ -6942,7 +6942,7 @@

ads-client.js


- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/docs/global.html b/docs/global.html index fe6d6d5..72adc61 100644 --- a/docs/global.html +++ b/docs/global.html @@ -3627,7 +3627,7 @@
Properties:

- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/docs/index.html b/docs/index.html index 107b3af..f7e2ca5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -62,17 +62,19 @@

ads-client

npm version -Donate +Donate GitHub License

Beckhoff TwinCAT ADS client library for Node.js (unofficial). Connects to Beckhoff TwinCAT automation systems using ADS protocol.

Coded from scratch using TwinCAT ADS specification and Beckhoff.TwinCAT.Ads nuget package. Inspiration from similar projects like node-ads, beckhoff-js and iecstruct.

-

There is automatically created documentation available at https://jisotalo.github.io/ads-client/

+

There is automatically created documentation available at https://jisotalo.fi/ads-client/

Project status

This project is currently "ready". It's maintained actively and used in projects by the author and others (also lot's of commercial projects)

Bugs are fixed if found and new features can be added. Please let me know if you have any ideas!

-

And if you want you can buy me a beer using PayPal :)

-

Donate

+

If you want to support my work, you can do it using PayPal. I can provide you support in exchange.

+

Donate

+

Version 2

+

Version 2 is under development in 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.

Using Node-RED?

Check out the node-red-contrib-ads-client package. It's an ads-client wrapper for Node-RED to get the same functionality.

Table of contents

@@ -177,6 +179,10 @@

Installation

Install the npm package using npm command:

npm i ads-client
 
+

If you are using TypeScript, install unofficial types using npm command (thanks Christian Rishøj):

+
npm install --save @types/ads-client
+
+

Note: Version 2 under development will be written in 100% TypeScript

Include the module in your code

const ads = require('ads-client')
 
@@ -1656,10 +1662,15 @@

Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.)

+
    +
  • You can only use raw commands (such as readRaw(), writeRaw(), subscribeRaw()) as these devices provide no symbols
  • +
  • See issue 114 and issue 116 for starters
  • +

Automatic testing

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...)

Separate PLC project is required for testing, see https://github.com/jisotalo/ads-client-test-plc-project for more project and more info.

-

Tests are run with command npm test (not in npm version, please clone this repository).

+

Tests are run with command npm test or npm run test-um (usermode runtime) (not in npm version, please clone this repository).

Documentation

You can find the full html documentation from the project GitHub home page as well as from ./docs/ folder in the repository.

License

@@ -1694,7 +1705,7 @@

License


- Documentation generated by JSDoc 3.6.7 on Tue May 02 2023 21:37:58 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Sat Sep 23 2023 08:34:17 GMT+0300 (Itä-Euroopan kesäaika) using the docdash theme.
diff --git a/package-lock.json b/package-lock.json index 6e6ab4d..968b984 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ads-client", - "version": "1.14.1", + "version": "1.14.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ads-client", - "version": "1.14.1", + "version": "1.14.3", "license": "MIT", "dependencies": { "debug": "^4.3.3", @@ -1021,6 +1021,28 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true + }, "node_modules/@types/node": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", @@ -1520,10 +1542,13 @@ "dev": true }, "node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/error-ex": { "version": "1.3.2", @@ -2435,9 +2460,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2572,31 +2597,32 @@ } }, "node_modules/jsdoc": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", - "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.1", + "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^10.0.0", - "markdown-it-anchor": "^5.2.7", - "marked": "^2.0.3", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.13.1" + "underscore": "~1.13.2" }, "bin": { "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=8.15.0" + "node": ">=12.0.0" } }, "node_modules/jsesc": { @@ -2663,9 +2689,9 @@ "dev": true }, "node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "dependencies": { "uc.micro": "^1.0.1" @@ -2731,14 +2757,14 @@ } }, "node_modules/markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, @@ -2747,30 +2773,37 @@ } }, "node_modules/markdown-it-anchor": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", - "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "peerDependencies": { + "@types/markdown-it": "*", "markdown-it": "*" } }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { - "marked": "bin/marked" + "marked": "bin/marked.js" }, "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/merge-stream": { @@ -3172,9 +3205,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3368,7 +3401,7 @@ "node_modules/taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "node_modules/terminal-link": { @@ -4417,6 +4450,28 @@ "@types/istanbul-lib-report": "*" } }, + "@types/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==", + "dev": true + }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true + }, "@types/node": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", @@ -4797,9 +4852,9 @@ "dev": true }, "entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true }, "error-ex": { @@ -5484,9 +5539,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5594,25 +5649,26 @@ } }, "jsdoc": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", - "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.1", + "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^10.0.0", - "markdown-it-anchor": "^5.2.7", - "marked": "^2.0.3", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.13.1" + "underscore": "~1.13.2" } }, "jsesc": { @@ -5661,9 +5717,9 @@ "dev": true }, "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "requires": { "uc.micro": "^1.0.1" @@ -5717,35 +5773,43 @@ } }, "markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "requires": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + } } }, "markdown-it-anchor": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", - "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "requires": {} }, "marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "merge-stream": { @@ -6043,9 +6107,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "shebang-command": { @@ -6188,7 +6252,7 @@ "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "terminal-link": { diff --git a/package.json b/package.json index e9a774a..11ab93e 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "ads-client", - "version": "1.14.2", + "version": "1.14.3", "description": "Beckhoff TwinCAT ADS client library for Node.js (unofficial). Connects to Beckhoff TwinCAT automation systems using ADS protocol.", "main": "./src/ads-client.js", "scripts": { "test": "jest --runInBand", + "test-um": "set ADS_CLIENT_TEST_AMS=192.168.4.1.1.1 && jest --runInBand", "generate-docs": "jsdoc ./src/ads-client.js ./README.md -c ./jsdoc-conf.json -d ./docs/ -t ./node_modules/docdash/" }, "keywords": [ diff --git a/src/ads-client.js b/src/ads-client.js index 4fe7d30..91a3c6c 100644 --- a/src/ads-client.js +++ b/src/ads-client.js @@ -894,7 +894,7 @@ class Client extends EventEmitter { } catch (err) { return reject(new ClientException(this, 'readSymbol()', `Reading symbol ${variableName} failed: Reading data type failed`, err)) } - + //4. Parse the data to javascript object let data = {} try { @@ -3658,7 +3658,7 @@ function _reconnect(forceDisconnect = false, isReconnecting = false) { debug(`_reconnect(): Connection and some subscriptions reinitialized. Connection is back.`) }) - + this.emit('reconnect') resolve(res) @@ -3945,7 +3945,9 @@ async function _onConnectionLost(socketFailure = false) { _console.call(this, 'WARNING: Connection was lost and setting autoReconnect=false. Quiting.') try { await this.disconnect(true) - } catch { } + } catch (err) { + debugD(`_onConnectionLost(): Error during disconnecting. Quiting.`) + } return } @@ -3965,7 +3967,7 @@ async function _onConnectionLost(socketFailure = false) { //Try to reconnect _reconnect.call(this, socketFailure, true) .then(res => { - + //Success -> remove timer _clearTimer(this._internals.reconnectionTimer) @@ -3974,7 +3976,7 @@ async function _onConnectionLost(socketFailure = false) { //Reconnecting failed if (firstTime) _console.call(this, `WARNING: Reconnecting failed. Keeping trying in the background every ${this.settings.reconnectInterval} ms...`) - + //If this is still a valid timer, start over again if (this._internals.reconnectionTimer.id === timerId) { //Creating a new timer with the same id @@ -4018,7 +4020,7 @@ function _clearTimer(timerObject) { //Increasing timer id timerObject.id = timerObject.id < Number.MAX_SAFE_INTEGER ? timerObject.id + 1 : 0; } - + @@ -4294,7 +4296,7 @@ function _systemManagerStatePoller() { let oldState = this.metaData.systemManagerState //If the timer has changed, quit here - if (this._internals.systemManagerStatePoller.id !== timerId){ + if (this._internals.systemManagerStatePoller.id !== timerId) { return } @@ -4353,7 +4355,7 @@ function _systemManagerStatePoller() { () => poller(this._internals.systemManagerStatePoller.id), this.settings.checkStateInterval ) - + } @@ -5241,7 +5243,7 @@ function _parseJsObjectToBuffer(value, dataType, objectPathStr = '', isArraySubI //Struct or array subitem - Go through each subitem if ((dataType.arrayData.length === 0 || isArraySubItem) && dataType.subItems.length > 0) { buffer = Buffer.alloc(dataType.size) - + for (const subItem of dataType.subItems) { //Try the find the subitem from javascript object let key = null @@ -6040,11 +6042,16 @@ function _parseAdsData(packet, data) { case ADS.ADS_COMMAND.ReadWrite: case ADS.ADS_COMMAND.Read: - //0..3 Ads error number ads.errorCode = data.readUInt32LE(pos) pos += 4 + if (data.byteLength <= 4) { + ads.dataLength = 0 + ads.data = Buffer.alloc(0) + break + } + //4..7 Data length (bytes) ads.dataLength = data.readUInt32LE(pos) pos += 4 @@ -6055,8 +6062,6 @@ function _parseAdsData(packet, data) { break - - //-------------- Write --------------- case ADS.ADS_COMMAND.Write: @@ -6066,8 +6071,6 @@ function _parseAdsData(packet, data) { break - - //-------------- Device info --------------- case ADS.ADS_COMMAND.ReadDeviceInfo: @@ -6077,6 +6080,10 @@ function _parseAdsData(packet, data) { ads.data = {} + if (data.byteLength <= 4) { + break + } + //4 Major version ads.data.majorVersion = data.readUInt8(pos) pos += 1 @@ -6094,10 +6101,6 @@ function _parseAdsData(packet, data) { break - - - - //-------------- Device status --------------- case ADS.ADS_COMMAND.ReadState: @@ -6107,6 +6110,10 @@ function _parseAdsData(packet, data) { ads.data = {} + if (data.byteLength <= 4) { + break + } + //4..5 ADS state ads.data.adsState = data.readUInt16LE(pos) ads.data.adsStateStr = ADS.ADS_STATE.toString(ads.data.adsState) @@ -6118,9 +6125,6 @@ function _parseAdsData(packet, data) { break - - - //-------------- Add notification --------------- case ADS.ADS_COMMAND.AddNotification: @@ -6130,15 +6134,16 @@ function _parseAdsData(packet, data) { ads.data = {} + if (data.byteLength <= 4) { + break + } + //4..7 Notification handle ads.data.notificationHandle = data.readUInt32LE(pos) pos += 4 break - - - //-------------- Delete notification --------------- case ADS.ADS_COMMAND.DeleteNotification: @@ -6148,8 +6153,6 @@ function _parseAdsData(packet, data) { break - - //-------------- Notification --------------- case ADS.ADS_COMMAND.Notification: @@ -6157,8 +6160,6 @@ function _parseAdsData(packet, data) { break - - //-------------- WriteControl --------------- case ADS.ADS_COMMAND.WriteControl: @@ -6168,7 +6169,6 @@ function _parseAdsData(packet, data) { break - default: //Unknown command, return a custom error debug(`_parseAdsResponse: Unknown ads command in response: ${packet.ams.adsCommand}`) diff --git a/test/ads-client.test.js b/test/ads-client.test.js index 272b46b..000c47d 100644 --- a/test/ads-client.test.js +++ b/test/ads-client.test.js @@ -25,11 +25,11 @@ SOFTWARE. * This must match with GVL_AdsClientTests.VERSION */ const PLC_PROJECT_VERSION = '1.0.0.0' - const ads = require('../src/ads-client') +const AMS_NET_ID = (process.env['ADS_CLIENT_TEST_AMS'] ?? 'localhost').trim() const client = new ads.Client({ - targetAmsNetId: 'localhost', + targetAmsNetId: AMS_NET_ID, targetAdsPort: 851 }) @@ -44,7 +44,7 @@ describe('connection', () => { test('checking ads client settings', async () => { expect(client).toBeInstanceOf(ads.Client) expect(client).toHaveProperty('settings') - expect(client.settings.targetAmsNetId).toBe('127.0.0.1.1.1') + expect(client.settings.targetAmsNetId).toBe(AMS_NET_ID === 'localhost' ? '127.0.0.1.1.1' : AMS_NET_ID) expect(client.settings.targetAdsPort).toBe(851) })