FiniteStateMachine.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package ArmarXCore
17  * @author Christian Dreher <c.dreher@kit.edu>
18  * @date 2020
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 
24 #pragma once
25 
26 
27 // STD/STL
28 #include <exception>
29 #include <functional>
30 #include <map>
31 #include <memory>
32 #include <optional>
33 #include <tuple>
34 
35 
36 namespace armarx
37 {
38 
39 
40  template <typename T, typename ChainType, typename StatusType, StatusType failure_status>
42  {
43 
44  public:
45 
46  using state = std::function<StatusType(T*)>;
47  static const StatusType failure_status_value = failure_status;
48 
49  protected:
50 
51  mutable T* m_context;
52  std::optional<state> m_start_state;
53  std::optional<state> m_init_from_state;
54  std::optional<state> m_init_to_state;
55  std::optional<state> m_default_fail_state;
56  std::map<long, std::optional<state>> m_states;
57  std::map<std::tuple<long, StatusType>, long> m_transitions;
58 
59  public:
60 
62  {
63  m_context = &context;
64  }
65 
66  FiniteStateMachine(std::shared_ptr<T> context)
67  {
68  m_context = *context;
69  }
70 
72  {
73  m_context = context;
74  }
75 
76  ChainType&
77  start_from(state start_state)
78  {
79  m_start_state = start_state;
80  return *static_cast<ChainType*>(this);
81  }
82 
83  ChainType&
84  from(state from_state)
85  {
86  if (not m_init_from_state)
87  {
88  m_init_from_state = from_state;
89  }
90  else
91  {
92  throw std::logic_error{"From-state was already set. Did you forget to append "
93  "on_success() or on_failure()?"};
94  }
95 
96  return *static_cast<ChainType*>(this);
97  }
98 
99  ChainType&
100  to(state to_state)
101  {
102  if (not m_init_to_state)
103  {
104  m_init_to_state = to_state;
105  }
106  else
107  {
108  throw std::logic_error{"To-state was already set. Did you forget to append "
109  "on_success() or on_failure()?"};
110  }
111 
112  return *static_cast<ChainType*>(this);
113  }
114 
115  ChainType&
116  on(StatusType status)
117  {
118  if (not m_init_from_state or not m_init_to_state)
119  {
120  throw std::logic_error{"Both from-state and to-state must be set with "
121  "from(...).to(...)."};
122  }
123 
124  // Get the ID's of the two functions.
125  const long id1 = get_id(m_init_from_state);
126  const long id2 = get_id(m_init_to_state);
127 
128  // Make sure that the states are known.
129  if (m_states.find(id1) == m_states.end())
130  {
131  m_states[id1] = m_init_from_state.value();
132  }
133  if (m_states.find(id2) == m_states.end())
134  {
135  m_states[id2] = m_init_to_state.value();
136  }
137 
138  // Add transitions.
139  m_transitions[std::make_tuple(id1, status)] = id2;
140 
141  m_init_from_state = std::nullopt;
142  m_init_to_state = std::nullopt;
143 
144  return *static_cast<ChainType*>(this);
145  }
146 
147  ChainType&
149  {
150  return on(failure_status_value);
151  }
152 
153  ChainType&
155  {
156  if (m_init_from_state)
157  {
158  throw std::logic_error{"When using on_any_failure(), from-state must be unset."};
159  }
160 
162  m_init_to_state = std::nullopt;
163 
164  return *static_cast<ChainType*>(this);
165  }
166 
167  virtual
168  StatusType
169  run()
170  const
171  {
172  if (m_init_from_state.has_value() or m_init_to_state.has_value())
173  {
174  throw std::logic_error{"From-state or to-state set but not commited with .on()"};
175  }
176 
177  if (not m_start_state.has_value())
178  {
179  throw std::logic_error{"Cannot run without a start state."};
180  }
181 
182  long current_state_id;
183  StatusType status;
184  std::optional<state> next_state = m_start_state;
185 
186  do
187  {
188  current_state_id = get_id(next_state);
189  status = next_state.value()(m_context);
190  next_state = get_next_state(current_state_id, status);
191  }
192  while (status != failure_status_value and next_state.has_value());
193 
195  {
196  if (not next_state.has_value() and m_default_fail_state.has_value())
197  {
198  next_state = m_default_fail_state;
199  }
200 
201  status = next_state.value()(m_context);
202  }
203 
204  return status;
205  }
206 
207  protected:
208 
209  std::optional<state>
210  get_next_state(const long state_id, const StatusType status)
211  const
212  {
213  auto key = std::make_tuple(state_id, status);
214  if (m_transitions.find(key) != m_transitions.end())
215  {
216  const long target_id = m_transitions.at(key);
217  return m_states.at(target_id);
218  }
219 
220  return std::nullopt;
221  }
222 
223  long
224  get_id(std::optional<state> fn)
225  const
226  {
227  return get_id(fn.value());
228  }
229 
230  long
231  get_id(const state& fn)
232  const
233  {
234  return *(long*)(char*)&fn;
235  }
236 
237  };
238 
239 
240  template <typename T>
242  public FiniteStateMachine<T, BoolFiniteStateMachine<T>, bool, false>
243  {
244 
245  public:
246 
248 
251  {
252  this->on(true);
253  return *this;
254  }
255 
256  };
257 
258 
259  enum class status
260  {
261  success,
262  failure
263  };
264 
265 
266  template <typename T>
268  public FiniteStateMachine<T, BinaryFiniteStateMachine<T>, status, status::failure>
269  {
270 
271  public:
272 
275 
278  {
279  this->on(status::success);
280  return *this;
281  }
282 
283  };
284 
285 
286  /**
287  * User-defined template argument deductions.
288  */
289  template <typename T>
290  BoolFiniteStateMachine(T*) -> BoolFiniteStateMachine<T>;
291  template <typename T>
292  BoolFiniteStateMachine(T&) -> BoolFiniteStateMachine<T>;
293  template <typename T>
294  BoolFiniteStateMachine(std::shared_ptr<T>) -> BoolFiniteStateMachine<T>;
295  template <typename T>
296  BinaryFiniteStateMachine(T*) -> BinaryFiniteStateMachine<T>;
297  template <typename T>
298  BinaryFiniteStateMachine(T&) -> BinaryFiniteStateMachine<T>;
299  template <typename T>
300  BinaryFiniteStateMachine(std::shared_ptr<T>) -> BinaryFiniteStateMachine<T>;
301 
302 
303 }
armarx::FiniteStateMachine::m_states
std::map< long, std::optional< state > > m_states
Definition: FiniteStateMachine.h:56
armarx::FiniteStateMachine::m_init_from_state
std::optional< state > m_init_from_state
Definition: FiniteStateMachine.h:53
armarx::FiniteStateMachine::get_id
long get_id(const state &fn) const
Definition: FiniteStateMachine.h:231
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(T &context)
Definition: FiniteStateMachine.h:61
armarx::FiniteStateMachine::m_default_fail_state
std::optional< state > m_default_fail_state
Definition: FiniteStateMachine.h:55
armarx::FiniteStateMachine::on_failure
ChainType & on_failure()
Definition: FiniteStateMachine.h:148
armarx::status::failure
@ failure
armarx::FiniteStateMachine::get_next_state
std::optional< state > get_next_state(const long state_id, const StatusType status) const
Definition: FiniteStateMachine.h:210
armarx::status
status
Definition: FiniteStateMachine.h:259
armarx::FiniteStateMachine::start_from
ChainType & start_from(state start_state)
Definition: FiniteStateMachine.h:77
armarx::BinaryFiniteStateMachine
BinaryFiniteStateMachine(T *) -> BinaryFiniteStateMachine< T >
armarx::FiniteStateMachine::to
ChainType & to(state to_state)
Definition: FiniteStateMachine.h:100
armarx::BoolFiniteStateMachine
BoolFiniteStateMachine(T *) -> BoolFiniteStateMachine< T >
User-defined template argument deductions.
armarx::FiniteStateMachine::on
ChainType & on(StatusType status)
Definition: FiniteStateMachine.h:116
armarx::BoolFiniteStateMachine::on_success
BoolFiniteStateMachine & on_success()
Definition: FiniteStateMachine.h:250
armarx::BinaryFiniteStateMachine
Definition: FiniteStateMachine.h:267
armarx::FiniteStateMachine::run
virtual StatusType run() const
Definition: FiniteStateMachine.h:169
armarx::BinaryFiniteStateMachine::on_success
BinaryFiniteStateMachine & on_success()
Definition: FiniteStateMachine.h:277
armarx::FiniteStateMachine::m_context
T * m_context
Definition: FiniteStateMachine.h:51
armarx::FiniteStateMachine
Definition: FiniteStateMachine.h:41
armarx::FiniteStateMachine::from
ChainType & from(state from_state)
Definition: FiniteStateMachine.h:84
armarx::FiniteStateMachine::m_start_state
std::optional< state > m_start_state
Definition: FiniteStateMachine.h:52
armarx::FiniteStateMachine::m_init_to_state
std::optional< state > m_init_to_state
Definition: FiniteStateMachine.h:54
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(std::shared_ptr< T > context)
Definition: FiniteStateMachine.h:66
armarx::BoolFiniteStateMachine
Definition: FiniteStateMachine.h:241
armarx::FiniteStateMachine::get_id
long get_id(std::optional< state > fn) const
Definition: FiniteStateMachine.h:224
armarx::FiniteStateMachine::m_transitions
std::map< std::tuple< long, StatusType >, long > m_transitions
Definition: FiniteStateMachine.h:57
armarx::FiniteStateMachine::on_any_failure
ChainType & on_any_failure()
Definition: FiniteStateMachine.h:154
T
float T
Definition: UnscentedKalmanFilterTest.cpp:35
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(T *context)
Definition: FiniteStateMachine.h:71
armarx::FiniteStateMachine< T, BoolFiniteStateMachine< T >, bool, false >::state
std::function< bool(T *)> state
Definition: FiniteStateMachine.h:46
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::status::success
@ success
armarx::FiniteStateMachine::failure_status_value
static const StatusType failure_status_value
Definition: FiniteStateMachine.h:47