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 namespace armarx
36 {
37 
38 
39  template <typename T, typename ChainType, typename StatusType, StatusType failure_status>
41  {
42 
43  public:
44  using state = std::function<StatusType(T*)>;
45  static const StatusType failure_status_value = failure_status;
46 
47  protected:
48  mutable T* m_context;
49  std::optional<state> m_start_state;
50  std::optional<state> m_init_from_state;
51  std::optional<state> m_init_to_state;
52  std::optional<state> m_default_fail_state;
53  std::map<long, std::optional<state>> m_states;
54  std::map<std::tuple<long, StatusType>, long> m_transitions;
55 
56  public:
58  {
59  m_context = &context;
60  }
61 
62  FiniteStateMachine(std::shared_ptr<T> context)
63  {
64  m_context = *context;
65  }
66 
68  {
69  m_context = context;
70  }
71 
72  ChainType&
73  start_from(state start_state)
74  {
75  m_start_state = start_state;
76  return *static_cast<ChainType*>(this);
77  }
78 
79  ChainType&
80  from(state from_state)
81  {
82  if (not m_init_from_state)
83  {
84  m_init_from_state = from_state;
85  }
86  else
87  {
88  throw std::logic_error{"From-state was already set. Did you forget to append "
89  "on_success() or on_failure()?"};
90  }
91 
92  return *static_cast<ChainType*>(this);
93  }
94 
95  ChainType&
96  to(state to_state)
97  {
98  if (not m_init_to_state)
99  {
100  m_init_to_state = to_state;
101  }
102  else
103  {
104  throw std::logic_error{"To-state was already set. Did you forget to append "
105  "on_success() or on_failure()?"};
106  }
107 
108  return *static_cast<ChainType*>(this);
109  }
110 
111  ChainType&
112  on(StatusType status)
113  {
114  if (not m_init_from_state or not m_init_to_state)
115  {
116  throw std::logic_error{"Both from-state and to-state must be set with "
117  "from(...).to(...)."};
118  }
119 
120  // Get the ID's of the two functions.
121  const long id1 = get_id(m_init_from_state);
122  const long id2 = get_id(m_init_to_state);
123 
124  // Make sure that the states are known.
125  if (m_states.find(id1) == m_states.end())
126  {
127  m_states[id1] = m_init_from_state.value();
128  }
129  if (m_states.find(id2) == m_states.end())
130  {
131  m_states[id2] = m_init_to_state.value();
132  }
133 
134  // Add transitions.
135  m_transitions[std::make_tuple(id1, status)] = id2;
136 
137  m_init_from_state = std::nullopt;
138  m_init_to_state = std::nullopt;
139 
140  return *static_cast<ChainType*>(this);
141  }
142 
143  ChainType&
145  {
146  return on(failure_status_value);
147  }
148 
149  ChainType&
151  {
152  if (m_init_from_state)
153  {
154  throw std::logic_error{"When using on_any_failure(), from-state must be unset."};
155  }
156 
158  m_init_to_state = std::nullopt;
159 
160  return *static_cast<ChainType*>(this);
161  }
162 
163  virtual StatusType
164  run() const
165  {
166  if (m_init_from_state.has_value() or m_init_to_state.has_value())
167  {
168  throw std::logic_error{"From-state or to-state set but not commited with .on()"};
169  }
170 
171  if (not m_start_state.has_value())
172  {
173  throw std::logic_error{"Cannot run without a start state."};
174  }
175 
176  long current_state_id;
177  StatusType status;
178  std::optional<state> next_state = m_start_state;
179 
180  do
181  {
182  current_state_id = get_id(next_state);
183  status = next_state.value()(m_context);
184  next_state = get_next_state(current_state_id, status);
185  } while (status != failure_status_value and next_state.has_value());
186 
188  {
189  if (not next_state.has_value() and m_default_fail_state.has_value())
190  {
191  next_state = m_default_fail_state;
192  }
193 
194  status = next_state.value()(m_context);
195  }
196 
197  return status;
198  }
199 
200  protected:
201  std::optional<state>
202  get_next_state(const long state_id, const StatusType status) const
203  {
204  auto key = std::make_tuple(state_id, status);
205  if (m_transitions.find(key) != m_transitions.end())
206  {
207  const long target_id = m_transitions.at(key);
208  return m_states.at(target_id);
209  }
210 
211  return std::nullopt;
212  }
213 
214  long
215  get_id(std::optional<state> fn) const
216  {
217  return get_id(fn.value());
218  }
219 
220  long
221  get_id(const state& fn) const
222  {
223  return *(long*)(char*)&fn;
224  }
225  };
226 
227  template <typename T>
229  public FiniteStateMachine<T, BoolFiniteStateMachine<T>, bool, false>
230  {
231 
232  public:
234 
237  {
238  this->on(true);
239  return *this;
240  }
241  };
242 
243 
244  enum class status
245  {
246  success,
247  failure
248  };
249 
250  template <typename T>
252  public FiniteStateMachine<T, BinaryFiniteStateMachine<T>, status, status::failure>
253  {
254 
255  public:
258 
261  {
262  this->on(status::success);
263  return *this;
264  }
265  };
266 
267  /**
268  * User-defined template argument deductions.
269  */
270  template <typename T>
271  BoolFiniteStateMachine(T*) -> BoolFiniteStateMachine<T>;
272  template <typename T>
273  BoolFiniteStateMachine(T&) -> BoolFiniteStateMachine<T>;
274  template <typename T>
275  BoolFiniteStateMachine(std::shared_ptr<T>) -> BoolFiniteStateMachine<T>;
276  template <typename T>
277  BinaryFiniteStateMachine(T*) -> BinaryFiniteStateMachine<T>;
278  template <typename T>
279  BinaryFiniteStateMachine(T&) -> BinaryFiniteStateMachine<T>;
280  template <typename T>
281  BinaryFiniteStateMachine(std::shared_ptr<T>) -> BinaryFiniteStateMachine<T>;
282 
283 
284 } // namespace armarx
armarx::FiniteStateMachine::m_states
std::map< long, std::optional< state > > m_states
Definition: FiniteStateMachine.h:53
armarx::FiniteStateMachine::m_init_from_state
std::optional< state > m_init_from_state
Definition: FiniteStateMachine.h:50
armarx::FiniteStateMachine::get_id
long get_id(const state &fn) const
Definition: FiniteStateMachine.h:221
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(T &context)
Definition: FiniteStateMachine.h:57
armarx::FiniteStateMachine::m_default_fail_state
std::optional< state > m_default_fail_state
Definition: FiniteStateMachine.h:52
armarx::FiniteStateMachine::on_failure
ChainType & on_failure()
Definition: FiniteStateMachine.h:144
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:202
armarx::status
status
Definition: FiniteStateMachine.h:244
armarx::FiniteStateMachine::start_from
ChainType & start_from(state start_state)
Definition: FiniteStateMachine.h:73
armarx::BinaryFiniteStateMachine
BinaryFiniteStateMachine(T *) -> BinaryFiniteStateMachine< T >
armarx::FiniteStateMachine::to
ChainType & to(state to_state)
Definition: FiniteStateMachine.h:96
armarx::BoolFiniteStateMachine
BoolFiniteStateMachine(T *) -> BoolFiniteStateMachine< T >
User-defined template argument deductions.
armarx::FiniteStateMachine::on
ChainType & on(StatusType status)
Definition: FiniteStateMachine.h:112
armarx::BoolFiniteStateMachine::on_success
BoolFiniteStateMachine & on_success()
Definition: FiniteStateMachine.h:236
armarx::BinaryFiniteStateMachine
Definition: FiniteStateMachine.h:251
armarx::FiniteStateMachine::run
virtual StatusType run() const
Definition: FiniteStateMachine.h:164
armarx::BinaryFiniteStateMachine::on_success
BinaryFiniteStateMachine & on_success()
Definition: FiniteStateMachine.h:260
armarx::FiniteStateMachine::m_context
T * m_context
Definition: FiniteStateMachine.h:48
armarx::FiniteStateMachine
Definition: FiniteStateMachine.h:40
armarx::FiniteStateMachine::from
ChainType & from(state from_state)
Definition: FiniteStateMachine.h:80
armarx::FiniteStateMachine::m_start_state
std::optional< state > m_start_state
Definition: FiniteStateMachine.h:49
armarx::FiniteStateMachine::m_init_to_state
std::optional< state > m_init_to_state
Definition: FiniteStateMachine.h:51
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(std::shared_ptr< T > context)
Definition: FiniteStateMachine.h:62
armarx::BoolFiniteStateMachine
Definition: FiniteStateMachine.h:228
armarx::FiniteStateMachine::get_id
long get_id(std::optional< state > fn) const
Definition: FiniteStateMachine.h:215
armarx::FiniteStateMachine::m_transitions
std::map< std::tuple< long, StatusType >, long > m_transitions
Definition: FiniteStateMachine.h:54
armarx::FiniteStateMachine::on_any_failure
ChainType & on_any_failure()
Definition: FiniteStateMachine.h:150
T
float T
Definition: UnscentedKalmanFilterTest.cpp:38
armarx::FiniteStateMachine::FiniteStateMachine
FiniteStateMachine(T *context)
Definition: FiniteStateMachine.h:67
armarx::FiniteStateMachine< T, BoolFiniteStateMachine< T >, bool, false >::state
std::function< bool(T *)> state
Definition: FiniteStateMachine.h:44
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::status::success
@ success
armarx::FiniteStateMachine::failure_status_value
static const StatusType failure_status_value
Definition: FiniteStateMachine.h:45