42static const int WRITE_RETRY_COUNT_ON_UNKNOWN = 10;
45static const int READ_RETRY_COUNT_ON_UNKNOWN = 10;
48static const int RAILCOM_POM_OP_TIMEOUT_MSEC = 2000;
54TractionCvSpace::TractionCvSpace(MemoryConfigHandler *parent,
61 , railcomHub_(railcom_hub)
62 , errorCode_(ERROR_NOOP)
66 parent_->registry()->insert(
nullptr, spaceId_,
this);
70TractionCvSpace::~TractionCvSpace()
72 parent_->registry()->erase(
nullptr, spaceId_,
this);
73 if (errorCode_ == ERROR_PENDING)
79bool TractionCvSpace::set_node(
Node *node)
85 NodeID id = node->node_id();
86 if ((
id & 0xFFFF00000000ULL) != TractionDefs::NODE_ID_DCC)
91 if ((
id & 0xFFFF) == currId_)
95 currId_ = (
id & 0xFFFF);
98 if (!TractionDefs::legacy_address_from_train_node_id(
id, &type, &addr)) {
103 case dcc::TrainAddressType::DCC_LONG_ADDRESS:
104 dccAddressNum_ = addr;
107 case dcc::TrainAddressType::DCC_SHORT_ADDRESS:
108 dccAddressNum_ = addr;
115 errorCode_ = ERROR_NOOP;
119const unsigned TractionCvSpace::MAX_CV;
121size_t TractionCvSpace::read(
const address_t source, uint8_t *dst,
size_t len,
124 if (source == OFFSET_CV_INDEX) {
125 lastIndexedNode_ = currId_;
126 uint8_t* lastcv = (uint8_t*)&lastIndexedCv_;
127 if (len > 0) dst[0] = lastcv[3];
128 if (len > 1) dst[1] = lastcv[2];
129 if (len > 2) dst[2] = lastcv[1];
130 if (len > 3) dst[3] = lastcv[0];
131 return std::min(len,
size_t(4));
134 if (source == OFFSET_CV_VALUE || source == OFFSET_CV_VERIFY_RESULT)
136 if (currId_ != lastIndexedNode_)
138 *error = Defs::ERROR_PERMANENT;
142 cv = lastIndexedCv_ - 1;
145 if (source < OFFSET_CV_INDEX)
149 LOG(
INFO,
"cv read %" PRIu32, cv);
152 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
153 errorCode_ = ERROR_NOOP;
158 if (errorCode_ == ERROR_OK)
161 errorCode_ = ERROR_NOOP;
164 else if (errorCode_ == _ERROR_TIMEOUT)
166 *error = Defs::ERROR_OPENLCB_TIMEOUT;
167 errorCode_ = ERROR_NOOP;
170 else if (errorCode_ == _ERROR_BUSY)
172 *error = Defs::ERROR_OUT_OF_ORDER;
173 errorCode_ = ERROR_NOOP;
179 errorCode_ = ERROR_NOOP;
182 if ((source == OFFSET_CV_VALUE) || (source <= MAX_CV))
184 start_flow(
STATE(try_read1));
186 else if (source == OFFSET_CV_VERIFY_RESULT)
188 start_flow(
STATE(pgm_verify));
192 DIE(
"Have not started the flow but will return AGAIN.");
194 *error = ERROR_AGAIN;
202 return invoke_subflow_and_wait(
204 STATE(pgm_verify_wait_flush),
205 ProgrammingTrackRequest::ENTER_SERVICE_MODE);
212 if (b->data()->resultCode != 0)
215 errorCode_ = _ERROR_BUSY;
224 return invoke_subflow_and_wait(
226 STATE(pgm_verify_packet), ProgrammingTrackRequest::SEND_RESET, 15);
243 return invoke_subflow_and_wait(
245 ProgrammingTrackRequest::SEND_PROGRAMMING_PACKET, pkt, 15);
252 if (b->data()->hasAck_)
260 errorCode_ = ERROR_OK;
261 return invoke_subflow_and_wait(
263 STATE(pgm_verify_reset_done), ProgrammingTrackRequest::SEND_RESET, 15);
270 return invoke_subflow_and_wait(
272 ProgrammingTrackRequest::EXIT_SERVICE_MODE);
284 return allocate_and_call(track_,
STATE(fill_read1_packet));
288 return allocate_and_call(track_,
STATE(fill_write1_packet));
293 auto *b = get_allocation_result(track_);
294 b->data()->start_dcc_packet();
303 b->data()->add_dcc_pom_read1(cvNumber_);
304 b->data()->feedback_key =
reinterpret_cast<uintptr_t
>(
this);
306 b->data()->packet_header.rept_count = 1;
307 errorCode_ = ERROR_PENDING;
308 railcomHub_->register_port(
this);
315 LOG(
WARNING,
"railcom read returned status %d value %d", errorCode_, cvData_);
316 switch (errorCode_) {
318 errorCode_ = _ERROR_TIMEOUT;
319 railcomHub_->unregister_port(
this);
324 case ERROR_UNKNOWN_RESPONSE:
325 if (numTry_ >= READ_RETRY_COUNT_ON_UNKNOWN) {
326 errorCode_ = _ERROR_TIMEOUT;
330 return call_immediately(
STATE(try_read1));
333 errorCode_ = _ERROR_TIMEOUT;
336 return call_immediately(
STATE(try_read1));
341size_t TractionCvSpace::write(address_t destination,
const uint8_t *src,
342 size_t len, errorcode_t *error,
Notifiable *again)
344 if (destination == OFFSET_CV_INDEX) {
345 lastIndexedNode_ = currId_;
346 uint8_t* lastcv = (uint8_t*)&lastIndexedCv_;
347 if (len > 0) lastcv[3] = src[0];
348 if (len > 1) lastcv[2] = src[1];
349 if (len > 2) lastcv[1] = src[2];
350 if (len > 3) lastcv[0] = src[3];
351 return std::min(len,
size_t(4));
353 if (destination == OFFSET_CV_VERIFY_VALUE)
355 lastVerifyValue_ = src[0];
358 if (destination == OFFSET_CV_VALUE) {
359 if (currId_ != lastIndexedNode_) {
360 *error = Defs::ERROR_TEMPORARY;
364 destination = lastIndexedCv_ - 1;
367 LOG(
INFO,
"cv write %" PRIu32
" := %d", destination, *src);
368 if (destination > MAX_CV)
370 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
371 errorCode_ = ERROR_NOOP;
374 if (errorCode_ == ERROR_OK && destination == cvNumber_)
376 errorCode_ = ERROR_NOOP;
379 if (errorCode_ == _ERROR_TIMEOUT && destination == cvNumber_)
381 *error = Defs::ERROR_TEMPORARY | 1;
382 errorCode_ = ERROR_NOOP;
386 cvNumber_ = destination;
389 errorCode_ = ERROR_NOOP;
390 start_flow(
STATE(try_write1));
391 *error = ERROR_AGAIN;
398 auto *b = get_allocation_result(track_);
399 b->data()->start_dcc_packet();
408 b->data()->add_dcc_pom_write1(cvNumber_, cvData_);
409 b->data()->feedback_key =
reinterpret_cast<uintptr_t
>(
this);
413 b->data()->packet_header.rept_count = 3;
414 errorCode_ = ERROR_PENDING;
415 railcomHub_->register_port(
this);
422 LOG(
WARNING,
"railcom write returned status %d value %d", errorCode_, cvData_);
426 errorCode_ = _ERROR_TIMEOUT;
427 railcomHub_->unregister_port(
this);
432 case ERROR_UNKNOWN_RESPONSE:
433 if (numTry_ >= WRITE_RETRY_COUNT_ON_UNKNOWN)
437 return call_immediately(
STATE(try_read1));
440 return call_immediately(
STATE(try_write1));
444 errorCode_ = _ERROR_TIMEOUT;
448 return call_immediately(
STATE(try_write1));
453void TractionCvSpace::record_railcom_status(
unsigned code)
457 railcomHub_->unregister_port(
this);
463 if (errorCode_ != ERROR_PENDING)
475 return record_railcom_status(ERROR_NO_RAILCOM_CH2_DATA);
478 unsigned new_status = ERROR_PENDING;
479 for (
const auto& e : interpretedResponse_) {
480 if (e.railcom_channel != 2)
continue;
482 case dcc::RailcomPacket::BUSY:
483 if (new_status == ERROR_PENDING) {
484 new_status = _ERROR_BUSY;
487 case dcc::RailcomPacket::NACK:
488 if (new_status == ERROR_PENDING) {
496 case dcc::RailcomPacket::ACK:
497 if (new_status == ERROR_PENDING) {
503 case dcc::RailcomPacket::GARBAGE:
504 if (new_status == ERROR_PENDING) {
509 case dcc::RailcomPacket::MOB_POM:
510 cvData_ = e.argument;
511 new_status = ERROR_OK;
517 if (new_status == ERROR_PENDING)
522 return record_railcom_status(new_status);
BufferPtr< T > get_buffer_deleter(Buffer< T > *b)
Helper function to create a BufferPtr of an appropriate type without having to explicitly specify the...
std::unique_ptr< Buffer< T >, BufferDelete< T > > AutoReleaseBuffer
This class will automatically unref a Buffer when going out of scope.
void parse_railcom_data(const dcc::Feedback &fb, std::vector< struct RailcomPacket > *output)
Interprets the data from a railcom feedback.
std::string railcom_debug(const Feedback &fb)
Formats a dcc::Feedback message into a debug string.
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Base class for all QMember types that hold data in an expandable format.
T * data()
get a pointer to the start of the data.
Templated implementation of the HubFlow.
An object that can schedule itself on an executor to run.
Return type for a state flow callback.
Base class for state machines.
Base class for NMRAnet nodes conforming to the asynchronous interface.
TrainAddressType
Which address type this legacy train node uses.
#define LOG(level, message...)
Conditionally write a message to the logging output.
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
static const int INFO
Loglevel that is printed by default, reporting some status information.
#define DIE(MSG)
Unconditionally terminates the current process with a message.
uint64_t NodeID
48-bit NMRAnet Node ID type
long long os_get_time_monotonic(void)
Get the monotonic time since the system started.
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
uint8_t ch2Size
Number of bytes in channel two.
uint8_t channel
Used by multi-channel railcom receiver drivers.
uintptr_t feedbackKey
Opaque identifier that allows linking outgoing dcc::Packet sent to the DCC waveform generator to the ...
Strongly typed wrapper representing a long DCC address.
Strongly typed wrapper representing a short DCC address.
Structure used for reading (railcom) feedback data from DCC / Railcom device drivers.
Represents a command to be sent to the track driver.
void set_dcc_svc_verify_byte(unsigned cv_number, uint8_t value)
Sets the packet to a DCC service mode packet verifying the contents of an entire CV.
void set_dcc_svc_verify_bit(unsigned cv_number, unsigned bit, bool expected)
Sets the packet to a DCC service mode packet verifying the contents of a single bit in a CV.