Giter VIP home page Giter VIP logo

ads-client's People

Contributors

dependabot[bot] avatar hopperpop avatar jisotalo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ads-client's Issues

convertFromRaw() fails at converting ARRAY data

It seems that the convertFromRaw() method fails when the given raw buffer data should be converted to array. convertToRaw() works fine.

It has a check if buffer and datatype have the same size. However, it doesn't work with arrays.

While the bug isn't fixed yet, commenting the line 2672 of ads-client.js disables size checking. The conversion itself works fine.

//Make sure lengths are the same
if (dataType.size != rawData.byteLength) {
  //Following row is commented out
  //return reject(new ClientException(this, 'convertFromRaw()', `Given data buffer and data type sizes mismatch: Buffer is ${rawData.byteLength} bytes and data type is ${dataType.size} bytes`))
}

Adding an automatic reconnect

During development of #4 if was quickly clear that the AMS router is not sending any notes to other than local clients.

Therefore it's only possible to detect system manager run<->config changes only if connected to PLC runtime that is running on the same machine.

If connected to a remote machine, during run<->config change, the router drops the connection without any additional messages (node.js socket event end). This can be used to determine changes.

So to successfully detect connection changes and reinitialize/reconnect, the steps should be something like the following:

  1. Detect AMS router stop<->start cycle OR detect a connection loss
  2. Save existing user subscriptions to temporary location
  3. Close the connection and remove all subscriptions (or if already closed, just remove all subscriptions)
  4. Reconnect to the system. If it fails, wait a while and try again
  5. Read system manager state. If it's not run (e.g. config), wait a while and try again
  6. Read PLC runtime state. If it fails, wait a while and try again
  7. Subscribe to all previously used subscriptions using the temporary subscription data saved in step 2
  8. Delete temporary subscription data
  9. Connection is now back in normal state

When this is implemented, the #4 should be OK too.

Fails to connect multiple instances.

Issue

On my windows machine with TwinCAT installed, I am able to create and connect more than one instance. However, when I try the same thing on a Raspberry Pi, the second one fails.

The static route for the Pi is added on my TwinCAT PLC. I originally tried having a different localAmsNetId for each instance and adding two separate static routes. The second appeared as a subroute in TwinCAT.

Then, when I logged what was happening on my dev machine, I saw localAmsNetId were the same, only the ports were different. So, I tried that as well on the Pi (shown below) and it still will not connect a second instance from the Pi.

Have you tried this before or do you have any thoughts on what else I can look at?

General Info

Target:

  • CX-5130
    -TwinCAT 3.1 4024.7

Local Dev

  • Windows 10
  • TwinCAT 3.1 4024.10
  • NodeJS v12.16.1
  • ads-client v1.10.1

Target Server

  • Raspberry Pi 4
  • Raspbian 10 (buster)
  • NodeJS v12.17.0
  • ads-client v1.10.1

Details

On Windows:

const ads = require('ads-client');

const run = async () => {

    try {

        const client1 = new ads.Client({
            targetAmsNetId: '5.81.107.190.1.1',
            targetAdsPort: 851,
        });
    
        const client2 = new ads.Client({
            targetAmsNetId: '5.81.107.190.1.1',
            targetAdsPort: 851,
        });

        console.log(`Connected 1: ${JSON.stringify(await client1.connect(), null, 2)}`)
        console.log(`Connected 2:  ${JSON.stringify(await client2.connect(), null, 2)}`)

        await client1.disconnect();
        await client2.disconnect();

    } catch(error){
        console.log(`ERROR: ${error.message}`)
    }

};

run();

Result:

Connected 1: {
  "connected": true,
  "isLocal": false,
  "localAmsNetId": "192.168.1.200.1.1",
  "localAdsPort": 33788,
  "targetAmsNetId": "5.81.107.190.1.1",
  "targetAdsPort": 851
}
Connected 2:  {
  "connected": true,
  "isLocal": false,
  "localAmsNetId": "192.168.1.200.1.1",
  "localAdsPort": 33789,
  "targetAmsNetId": "5.81.107.190.1.1",
  "targetAdsPort": 851
}

On the Raspberry Pi

const ads = require('ads-client');
const getPort = require('get-port');

const getNewPort = async () => getPort({port: getPort.makeRange(55700,58700)});

const run = async () => {

    try {

        const port1 = await getNewPort();
        const port2 = await getNewPort(); 
        
        console.log('PORTS:',port1,port2);
        
        const client1 = new ads.Client({
            localAmsNetId: '192.168.2.101.1.1',
            localAdsPort: port1,
            targetAmsNetId: '5.81.107.190.1.1',
            targetAdsPort: 851,
            routerAddress: '10.168.171.31',
            routerTcpPort: 48898 
          });
          
          const client2 = new ads.Client({
            localAmsNetId: '192.168.2.101.1.1',
            localAdsPort: port2,
            targetAmsNetId: '5.81.107.190.1.1',
            targetAdsPort: 851,
            routerAddress: '10.168.171.31',
            routerTcpPort: 48898 
          });

        console.log(`Connected 1: ${JSON.stringify(await client1.connect(), null, 2)}`)
        console.log(`Connected 2:  ${JSON.stringify(await client2.connect(), null, 2)}`)

        await client1.disconnect();
        await client2.disconnect();

    } catch(error){
        console.log(`ERROR: ${error.message}`)
    }

};

run();

Result:

PORTS: 55700 55701
Connected 1: {
  "connected": true,
  "isLocal": false,
  "localAmsNetId": "192.168.2.101.1.1",
  "localAdsPort": 55700,
  "targetAmsNetId": "5.81.107.190.1.1",
  "targetAdsPort": 851
}
ERROR: Connection failed: Device system manager state read failed

Uncaught exception ECONNRESET after restarting PLC

Hello,

I would like to be able to brace for a power outage of the PLC (including the switch that connects the PLC to the computer running node-ads).

When I turn off the power cabinet I get a connectionLost which is correct. However when it comes back online I get a

events.js:292
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
Emitted 'error' event on Socket instance at:
    at emitErrorNT (internal/streams/destroy.js:106:8)
    at emitErrorCloseNT (internal/streams/destroy.js:74:3)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  errno: -4077,
  code: 'ECONNRESET',
  syscall: 'read'
}

which I assume happens when it is trying to reconnect.

I am not sure how to catch this error. I would be fine with catching this moment and attempting the reconnection myself, however this crashes my whole application.

I already upgraded to the latest version 1.5.1

readAndCacheDataTypes() and getDataType() inconsistency

Good day.

Noticed something interesting - call to .getDataType(...) produces a structure, which has two fields called "adsDataType" and "adsDataTypeStr". Now from what I can see, a call to .readAndCacheDataTypes() produces a map, with children structures, identical in their content to the call to .getDataType(), with the exception that "adsDataType" and "adsDataTypeStr" are keyed as "dataType" and "dataTypeStr". Outside of that, both results, in terms of their schema, look identical.

Wondering if that difference is intentional or not? The reason is, if parsing the type information, two difference parser functions need to be made to account for the difference in the names, which doesn't seem necessary, unless of course, I am missing the reason for the different names.

Updating ADS error codes

It seems that there are new ADS error codes available, such as

0x1D | 29 | 0x9811 001D | ERR_TLSSEND | TLS send error – secure ADS connection failed.
0x1E | 30 | 0x9811 001E | ERR_ACCESSDENIED | Access denied – secure ADS access denied.

The error code list needs to be updated.

Starting/stopping the PLC

Adding new commands that use ADS Write Control command to start/stop PLC.

  • Start PLC runtime
  • Stop PLC runtime
  • Set system to run mode (if possible)
  • Set system to config mode (if possible)

Implement connection related events for end-user

As the issue #18 will handle reconnecting in different situations, it should be possible for the end-user to receive events explaining the situation.

For example

  • client.on('connect')
  • client.on('disconnect')

and so on.

Fails to write on large number of batch writes.

Issue

I've been testing how many tags I can read/write at the same time by mapping over an array of tags to read or write inside of a Promise.all. It varies on how many it can do at a time. Sometimes it's 400. Sometimes it's 800. I've found that if I batch them in smaller quantities, then it's not an issue. 200 tags at a time appears to work consistently at the moment.

To test, I setup an array of 1000 DINTs and tried to write them as individual tags at the same time.

The error message thrown from readSymbol is Tag write failed Writing symbol Main.testRead[778] failed: Writing the data failed. (Specific to the particular tag)

General Information

Target

  • CX-5130
  • TwinCAT 3.1 4024.7

Local Dev

  • Windows 10
  • TwinCAT 3.1 4024.10
  • NodeJS v12.16.1
  • ads-client v1.10.1

Examples

I have a TypeScript wrapper around the ads client.

writeTag method:

    public writeTag = async ({name, value}: ITag) : Promise<boolean> => {
        try {
            await this._client.writeSymbol(name, value);
            return true;
        }
        catch {
            return false;
        }
    };

writeTagGroup method where ITagGroup is an array of ITag: (doesn't write all values)

    public writeTagGroup = async (tagGroup: ITagGroup) : Promise<boolean> => {
        try{
            const result = await Promise.all(tagGroup.map(this.writeTag));
            return result.every(isTrue);
        }
        catch{
            return false;
        }
    }

batched writeTagGroup method: (appears to work so far)

    public writeTagGroup = async (tagGroup: ITagGroup) : Promise<boolean> => {
        try{
            const writeGroup = [...tagGroup];
            const result : boolean[] = [];
            while(writeGroup.length){
                result.concat(await Promise.all(writeGroup.splice(0,200).map(this.writeTag)));
            }
            return result.every(isTrue);
        }
        catch{
            return false;
        

Cannot connect to twincat

Hi,
Im getting an unhandled rejection while trying to initiate connection to the PLC. Maybe someone can point to the silly mistake....

Unhandled Rejection (TypeError): net.Socket is not a constructor

client.connect()
| ^ 24 | .then(res => {
25 | console.log(Connected to the ${res.targetAmsNetId})
26 | console.log(Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort})

I'm using node version 14.16.1

Add support for BIT data type

At the moment, BIT type is not supported. Not so trivial to add as the base size at Javascript is byte.

Is it worth doing? Not very used data type.

Update metadata when AMS router changes state

AMS router sends a notification when it changes state (between stop<->start). This happens when PLC is set to config mode or run mode, for example.

When the AMS router state changes to run, we need to check if the PLC runtime is available. If it's available, update metadata and notification handles.

Relates also to #3

MultiRead and MultiWrite

Hi,
very nice work!!

What about to add support for multiRead / multiWrite ecc..?

For example for multiRead something like

`
readRawMulti(variablesArray) {
return new Promise(async (resolve, reject) => {

  if (variablesArray == null) {
    return reject(new ClientException('readRaw()', `Some of parameters (variablesArray) are not assigned`))
  }

  debug(`readRawMulti(): Reading data from ${JSON.stringify({ variablesArray })}`)
  
  let dataSize = 0;
  for(let i = 0; i < variablesArray.length; i++){
    dataSize = dataSize + variablesArray[i].byteSize
  }

  //Allocating bytes for request
  const data = Buffer.alloc(16 + variablesArray.length*12)
  let pos = 0

  //0..3 IndexGroup: reserved for SumCommandRead
  data.writeUInt32LE(ADS.ADS_RESERVED_INDEX_GROUPS.SumCommandRead, pos)
  pos += 4
  
  //4..7 IndexOffset: number of SumCommands
  data.writeUInt32LE(variablesArray.length, pos)
  pos += 4
  
  //8..11 Read length: Length of data in bytes, which should be read. RequestedDataSize + Length of requested data + 4 byte errorcode per sum_command;
  data.writeUInt32LE(dataSize + 4 * variablesArray.length, pos)
  pos += 4

  //12..15 Write Length: Length of data in bytes, which should be written. 12*sum_command
  data.writeUInt32LE(12 * variablesArray.length, pos)
  pos += 4

  //16... Data: Data which are written in the ADS device.
  for(let i = 0; i < variablesArray.length; i++){
    
    data.writeUInt32LE(variablesArray[i].indexGroup, pos)
    pos += 4

    data.writeUInt32LE(variablesArray[i].indexOffset, pos)
    pos += 4

    data.writeUInt32LE(variablesArray[i].byteSize, pos)
    pos += 4
  }

  
  _sendAdsCommand.call(this, ADS.ADS_COMMAND.ReadWrite, data)
    .then((res) => {
      debug(`readRaw(): Data read - ${res.ads.data.byteLength} bytes from ${JSON.stringify({ variablesArray })}`)
      
      resolve(res.ads.data)

    })
    .catch((res) => {
      debug(`readRaw(): Reading data from ${JSON.stringify({ variablesArray })} failed: %o`, res)
      reject(new ClientException('readRaw()', `Reading data failed`, res))
    })
})

}

`

And for example testing with 2 INT:

`
client.readRawMulti([{indexGroup: 16448,indexOffset: 387930, byteSize: 2}, {indexGroup: 16448,indexOffset: 400092, byteSize: 2}])
.then(res => {

  var myValues = [];   
  var pos = 0;  

  // Check ads errors
  var checkErrors = [];


  for (var i = 0; i<2; i++) {
    checkErrors.push(res.readUInt32LE(pos))
    pos += 4
  }


  // Reading 2 INT
  for (var i = 0; i<2; i++){
    myValues.push(res.readInt16LE(pos))
    pos += 2
  }
  
  console.log('Errors : ' + checkErrors  )
  console.log('Values : ' + myValues)

})
.catch(err => {
  console.log('Something failed:', err)
})`

Library for Node-RED?

Hi there,

We are searching an ADS Library for our company and i have found your Project.
It looks very promising!

We are working with Node-RED.
Are you thinking of writing a Node-RED library for this?
It would take 2 nodes:

  • Input (read from PLC)
  • Ouput (write to PLC)

I think it would make sense so that it can be better supported than if we were to do this ourselves.

Are you interested?

I know it's not a bug, but there might still be some general interest ...? ;-)

How to read/write bit?

How can I read/write bit value using index group/offset, hence readRaw/writeRaw?

// Thanks for the awesome library, it's precious!

ads-client in a Docker

Hi

I have a problem when running Node-RED in a docker.
Communication then only works if I run the container with --network host . In other words, the entire host network into the container route.
https://docs.docker.com/network/network-tutorial-host/
Since this isn't the goal of Docker, I'm wondering what ports I need to route for the configuration to work.

Raspberry Pi 4 with docker container
CX9020 TwinCAT 2.11 Runtime

I have already tried the following ports:

  • 48898/tcp + 48898/udp (localTcpPort)
  • 48899/tcp + 48899/udp (found on Beckhoff site)
  • 801/tcp + 801/udp (TwinCAT 2.11 Runtime)
  • 32767/tcp + 32767/udp (localAdsPort)

image

Throwing errors if not connected

At the moment if trying to do anything without connecting first, an ugly error is returned (like ClientException: Cannot read property 'split' of null (Cannot read property 'split' of null)).

Adding an custom exception like Client is not connected.

Add support for multi-dimensional arrays

At the moment multi-dimensional arrays are not handled correctly. Datatype information includes correctly parsed multi-dimensional array data, but it's not parsed to Javascript object correctly.

Example:
ARRAY [0..9,1..5] OF STRING(80)
Type:

{ name: '',
  type: 'STRING(80)',
  size: 81,
  offset: 0,
  adsDataType: 30,
  adsDataTypeStr: 'ADST_STRING',
  comment: '',
  attributes: [],
  arrayData:
   [ { startIndex: 0, length: 10 }, { startIndex: 1, length: 5 } ],

Result as javascript:
[ '', '', '', '', '', '', '', '', '', '' ]

Reading symbol info failed [TC3]

Hey, first of all thank you for this great looking project!

I have a problem getting the example to run. While I can connect to my local target (after enabling the TCP loopback) I cannot seem to read any variables. I always get:
Something failed: ClientException: Reading symbol PLC1.GLOBALS.FAKE_SUB_SYSTEM_COUNTER_MAX failed: Reading symbol info failed

The symbols are mapped when I check them through the HMI Configuration Window:
image

Is there another step necessary to expose them somehow to the ADS interface?

TwinCAT 2 and TwinCAT 3 (4020 and lower) support

Starting from TwinCAT 3.1.4022, the base data types like INT, REAL etc. are available from ADS API.

Now when the library is parsing data, it queries the data types from the PLC and PLC returns data type info. We don't need to know base data type info ourselves. This is how this library works.

TwinCAT 2 and TwinCAT 3 (< 4020) do not have this information. When the library asks for REAL data type, the PLC responds that symbol was not found. We need to manually have base data type info ourselves. This seems to be quite easy task as the library already has base data types listed. Basically if a base data type info is not found from PLC, we use our own data.

After this issue is closed, the library should support all TwinCAT versions since 2.11. Just tested first prototype with 2.11 and seems to work fine.

NOTE: TwinCAT 3.1.4020 support means that the PLC project is compiled with compiler version 4020 or lower. If runtime is 4024 but compiler is 4020, it's not working.

Adding "how to read data behing pointers" to README

It's possible to read data behing PLC pointer variables. Adding examples to the README.

Simple example.

//Reading data behind variable "ptr : POINTER TO ARRAY[1..10] OF INT"
data = await client.convertFromRaw(await client.readRawByName('PRG_Test.ptr^'), 'ARRAY [1..10] OF INT')
console.log('Data behind pointer:', data)

Result:

Data behind pointer: [
  1, 2, 3, 4, 5,
  0, 0, 0, 0, 0
]

Running 1.12.0 in an electron environment

Hey there,

we just tried to include this in an electron environment but it seems one of your dependencies has an issue at the referenced version, see here: ashtuchkin/iconv-lite#204

Could you please check, if you can update this package to a version >= 0.6.0?

Thanks a lot!
Best regards, Andy

Array information startIndex is unsigned when it should be signed

If having an array for example like this
array2D : ARRAY[-5..5, 1..100] OF REAL

Reading the data type will show the startIndex property wrong:

arrayData: [
    { startIndex: 4294967291, length: 11 },
    { startIndex: 1, length: 100 }
  ],

It should be:
arrayData: [ { startIndex: -5, length: 11 }, { startIndex: 1, length: 100 } ],

Reason:
startIndex = data.readUInt32LE(pos) should be startIndex = data.readInt32LE(pos) everywhere.

Won't cause problems as it's not used in ads-client. However someone using the library might have issues.

Uncaught Exception

I was trying to connect to a plc from a machine with TwinCat v3.1.4022.22, using the node-red wrapper (v1.10.4).
For a still unknown reason the local router seems to drop the connection with "ECONNRESET".

24 May 23:07:08 - [info] [ads-client-connection:287b52f4.ada39e] Connecting to 5.66.134.244.1.1:851...
  ads-client connect(): Starting to connect localhost:48898 +0ms
24 May 23:07:08 - [info] Started flows
  ads-client connect(): Socket connection established to localhost:48898 +7ms
  ads-client:details _registerAdsPort(): Registering an ADS port from ADS router localhost:48898 +0ms
  ads-client:raw-data IO out ------> 8 bytes : 0010020000000000 +0ms
  ads-client connect(): Socket connect failed: Error: read ECONNRESET
  ads-client     at TCP.onStreamRead (internal/stream_base_commons.js:209:20) {
  ads-client   errno: -4077,
  ads-client   code: 'ECONNRESET',
  ads-client   syscall: 'read'
  ads-client } +1ms
24 May 23:07:08 - [error] [ads-client-connection:287b52f4.ada39e] Connecting to 5.66.134.244.1.1:851 failed - Connection to localhost:48898 failed (socket error -4077) (read ECONNRESET)
24 May 23:07:08 - [info] [ads-client-connection:287b52f4.ada39e] Failed to connect 5.66.134.244.1.1:851 at startup

After playing around and setting the local AmsNetId and ADS port manually, I started to have Uncaught Exception. For node-red these are fatal and only way to recover is to run it in safe-mode (nodes not running at startup) and undo the manual settings.

24 May 22:47:39 - [info] [ads-client-connection:287b52f4.ada39e] Connecting to 5.66.134.244.1.1:851...
  ads-client connect(): Starting to connect localhost:48898 +0ms
24 May 22:47:39 - [info] Started flows
  ads-client connect(): Socket connection established to localhost:48898 +6ms
  ads-client:details _registerAdsPort(): Registering an ADS port from ADS router localhost:48898 +0ms
  ads-client _registerAdsPort(): Local AmsNetId and ADS port manually given so using 192.168.14.148.1.4:841 +0ms
  ads-client connect(): ADS port registered from router. We are 192.168.14.148.1.4:841 +1ms
  ads-client:details readSystemManagerState(): Reading device system manager state +0ms
  ads-client:details _sendAdsCommand(): Sending an ads command ReadState (0 bytes): { amsTcp: { command: 0, commandStr: 'AMS_TCP_PORT_AMS_CMD' }, ams: { targetAmsNetId: '5.66.134.244.1.1', targetAdsPort: 10000, sourceAmsNetId: '192.168.14.148.1.4', sourceAdsPort: 841, adsCommand: 4, adsCommandStr: 'ReadState', stateFlags: 4, stateFlagsStr: 'AdsCommand, Tcp, Request', dataLength: 0, errorCode: 0, invokeId: 0 }, ads: { rawData: <Buffer > } } +1ms
  ads-client:details _createAmsHeader(): AMS header created (32 bytes) +3ms
  ads-client:raw-data _createAmsHeader(): AMS header created: '054286f401011027c0a80e940104490304000400000000000000000000000000' +0ms
  ads-client:details _createAmsTcpHeader(): AMS/TCP header created (6 bytes) +2ms
  ads-client:raw-data _createAmsTcpHeader(): AMS/TCP header created: '000020000000' +1ms
  ads-client:details _createAmsTcpRequest(): AMS/TCP request created (38 bytes) +1ms
  ads-client:raw-data IO out ------> 38 bytes : 000020000000054286f401011027c0a80e940104490304000400000000000000000000000000 +2ms
24 May 22:47:39 - [red] Uncaught Exception:
24 May 22:47:39 - Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:209:20)

Digging into the code I found:

        //Remove the socket events that were used only during connect()
        socket.removeAllListeners('error')
        socket.removeAllListeners('close')
        socket.removeAllListeners('end')

When disabling the socket.removeAllListeners('error') line, the error get caught and the socket gets destroyed. Here we get a new Uncaught Exception:

  ads-client:details _sendAdsCommand(): Sending an ads command ReadState (0 bytes): { amsTcp: { command: 0, commandStr: 'AMS_TCP_PORT_AMS_CMD' }, ams: { targetAmsNetId: '5.66.134.244.1.1', targetAdsPort: 10000, sourceAmsNetId: '192.168.14.148.1.4', sourceAdsPort: 841, adsCommand: 4, adsCommandStr: 'ReadState', stateFlags: 4, stateFlagsStr: 'AdsCommand, Tcp, Request', dataLength: 0, errorCode: 0, invokeId: 0 }, ads: { rawData: <Buffer > } } +2ms
  ads-client:details _createAmsHeader(): AMS header created (32 bytes) +2ms
  ads-client:raw-data _createAmsHeader(): AMS header created: '054286f401011027c0a80e940104490304000400000000000000000000000000' +0ms
  ads-client:details _createAmsTcpHeader(): AMS/TCP header created (6 bytes) +3ms
  ads-client:raw-data _createAmsTcpHeader(): AMS/TCP header created: '000020000000' +3ms
  ads-client:details _createAmsTcpRequest(): AMS/TCP request created (38 bytes) +3ms
  ads-client:raw-data IO out ------> 38 bytes : 000020000000054286f401011027c0a80e940104490304000400000000000000000000000000 +2ms
  ads-client:details but what about here +1ms
  ads-client connect(): Socket connect failed: Error: read ECONNRESET
  ads-client     at TCP.onStreamRead (internal/stream_base_commons.js:209:20) {
  ads-client   errno: -4077,
  ads-client   code: 'ECONNRESET',
  ads-client   syscall: 'read'
  ads-client } +13ms
24 May 22:51:56 - [error] [ads-client-connection:287b52f4.ada39e] Connecting to 5.66.134.244.1.1:851 failed - Connection to localhost:48898 failed (socket error -4077) (read ECONNRESET)
24 May 22:51:56 - [info] [ads-client-connection:287b52f4.ada39e] Failed to connect 5.66.134.244.1.1:851 at startup
  ads-client:details _sendAdsCommand(): Timeout for command ReadState with invokeId 0 - No response +2s
  ads-client readSystemManagerState(): Device system manager state read failed +2s
  ads-client disconnect(): Starting to close connection (force: false) +1ms
  ads-client unsubscribeAll(): Unsubscribing from all notifications +1ms
  ads-client unsubscribeAll(): Unsubscribed from 0 notifications +1ms
  ads-client _unsubscribeAllInternals(): Unsubscribing from all notifications +0ms
  ads-client _unsubscribeAllInternals(): Unsubscribed from 0 notifications +0ms
  ads-client:details _unregisterAdsPort(): Unregister ads port 841 from localhost:48898 +5ms
  ads-client _unregisterAdsPort(): Local AmsNetId and ADS port manually given so no need to unregister +1ms
  ads-client disconnect(): Connection closed successfully +2ms
  ads-client:details _unregisterAdsPort(): Socket closed +2ms
24 May 22:51:58 - [red] Uncaught Exception:
24 May 22:51:58 - TypeError: Cannot read property 'destroy' of null
    at C:\Users\Documents\node-red-contrib-ads-client\node_modules\ads-client\src\ads-client.js:3563:32
    at processTicksAndRejections (internal/process/task_queues.js:79:21)

In the end there are three issues:

  • Uncaught Exception in ads-client. Why are the error/close/end listeners removed?
  • Uncaught Exception in node-red during destroy.
  • Unknown issue on my machine that cases all this. I tried on another machine with a more recent TwinCat version and didn't had an issues connecting.

Reconnection updates

At the moment the ads-client has problems reconneting to remote system if the PLC is restarted (clicking green "start/restart" button). As the system manager state is read every 1 seconds or so, it can't catch that event and subscriptions won't get reinitialized.

Perhaps there are some better ways?

Other scenarios work fine, for example run->config->run, ethernet cable disconnect and so on.

Performance issue of parsing code

Hi,

I'm currently evaluating your project for usage in the software of my company. Impressive work! Your library is well documented.

While using it I came to a performance issue when subscribing to an array of 100 LREAL values. After some time the subscription gets lost because the mailbox of the TwinCAT router gets overfilled. I've profiled the code and have seen that there is a very time consuming operation running everytime data is send or received via ADS which you'll find in the following code block:

let type = this.types.find(type => {
const re = new RegExp('^(' + type.name.join('|') + ')([\\[\\(](.*)[\\)\\]])*$', 'i')
//Using match instead of test so we get capture groups too
regExpRes = name.trim().match(re)
return (regExpRes != null)
})

It seems that you look for the type of the read or written symbol everytime an ADS request is made or received to be able to parse it. I've seen approaches where this task was solved in a less time consuming manner. Is there a reason you've explicitly chose this way?

Best,

Martin

Connect to targetAmsNetId on OSX Catalina fails

Hello there,

first of all – thank you for that awesome library.

I'm using it to talk a Beckhoff PLC and it works like a charm. On one of my computers I run into a problem. Even the minimal example would not connect:

const ads = require('ads-client')

const client = new ads.Client({
  targetAmsNetId: '5.85.243.162.1.1',
  targetAdsPort: 851,
})

client.connect()
  .then(res => {   
    console.log(`Connected to the ${res.targetAmsNetId}`)
    console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`)

    return client.disconnect()
  })
  .then(() => {
    console.log('Disconnected')
  })
  .catch(err => {
    console.log('Something failed:', err)
  })

Results in this error message:
Something failed: ClientException: Connection to localhost:48898 failed (socket error -61) (connect ECONNREFUSED 127.0.0.1:48898)

(OSX Catalina, internal Firewall is turned OFF, no virus scanners etc.)

I wonder, why this tries to connect to localhost instead of the NetId. Is this expected behaviour? On the other machine (Windows) it connects just fine, printing:

Connected to the 5.85.243.162.1.1
PLC Router assigned us AmsNetId 169.254.105.221.1.1 and port 40334

Thanks in advance
Hans

change twincat state

Hi,

first of all, sorry that im making an issue for this, i didn't know any other way to contact.
second of all, very impressive work you've done here, im using this npm package and s working great for me.

last of all, just a question because i cant find any documentation on it. I see the Twoncat state can be read, but not written. Is this possible at all?

to be more specific, i would like to make a "software-upgrade" trough a webinterface, but for that i need to set the state from RUN to CONFIG

kind regards

Maxime

Read Attributes of Data readAndCacheSymbols() Method

Good day

I have been trying to work out if this is a limitation of ADS when it comes to Symbols located directly in Program file types, or is the data simply not fetched. When receiving data with the .readAndCacheSymbols() method, the data contains the 'Attributes' entry in flagsStr, but the attributes field itself seems to be missing. By comparison subItems which are gathered by get data types in structured value, has all the attributes in { key : string, value : string } format.

Is that data unavailable through ADS by any chance?

ReadWriteRaw

Adding a method that allows raw usage of ADS command ReadWrite.

Getting Remote Routes via ADS

Hi this is no real "issue" more a "question". I am working on a windows environment. The local system runs a twincat service. There are several remote routes managed on this specific machine. Is there any way to get the remote Routes (AmsNetIds) from this machine with an ADS command? I couldn't realy find any documentation regarding the Ports and Index Group combinations.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.