Skip to content
Snippets Groups Projects
Commit eb767412 authored by al.schwinn's avatar al.schwinn
Browse files

#33 - Refactor isTimeToReconnect

parent 90e51e6b
No related branches found
No related tags found
No related merge requests found
...@@ -27,7 +27,6 @@ namespace Silecs ...@@ -27,7 +27,6 @@ namespace Silecs
// static definition // static definition
bool Connection::isAlive_ = false; bool Connection::isAlive_ = false;
const unsigned int Connection::numberConn_ = 3;
//------------------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------------------
Connection::Connection(PLC* thePLC) Connection::Connection(PLC* thePLC)
...@@ -42,18 +41,14 @@ Connection::Connection(PLC* thePLC) ...@@ -42,18 +41,14 @@ Connection::Connection(PLC* thePLC)
isConnected_ = false; isConnected_ = false;
//Reset the Reconnection mechanism //Reset the Reconnection mechanism
timeConn_ = NULL; lastReconnectionAttempt_ = 0;
delayConn_ = UrgentConnection; //initial reconnection delay reconnectDelay_ = shortDelay; //initial reconnection delay
remainConn_ = numberConn_; //initial number of attempt reconnectAttempts_ = 0;
} }
//------------------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------------------
Connection::~Connection() Connection::~Connection()
{ {
//Connection has been closed before from the concrete connection object
if (timeConn_ != NULL)
delete timeConn_;
delete connMux_; delete connMux_;
delete writeMux_; delete writeMux_;
delete readMux_; delete readMux_;
...@@ -94,11 +89,6 @@ void Connection::disable(PLC* thePLC) ...@@ -94,11 +89,6 @@ void Connection::disable(PLC* thePLC)
//------------------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------------------
bool Connection::doOpen(PLC* thePLC) bool Connection::doOpen(PLC* thePLC)
{ {
//LOG((COMM|DIAG)) << "Start attempt to open connection ..."; .. commented, seem like this creates to many log-entries for KIBANA
bool isReachable = false;
bool isOpen = false;
bool justConnected = false;
{ {
Lock lock(connMux_); Lock lock(connMux_);
if (isConnected_) if (isConnected_)
...@@ -117,75 +107,74 @@ bool Connection::doOpen(PLC* thePLC) ...@@ -117,75 +107,74 @@ bool Connection::doOpen(PLC* thePLC)
return isConnected_; return isConnected_;
} }
if (isTimeToReconnect()) if (!isTimeToReconnect())
{ // Do nothing, just wait a bit to dont pullute the log
return isConnected_;
}
if (ping(thePLC->getName().c_str(), NULL))
{ {
isReachable = (ping((char *)thePLC->getName().c_str(), NULL) == 0); logError(thePLC, false);
if (isReachable) return isConnected_;
{ }
LOG((COMM|DIAG)) << "It's time to reconnect";
// It's time to open the connection according to the (re)connection timing
// Let's try several times with limited delay (some ms).
// It allows wake-up frozen PLC (SIEMENS in particular) after long stop period.
bool isOpen = false;
unsigned int nbConn = 2; //for fast low-level iteration
for (unsigned int i = 0; i < nbConn; i++)
{
LOG((COMM|DIAG)) << "Attempt to open PLC connection ....";
isOpen = open(thePLC);
if (isOpen)
{
LOG((COMM|DIAG)) << "Connection opened successfully";
break;
}
usleep(100000); // wait 100ms
}
} //pingable?
if (!isOpen)
{
logError(thePLC, isReachable);
return isConnected_;
}
if (thePLC->isSharedConnection()) // It's time to open the connection according to the (re)connection timing
{ // Let's try several times with limited delay (some ms).
std::ostringstream os; // It allows wake-up frozen PLC (SIEMENS in particular) after long stop period.
os << "Shared connection with " << thePLC->getName() << " is established."; bool isOpen = false;
if (thePLC->theHeader_ != NULL) unsigned int nbConn = 2; //for fast low-level iteration
TRACE("info") << os.str(); for (unsigned int i = 0; i < nbConn; i++)
LOG(COMM) << os.str(); {
} LOG((COMM|DIAG)) << "Attempt to open PLC connection ....";
else isOpen = open(thePLC);
if (isOpen)
{ {
std::ostringstream os; //reset reconnection settings
os << "Connection with " << thePLC->getName() << ":" << thePLC->theCluster_->getClassName() << "/v" << thePLC->theCluster_->getClassVersion() << " is established."; reconnectDelay_ = shortDelay;
if (thePLC->theHeader_ != NULL) reconnectAttempts_ = 0;
TRACE("info") << os.str();
LOG(COMM) << os.str(); isAlive_ = true;
isConnected_ = true;
LOG((COMM|DIAG)) << "Connection opened successfully";
break;
} }
usleep(100000); // wait 100ms
}
isAlive_ = true; if (!isOpen)
isConnected_ = true; {
justConnected = true; //retentive registers synchronization is required! logError(thePLC, true);
return isConnected_;
//Connection status has changed: update the diagnostic variables }
LOG((COMM|DIAG)) << "Updating PLC status";
updateStatus(thePLC);
if (thePLC->isSharedConnection())
{
std::ostringstream os;
os << "Shared connection with " << thePLC->getName() << " is established.";
if (thePLC->theHeader_ != NULL)
TRACE("info") << os.str();
LOG(COMM) << os.str();
}
else
{
std::ostringstream os;
os << "Connection with " << thePLC->getName() << ":" << thePLC->theCluster_->getClassName() << "/v" << thePLC->theCluster_->getClassVersion() << " is established.";
if (thePLC->theHeader_ != NULL)
TRACE("info") << os.str();
LOG(COMM) << os.str();
} }
//Connection status has changed: update the diagnostic variables
LOG((COMM|DIAG)) << "Updating PLC status";
updateStatus(thePLC);
} //release lock } //release lock
/* Process the Retentive registers synchronization each time the PLC is just (re)connected. /* Process the Retentive registers synchronization each time the PLC is just (re)connected.
* This is a recursive call: performs task::execute method from doOpen that is * This is a recursive call: performs task::execute method from doOpen that is
* called into execute itself. The recursion is terminated when SilecsHeader connection is closed finally. * called into execute itself. The recursion is terminated when SilecsHeader connection is closed finally.
*/ */
if (justConnected) LOG((COMM|DIAG)) << "First connection - performing registers synchronization";
{ thePLC->updateLocalData();
LOG((COMM|DIAG)) << "First connection - performing registers synchronization";
thePLC->updateLocalData();
}
LOG((COMM|DIAG)) << "isConnected_:" << isConnected_;
return isConnected_; return isConnected_;
} }
...@@ -304,13 +293,6 @@ void Connection::updateStatus(PLC* thePLC) ...@@ -304,13 +293,6 @@ void Connection::updateStatus(PLC* thePLC)
//Connection status has changed (opened/closed) //Connection status has changed (opened/closed)
//Update the PLC diagnostic variables //Update the PLC diagnostic variables
thePLC->updateStatus(); thePLC->updateStatus();
//Reset reconnection mecanisme in case of connection succeed
if (isConnected_)
{
delayConn_ = UrgentConnection; //initial reconnection delay
remainConn_ = numberConn_; //initial number of attempt
}
} }
//------------------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -318,16 +300,15 @@ void Connection::logError(PLC* thePLC, bool isReachable) ...@@ -318,16 +300,15 @@ void Connection::logError(PLC* thePLC, bool isReachable)
{ {
std::string errorMsg = isReachable ? "Connection with " + thePLC->getName() + ":" + thePLC->theCluster_->getClassName() + "/v" + thePLC->theCluster_->getClassVersion() + " has failed.\n" : "Controller " + thePLC->getName() + " does not respond to ping, might be OFF!\n"; std::string errorMsg = isReachable ? "Connection with " + thePLC->getName() + ":" + thePLC->theCluster_->getClassName() + "/v" + thePLC->theCluster_->getClassVersion() + " has failed.\n" : "Controller " + thePLC->getName() + " does not respond to ping, might be OFF!\n";
if (delayConn_ == LongTermConnection) if (reconnectDelay_ == longDelay)
{ {
if (remainConn_ > 0) if (reconnectAttempts_ < MAX_CONNECTION_ATTEMPTS_PER_DELAY)
{ {
std::ostringstream os; std::ostringstream os;
os << errorMsg << "Periodic attempt to reconnect, delay " << delayConn_ << "s (tracing off)."; os << errorMsg << "Periodic attempt to reconnect, delay " << RECONNECTION_DELAYS[reconnectDelay_] << " seconds (tracing off).";
if (thePLC->theHeader_ != NULL) if (thePLC->theHeader_ != NULL)
TRACE("error") << os.str(); TRACE("error") << os.str();
LOG(COMM) << os.str(); LOG(COMM) << os.str();
remainConn_ = 1; //Try to reconnect again and again (=1 means disable tracing).
} }
/*else /*else
PLC does not respond anymore. It's probably stopped for a long time. PLC does not respond anymore. It's probably stopped for a long time.
...@@ -337,7 +318,7 @@ void Connection::logError(PLC* thePLC, bool isReachable) ...@@ -337,7 +318,7 @@ void Connection::logError(PLC* thePLC, bool isReachable)
else else
{ {
std::ostringstream os; std::ostringstream os;
os << errorMsg << "Next attempt to reconnect in " << delayConn_ << "s if requested. Remains: " << remainConn_; os << errorMsg << "Next attempt to reconnect in " << RECONNECTION_DELAYS[reconnectDelay_] << " seconds.";
if (thePLC->theHeader_ != NULL) if (thePLC->theHeader_ != NULL)
TRACE("error") << os.str(); TRACE("error") << os.str();
LOG(COMM) << os.str(); LOG(COMM) << os.str();
...@@ -347,39 +328,25 @@ void Connection::logError(PLC* thePLC, bool isReachable) ...@@ -347,39 +328,25 @@ void Connection::logError(PLC* thePLC, bool isReachable)
//------------------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------------------
bool Connection::isTimeToReconnect() bool Connection::isTimeToReconnect()
{ {
bool toBeReconnected = false; time_t now;
if (timeConn_ != NULL) time(&now);
{ //how many time from the last connect attempt double secondsElapsed = difftime(now, lastReconnectionAttempt_);
double delay = timeConn_->getDelay(S_UNIT);
if (delay >= double(delayConn_))
{
timeConn_->getValue(S_UNIT); //restart delay counting from now
toBeReconnected = true;
if (remainConn_ > 0) if ( secondsElapsed < RECONNECTION_DELAYS[reconnectDelay_])
--remainConn_; return false;
if (remainConn_ == 0)
{ lastReconnectionAttempt_ = now;
if (delayConn_ != LongTermConnection) reconnectAttempts_ ++;
{
if (delayConn_ == UrgentConnection) if( reconnectAttempts_ < MAX_CONNECTION_ATTEMPTS_PER_DELAY)
delayConn_ = ShortTermConnection; return true;
else
//(delayConn_ == ShortTermConnection) if( reconnectDelay_ < longDelay)
delayConn_ = LongTermConnection; {
remainConn_ = numberConn_; reconnectAttempts_ = 0;
} reconnectDelay_ = static_cast<ReconnectionDelay>(reconnectDelay_+ 1);
} }
} return true;
}
else
{
//This is the first connection attempt, just start the Time counter.
timeConn_ = new TsCounter(true); //using hardware clock
timeConn_->getValue(S_UNIT); //start delay counting from now
toBeReconnected = true;
}
return toBeReconnected;
} }
/* This macro is used to trap the unexpected broken pipe and /* This macro is used to trap the unexpected broken pipe and
...@@ -498,7 +465,7 @@ int rfcPing(char *ip, long ts) ...@@ -498,7 +465,7 @@ int rfcPing(char *ip, long ts)
} }
/*..........................................................*/ /*..........................................................*/
int Connection::ping(char *hostName, char *plcIP) int Connection::ping(const char *hostName, char *plcIP)
{ {
struct in_addr addr; struct in_addr addr;
struct hostent *hp; struct hostent *hp;
......
...@@ -24,13 +24,18 @@ namespace Silecs ...@@ -24,13 +24,18 @@ namespace Silecs
{ {
class PLC; class PLC;
// Time to wait (second) before next reconnection attempt
typedef enum typedef enum
{ {
UrgentConnection = 2, shortDelay = 0,
ShortTermConnection = 20, mediumDelay = 1,
LongTermConnection = 60 longDelay = 2
} ConnectionDelay; } ReconnectionDelay;
// Time to wait (second) before next reconnection attempt
const static double RECONNECTION_DELAYS[3] = {2, 20, 60};
// Maximum muber of connection attempts before using longer delay
const static unsigned int MAX_CONNECTION_ATTEMPTS_PER_DELAY = 3;
#ifdef __x86_64__ #ifdef __x86_64__
typedef long ChannelID; //pointer is 64bits word typedef long ChannelID; //pointer is 64bits word
...@@ -138,11 +143,9 @@ protected: ...@@ -138,11 +143,9 @@ protected:
Mutex* writeMux_; //Mutex used to protect the PLC write-channel resource Mutex* writeMux_; //Mutex used to protect the PLC write-channel resource
Mutex* connMux_; //Mutex used to protect the global PLC connection resource (for open/close, etc.) Mutex* connMux_; //Mutex used to protect the global PLC connection resource (for open/close, etc.)
// Time counter to manage the automatic reconnection time_t lastReconnectionAttempt_;
TsCounter* timeConn_; ReconnectionDelay reconnectDelay_; // time to wait before invoking reconnect (Seconds)
ConnectionDelay delayConn_; //current delay between two connection unsigned int reconnectAttempts_; // number of reconnection attempts ( will be cleared on successfull connect)
unsigned long remainConn_; //number of attempt before next slow-down
static const unsigned int numberConn_; //number of connection attempt for each connection delay
/* ping function is a function used to check /* ping function is a function used to check
* if PLC is ON (~ping) before trying to connect it. * if PLC is ON (~ping) before trying to connect it.
...@@ -161,7 +164,7 @@ protected: ...@@ -161,7 +164,7 @@ protected:
* intermediate connect_nonb() function is used to perform a non-blockant connection. * intermediate connect_nonb() function is used to perform a non-blockant connection.
* intermediate rfcPing() function is used to check if PLC is ON * intermediate rfcPing() function is used to check if PLC is ON
*/ */
static int ping(char *hostName, char *plcIP); static int ping(const char *hostName, char *plcIP);
// Communication Diagnostic & Monitoring // Communication Diagnostic & Monitoring
static bool isAlive_; // PLC has repliyed to the ping: true/false static bool isAlive_; // PLC has repliyed to the ping: true/false
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment