Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
ConfigUpdateFlow.hxx
Go to the documentation of this file.
1
37#ifndef _OPENLCB_CONFIGUPDATEFLOW_HXX_
38#define _OPENLCB_CONFIGUPDATEFLOW_HXX_
39
40#include "openmrn_features.h"
43#include "openlcb/NodeInitializeFlow.hxx"
45
46#if OPENMRN_FEATURE_REBOOT
47extern "C" {
49extern void reboot();
50}
51#endif // OPENMRN_FEATURE_REBOOT
52
53namespace openlcb
54{
55
62 private Atomic
63{
64public:
65 ConfigUpdateFlow(If *iface)
66 : StateFlowBase(iface)
68 , needsReboot_(0)
69 , needsReInit_(0)
70 , fd_(-1)
71 {
72 }
73
76 int open_file(const char *path);
78 void init_flow();
80 void factory_reset();
81
84 int get_fd()
85 {
86 return fd_;
87 }
88
89#ifdef GTEST
90 void TEST_set_fd(int fd)
91 {
92 fd_ = fd;
93 }
94 bool TEST_is_terminated()
95 {
96 return is_terminated();
97 }
98 bool TEST_get_needs_reboot()
99 {
100 return needsReboot_;
101 }
102 bool TEST_get_needs_reinit()
103 {
104 return needsReInit_;
105 }
106#endif // GTEST
107
108 void trigger_update() override
109 {
110 AtomicHolder h(this);
112 needsReboot_ = 0;
113 needsReInit_ = 0;
114 if (is_state(exit().next_state()))
115 {
116 start_flow(STATE(call_next_listener));
117 }
118 }
119
120 void register_update_listener(ConfigUpdateListener *listener) override;
121 void unregister_update_listener(ConfigUpdateListener *listener) override;
122private:
123 Action call_next_listener()
124 {
125 ConfigUpdateListener *l = nullptr;
126 {
127 AtomicHolder h(this);
128 if (nextRefresh_ == listeners_.end())
129 {
130 return call_immediately(STATE(do_initial_load));
131 }
132 l = nextRefresh_.operator->();
133 ++nextRefresh_;
134 }
135 return call_listener(l, false);
136 }
137
138 Action call_listener(ConfigUpdateListener *l, bool is_initial)
139 {
140 if (fd_ < 0)
141 {
142 DIE("CONFIG_FILENAME not specified, or init() was not called, but "
143 "there are configuration listeners.");
144 }
146 l->apply_configuration(fd_, is_initial, n_.reset(this));
147 switch (action)
148 {
150 {
151 break;
152 }
154 {
155 needsReInit_ = 1;
156 break;
157 }
159 {
160 needsReboot_ = 1;
161 break;
162 }
163 }
164 return wait();
165 }
166
167 Action do_initial_load()
168 {
169 ConfigUpdateListener *l = nullptr;
170 {
171 AtomicHolder h(this);
173 {
176 }
177 }
178 if (!l)
179 {
180 return apply_action();
181 }
182 return call_listener(l, true);
183 }
184
186 {
188 if (needsReboot_)
189 {
190#if OPENMRN_FEATURE_REBOOT
191 reboot();
192#endif
193 }
194 if (needsReInit_)
195 {
196 // Takes over ownership of itself, will delete when done.
197 new ReinitAllNodes(static_cast<If *>(service()));
198 }
199 return exit();
200 }
201
202 typedef TypedQueue<ConfigUpdateListener> queue_type;
211 unsigned needsReboot_ : 1;
213 unsigned needsReInit_ : 1;
214 int fd_;
216};
217
218} // namespace openlcb
219
220#endif // _OPENLCB_CONFIGUPDATEFLOW_HXX_
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
Abstract class for components that need to receive configuration from EEPROM.
UpdateAction
Specifies what additional steps are needed to apply the new configuration.
@ REINIT_NEEDED
Need to perform application-level reinitialization.
@ UPDATED
No additional step is necessary.
@ REBOOT_NEEDED
Need to reboot the hardware.
virtual UpdateAction apply_configuration(int fd, bool initial_load, BarrierNotifiable *done)=0
Notifies the component that there is new configuration available for loading.
Virtual interface for the config update listeners to register themselves for receiving configuration ...
STL-compatible iterator for TypedQueue.
end_iterator end()
Return type for a state flow callback.
Base class for state machines.
Service * service()
Return a pointer to the service I am bound to.
bool is_terminated()
StateFlowBase()
Default constructor.
Action exit()
Terminate current StateFlow activity.
void start_flow(Callback c)
Resets the flow to the specified state and starts it.
Action wait()
Wait for an asynchronous call.
Action call_immediately(Callback c)
Imediately call the next state upon return.
bool is_state(Callback c)
A simple, fast, type-safe single-linked queue class with non-virtual methods.
T * pop_front()
Removes the entry at the front of the queue.
iterator begin()
void push_front(T *entry)
Inserts an entry to the front of the queue.
Implementation of the ConfigUpdateService: state flow issuing all the calls to the registered ConfigU...
int open_file(const char *path)
Must be called once (only) before calling anything else.
queue_type pendingListeners_
All listeners that have not yet been added to listeners_ and their initial load needs to be called.
void trigger_update() override
Executes an update in response to the configuration having changed.
queue_type listeners_
All registered update listeners. Protected by Atomic *this.
unsigned needsReboot_
did anybody request a reboot to happen?
void init_flow()
Asynchronously invokes all update listeners with the config FD.
void register_update_listener(ConfigUpdateListener *listener) override
Adds a config update listener to be called upon configuration updates.
void unregister_update_listener(ConfigUpdateListener *listener) override
Removes a config update listener.
void factory_reset()
Synchronously invokes all update listeners to factory reset.
unsigned needsReInit_
did anybody request a node reinit to happen?
queue_type::iterator nextRefresh_
Where are we in the refresh cycle.
Abstract class representing an OpenLCB Interface.
Definition If.hxx:185
StateFlow that iterates through all local nodes and sends out node initialization complete for each o...
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143