Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
RailcomPortDebug.hxx
Go to the documentation of this file.
1
35#ifndef _DCC_RAILCOMPORTDEBUG_HXX
36#define _DCC_RAILCOMPORTDEBUG_HXX
37
38#include "dcc/RailcomHub.hxx"
39#include "utils/LimitedPool.hxx"
40
41namespace dcc
42{
43
47{
48public:
51 : parent_(source)
52 {
53 source->register_port(this);
54 }
55
57 {
59 }
60
61private:
65 string display_railcom_data(const uint8_t *data, int len)
66 {
67 static char buf[200];
68 int ofs = 0;
69 HASSERT(len <= 6);
70 for (int i = 0; i < len; ++i)
71 {
72 ofs += sprintf(buf + ofs, "0x%02x (0x%02x), ", data[i],
73 dcc::railcom_decode[data[i]]);
74 }
75 uint8_t type = (dcc::railcom_decode[data[0]] >> 2);
76 if (len == 2)
77 {
78 uint8_t payload = dcc::railcom_decode[data[0]] & 0x3;
79 payload <<= 6;
80 payload |= dcc::railcom_decode[data[1]];
81 switch (type)
82 {
83 case dcc::RMOB_ADRLOW:
84 ofs += sprintf(buf + ofs, "adrlow=%d", payload);
85 break;
86 case dcc::RMOB_ADRHIGH:
87 ofs += sprintf(buf + ofs, "adrhigh=%d", payload);
88 break;
89 case dcc::RMOB_EXT:
90 ofs += sprintf(buf + ofs, "ext=%d", payload);
91 break;
92 case dcc::RMOB_DYN:
93 ofs += sprintf(buf + ofs, "dyn=%d", payload);
94 break;
95 case dcc::RMOB_SUBID:
96 ofs += sprintf(buf + ofs, "subid=%d", payload);
97 break;
98 default:
99 ofs += sprintf(buf + ofs, "type-%d=%d", type, payload);
100 }
101 }
102 return string(buf, ofs);
103 }
104
110 {
112 dcc::Feedback &fb = *d->data();
113 if (fb.feedbackKey <= 1000)
114 return;
115 if (fb.ch1Size && fb.channel != 0xff)
116 {
117 LOG(INFO, "Railcom %x CH1 data(%" PRIuPTR "): %s", fb.channel,
118 fb.feedbackKey,
119 display_railcom_data(fb.ch1Data, fb.ch1Size).c_str());
120 }
121 if (fb.ch2Size && fb.channel != 0xff)
122 {
123 LOG(INFO, "Railcom %x CH2 data(%" PRIuPTR "): %s", fb.channel,
124 fb.feedbackKey,
125 display_railcom_data(fb.ch2Data, fb.ch2Size).c_str());
126 }
127 }
128
131};
132
137{
138public:
143 : parent_(source)
144 , output_(output)
145 {
146 source->register_port(this);
147 }
148
150 {
152 }
153
154private:
160 {
162 dcc::Feedback &fb = *d->data();
163 if (fb.channel >= 0xfe)
164 {
165 // Occupancy feedback, not railcom data.
166 return;
167 }
168 unsigned correct = 0;
169 unsigned total = 0;
170 for (unsigned i = 0; i < fb.ch1Size; i++)
171 {
172 ++total;
173 correct += (dcc::railcom_decode[fb.ch1Data[i]] != RailcomDefs::INV)
174 ? 1
175 : 0;
176 }
177 for (unsigned i = 0; i < fb.ch2Size; i++)
178 {
179 ++total;
180 correct += (dcc::railcom_decode[fb.ch2Data[i]] != RailcomDefs::INV)
181 ? 1
182 : 0;
183 }
184 if (total > 0 && correct == total)
185 {
186 // Produces a short pulse on the output
187 output_->write(true);
188 for (volatile int i = 0; i < 3000; i++) { }
189 output_->write(false);
190 }
191 }
192
196 const Gpio *output_;
197}; // RailcomToGpioFlow
198
199} // namespace dcc
200
201namespace openlcb
202{
203
212{
213public:
215 dcc::RailcomHubPort *occupancy_port, bool ch1_enabled = true,
216 bool ack_enabled = true)
217 : dcc::RailcomHubPort(node->iface())
218 , parent_(parent)
219 , node_(node)
220 , occupancyPort_(occupancy_port)
221 , ch1Enabled_(ch1_enabled)
222 , ackEnabled_(ack_enabled)
223 {
224 if (parent_)
225 {
226 parent_->register_port(this);
227 }
228 }
229
231 : dcc::RailcomHubPort(node->iface())
232 , parent_(nullptr)
233 , node_(node)
234 , occupancyPort_(nullptr)
235 {
236 }
237
239 {
240 if (parent_)
241 {
242 parent_->unregister_port(this);
243 }
244 }
245
246 Action entry() override
247 {
248 if (message()->data()->channel == 0xff)
249 {
250 if (occupancyPort_) {
251 occupancyPort_->send(transfer_message());
252 } else {
253 release();
254 }
255 return exit();
256 }
257 if (message()->data()->channel >= 0xf0)
258 {
259 return release_and_exit();
260 }
261 if (outputPool_.free_items() == 0)
262 {
263 return release_and_exit();
264 }
265 if (message()->data()->ch1Size && ch1Enabled_)
266 {
267 return allocate_and_call(
268 node_->iface()->global_message_write_flow(),
269 STATE(ch1_msg_allocated), &outputPool_);
270 }
271 else
272 {
273 return call_immediately(STATE(maybe_send_ch2));
274 }
275 }
276
277 Action ch1_msg_allocated()
278 {
279 auto *b =
281
282 b->data()->reset(
283 static_cast<openlcb::Defs::MTI>(openlcb::Defs::MTI_XPRESSNET + 2),
284 node_->node_id(), string());
285 b->data()->payload.push_back(message()->data()->channel | 0x10);
286 b->data()->payload.append(
287 (char *)message()->data()->ch1Data, message()->data()->ch1Size);
288 node_->iface()->global_message_write_flow()->send(b);
289
290 return call_immediately(STATE(maybe_send_ch2));
291 }
292
293 Action maybe_send_ch2()
294 {
295 if (message()->data()->ch2Size &&
296 outputPool_.free_items() != 0 &&
297 (ackEnabled_ ||
298 dcc::railcom_decode[message()->data()->ch2Data[0]] !=
300 {
301 return allocate_and_call(
302 node_->iface()->global_message_write_flow(),
303 STATE(ch2_msg_allocated), &outputPool_);
304 }
305 else
306 {
307 return release_and_exit();
308 }
309 }
310
311 Action ch2_msg_allocated()
312 {
313 auto *b =
315
316 b->data()->reset(
317 static_cast<openlcb::Defs::MTI>(openlcb::Defs::MTI_XPRESSNET + 3),
318 node_->node_id(), string());
319 b->data()->payload.push_back(message()->data()->channel | 0x20);
320 b->data()->payload.append(
321 (char *)message()->data()->ch2Data, message()->data()->ch2Size);
322 node_->iface()->global_message_write_flow()->send(b);
323
324 return release_and_exit();
325 }
326
327 dcc::RailcomHubFlow *parent_{nullptr};
328 Node *node_;
329 dcc::RailcomHubPort *occupancyPort_;
330 LimitedPool outputPool_{sizeof(Buffer<openlcb::GenMessage>), 20};
332 uint8_t ch1Enabled_ : 1;
334 uint8_t ackEnabled_ : 1;
335};
336
337} // namespace openlcb
338
339#endif // _DCC_RAILCOMPORTDEBUG_HXX
std::unique_ptr< Buffer< T >, BufferDelete< T > > AutoReleaseBuffer
This class will automatically unref a Buffer when going out of scope.
Definition Buffer.hxx:256
const uint8_t railcom_decode[256]
Table for 8-to-6 decoding of railcom data.
Definition RailCom.cxx:47
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
Abstract class for message recipients.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Templated implementation of the HubFlow.
Definition Hub.hxx:150
void register_port(port_type *port)
Adds a new port.
Definition Hub.hxx:167
void unregister_port(port_type *port)
Removes a previously added port.
Definition Hub.hxx:174
OS-independent abstraction for GPIO.
Definition Gpio.hxx:43
virtual void write(Value new_state) const =0
Writes a GPIO output pin (set or clear to a specific state).
Implementation of a Pool interface that takes memory from mainBufferPool (configurable) but limits th...
size_t free_items() override
Number of free items in the pool.
Node information.
Definition Devtab.hxx:549
Return type for a state flow callback.
Action allocate_and_call(FlowInterface< Buffer< T > > *target_flow, Callback c, Pool *pool=nullptr)
Allocates a buffer from a pool and proceed to the next state when allocation is successful.
Buffer< T > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
Action exit()
Terminates the processing of this flow.
Action release_and_exit()
Terminates the processing of the current message.
State flow with a given typed input queue.
void release() OVERRIDE
Unrefs the current buffer.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
MessageType * transfer_message()
Releases ownership of the current message.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Registers as a member ofthe railcom hub.
dcc::RailcomHubFlow * parent_
Flow to which we are registered.
void send(Buffer< dcc::RailcomHubData > *d, unsigned prio) OVERRIDE
Incoming railcom data.
string display_railcom_data(const uint8_t *data, int len)
Helper function to turn some railcom data into string.
RailcomPrintfFlow(dcc::RailcomHubFlow *source)
Constructor.
This flow listens to Railcom packets coming from the hub, and if they are correctly decoded,...
RailcomToGpioFlow(dcc::RailcomHubFlow *source, const Gpio *output)
Constructor.
const Gpio * output_
Output gpio to toggle.
void send(Buffer< dcc::RailcomHubData > *d, unsigned prio) OVERRIDE
Incoming railcom data.
dcc::RailcomHubFlow * parent_
Flow to which we are registered.
MessageHandler * global_message_write_flow()
Definition If.hxx:200
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
This flow proxies all incoming railcom traffic to the openlcb bus in non-standard messages.
uint8_t ch1Enabled_
True if we should transmit channel1 data.
uint8_t ackEnabled_
True if we should transmit data that starts with an ACK.
Action entry() override
Entry into the StateFlow activity.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t ch2Size
Number of bytes in channel two.
Definition railcom.h:49
uint8_t ch1Size
Number of bytes in channel one.
Definition railcom.h:45
uint8_t channel
Used by multi-channel railcom receiver drivers.
Definition railcom.h:54
uint8_t ch1Data[2]
Payload of channel 1.
Definition railcom.h:47
uintptr_t feedbackKey
Opaque identifier that allows linking outgoing dcc::Packet sent to the DCC waveform generator to the ...
Definition railcom.h:58
uint8_t ch2Data[6]
Payload of channel 2.
Definition railcom.h:51
Structure used for reading (railcom) feedback data from DCC / Railcom device drivers.
Definition RailCom.hxx:50
@ ACK
Railcom ACK; the decoder received the message ok.
Definition RailCom.hxx:103
@ INV
invalid value (not conforming to the 4bit weighting requirement)
Definition RailCom.hxx:100
MTI
Known Message type indicators.