From f7496b78532a767c1afea1c88ca0560fab67bbad Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 20 Oct 2024 01:23:17 +0200 Subject: [PATCH] add HEARTBEAT_CRITICAL to enforce heartbeat check --- WiThrottle.cpp | 50 +++++++++++++++++++++++++++++++++++++------------- WiThrottle.h | 13 +++++++++++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 11050b92c..eff4b5e0f 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -1,7 +1,7 @@ /* * © 2021 Neil McKechnie * © 2021 Mike S - * © 2020-2022 Harald Barth + * © 2020-2024 Harald Barth * © 2020-2021 M Steve Todd * © 2020-2021 Chris Harlow * All rights reserved. @@ -97,12 +97,18 @@ WiThrottle::WiThrottle( int wificlientid) { nextThrottle=firstThrottle; firstThrottle= this; clientid=wificlientid; +#ifdef HEARTBEAT_CRITICAL + heartBeatEnable=true; // do not run without heartbeat +#else heartBeatEnable=false; // until client turns it on +#endif mostRecentCab=0; for (int loco=0;lococlientid); if (firstThrottle== this) { firstThrottle=this->nextThrottle; @@ -140,7 +146,12 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { switch (cmd[0]) { case '*': // heartbeat control if (cmd[1]=='+') heartBeatEnable=true; - else if (cmd[1]=='-') heartBeatEnable=false; + else if (cmd[1]=='-') { +#ifdef HEARTBEAT_CRITICAL + eStop(); +#endif + heartBeatEnable=false; + } break; case 'P': if (cmd[1]=='P' && cmd[2]=='A' ) { //PPA power mode @@ -303,10 +314,14 @@ void WiThrottle::locoAction(RingStream * stream, byte* aval, char throttleChar, switch (aval[0]) { case 'V': // Vspeed { - int witSpeed=getInt(aval+1); + uint8_t dccSpeed = WiTToDCCSpeed(getInt(aval+1)); +#ifdef HEARTBEAT_CRITICAL + if (heartBeatEnable == false) // if there is no heartBeat, keep throttle at + dccSpeed = 1; // emegency stop (dccSpeed 1) +#endif LOOPLOCOS(throttleChar, cab) { mostRecentCab=myLocos[loco].cab; - DCC::setThrottle(myLocos[loco].cab, WiTToDCCSpeed(witSpeed), DCC::getThrottleDirection(myLocos[loco].cab)); + DCC::setThrottle(myLocos[loco].cab, dccSpeed, DCC::getThrottleDirection(myLocos[loco].cab)); // SetThrottle will cause speed change broadcast } } @@ -394,19 +409,28 @@ void WiThrottle::loop(RingStream * stream) { wt->checkHeartbeat(stream); } +void WiThrottle::eStop() { + LOOPLOCOS('*', -1) { + if (myLocos[loco].throttle!='\0') { + if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab); + DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab)); // speed 1 is eStop + } + } +} + void WiThrottle::checkHeartbeat(RingStream * stream) { // if eStop time passed... eStop any locos still assigned to this client and then drop the connection if(heartBeatEnable && (millis()-heartBeat > ESTOP_SECONDS*1000)) { - if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout, drop connection"), millis(), clientid, ESTOP_SECONDS); - LOOPLOCOS('*', -1) { - if (myLocos[loco].throttle!='\0') { - if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab); - DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab)); // speed 1 is eStop - heartBeat=millis(); // We have just stopped everyting, we don't need to do that again at next loop. - } + if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout"), millis(), clientid, ESTOP_SECONDS); + if (missedHeartbeatCounter++ > 10) { + if (Diag::WITHROTTLE) DIAG(F("Too many missed heartbeats, drop connection")); + delete this; + return; } - // if it does come back, the throttle should re-acquire - delete this; + eStop(); + heartBeat=millis(); // We have just stopped everyting, we don't need to do that again at next loop. + // Destructor will take care of estop. If it does come back, the throttle should re-acquire + // delete this; return; } diff --git a/WiThrottle.h b/WiThrottle.h index 67569433d..baa1fb4bb 100644 --- a/WiThrottle.h +++ b/WiThrottle.h @@ -1,6 +1,7 @@ /* * © 2021 Mike S * © 2020-2021 Chris Harlow + * © 2024 Harald Barth * All rights reserved. * * This file is part of Asbelos DCC API @@ -20,7 +21,7 @@ */ #ifndef WiThrottle_h #define WiThrottle_h - +#include "defines.h" #include "RingStream.h" struct MYLOCO { @@ -45,9 +46,15 @@ class WiThrottle { ~WiThrottle(); static const int MAX_MY_LOCO=10; // maximum number of locos assigned to a single client +#ifdef HEARTBEAT_CRITICAL + static const int HEARTBEAT_SECONDS=1; + static const int HEARTBEAT_PRELOAD=2; + static const int ESTOP_SECONDS=3; +#else static const int HEARTBEAT_SECONDS=10; // heartbeat at 10 secs to provide messaging transport static const int HEARTBEAT_PRELOAD=2; // request fast callback when connecting multiple messages - static const int ESTOP_SECONDS=20; // eStop if no incoming messages for more than 8secs + static const int ESTOP_SECONDS=20; // eStop if no incoming messages for more than 20secs +#endif static WiThrottle* firstThrottle; static int getInt(byte * cmd); static int getLocoId(byte * cmd); @@ -62,6 +69,7 @@ class WiThrottle { MYLOCO myLocos[MAX_MY_LOCO]; bool heartBeatEnable; unsigned long heartBeat; + uint8_t missedHeartbeatCounter = 0; bool introSent=false; bool turnoutsSent=false; bool rosterSent=false; @@ -75,6 +83,7 @@ class WiThrottle { void multithrottle(RingStream * stream, byte * cmd); void locoAction(RingStream * stream, byte* aval, char throttleChar, int cab); void accessory(RingStream *, byte* cmd); + void eStop(); void checkHeartbeat(RingStream * stream); void markForBroadcast2(int cab); void sendIntro(Print * stream);