birkenfeld / ads-rs

Rust crate to access PLCs via the Beckhoff ADS protocol
https://crates.io/crates/ads
Apache License 2.0
40 stars 8 forks source link

TwinCat 2 Connection issue #19

Closed HectorBailey closed 1 year ago

HectorBailey commented 1 year ago

Hi, Firstly nice job on the lib, I have used it for TC3 in the past and it works very well!

However, I am trying to connect to a TC2 PLC with this rust lib, however I am having some issues. I am using TC2 v 2.11.0 Build number: 2126. running on 32bit windows

Code:

let client = ads::Client::new(("192.168.0.101", ads::PORT), ads::Timeouts::new(Duration::new(5,0)), 
                                  ads::Source::Addr(ads::AmsAddr::new([192, 168, 0, 101, 1, 1].into(), 801)))?;

// Specify the target ADS device to talk to, by NetID and AMS port.
// Port 851 usually refers to the first PLC instance.
let device = client.device(ads::AmsAddr::new([192, 168, 0, 101, 1, 1].into(), 801));

// Ensure that the PLC instance is running.
assert!(device.get_state()?.0 == ads::AdsState::Run);

Output:

Finished dev [unoptimized + debuginfo] target(s) in 5.87s
Running `target\i686-pc-windows-msvc\debug\compliance_bridge.exe`
Error: Io("receiving reply (route set?)", Kind(TimedOut))
error: process didn't exit successfully: `target\i686-pc-windows-msvc\debug\compliance_bridge.exe` (exit code: 1)

Not sure exactly what is going on here, as I have a route through to the PLC on route manager.

It is worth noting that if I try to do the same thing with python it seems to work using the pyads lib. So, I don't think it's an issue with the PLC or connection etc.

connection = pyads.Connection(ams_net_id='192.168.0.101.1.1', ams_net_port=801, ip_address='192.168.0.101')

connection.open()

print(connection.read_device_info()[1].version)

On another note, when trying to connect to the PLC from localhost, by running the exe on the CX (As that will be the end goal for the code to run on the PLC) I also get an error relating to requesting the Port from the router.

let client = ads::Client::new(("127.0.0.1", ads::PORT), ads::Timeouts::new(Duration::new(5,0)), ads::Source::Request).unwrap();

let device = client.device(ads::AmsAddr::new(ads::AmsNetId::local(), 801));

Let me know if I can help you to replicate the error or debug further, thanks in advance!

birkenfeld commented 1 year ago

Thanks for the report!

In the first Client::new, the Source looks suspect. It should not be the same NetID as the target.

As for the second one connecting to localhost, this uses a completely undocumented call into the AMS router, which I adapted from another free ADS library. It is possibly not compatible with TC2...

HectorBailey commented 1 year ago

Thank you for you're quick response!

Ah, sorry I was probably trying lots of combinations and messed up the source. I will have another go when I have access to the CX again, hopefully this Sunday or Monday and update this thread.

Okay, no worries! I was looking at connecting to local host without using the ads::Source::Request call to the AMS router, which looking at previous issues you said was possible - I am guessing I would use the same code snippet as I have done for a remote connection and create a route within the CX to the CX, would I still need a different source address? If so, what would that be?

Thank you again for your time!

birkenfeld commented 1 year ago

I don't know actually, I never tried connecting from a local PLC. You could try connecting via the external IP (192.168...) but not sure if it works in TC to have a route with the same source and destination...

HectorBailey commented 1 year ago

Hi,

🎉 I have managed to get it sorted! 🎉

So, with remote routes you were 100% right, I was being a bit stupid and the source was set wrong - needed to be set to my AMS net id - Doh! So that worked well.

Now, on to a local PLC connection, to do this you need to create a sub route on the PLC with a different AMS net ID, as you said it a route wont work with the same source and destination. - So, here is what my routes look like on the CX (You can ignore the "TC2" route that is just the route from my laptop to the CX) CX_Routes

So, once I had that setup I played with which I should use as source and destination, make sure to use the Sub route as your source and the main as your destination and it should work!

let client = ads::Client::new(("127.0.0.1", ads::PORT), ads::Timeouts::new(Duration::new(5,0)), 
                              ads::Source::Addr(ads::AmsAddr::new([192, 168, 2, 11, 1, 2].into(), 801)))?;

// Specify the target ADS device to talk to, by NetID and AMS port.
let device = client.device(ads::AmsAddr::new([192, 168, 2, 11, 1, 1].into(), 801));

// Ensure that the PLC instance is running.
assert!(device.get_state()?.0 == ads::AdsState::Run);

println!("{:?}", device.get_info());

Output:

Ok(DeviceInfo { name: "TCatPlcCtrl", major: 2, minor: 11, version: 2623 })

I hope this helps someone in the future with a similar issue!