Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TractionDefs.hxx
Go to the documentation of this file.
1
36#ifndef _OPENLCB_TRACTIONDEFS_HXX_
37#define _OPENLCB_TRACTIONDEFS_HXX_
38
39#include <cmath>
40#include <stdint.h>
41
42#include "dcc/Defs.hxx"
43#include "openlcb/If.hxx"
44#include "openlcb/Payload.hxx"
45#include "openlcb/Velocity.hxx"
47
48namespace openlcb {
49
53
56SpeedType fp16_to_speed(const void *fp16);
57
64void speed_to_fp16(SpeedType speed, void *fp16);
65
69 static const uint64_t IS_TRAIN_EVENT = 0x0101000000000303ULL;
71 static const uint64_t IS_PROXY_EVENT = 0x0101000000000304ULL;
73 static constexpr uint64_t ACTIVATE_BASIC_DCC_ACCESSORY_EVENT_BASE = 0x0101020000FF0000ULL;
75 static constexpr uint64_t INACTIVATE_BASIC_DCC_ACCESSORY_EVENT_BASE = 0x0101020000FE0000ULL;
77 static constexpr uint64_t EXT_DCC_ACCESSORY_EVENT_BASE = 0x0101020001000000ULL;
79 static const uint64_t NODE_ID_DC_BLOCK = 0x060000000000ULL;
81 static const uint64_t NODE_ID_DCC = 0x060100000000ULL;
83 static const uint16_t DCC_LONG_SELECTOR = 0xC000;
85 static const uint64_t NODE_ID_TMCC = 0x060200000000ULL;
87 static const uint64_t NODE_ID_MARKLIN_MOTOROLA = 0x060300000000ULL;
89 static const uint64_t NODE_ID_MTH_DCS = 0x060400000000ULL;
91 static const uint64_t NODE_ID_MASK = 0xFFFF00000000ULL;
92
93 enum
94 {
95 // Byte 0 of request commands.
96 REQ_SET_SPEED = 0x00,
97 REQ_SET_FN = 0x01,
98 REQ_EMERGENCY_STOP = 0x02,
99
100 REQ_QUERY_SPEED = 0x10,
101 REQ_QUERY_FN = 0x11,
102
103 REQ_CONTROLLER_CONFIG = 0x20,
104 REQ_CONSIST_CONFIG = 0x30,
105 REQ_TRACTION_MGMT = 0x40,
106
108 REQ_MASK = 0x7F,
112
113 // Byte 1 of REQ_CONTROLLER_CONFIG command
114 CTRLREQ_ASSIGN_CONTROLLER = 0x01,
115 CTRLREQ_RELEASE_CONTROLLER = 0x02,
116 CTRLREQ_QUERY_CONTROLLER = 0x03,
117 CTRLREQ_NOTIFY_CONTROLLER_CHANGED = 0x04,
118
119 // Byte 1 of REQ_CONSIST_CONFIG command
120 CNSTREQ_ATTACH_NODE = 0x01,
121 CNSTREQ_DETACH_NODE = 0x02,
122 CNSTREQ_QUERY_NODES = 0x03,
123
124 // Byte 1 of REQ_TRACTION_MGMT command
125 MGMTREQ_RESERVE = 0x01,
126 MGMTREQ_RELEASE = 0x02,
127 MGMTREQ_NOOP = 0x03,
128 // Byte 0 of response commands
129 RESP_QUERY_SPEED = REQ_QUERY_SPEED,
130 RESP_QUERY_FN = REQ_QUERY_FN,
131 RESP_CONTROLLER_CONFIG = REQ_CONTROLLER_CONFIG,
132 RESP_CONSIST_CONFIG = REQ_CONSIST_CONFIG,
133 RESP_TRACTION_MGMT = REQ_TRACTION_MGMT,
134
135 // Status byte of the Speed Query response
136 SPEEDRESP_STATUS_IS_ESTOP = 1,
137
138 // Byte 1 of Controller Configuration response
139 CTRLRESP_ASSIGN_CONTROLLER = CTRLREQ_ASSIGN_CONTROLLER,
140 CTRLRESP_QUERY_CONTROLLER = CTRLREQ_QUERY_CONTROLLER,
141 CTRLRESP_NOTIFY_CONTROLLER_CHANGED = CTRLREQ_NOTIFY_CONTROLLER_CHANGED,
142
143 // Byte 2 of assign controller response
144 CTRLRESP_ASSIGN_ERROR_TRAIN = 0x02,
145 CTRLRESP_ASSIGN_ERROR_CONTROLLER = 0x01,
146
147 // Byte 1 of Consist Configuration response
148 CNSTRESP_ATTACH_NODE = CNSTREQ_ATTACH_NODE,
149 CNSTRESP_DETACH_NODE = CNSTREQ_DETACH_NODE,
150 CNSTRESP_QUERY_NODES = CNSTREQ_QUERY_NODES,
151
152 CNSTFLAGS_ALIASVALID = 0x01,
153 CNSTFLAGS_REVERSE = 0x02,
154 CNSTFLAGS_LINKF0 = 0x04,
155 CNSTFLAGS_LINKFN = 0x08,
156 CNSTFLAGS_HIDE = 0x80,
157
158 // Byte 1 of Traction Management replies
159 MGMTRESP_RESERVE = MGMTREQ_RESERVE,
160 MGMTRESP_HEARTBEAT = 0x03,
161
162 PROXYREQ_ALLOCATE = 0x01,
163 PROXYREQ_ATTACH = 0x02,
164 PROXYREQ_DETACH = 0x03,
165 PROXYREQ_MANAGE = 0x80,
166
167 PROXYRESP_ALLOCATE = PROXYREQ_ALLOCATE,
168 PROXYRESP_ATTACH = PROXYREQ_ATTACH,
169 PROXYRESP_MANAGE = PROXYREQ_MANAGE,
170
171 // byte 1 of PROXYREQ_MANAGE commands
172 PROXYREQ_MANAGE_RESERVE = 0x01,
173 PROXYREQ_MANAGE_RELEASE = 0x02,
174
175 PROXYRESP_MANAGE_RESERVE_REPLY = 0x01,
176
177 // Legacy Technology IDs from the GenTractionProxyWN.
178 PROXYTYPE_DCC = 1,
179 PROXYTYPE_DC = 2,
180 PROXYTYPE_MARKLIN_DIGITAL = 3,
181 PROXYTYPE_MARKLIN_DELTA = 4,
182 PROXYTYPE_MARKLIN_MFX = 5,
183 PROXYTYPE_SELECTRIX = 6,
184 PROXYTYPE_MTH_DCS = 7,
185 PROXYTYPE_LIONEL_TMCC = 8,
186
190 // These declare the configuration space layout. They are always the
191 // third byte of the address in the configuration memory space, aka
192 // address = type << 16 + offset.
202 };
203
216 dcc::TrainAddressType type, uint32_t addr)
217 {
218 switch (type)
219 {
220 case dcc::TrainAddressType::DCC_SHORT_ADDRESS:
221 return NODE_ID_DCC | addr;
222 case dcc::TrainAddressType::DCC_LONG_ADDRESS:
223 return NODE_ID_DCC | DCC_LONG_SELECTOR | addr;
224 case dcc::TrainAddressType::MM:
225 return NODE_ID_MARKLIN_MOTOROLA | addr;
226 default:
227 DIE("Unknown train address type");
228 }
229 }
230
242 NodeID id, dcc::TrainAddressType *type, uint32_t *addr)
243 {
244 HASSERT(type);
245 HASSERT(addr);
246 if ((id & NODE_ID_MASK) == NODE_ID_DCC)
247 {
248 *addr = (id & 0x3FFF);
249 if (((id & DCC_LONG_SELECTOR) == DCC_LONG_SELECTOR) ||
250 (*addr >= 128u))
251 {
252 // overlapping long address
253 *type = dcc::TrainAddressType::DCC_LONG_ADDRESS;
254 }
255 else
256 {
257 *type = dcc::TrainAddressType::DCC_SHORT_ADDRESS;
258 }
259 return true;
260 }
261 else if ((id & NODE_ID_MASK) == NODE_ID_MARKLIN_MOTOROLA)
262 {
263 *type = dcc::TrainAddressType::MM;
264 *addr = (id & 0x3FFF);
265 return true;
266 }
267 return false;
268 }
269
278 dcc::TrainAddressType type, uint32_t addr)
279 {
280 string ret(14, 0);
281 char *s = &ret[0];
282 if ((type == dcc::TrainAddressType::DCC_LONG_ADDRESS) && (addr < 128))
283 {
284 s[0] = '0';
285 s++;
286 }
287 char *e = integer_to_buffer(addr, s);
288 ret.resize(e - &ret[0]);
289 if (type == dcc::TrainAddressType::MM)
290 {
291 ret.push_back('M');
292 }
293 else if (addr < 128)
294 {
295 if (type == dcc::TrainAddressType::DCC_SHORT_ADDRESS)
296 {
297 ret.push_back('S');
298 }
299 else
300 {
301 ret.push_back('L');
302 }
303 }
304 return ret;
305 }
306
313 static string guess_train_node_name(NodeID node_id)
314 {
315 dcc::TrainAddressType decoded_type;
316 uint32_t decoded_address = 0xFFFF;
318 node_id, &decoded_type, &decoded_address))
319 {
320 // We recognized this as a legacy address. Use the legacy node name
321 // to display.
322 return train_node_name_from_legacy(decoded_type, decoded_address);
323 }
324 else
325 {
326 // Format the node MAC address.
327 uint8_t idbuf[6];
328 node_id_to_data(node_id, idbuf); // convert to big-endian
329 return mac_to_string(idbuf);
330 }
331 }
332
333 static Payload estop_set_payload() {
334 Payload p(1, 0);
335 p[0] = REQ_EMERGENCY_STOP;
336 return p;
337 }
338
339 static Payload speed_set_payload(Velocity v) {
340 Payload p(3, 0);
341 p[0] = REQ_SET_SPEED;
342 speed_to_fp16(v, &p[1]);
343 return p;
344 }
345
346 static Payload speed_get_payload() {
347 Payload p(1, 0);
348 p[0] = REQ_QUERY_SPEED;
349 return p;
350 }
351
360 const Payload &p, Velocity *v, bool *is_estop = nullptr)
361 {
362 if (p.size() < 3)
363 {
364 return false;
365 }
366 *v = fp16_to_speed(p.data() + 1);
367 if (std::isnan(v->speed()))
368 {
369 return false;
370 }
371 if (is_estop)
372 {
373 if (p.size() < 4)
374 {
375 return false;
376 }
377 *is_estop = (p[3] & SPEEDRESP_STATUS_IS_ESTOP) != 0;
378 }
379 return true;
380 }
381
382 static Payload fn_set_payload(unsigned address, uint16_t value) {
383 Payload p(6, 0);
384 p[0] = REQ_SET_FN;
385 p[1] = (address >> 16) & 0xff;
386 p[2] = (address >> 8) & 0xff;
387 p[3] = address & 0xff;
388 p[4] = (value >> 8) & 0xff;
389 p[5] = value & 0xff;
390 return p;
391 }
392
393 static Payload fn_get_payload(unsigned address) {
394 Payload p(4, 0);
395 p[0] = REQ_QUERY_FN;
396 p[1] = (address >> 16) & 0xff;
397 p[2] = (address >> 8) & 0xff;
398 p[3] = address & 0xff;
399 return p;
400 }
401
407 static bool fn_get_parse(
408 const Payload &p, uint16_t *value, unsigned *address)
409 {
410 if (p.size() < 6)
411 {
412 return false;
413 }
414 unsigned num = uint8_t(p[1]);
415 num <<= 8;
416 num |= uint8_t(p[2]);
417 num <<= 8;
418 num |= uint8_t(p[3]);
419 *address = num;
420 *value = (((uint16_t)p[4]) << 8) | uint8_t(p[5]);
421 return true;
422 }
423
424 static Payload assign_controller_payload(Node *ctrl)
425 {
426 Payload p(9, 0);
427 p[0] = REQ_CONTROLLER_CONFIG;
428 p[1] = CTRLREQ_ASSIGN_CONTROLLER;
429 p[2] = 0;
430 node_id_to_data(ctrl->node_id(), &p[3]);
431 return p;
432 }
433
434 static Payload release_controller_payload(Node *ctrl)
435 {
436 Payload p(9, 0);
437 p[0] = REQ_CONTROLLER_CONFIG;
438 p[1] = CTRLREQ_RELEASE_CONTROLLER;
439 p[2] = 0;
440 node_id_to_data(ctrl->node_id(), &p[3]);
441 return p;
442 }
443
444 static Payload consist_add_payload(NodeID slave, uint8_t flags)
445 {
446 Payload p(9, 0);
447 p[0] = REQ_CONSIST_CONFIG;
448 p[1] = CNSTREQ_ATTACH_NODE;
449 p[2] = flags;
450 node_id_to_data(slave, &p[3]);
451 return p;
452 }
453
454 static Payload consist_add_response(NodeID slave, uint16_t error_code)
455 {
456 Payload p(10, 0);
457 p[0] = RESP_CONSIST_CONFIG;
458 p[1] = CNSTRESP_ATTACH_NODE;
459 node_id_to_data(slave, &p[2]);
460 error_to_data(error_code, &p[8]);
461 return p;
462 }
463
464 static Payload consist_del_payload(NodeID slave)
465 {
466 Payload p(9, 0);
467 p[0] = REQ_CONSIST_CONFIG;
468 p[1] = CNSTREQ_DETACH_NODE;
469 node_id_to_data(slave, &p[3]);
470 return p;
471 }
472
473 static Payload consist_del_response(NodeID slave, uint16_t error_code)
474 {
475 Payload p = consist_add_response(slave, error_code);
476 p[1] = CNSTRESP_DETACH_NODE;
477 return p;
478 }
479
480 static Payload consist_qry_payload()
481 {
482 Payload p(2, 0);
483 p[0] = REQ_CONSIST_CONFIG;
484 p[1] = CNSTREQ_QUERY_NODES;
485 return p;
486 }
487
488 static Payload consist_qry_payload(uint8_t arg)
489 {
490 Payload p(3, 0);
491 p[0] = REQ_CONSIST_CONFIG;
492 p[1] = CNSTREQ_QUERY_NODES;
493 p[2] = arg;
494 return p;
495 }
496
497 static Payload consist_qry_response_short(uint8_t num_entries)
498 {
499 Payload p(3, 0);
500 p[0] = RESP_CONSIST_CONFIG;
501 p[1] = CNSTRESP_QUERY_NODES;
502 p[2] = num_entries;
503 return p;
504 }
505
506 static Payload consist_qry_response_long(
507 uint8_t num_entries, uint8_t index, uint8_t flags, NodeID slave)
508 {
509 Payload p(11, 0);
510 p[0] = RESP_CONSIST_CONFIG;
511 p[1] = CNSTRESP_QUERY_NODES;
512 p[2] = num_entries;
513 p[3] = index;
514 p[4] = flags;
515 node_id_to_data(slave, &p[5]);
516 return p;
517 }
518
521 static Payload heartbeat_request_payload(uint8_t deadline_sec = 3)
522 {
523 Payload p(3, 0);
524 p[0] = RESP_TRACTION_MGMT;
525 p[1] = MGMTRESP_HEARTBEAT;
526 p[2] = deadline_sec;
527 return p;
528 }
529
532 {
533 Payload p(2, 0);
534 p[0] = REQ_TRACTION_MGMT;
535 p[1] = MGMTREQ_NOOP;
536 return p;
537 }
538};
539
540} // namespace openlcb
541
542#endif // _OPENLCB_TRACTIONDEFS_HXX_
Node information.
Definition Devtab.hxx:549
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
This class provides a mechanism for working with velocity in different forms.
Definition Velocity.hxx:73
float speed() const
Return the speed independent of direction.
Definition Velocity.hxx:156
TrainAddressType
Which address type this legacy train node uses.
Definition dcc/Defs.hxx:46
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
void node_id_to_data(NodeID id, void *data)
Convenience function to render a 48-bit NMRAnet node ID into an existing buffer.
Definition If.cxx:52
void speed_to_fp16(SpeedType speed, void *fp16)
Renders a SpeedType value to an unaligned memory address, typically to the output buffer.
uint64_t NodeID
48-bit NMRAnet Node ID type
SpeedType fp16_to_speed(const void *fp16)
Parses a SpeedType value from an unaligned memory address, typically from the input buffer.
void error_to_data(uint16_t error_code, void *data)
Writes an error code into a payload object at a given pointer.
Definition If.cxx:78
string Payload
Container that carries the data bytes in an NMRAnet message.
Velocity SpeedType
Represents an OpenLCB speed value with accessors to convert to and from various formats.
Static constants and helper functions for the Traciton protocol family.
static bool fn_get_parse(const Payload &p, uint16_t *value, unsigned *address)
Parses the response payload of a GET_FN packet.
static string guess_train_node_name(NodeID node_id)
Renders a guess at the traction node name.
static const uint64_t NODE_ID_DC_BLOCK
Node ID space allocated for DC blocks.
static const uint16_t DCC_LONG_SELECTOR
Long addresses should OR this selector to {}.
static constexpr uint64_t INACTIVATE_BASIC_DCC_ACCESSORY_EVENT_BASE
Base address of DCC accessory decoder well-known event range (inactive)
static const uint64_t NODE_ID_MTH_DCS
Node ID space allocated for the MTH DCS protocol.
static bool legacy_address_from_train_node_id(NodeID id, dcc::TrainAddressType *type, uint32_t *addr)
Converts a node ID to a legacy address if possible.
static const uint64_t NODE_ID_DCC
Node ID space allocated for DCC locomotives.
static const uint64_t NODE_ID_MARKLIN_MOTOROLA
Node ID space allocated for the Marklin-Motorola protocol.
static const uint64_t IS_TRAIN_EVENT
This event should be produced by train nodes.
static Payload noop_payload()
Generates a Noop message, to be sent from the throttle to the train node.
static const uint64_t IS_PROXY_EVENT
This event should be produced by traction proxy nodes.
static constexpr uint64_t EXT_DCC_ACCESSORY_EVENT_BASE
Base address of DCC extended accessory decoder well-known event range.
static bool speed_get_parse_last(const Payload &p, Velocity *v, bool *is_estop=nullptr)
Parses the response payload of a GET_SPEED packet.
static NodeID train_node_id_from_legacy(dcc::TrainAddressType type, uint32_t addr)
Converts a legacy address to an NMRAnet node ID.
static string train_node_name_from_legacy(dcc::TrainAddressType type, uint32_t addr)
Converts a legacy address to a user-visible name.
static const uint64_t NODE_ID_MASK
Node ID space mask.
static const uint64_t NODE_ID_TMCC
Node ID space allocated for TMCC protocol.
static constexpr uint64_t ACTIVATE_BASIC_DCC_ACCESSORY_EVENT_BASE
Base address of DCC accessory decoder well-known event range (active)
static Payload heartbeat_request_payload(uint8_t deadline_sec=3)
Generates a Heartbeat Request, to be sent from the train node to the controller.
@ FNCONFIG_FN
F0-F28 are at offset 0 to 28 here.
@ FUNCTION_CONFIG_MEMORY_SPACE
This is the memory space number for accessing an NMRA DCC locomotive's functions via the memory confi...
@ FNCONFIG_BINARYSTATE_SHORT
Binary State Control Instruction short form.
@ REQ_MASK
Mask to apply to the command byte of the requests.
@ FNCONFIG_BINARYSTATE_LONG
Binary State Control Instruction long form.
@ FNCONFIG_ANALOG_OUTPUT
Analog outputs, defined by NMRA DCC WG Topic 9910241.
@ REQ_LISTENER
Flag bit in the command byte set when a listener command is forwarded.