Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
ProgrammingTrackBackend.hxx
Go to the documentation of this file.
1
36#ifndef _DCC_PROGRAMMINGTRACKBACKEND_HXX_
37#define _DCC_PROGRAMMINGTRACKBACKEND_HXX_
38
39#include <functional>
40
41#include "dcc/DccOutput.hxx"
42#include "dcc/Packet.hxx"
43#include "dcc/PacketSource.hxx"
44#include "dcc/UpdateLoop.hxx"
46#include "openlcb/Datagram.hxx"
47#include "utils/Singleton.hxx"
48
49extern "C" {
50void enable_dcc();
51}
52
53// If defined, adds instrumentation calls to a logging function.
54// #define DEBUG_PROGRAMTRACK_BACKEND
55
57{
58 enum EnterServiceMode
59 {
60 ENTER_SERVICE_MODE
61 };
62
63 enum ExitServiceMode
64 {
65 EXIT_SERVICE_MODE
66 };
67
68 enum SendReset
69 {
70 SEND_RESET
71 };
72
73 enum SendProgrammingPacket
74 {
75 SEND_PROGRAMMING_PACKET
76 };
77
79 void reset(EnterServiceMode)
80 {
81 reset_base();
83 }
84
86 void reset(ExitServiceMode)
87 {
88 reset_base();
90 }
91
94 void reset(SendReset, unsigned count)
95 {
96 reset_base();
98 repeatCount_ = count;
99 }
100
109 void reset(SendProgrammingPacket, dcc::Packet pkt, unsigned count,
110 bool terminate_on_ack = true)
111 {
112 reset_base();
114 packetToSend_ = pkt;
115 repeatCount_ = count;
116 terminateOnAck_ = terminate_on_ack ? 1 : 0;
117 }
118
119 enum class Type
120 {
122 ENTER_SERVICE_MODE,
124 EXIT_SERVICE_MODE,
126 SEND_RESET,
129 };
130
133
137
140 unsigned repeatCount_ : 16;
141
144 unsigned terminateOnAck_ : 1;
145
147 unsigned hasAck_ : 1;
148
152 unsigned hasShortCircuit_ : 1;
153
154private:
158 {
160 hasAck_ = 0;
162 terminateOnAck_ = 0;
163 repeatCount_ = 0;
164 }
165};
166
167extern void progdebug_log_packet(dcc::Packet *pkt);
168extern void progdebug_log_string(const char *s);
169
170class ProgrammingTrackBackend : public CallableFlow<ProgrammingTrackRequest>,
172 public Singleton<ProgrammingTrackBackend>
173{
174public:
176 std::function<void()> enable_program_track_mode,
177 std::function<void()> disable_program_track_mode)
179 , enableProgramTrackMode_(std::move(enable_program_track_mode))
180 , disableProgramTrackMode_(std::move(disable_program_track_mode))
182 {
183 }
184
186 {
188 OPERATION_PENDING = openlcb::DatagramClient::OPERATION_PENDING,
189 };
190
191 Action entry() override
192 {
194 auto* pgm = get_dcc_output(DccOutput::PGM);
195 const bool has_short = pgm->get_disable_output_reasons() &
197
198 switch (request()->cmd_)
199 {
201 return call_immediately(STATE(enter_service_mode));
202
204 return call_immediately(STATE(exit_service_mode));
205
207 if (has_short)
208 {
210 }
211 return call_immediately(STATE(send_reset));
212
214 if (has_short)
215 {
217 }
218 return call_immediately(STATE(send_service_packet));
219 }
220 DIE("Unknown programming track request command");
221 }
222
226 {
227 if (!has_request())
228 {
229 return;
230 }
231 request()->hasAck_ = 1;
232 if (request()->terminateOnAck_ && isWaitingForPackets_)
233 {
234 // Early exit criteria reached. Wake up the flow and return.
237 // resume flow
238 notify();
239 }
240 }
241
244 {
245 if (!has_request())
246 {
247 return;
248 }
251 }
252
253private:
256 static constexpr unsigned QUEUE_FLUSH_PACKET_COUNT = 8;
257
258 Action enter_service_mode()
259 {
260 // 1. switch short circuit detector to service mode
261 //
262 // 2. register exclusive packet source with the update loop.
263 //
264 // 3. switch the power outputs to the programming track. That's
265 // dependant on the hardware revision.
267 if (!packet_processor_add_refresh_source(
269 {
270 // There was another high priority source, probably we are in ESTOP.
271 packet_processor_remove_refresh_source(this);
272 return return_with_error(openlcb::Defs::ERROR_OUT_OF_ORDER);
273 }
274 // Flushes the packet queue with reset packets.
276 request()->packetToSend_.packet_header.send_long_preamble = 1;
277 if (!request()->repeatCount_)
278 {
280 }
283
284 }
285
289 auto* pgm = get_dcc_output(DccOutput::PGM);
290 if (pgm)
291 {
292 // Enables power to the program track now that we have only reset
293 // packets in the queue.
294 pgm->clear_disable_output_for_reason(
296 }
297 return return_ok();
298 }
299
300 Action exit_service_mode()
301 {
302 // Algorithm for exiting service mode:
303 // 1. disable the output
304 // 2. flush the DCC send queue with idle packets
305 // 3. call the hardware to switch over to mainline mode
306 // 4. reenable normal packet source by removing override source.
307 auto* pgm = get_dcc_output(DccOutput::PGM);
308 if (pgm)
309 {
310 // Symmetric to {\link reset_flush_done }.
311 pgm->disable_output_for_reason(
313 }
314
316 if (!request()->repeatCount_)
317 {
319 }
322 }
323
327 {
329 packet_processor_remove_refresh_source(this);
330
331 return return_ok();
332 }
333
334 Action send_reset()
335 {
336
337 // record that we want to send reset packets.
339 request()->packetToSend_.packet_header.send_long_preamble = 1;
340 return call_immediately(STATE(send_service_packet));
341 }
342
343 Action send_service_packet()
344 {
346 // This pauses the flow until somebody calls notify(), then continues
347 // in the given state.
348 request()->packetToSend_.packet_header.send_long_preamble = 1;
350 }
351
353 {
354 // this will terminate the current flow with a 0 error value.
356 return return_ok();
357 }
358
363 void get_next_packet(unsigned code, dcc::Packet *packet) override
364 {
365 if (!has_request())
366 {
368#ifdef DEBUG_PROGRAMTRACK_BACKEND
369 progdebug_log_packet(packet);
370#endif
371 return;
372 }
373
374 *packet = request()->packetToSend_;
375#ifdef DEBUG_PROGRAMTRACK_BACKEND
376 progdebug_log_packet(packet);
377#endif
378 if (request()->repeatCount_ > 0)
379 {
381 }
382 else
383 {
385 {
386#ifdef DEBUG_PROGRAMTRACK_BACKEND
387 progdebug_log_string("done flush");
388#endif
391 // resume flow
392 notify();
393 }
394 }
395 }
396
398 std::function<void()> enableProgramTrackMode_;
400 std::function<void()> disableProgramTrackMode_;
401
405};
406
407#endif // _DCC_PROGRAMMINGTRACKBACKEND_HXX_
DccOutput * get_dcc_output(DccOutput::Type type)
Public API accessor for applications to get the object representing the output hardware.
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Action return_ok()
Terminates the flow and returns the request buffer to the caller with an error code of OK (zero).
Action return_with_error(int error)
Terminates the flow and returns the request buffer to the caller with an specific error code.
ProgrammingTrackRequest * request()
@ PGM
DCC output of the program track.
Definition DccOutput.hxx:54
@ INITIALIZATION_PENDING
Set as 1 during construction time, to be cleared by the application when the initialization is comple...
@ SHORTED
Short detector says this output is shorted.
void notify_service_mode_short()
Call this function when the service mode current limit is exceeded.
static constexpr unsigned QUEUE_FLUSH_PACKET_COUNT
How many packets we should send to the track in order to assume that the packet queue has been comple...
Action reset_flush_done()
Called during service mode enter when we managed to flush all packets from the queue.
unsigned isWaitingForPackets_
1 if the flow is blocked waiting for sending out the respective number of packets.
Action entry() override
Entry into the StateFlow activity.
Action exit_flush_done()
Called during service mode exit when we managed to flush all packets from the queue.
std::function< void()> disableProgramTrackMode_
Callback to connect to the program track hardware control.
std::function< void()> enableProgramTrackMode_
Callback to connect to the program track hardware control.
void get_next_packet(unsigned code, dcc::Packet *packet) override
Function that is called by the track driver when we need to generate a DCC packet to the track.
@ OPERATION_PENDING
cleared when done is called.
void notify_service_mode_ack()
Call this function when the service mode acknowledgement is detected by the short detector.
Collection of related state machines that pend on incoming messages.
Singleton class.
Definition Singleton.hxx:65
Return type for a state flow callback.
Service * service()
Return a pointer to the service I am bound to.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
void notify() override
Wakeup call arrived. Schedules *this on the executor.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Abstract class that is a packet source but not a TrainImpl.
static constexpr unsigned PROGRAMMING_PRIORITY
Priority value to be used for service mode programming source.
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
All callable flow request objects have to derive from this struct.
void reset_base()
Call this from all instances of reset(...).
int resultCode
If high bits are zero, this is a 16-bit OpenLCB result code.
uint8_t send_long_preamble
1: send long preamble instead of packet.
Definition packet.h:69
dcc::Packet packetToSend_
This packet will be repeated on the programming track for SEND_SERVICE_PACKET.
void reset(EnterServiceMode)
Set up command to enter service mode.
@ ENTER_SERVICE_MODE
Switch from normal mode to service mode.
@ SEND_RESET
Send some number of reset commands.
@ SEND_SERVICE_PACKET
Send a specific service mode DCC packet.
@ EXIT_SERVICE_MODE
Switch from service mode back to normal operations mode.
void reset(ExitServiceMode)
Set up command to exit service mode.
Type cmd_
What is the instruction to do.
unsigned repeatCount_
How many times maximum to repeat the packet before timing out on no acknowledgement,...
void reset(SendProgrammingPacket, dcc::Packet pkt, unsigned count, bool terminate_on_ack=true)
Set up command to send some number of service mode packets, waiting for an acknowledgement.
unsigned hasShortCircuit_
Set to 1 if the programming track has reached the current limit.
unsigned hasAck_
Output arguments. These are filled by the flow upon return.
void reset_base()
Resets all internal variables to default state.
unsigned terminateOnAck_
Should we short-cut sending the packet repetitions when we've seen an ack.
void reset(SendReset, unsigned count)
Set up command to send some number of reset packets.
Represents a command to be sent to the track driver.
Definition Packet.hxx:52
void set_dcc_reset_all_decoders()
Creates a DCC reset-all-decoders packet.
Definition Packet.cxx:90
void set_dcc_idle()
Creates a DCC idle packet.
Definition Packet.cxx:81