1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-27 01:56:14 +01:00

Delay Turnout <H> response following movement.

This commit is contained in:
Neil McKechnie 2021-11-11 13:30:37 +00:00
parent 0e78cf6e55
commit 8e6c232d05
3 changed files with 92 additions and 19 deletions

View File

@ -129,6 +129,7 @@ void DCCEXParser::loop(Stream &stream)
}
}
Sensor::checkAll(&stream); // Update and print changes
Turnout::loop(); // Check for outstanding turnout responses
}
int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], const byte *cmd)

View File

@ -42,6 +42,8 @@
* Public static data
*/
/* static */ int Turnout::turnoutlistHash = 0;
/* static */ unsigned long Turnout::_lastLoopEntry = 0;
/*
* Protected static functions
@ -67,6 +69,48 @@
}
turnoutlistHash++;
}
/* static */ void Turnout::loop() {
// Here, we check whether the turnout state needs updating.
// For Servo and VPIN turnouts, we need to check the isBusy() status of the device
// and when it goes to 0 we can send out a turnout notification.
// We allow a small delay before we start checking isBusy(), to allow the
// operation time to start.
unsigned long currentMillis = millis();
// Check for updates once every 100ms.
if (currentMillis - _lastLoopEntry > 100) {
_lastLoopEntry = currentMillis;
Turnout *tt = _firstTurnout;
for ( ; tt != 0; tt=tt->_nextTurnout) {
if (tt->_delayResponse > 1) {
tt->_delayResponse--;
} else if (tt->_delayResponse == 1) {
// Pointer has been triggered and grace time has elapsed, so check if completed
if (!tt->isPending()) {
tt->sendResponse();
}
}
}
}
}
// Function called when the turnout has changed.
void Turnout::sendResponse() {
turnoutlistHash++; // let withrottle know something changed
#if defined(RMFT_ACTIVE)
RMFT2::turnoutEvent(_turnoutData.id, _turnoutData.closed);
#endif
// Send message to JMRI etc. over Serial USB. This is done here
// to ensure that the message is sent when the turnout operation
// is not initiated by a Serial command.
printState(&Serial);
// Clear flag pending operation
_delayResponse = 0;
}
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed;
void Turnout::printState(Print *stream) {
@ -139,24 +183,22 @@
if (!tt) return false;
bool ok = tt->setClosedInternal(closeFlag);
// Check return value to see if this type of turnout is immediate
// or if we need to check for completion.
if (ok) {
turnoutlistHash++; // let withrottle know something changed
// Write byte containing new closed/thrown state to EEPROM if required. Note that eepromAddress
// is always zero for LCN turnouts.
if (EEStore::eeStore->data.nTurnouts > 0 && tt->_eepromAddress > 0)
EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags);
#if defined(RMFT_ACTIVE)
RMFT2::turnoutEvent(id, closeFlag);
#endif
// Send message to JMRI etc. over Serial USB. This is done here
// to ensure that the message is sent when the turnout operation
// is not initiated by a Serial command.
tt->printState(&Serial);
tt->sendResponse();
} else {
// Set flag indicating that the operation is pending and needs to be checked for completion
// starting in 4 loop counts (starts checking at 1).
tt->_delayResponse = 5;
}
return ok;
// Write byte containing new closed/thrown state to EEPROM if required. Note that eepromAddress
// is always zero for LCN turnouts.
if (EEStore::eeStore->data.nTurnouts > 0 && tt->_eepromAddress > 0)
EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags);
return true;
}
// Load all turnout objects
@ -301,10 +343,16 @@
IODevice::writeAnalogue(_servoTurnoutData.vpin,
close ? _servoTurnoutData.closedPosition : _servoTurnoutData.thrownPosition, _servoTurnoutData.profile);
_turnoutData.closed = close;
return false; // Don't send update messages yet.
#else
(void)close; // avoid compiler warnings
#endif
return true;
#endif
}
// Determine if the turnout is moving into its new state.
bool ServoTurnout::isPending() {
return IODevice::isBusy(_servoTurnoutData.vpin);
}
void ServoTurnout::save() {
@ -458,12 +506,22 @@
!_turnoutData.closed);
}
// setClosedInternal is called from the base class's setClosed() method. It returns true if the
// operation is considered to be completed, and false if it is started but not completed.
// For VPINs which are attached to servos, we ought to return false so that the confirmation
// message to JMRI is deferred until the servo movement is completed. However, if we do this
// it will also affect VPINs that are GPIO pins; when it polls the pin it will put the pin into
// input mode, which is not desirable. Hence, we return true here so that no polling takes place.
bool VpinTurnout::setClosedInternal(bool close) {
IODevice::write(_vpinTurnoutData.vpin, close);
_turnoutData.closed = close;
return true;
}
bool VpinTurnout::isPending() {
return IODevice::isBusy(_vpinTurnoutData.vpin);
}
void VpinTurnout::save() {
// Write turnout definition and current position to EEPROM
// First write common servo data, then

View File

@ -72,6 +72,9 @@ protected:
// Pointer to next turnout on linked list.
Turnout *_nextTurnout = 0;
// Delay counter for responses. If non-zero, there is a pending response to be sent.
uint8_t _delayResponse = 0;
/*
* Constructor
*/
@ -88,6 +91,7 @@ protected:
static Turnout *_firstTurnout;
static int _turnoutlistHash;
static unsigned long _lastLoopEntry;
/*
* Virtual functions
@ -95,7 +99,8 @@ protected:
virtual bool setClosedInternal(bool close) = 0; // Mandatory in subclass
virtual void save() {}
virtual bool isPending() { return false; };
/*
* Static functions
*/
@ -103,7 +108,12 @@ protected:
static Turnout *get(uint16_t id);
static void add(Turnout *tt);
/*
* Other functions
*/
void sendResponse();
public:
/*
* Static data
@ -155,6 +165,8 @@ public:
inline static Turnout *first() { return _firstTurnout; }
static void loop();
// Load all turnout definitions.
static void load();
// Load one turnout definition
@ -201,6 +213,7 @@ protected:
// ServoTurnout-specific code for throwing or closing a servo turnout.
bool setClosedInternal(bool close) override;
void save() override;
bool isPending() override;
};
@ -265,6 +278,7 @@ public:
protected:
bool setClosedInternal(bool close) override;
void save() override;
bool isPending() override;
};