|
|
@@ -0,0 +1,152 @@
|
|
|
+#ifndef SOPHIAR2_TRISTATE_OBJ_H
|
|
|
+#define SOPHIAR2_TRISTATE_OBJ_H
|
|
|
+
|
|
|
+#include <boost/logic/tribool.hpp>
|
|
|
+
|
|
|
+namespace sophiar {
|
|
|
+
|
|
|
+// template<typename T>
|
|
|
+// concept Tristate_Impl = requires(T a) {
|
|
|
+// { a.on_init() } -> std::convertible_to<boost::tribool>;
|
|
|
+// { a.on_start() } -> std::convertible_to<boost::tribool>;
|
|
|
+// a.on_stop();
|
|
|
+// a.on_reset();
|
|
|
+// };
|
|
|
+
|
|
|
+ template<typename DeriveT>
|
|
|
+ class tristate_obj {
|
|
|
+ public:
|
|
|
+
|
|
|
+ using state_type = uint8_t;
|
|
|
+ static const state_type ST_INITIAL = 0;
|
|
|
+ static const state_type ST_INITIALIZING = 1;
|
|
|
+ static const state_type ST_PENDING = 2;
|
|
|
+ static const state_type ST_STARING = 3;
|
|
|
+ static const state_type ST_RUNNING = 4;
|
|
|
+
|
|
|
+ state_type state = ST_INITIAL;
|
|
|
+
|
|
|
+ boost::tribool init() {
|
|
|
+ if (state == ST_INITIAL) {
|
|
|
+ auto ret_val = get_real_ptr()->on_init();
|
|
|
+ if (ret_val == false) {
|
|
|
+ get_real_ptr()->on_reset();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (ret_val == true) {
|
|
|
+ state = ST_PENDING;
|
|
|
+ call_after_init();
|
|
|
+ } else {
|
|
|
+ state = ST_INITIALIZING;
|
|
|
+ }
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+ if (state == ST_INITIALIZING) {
|
|
|
+ return boost::indeterminate;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::tribool start() {
|
|
|
+ if (state < ST_PENDING) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (state == ST_PENDING) {
|
|
|
+ auto ret_val = get_real_ptr()->on_start();
|
|
|
+ if (ret_val == false) {
|
|
|
+ get_real_ptr()->on_stop();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (ret_val == true) {
|
|
|
+ state = ST_RUNNING;
|
|
|
+ call_after_start();
|
|
|
+ } else {
|
|
|
+ state = ST_STARING;
|
|
|
+ }
|
|
|
+ return ret_val;
|
|
|
+ }
|
|
|
+ if (state == ST_STARING) {
|
|
|
+ return boost::indeterminate;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void stop() {
|
|
|
+ if (state < ST_STARING) return;
|
|
|
+ get_real_ptr()->on_stop();
|
|
|
+ auto old_state = state;
|
|
|
+ state = ST_PENDING;
|
|
|
+ if (old_state == ST_RUNNING) {
|
|
|
+ call_after_stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void reset() {
|
|
|
+ if (state == ST_INITIAL) return;
|
|
|
+ if (state > ST_PENDING) {
|
|
|
+ stop();
|
|
|
+ }
|
|
|
+ get_real_ptr()->on_reset();
|
|
|
+ auto old_state = state;
|
|
|
+ state = ST_INITIAL;
|
|
|
+ if (old_state == ST_PENDING) {
|
|
|
+ call_after_reset();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected:
|
|
|
+
|
|
|
+ void init_finished(bool flag = true) {
|
|
|
+ if (state != ST_INITIALIZING) return;
|
|
|
+ if (flag) {
|
|
|
+ state = ST_PENDING;
|
|
|
+ call_after_init();
|
|
|
+ } else {
|
|
|
+ get_real_ptr()->on_reset();
|
|
|
+ state = ST_INITIAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void start_finished(bool flag = true) {
|
|
|
+ if (state != ST_STARING) return;
|
|
|
+ if (flag) {
|
|
|
+ state = ST_RUNNING;
|
|
|
+ call_after_start();
|
|
|
+ } else {
|
|
|
+ get_real_ptr()->on_stop();
|
|
|
+ state = ST_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+
|
|
|
+ auto get_real_ptr() {
|
|
|
+ return static_cast<DeriveT *>(this);
|
|
|
+ }
|
|
|
+
|
|
|
+#define CHECK_AND_CALL_AFTER(cmd, old_state) \
|
|
|
+ void call_after_##cmd() { \
|
|
|
+ if constexpr(requires(DeriveT a) { \
|
|
|
+ a.after_##cmd(); \
|
|
|
+ }) { \
|
|
|
+ get_real_ptr()->after_##cmd(); \
|
|
|
+ } \
|
|
|
+ if constexpr(requires(DeriveT a) { \
|
|
|
+ a.ater_state_change(state_type{}); \
|
|
|
+ }) { \
|
|
|
+ get_real_ptr()->after_state_change(old_state); \
|
|
|
+ } \
|
|
|
+ }
|
|
|
+
|
|
|
+ CHECK_AND_CALL_AFTER(init, ST_INITIAL)
|
|
|
+ CHECK_AND_CALL_AFTER(start, ST_PENDING)
|
|
|
+ CHECK_AND_CALL_AFTER(stop, ST_RUNNING)
|
|
|
+ CHECK_AND_CALL_AFTER(reset, ST_PENDING)
|
|
|
+
|
|
|
+#undef CHECK_AND_CALL_AFTER
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+#endif //SOPHIAR2_TRISTATE_OBJ_H
|