Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AmsRouter does not permit renewing a broken TCP connection #197

Open
FloCD opened this issue Apr 4, 2023 · 2 comments
Open

AmsRouter does not permit renewing a broken TCP connection #197

FloCD opened this issue Apr 4, 2023 · 2 comments

Comments

@FloCD
Copy link

FloCD commented Apr 4, 2023

Hello,

i am currently testing the ADS library, especially reconnection behaviour and behaviour with more than one connection.

What works so far (in synchronous fashion):

  • Connecting and reconnecting one connection works
  • connecting and reconnecting two connections to two different targets works

Therefore I have (based on the example) written a small program which synchronously connects two connections to the same target. One Connection only connects initially, the second one reads values from the SPS and also tries to reconnect (when the SPS is going into config mode):

#include "AdsLib.h"
#include "AdsVariable.h"

#include <array>
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include <ctime>
#include <signal.h>


static bool readByNameExample(std::unique_ptr<AdsDevice>& route, const std::string& loggingPrefix)
{
    try {
        AdsVariable<uint8_t> readVar {*route, "MAIN.testVar"};
        std::cout << loggingPrefix << ": " << __FUNCTION__ << "():\n";
        std::cout << loggingPrefix << ": ADS read " << std::hex << (uint32_t)readVar << '\n';
    } catch (const AdsException& ex) {
        std::cout << loggingPrefix << ": Error: " << ex.errorCode << "\n";
        std::cout << loggingPrefix << ": AdsException message: " << ex.what() << "\n";
        
        return false;
    }
    return true;
}

static const AmsNetId remoteNetId_1 {<NETID1>};
static const char remoteIpV4_1[] = "<IPADDRESS1>";
//static const AmsNetId remoteNetId_2 {<NETID2};
//static const char remoteIpV4_2[] = "<IPADDRESS2>";

static void Connect(std::unique_ptr<AdsDevice>& route, const std::string& remoteIpV4, const AmsNetId& remoteNetId, const std::string& loggingPrefix)
{
    while(true){
        std::cout << loggingPrefix << ": Connecting ..." << "\n";
        bool connected{true};
    	try {
            route = std::make_unique<AdsDevice>(remoteIpV4, remoteNetId, 899);
        } catch (const AdsException& ex) {
            std::cout << loggingPrefix << ": Error: " << ex.errorCode << "\n";
            std::cout << loggingPrefix << ": AdsException message: " << ex.what() << "\n";
            connected = false;
        } catch (const std::runtime_error& ex) {
                std::cout << loggingPrefix << ": " << ex.what() << '\n';
                connected = false;
        }
	
        if(connected){
            break;
        }
    }
    std::cout << loggingPrefix << ": Connected!" << "\n";
}

static void Disconnect(std::unique_ptr<AdsDevice>& route, const std::string& loggingPrefix)
{
    route.reset();
    std::cout <<  loggingPrefix << ": disconnected!" << '\n';
}

enum class CONN_STATE
{
    DISCONNECTED,
    CONNECTED,
    READING,
    RECONNECT_EXPECTED
};

static CONN_STATE stateRoute1{CONN_STATE::DISCONNECTED};
static CONN_STATE stateRoute2{CONN_STATE::DISCONNECTED};

static void ConnectionStateMachine(CONN_STATE& connectionState, 
                                   std::unique_ptr<AdsDevice>& route, 
                                   const std::string& remoteIpV4, 
                                   const AmsNetId& remoteNetId,
                                   const std::string& loggingPrefix)
{
    switch(connectionState)
    {
        case CONN_STATE::DISCONNECTED:
            Connect(route, remoteIpV4, remoteNetId, loggingPrefix);
            connectionState = CONN_STATE::CONNECTED;
            break;
        case CONN_STATE::CONNECTED:
            connectionState = CONN_STATE::READING;
            break;
        case CONN_STATE::READING:
            if(!readByNameExample(route, loggingPrefix))
            {
                connectionState = CONN_STATE::RECONNECT_EXPECTED;
                break;
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
            break;
        case CONN_STATE::RECONNECT_EXPECTED:
            Disconnect(route, loggingPrefix);
            std::this_thread::sleep_for(std::chrono::seconds(10));
            connectionState = CONN_STATE::DISCONNECTED;
            break;
        default:
            break;
    }
} 


static void runExample()
{
    signal(SIGPIPE, SIG_IGN);

    // uncomment and adjust if automatic AmsNetId deduction is not working as expected
    bhf::ads::SetLocalAddress({<LOCAL_NETID>});
    
    std::unique_ptr<AdsDevice> route1;
    std::unique_ptr<AdsDevice> route2;

    ConnectionStateMachine(stateRoute1, route1, remoteIpV4_1, remoteNetId_1, "connection 1");
    while(true)
    {
        ConnectionStateMachine(stateRoute2, route2, remoteIpV4_1, remoteNetId_1, "connection 2");
    }
}

int main()
{
    runExample();
    std::cout << "Hit ENTER to continue\n";
    //std::cin.ignore();
}

In the output the following happens:

connection 1: Connecting ...
connection 1: Connected!
connection 2: Connecting ...
connection 2: Connected!
connection 2: readByNameExample():
connection 2: ADS read 41
connection 2: readByNameExample():
connection 2: ADS read a5
connection 2: readByNameExample():
connection 2: ADS read a
connection 2: readByNameExample():
connection 2: ADS read 6e
connection 2: readByNameExample():
connection 2: ADS read d3
connection 2: readByNameExample():
connection 2: ADS read 38
connection 2: readByNameExample():
connection 2: ADS read 9d
connection 2: readByNameExample():
connection 2: ADS read 1
connection 2: readByNameExample():
connection 2: ADS read 66
connection 2: readByNameExample():
connection 2: ADS read ca
connection 2: readByNameExample():
connection 2: ADS read 2f
connection 2: readByNameExample():
connection 2: ADS read 93
2023-04-04T13:33:53+0200 Info: connection closed by remote
connection 2: Error: 745
connection 2: AdsException message: Ads operation failed with error code 1861.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
connection 2: Error: 745
connection 2: AdsException message: Ads operation failed with error code 1861.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:34:21+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:34:31+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:34:41+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:34:51+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:35:01+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:35:11+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:35:21+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!
connection 2: Connecting ...
connection 2: Connected!
2023-04-04T13:35:31+0200 Error: write frame failed with error: Broken pipe
connection 2: Error: ffffffff
connection 2: AdsException message: Ads operation failed with error code 4294967295.
connection 2: disconnected!

My guess is that the AmsRouter still holds the connection opened by one of the connections and therefore does not permit a clean disconnect (see function AmsRouter::DeleteIfLastConnection() ).

If there is the need for more information I am happy to help!

All the best

Florian

@pbruenn
Copy link
Member

pbruenn commented Apr 4, 2023

2023-04-04T13:33:53+0200 Info: connection closed by remote means the remote target has closed the TCP connection. Now, all your AdsDevice instances (routes) are invalid. You would have to delete all of them and add them back, to force AmsRouter to reestablish the TCP connection.
The AmsRouter class is no real ADS router like you find in TwinCAT. It is more an AMS -> TCP translator. Without any connection handling.

Your problem is similar to #46 #190

@FloCD
Copy link
Author

FloCD commented Apr 4, 2023

Yes I also think that this would be a possible way to resolve that issue. Are there any plans to enhance the library regarding this scenario?
PS: I doubt that issue #190 is similar to mine, because in my scenario I connect to a different target, not localhost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants