Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
SimpleStack.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_SIMPLESTACK_HXX_
36#define _OPENLCB_SIMPLESTACK_HXX_
37
38#include <fcntl.h>
39
40#include "executor/Executor.hxx"
41#include "nmranet_config.h"
42#include "openmrn_features.h"
51#include "openlcb/IfCan.hxx"
53#include "openlcb/NodeInitializeFlow.hxx"
58#include "utils/ActivityLed.hxx"
59#include "utils/GcTcpHub.hxx"
61#include "utils/HubDevice.hxx"
62#include "utils/HubDeviceNonBlock.hxx"
63#ifdef OPENMRN_FEATURE_FD_CAN_DEVICE
64#include "utils/HubDeviceSelect.hxx"
65#endif
66
67namespace openmrn_arduino
68{
69class OpenMRN;
70}
71
72namespace openlcb
73{
74
76extern const char CDI_DATA[];
77
80extern const char *const CONFIG_FILENAME;
83extern const size_t CONFIG_FILE_SIZE;
84
86{
87protected:
91 {
92 public:
93 virtual ~PhysicalIf()
94 {
95 }
96
98 virtual If *iface() = 0;
102 };
103
104public:
105 static const unsigned EXECUTOR_PRIORITIES = 5;
106
108 std::function<std::unique_ptr<PhysicalIf>()> create_if_helper);
109
116
119 {
120 return &service_;
121 }
122
125 {
126 return iface_;
127 }
128
132 {
133 return datagramService_;
134 }
135
138 {
139 return &infoFlow_;
140 }
141
144 virtual Node *node() = 0;
145
149 {
150 return &memoryConfigHandler_;
151 }
152
153 ConfigUpdateService *config_service()
154 {
155 return &configUpdateFlow_;
156 }
157
163 const Gpio *led, long long period = MSEC_TO_NSEC(33))
164 {
165 auto *al = new ActivityLed(iface(), led, period);
166 iface()->set_tx_hook(std::bind(&ActivityLed::activity, al));
167 }
168
171 void restart_stack();
172
173 friend class ::openmrn_arduino::OpenMRN;
174
177 void loop_executor(bool delay_start = false)
178 {
179 start_stack(delay_start);
181 }
182
185 void start_after_delay();
186
193 void start_executor_thread(const char *name, int priority,
194 size_t stack_size, bool delay_start = false)
195 {
196 start_stack(delay_start);
197 executor_.start_thread(name, priority, stack_size);
198 }
199
212 int create_config_file_if_needed(const InternalConfigData &ofs,
213 uint16_t expected_version, unsigned file_size);
214
218 int check_version_and_factory_reset(const InternalConfigData &ofs,
219 uint16_t expected_version, bool force = false);
220
222 static void factory_reset_all_events(
223 const InternalConfigData &ofs, uint64_t node_id, int fd);
224
231 static void set_event_offsets(const vector<uint16_t> *offsets);
232
237 void send_event(uint64_t event_id)
238 {
239 auto *b = node()->iface()->global_message_write_flow()->alloc();
240 b->data()->reset(Defs::MTI_EVENT_REPORT, node()->node_id(),
241 eventid_to_buffer(event_id));
242 node()->iface()->global_message_write_flow()->send(b);
243 }
244
252 Defs::MTI mti, NodeHandle dst, const string &payload = EMPTY_PAYLOAD)
253 {
254 auto *b = node()->iface()->addressed_message_write_flow()->alloc();
255 b->data()->reset(mti, node()->node_id(), dst, payload);
256 node()->iface()->addressed_message_write_flow()->send(b);
257 }
258
261 virtual uint64_t get_pip()
262 {
263 return 0;
264 }
265
266protected:
269 void start_stack(bool delay_start);
270
273 virtual void start_iface(bool restart) = 0;
274
276 virtual void start_node() = 0;
277
280 void default_start_node();
281
287 std::unique_ptr<PhysicalIf> ifaceHolder_;
289 If *iface_ {ifaceHolder_->iface()};
301
302 MemoryConfigHandler memoryConfigHandler_ {
303 datagramService_, nullptr, config_num_memory_spaces()};
304
307 std::unique_ptr<HubFlow> gcHub_;
309 std::unique_ptr<GCAdapterBase> gcAdapter_;
311 std::vector<std::unique_ptr<Destructable>> additionalComponents_;
312};
313
316{
317public:
318 SimpleCanStackBase(const openlcb::NodeID node_id);
319
325 {
326 return &static_cast<CanPhysicalIf *>(ifaceHolder_.get())->canHub0_;
327 }
328
330 void add_can_port_blocking(const char *device)
331 {
332 int can_fd = ::open(device, O_RDWR);
333 HASSERT(can_fd >= 0);
334 auto *port = new FdHubPort<CanHubFlow>(
336 additionalComponents_.emplace_back(port);
337 }
338
339#ifdef OPENMRN_FEATURE_FD_CAN_DEVICE
344 void add_can_port_async(const char *device)
345 {
346 auto *port = new HubDeviceNonBlock<CanHubFlow>(can_hub(), device);
347 additionalComponents_.emplace_back(port);
348 }
349
351 void add_can_port_select(const char *device)
352 {
353 auto *port = new HubDeviceSelect<CanHubFlow>(can_hub(), device);
354 additionalComponents_.emplace_back(port);
355 }
356
360 void add_can_port_select(int fd, Notifiable *on_error = nullptr)
361 {
362 auto *port = new HubDeviceSelect<CanHubFlow>(can_hub(), fd, on_error);
363 additionalComponents_.emplace_back(port);
364 }
365#endif // OPENMRN_FEATURE_FD_CAN_DEVICE
366
368 void add_gridconnect_port(const char *path, Notifiable *on_exit = nullptr);
369
370#if defined(__linux__) || defined(__MACH__)
376 void add_gridconnect_tty(const char *device, Notifiable *on_exit = nullptr);
377#endif
378#if defined(__linux__)
384 void add_socketcan_port_select(const char *device, int loopback = 1);
385#endif
386
390 void start_tcp_hub_server(int port = 12021)
391 {
394 gcHubServer_.reset(new GcTcpHub(can_hub(), port));
395 }
396
401 {
402 return gcHubServer_.get();
403 }
404
407 {
408 gcHubServer_.reset(nullptr);
409 }
410
412 void connect_tcp_gridconnect_hub(const char *host, int port)
413 {
414 int fd = ConnectSocket(host, port);
415 HASSERT(fd >= 0);
417 }
418
420 void print_all_packets(bool timestamped = false)
421 {
422 auto *port = new DisplayPort(&service_, timestamped);
424 additionalComponents_.emplace_back(port);
425 }
426
434 {
435 if (!gcHub_)
436 {
437 gcHub_.reset(new HubFlow(&service_));
439 gcHub_.get(), can_hub(), false));
440 }
441 return gcHub_.get();
442 }
443
444 IfCan *if_can()
445 {
446 return &static_cast<CanPhysicalIf *>(ifaceHolder_.get())->ifCan_;
447 }
448
451 void add_stream_support();
452
460 {
461 public:
463 {
465 }
466 };
467
468protected:
470 void start_iface(bool restart) override;
471
472private:
474 {
475 public:
479 config_local_alias_cache_size(),
480 config_remote_alias_cache_size(), config_local_nodes_count())
481 , datagramService_(&ifCan_, config_num_datagram_registry_entries(),
482 config_num_datagram_clients())
483 {
484 AddAliasAllocator(node_id, &ifCan_);
485 }
486
488 {
489 }
490
492 If *iface() override
493 {
494 return &ifCan_;
495 }
499 {
500 return &datagramService_;
501 }
510 };
511
515 std::unique_ptr<PhysicalIf> create_if(const openlcb::NodeID node_id);
516
518 std::unique_ptr<GcTcpHub> gcHubServer_;
519};
520
522{
523public:
524 SimpleTcpStackBase(const openlcb::NodeID node_id);
525
533 void add_tcp_port_select(int fd, Notifiable* on_error = nullptr)
534 {
535 if_tcp()->add_network_fd(fd, on_error);
536 }
537
538protected:
540 void start_iface(bool restart) override;
541
542private:
549 {
550 return &static_cast<TcpPhysicalIf *>(ifaceHolder_.get())->tcpHub_;
551 }
552
555 {
556 return &static_cast<TcpPhysicalIf *>(ifaceHolder_.get())->ifTcp_;
557 }
558
560 {
561 public:
564 , ifTcp_(node_id, &tcpHub_, config_local_nodes_count())
565 , datagramService_(&ifTcp_, config_num_datagram_registry_entries(),
566 config_num_datagram_clients())
567 {
568 }
569
571 {
572 }
573
575 If *iface() override
576 {
577 return &ifTcp_;
578 }
582 {
583 return &datagramService_;
584 }
592 };
593
597 std::unique_ptr<PhysicalIf> create_if(const openlcb::NodeID node_id);
598};
599
613{
614public:
615 SimpleCanStack(const openlcb::NodeID node_id);
616
619 Node *node() override
620 {
621 return &node_;
622 }
623
624private:
625 static const auto PIP_RESPONSE = Defs::EVENT_EXCHANGE | Defs::DATAGRAM |
626 Defs::MEMORY_CONFIGURATION | Defs::ABBREVIATED_DEFAULT_CDI |
627 Defs::SIMPLE_NODE_INFORMATION | Defs::CDI;
628
629 void start_node() override
630 {
632 }
633
636 uint64_t get_pip() override
637 {
638 return PIP_RESPONSE;
639 }
640
647};
648
650{
651public:
652 SimpleTcpStack(const openlcb::NodeID node_id);
653
656 Node *node() override
657 {
658 return &node_;
659 }
660
661private:
662 static const auto PIP_RESPONSE = Defs::EVENT_EXCHANGE | Defs::DATAGRAM |
663 Defs::MEMORY_CONFIGURATION | Defs::ABBREVIATED_DEFAULT_CDI |
664 Defs::SIMPLE_NODE_INFORMATION | Defs::CDI;
665
666 void start_node() override
667 {
669 }
670
673 uint64_t get_pip() override
674 {
675 return PIP_RESPONSE;
676 }
677
684};
685
688{
689public:
695 openlcb::TrainImpl *train, const char *fdi_xml, NodeID node_id);
696
699 Node *node() override
700 {
701 return &trainNode_;
702 }
703
704private:
705 static const auto PIP_RESPONSE = openlcb::Defs::SIMPLE_PROTOCOL_SUBSET |
706 openlcb::Defs::DATAGRAM | openlcb::Defs::MEMORY_CONFIGURATION |
707 openlcb::Defs::EVENT_EXCHANGE | openlcb::Defs::SIMPLE_NODE_INFORMATION |
708 openlcb::Defs::TRACTION_CONTROL | openlcb::Defs::TRACTION_FDI |
709 Defs::ABBREVIATED_DEFAULT_CDI | Defs::CDI;
710
711 void start_node() override;
712
715 uint64_t get_pip() override
716 {
717 return PIP_RESPONSE;
718 }
719
720 TrainService tractionService_ {iface()};
724 isTrainEventHandler {&trainNode_};
725 ReadOnlyMemoryBlock fdiBlock_;
730};
731
732} // namespace openlcb
733
734#endif // _OPENLCB_SIMPLESTACK_HXX_
void create_gc_port_for_can_hub(CanHubFlow *can_hub, int fd, Notifiable *on_exit, bool use_select)
Creates a new port on a CAN hub in gridconnect format for a select-compatible file descriptor.
GenericHubFlow< HubData > HubFlow
A generic hub that proxies packets of untyped (aka string) data.
Definition Hub.hxx:182
Operates an LED to visually display some activity.
void activity()
Call this function when activity happens.
Virtual interface for the config update listeners to register themselves for receiving configuration ...
This port prints all traffic from a (string-typed) hub to stdout.
Definition Hub.hxx:188
static Notifiable * DefaultInstance()
Implementation the ExecutorBase with a specific number of priority bands.
Definition Executor.hxx:266
void start_thread(const char *name, int priority, size_t stack_size)
Creates a new thread for running this executor.
Definition Executor.hxx:293
void thread_body()
If the executor was created with NO_THREAD, then this function needs to be called to run the executor...
Definition Executor.hxx:344
HubPort that connects a raw device to a strongly typed Hub.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
static GCAdapterBase * CreateGridConnectAdapter(HubFlow *gc_side, CanHubFlow *can_side, bool double_bytes)
This function connects an ASCII (GridConnect-format) CAN adapter to a binary CAN adapter,...
This class runs a CAN-bus HUB listening on TCP socket using the gridconnect format.
Definition GcTcpHub.hxx:47
void register_port(port_type *port)
Adds a new port.
Definition Hub.hxx:167
OS-independent abstraction for GPIO.
Definition Gpio.hxx:43
HubPort that connects a select-aware device to a strongly typed Hub.
This is an empty struct.
Definition Executor.hxx:256
An object that can schedule itself on an executor to run.
Collection of related state machines that pend on incoming messages.
ExecutorBase * executor()
Create this object statically to add an alias allocator to an already statically allocated interface.
Implementation of the DatagramService with the CANbus-specific OpenLCB datagram protocol.
Implementation of the ConfigUpdateService: state flow issuing all the calls to the registered ConfigU...
Transport-agnostic dispatcher of datagrams.
Definition Datagram.hxx:161
Trivial implementation of a virtual Node.
Global Event Service.
Class that advertises an event ID to be produced.
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
Network interface class for a character stream link that speaks the (point-to-point) TcpTransfer prot...
Definition IfTcp.hxx:67
void add_network_fd(int fd, Notifiable *on_error=nullptr)
Adds a network client connection to the device.
Definition IfTcp.cxx:83
Abstract class representing an OpenLCB Interface.
Definition If.hxx:185
MessageHandler * global_message_write_flow()
Definition If.hxx:200
void set_tx_hook(std::function< void()> hook)
Sets a transmit hook.
Definition If.hxx:337
MessageHandler * addressed_message_write_flow()
Definition If.hxx:210
Performs upon-startup initialization of virtual nodes.
Implementation of the Memory Access Configuration Protocol for OpenLCB.
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
An instance of this class will add Protocol Identification Protocol to an NMRAnet Node.
Memory space implementation that exports a some memory-mapped data as a read-only memory space.
Handler for the Simple Node Information Protocol requests.
IfCan ifCan_
Implementation of OpenLCB interface.
DatagramService * datagram_service() override
CanDatagramService datagramService_
Datagram service (and clients) matching the interface.
CanHubFlow canHub0_
This flow is the connection between the stack and the device drivers.
Helper class to add stream support straight after construction.
SimpleStack with a CAN-bus based interface and IO functions for CAN-bus.
void add_can_port_blocking(const char *device)
Adds a CAN bus port with synchronous driver API.
void start_tcp_hub_server(int port=12021)
Starts a TCP server on the specified port in listening mode.
void shutdown_tcp_hub_server()
Shuts down the GridConnect Hub server, if previously started.
void connect_tcp_gridconnect_hub(const char *host, int port)
Connects to a CAN hub using TCP with the gridconnect protocol.
std::unique_ptr< GcTcpHub > gcHubServer_
Holds the ownership of the TCP hub server (if one was created).
GcTcpHub * get_tcp_hub_server()
Retrieve the instance of the GridConnect Hub server, which was started with start_tcp_hub_server().
void add_stream_support()
Enables stream transport in the interface and in the memory config protocol.
std::unique_ptr< PhysicalIf > create_if(const openlcb::NodeID node_id)
Constructor helper function.
void add_gridconnect_port(const char *path, Notifiable *on_exit=nullptr)
Adds a gridconnect port to the CAN bus.
void print_all_packets(bool timestamped=false)
Causes all CAN packets to be printed to stdout.
void start_iface(bool restart) override
Helper function for start_stack et al.
HubFlow * gridconnect_hub()
Returns the hub to be used for gridconnect-format CANbus.
Helper class for bringing up all components needed for a typical OpenLCB node.
ProtocolIdentificationHandler pipHandler_
Handles PIP requests.
void start_node() override
Hook for clients to initialize the node-specific components.
Node * node() override
uint64_t get_pip() override
Get the PIP response value.
SNIPHandler snipHandler_
Handles SNIP requests.
DefaultNode node_
The actual node.
StateFlow for sending out medium-sized data payloads like the Simple Node Ident Info protocol.
Polymorphic class that can be implemented by CAN and TCP interfaces separately for appropriate constr...
virtual DatagramService * datagram_service()=0
void set_tx_activity_led(const Gpio *led, long long period=MSEC_TO_NSEC(33))
Adds an activiy LED which will be flashed every time a message is sent from this node to the network.
virtual Node * node()=0
SimpleInfoFlow infoFlow_
General flow for simple info requests.
void default_start_node()
Exports the memory config spaces that are typically used for a complex node.
std::unique_ptr< GCAdapterBase > gcAdapter_
Bridge between canHub_ and gcHub_. Lazily initialized.
If * iface_
The OpenLCB interface object. Owned by ifaceHolder_;.
ConfigUpdateFlow configUpdateFlow_
Calls the config listeners with the configuration FD.
void loop_executor(bool delay_start=false)
Donates the current thread to the executor.
std::unique_ptr< PhysicalIf > ifaceHolder_
Pointer to the polymorphic implementation of the OpenLCB If.
DatagramService * dg_service()
void send_event(uint64_t event_id)
Helper function to send an event report to the bus.
Service service_
Default service on the particular executor.
void start_after_delay()
Call this function when you used delay_start upon starting the executor.
Executor< EXECUTOR_PRIORITIES > * executor()
std::unique_ptr< HubFlow > gcHub_
All packets are forwarded to this hub in gridconnect format, if needed.
int create_config_file_if_needed(const InternalConfigData &ofs, uint16_t expected_version, unsigned file_size)
Tries to open the config file; if not existant, the size too small, or the version number is mismatch...
void restart_stack()
Reinitializes the node.
Executor< EXECUTOR_PRIORITIES > executor_
This executor's threads will be handled.
virtual void start_iface(bool restart)=0
Hook for descendant classes to start the interface.
EventService eventService_
Dispatches event protocol requests to the event handlers.
virtual void start_node()=0
Hook for clients to initialize the node-specific components.
std::vector< std::unique_ptr< Destructable > > additionalComponents_
Stores and keeps ownership of optional components.
DatagramService * datagramService_
The datagram service bound to the interface object.
int check_version_and_factory_reset(const InternalConfigData &ofs, uint16_t expected_version, bool force=false)
Checks the version information in the EEPROM and performs a factory reset if incorrect or if force is...
static void factory_reset_all_events(const InternalConfigData &ofs, uint64_t node_id, int fd)
Overwrites all events in the eeprom with a brand new event ID.
void start_executor_thread(const char *name, int priority, size_t stack_size, bool delay_start=false)
Instructs the executor to create a new thread and run in there.
SimpleInfoFlow * info_flow()
Accessor for clients that have their custom SNIP-like handler.
void start_stack(bool delay_start)
Call this function once after the actual IO ports are set up.
virtual uint64_t get_pip()
Get the PIP response value.
InitializeFlow initFlow_
The initialization flow takes care for node startup duties.
void send_message_to(Defs::MTI mti, NodeHandle dst, const string &payload=EMPTY_PAYLOAD)
Sends an addressed message to the bus.
MemoryConfigHandler * memory_config_handler()
static void set_event_offsets(const vector< uint16_t > *offsets)
Call this function at the beginning of appl_main, just before {} or { create_config_file_if_needed} i...
TcpDatagramService datagramService_
Datagram service (and clients) matching the interface.
IfTcp ifTcp_
Implementation of OpenLCB interface.
HubFlow tcpHub_
This flow is the connection between the stack and the device drivers.
DatagramService * datagram_service() override
void start_iface(bool restart) override
Helper function for start_stack et al.
std::unique_ptr< PhysicalIf > create_if(const openlcb::NodeID node_id)
Constructor helper function.
HubFlow * tcp_hub()
This function is not safe to use.
void add_tcp_port_select(int fd, Notifiable *on_error=nullptr)
Adds a new link to the TCP interface.
SNIPHandler snipHandler_
Handles SNIP requests.
uint64_t get_pip() override
Get the PIP response value.
DefaultNode node_
The actual node.
void start_node() override
Hook for clients to initialize the node-specific components.
Node * node() override
ProtocolIdentificationHandler pipHandler_
Handles PIP requests.
CAN-based stack with TrainNode.
ProtocolIdentificationHandler pipHandler_
Handles PIP requests.
TrainNodeWithId trainNode_
The actual node.
void start_node() override
Hook for clients to initialize the node-specific components.
SNIPHandler snipHandler_
Handles SNIP requests.
uint64_t get_pip() override
Get the PIP response value.
Implementation of the DatagramService on TCP transfer.
Abstract base class for train implementations.
Train node class with a fixed OpenLCB Node ID.
Collection of control flows necessary for implementing the Traction Protocol.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
uint64_t NodeID
48-bit NMRAnet Node ID type
const char CDI_DATA[]
This symbol contains the embedded text of the CDI xml file.
Payload eventid_to_buffer(uint64_t eventid)
Converts an Event ID to a Payload suitable to be sent as an event report.
Definition If.cxx:72
const char *const CONFIG_FILENAME
This symbol must be defined by the application to tell which file to open for the configuration liste...
const size_t CONFIG_FILE_SIZE
This symbol must be defined by the application.
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268
int ConnectSocket(const char *host, int port)
Connects a tcp socket to the specified remote host:port.
MTI
Known Message type indicators.
Container of both a NodeID and NodeAlias.