jisotalo / ads-client

Unofficial Node.js ADS library for connecting to Beckhoff TwinCAT automation systems using ADS protocol.
https://jisotalo.fi/ads-client/
MIT License
77 stars 18 forks source link

Fails to connect multiple instances. #47

Closed jhenson29 closed 3 years ago

jhenson29 commented 3 years ago

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:

Local Dev

Target Server

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
jisotalo commented 3 years ago

Hi Jeremy!

Thanks for reporting this. I tried to check the issue quickly with Wireshard and TcAmsAdsViewer.

It seems that when the second TCP connection is initialized with the router, the router closes it immediately. I'm quite sure that is hardcoded in the AMS router to accept only single connection from single host (except localhost, like your computer). Similar issues have been discussed here and there, like https://github.com/Beckhoff/ADS/issues/49#issuecomment-330198577.

So that means in order to have two connections from raspberry pi to the TwinCAT, an AMS router is needed to run on the raspberry. That router then redirects all commands to the TwinCAT and back to the clients. For example, you could try https://www.nuget.org/packages/Beckhoff.TwinCAT.Ads.TcpRouter/ which is made just for this.

I have also had an idea of creating an router with node.js but haven't got inspiration yet.

Is there a specal reason you want two connections? Perhaps there is another way to do this.

jhenson29 commented 3 years ago

I tried installing the Beckhoff router this morning, but I was having issues with it. I was able to get .Net core installed and running, but I couldn't get the Beckhoff router to install.

The multiple connections is because I have an existing HMI backend that runs up to 4 separate servers with PLC connections for different tasks and each one maintains it's own PLC connection. I was working on extending it to include connections to Beckhoff processors by adding this as an implementation to my PLC services. For my other driver (EtherNet/IP), the separate connections on separate servers keeps one from causing another to hang. So, for example, if I need to do write, it's not waiting on a read from some other longer running task.

I'll look at the Beckhoff router again and see if I can get it working. If not, I'll probably just run one more server with the actual ads client on it and proxy the implementation in the other servers so that my multiple servers route everything through that one connection.

Thanks for the reply and thank you for writing this library. Other than that multiple connection issue, which doesn't appear to be your issue, it's working really well.

freefly42 commented 1 year ago

I know this is an old issue, but for anyone trying to solve this, here is a solution.

The reason connecting to multiple instances doesn't work is the PLC will only allow one connection from any single IP address. The work around is to add an aliased IP address pair (one on the PLC, the other on the client machine) on the same subnet as each other but on a different subnet for each of the PLC instances. You will also have to use a different client AMS NetID for each of the clients and add a separate route for each of the clients. Both clients will use the same AMS NetID for the PLC. For example:

PLC1: 172.16.0.100/24 10.0.0.100.1.1 (PLC) <--> 172.16.0.50/24 172.16.0.50.1.1 (client IP and AMS NetID)

PLC2: 172.16.1.100/24 10.0.0.0.100.1.1 (PLC) <--> 172.16.1.50/24 172.16.1.50.1.1 (client IP and AMS NetID)

Even through they are coming through the same physical Ethernet device, ADS will treat them like they are coming from different addresses and will allow the connections.

jisotalo commented 1 year ago

Thanks @freefly42! I will see if this should be added to the FAQ