From 8359a8e777d18dd318c417e381abca110fd7dff3 Mon Sep 17 00:00:00 2001 From: Andrew Gresyk Date: Tue, 6 Jun 2023 10:22:40 +0300 Subject: [PATCH] 2.3.0 * reworked serialization --- development/hfsm2/detail/containers/array.hpp | 21 +- development/hfsm2/detail/containers/array.inl | 13 + .../hfsm2/detail/containers/bit_array.hpp | 8 +- .../hfsm2/detail/containers/bit_array.inl | 13 + .../hfsm2/detail/containers/task_list.hpp | 11 +- .../hfsm2/detail/containers/task_list.inl | 12 + development/hfsm2/detail/root.hpp | 109 +++- development/hfsm2/detail/root.inl | 256 ++++++-- development/hfsm2/detail/root/plan_data.hpp | 49 +- development/hfsm2/detail/root/plan_data.inl | 45 +- development/hfsm2/detail/root/registry.hpp | 12 +- development/hfsm2/detail/root/registry_1.inl | 13 + development/hfsm2/detail/root/registry_2.inl | 13 + .../hfsm2/detail/shared/bit_stream.hpp | 8 +- .../hfsm2/detail/shared/bit_stream.inl | 65 +- .../hfsm2/detail/structure/forward.hpp | 3 +- include/hfsm2/machine.hpp | 583 ++++++++++++++---- test/shared/test_bit_array.cpp | 9 +- test/test_serialization.cpp | 236 ++++--- 19 files changed, 1111 insertions(+), 368 deletions(-) diff --git a/development/hfsm2/detail/containers/array.hpp b/development/hfsm2/detail/containers/array.hpp index c26dac4..c188c28 100644 --- a/development/hfsm2/detail/containers/array.hpp +++ b/development/hfsm2/detail/containers/array.hpp @@ -7,7 +7,16 @@ template HFSM2_CONSTEXPR(11) T filler() noexcept { - return T{INVALID_SHORT}; + return T{}; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template <> +HFSM2_CONSTEXPR(11) +Short +filler() noexcept { + return INVALID_SHORT; } //------------------------------------------------------------------------------ @@ -40,6 +49,7 @@ class StaticArrayT final { HFSM2_CONSTEXPR(14) void fill(const Item filler) noexcept; HFSM2_CONSTEXPR(14) void clear() noexcept { fill(filler()); } + HFSM2_CONSTEXPR(14) bool empty() const noexcept; HFSM2_CONSTEXPR(14) Iterator begin() noexcept { return Iterator(*this, first()); } HFSM2_CONSTEXPR(11) CIterator begin() const noexcept { return CIterator(*this, first()); } @@ -85,8 +95,6 @@ class ArrayT final { static constexpr Index CAPACITY = NCapacity; public: - HFSM2_CONSTEXPR(14) void clear() noexcept { _count = 0; } - template HFSM2_CONSTEXPR(14) Index emplace(const TArgs &... args) noexcept; @@ -99,7 +107,10 @@ class ArrayT final { template HFSM2_CONSTEXPR(14) const Item& operator[] (const N index) const noexcept; - HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + + HFSM2_CONSTEXPR(14) void clear() noexcept { _count = 0; } + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return _count == 0; } template HFSM2_CONSTEXPR(14) ArrayT& operator += (const ArrayT& other) noexcept; @@ -132,7 +143,7 @@ class ArrayT final { #endif }; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//------------------------------------------------------------------------------ template class ArrayT final { diff --git a/development/hfsm2/detail/containers/array.inl b/development/hfsm2/detail/containers/array.inl index 0c8a7f9..7f519a9 100644 --- a/development/hfsm2/detail/containers/array.inl +++ b/development/hfsm2/detail/containers/array.inl @@ -35,6 +35,19 @@ StaticArrayT::fill(const Item filler) noexcept { item = filler; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +bool +StaticArrayT::empty() const noexcept { + for (const Item& item : _items) + if (item != filler()) + return false; + + return true; +} + //////////////////////////////////////////////////////////////////////////////// template diff --git a/development/hfsm2/detail/containers/bit_array.hpp b/development/hfsm2/detail/containers/bit_array.hpp index 66a1a88..c80e27a 100644 --- a/development/hfsm2/detail/containers/bit_array.hpp +++ b/development/hfsm2/detail/containers/bit_array.hpp @@ -19,7 +19,7 @@ struct Units final { template class BitArrayT final { public: - using Index = UCapacity; + using Index = UCapacity; static constexpr Index CAPACITY = NCapacity; static constexpr Index UNIT_COUNT = contain(CAPACITY, 8); @@ -102,6 +102,8 @@ class BitArrayT final { template HFSM2_CONSTEXPR(14) void clear() noexcept; + HFSM2_CONSTEXPR(14) bool empty() const noexcept; + template HFSM2_CONSTEXPR(14) bool get (const TIndex index) const noexcept; @@ -121,7 +123,7 @@ class BitArrayT final { HFSM2_CONSTEXPR(14) CBits cbits(const Units& units) const noexcept; private: - uint8_t _storage[UNIT_COUNT]; + uint8_t _storage[UNIT_COUNT] {}; }; //------------------------------------------------------------------------------ @@ -130,6 +132,8 @@ template <> class BitArrayT<0> final { public: HFSM2_CONSTEXPR(14) void clear() noexcept {} + + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return true; } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/development/hfsm2/detail/containers/bit_array.inl b/development/hfsm2/detail/containers/bit_array.inl index 7768cc3..122fd7d 100644 --- a/development/hfsm2/detail/containers/bit_array.inl +++ b/development/hfsm2/detail/containers/bit_array.inl @@ -191,6 +191,19 @@ BitArrayT::clear() noexcept { //------------------------------------------------------------------------------ +template +HFSM2_CONSTEXPR(14) +bool +BitArrayT::empty() const noexcept { + for (const uint8_t& unit : _storage) + if (unit != uint8_t{0}) + return false; + + return true; +} + +//------------------------------------------------------------------------------ + template template HFSM2_CONSTEXPR(14) diff --git a/development/hfsm2/detail/containers/task_list.hpp b/development/hfsm2/detail/containers/task_list.hpp index 3dfeeef..dc686aa 100644 --- a/development/hfsm2/detail/containers/task_list.hpp +++ b/development/hfsm2/detail/containers/task_list.hpp @@ -125,6 +125,8 @@ class TaskListT { using Item = TaskT; public: + HFSM2_CONSTEXPR(14) void clear() noexcept; + template HFSM2_CONSTEXPR(14) Index emplace(TArgs&&... args) noexcept; @@ -133,7 +135,8 @@ class TaskListT { HFSM2_CONSTEXPR(14) Item& operator[] (const Index i) noexcept; HFSM2_CONSTEXPR(11) const Item& operator[] (const Index i) const noexcept; - HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return _count == 0; } private: HFSM2_IF_ASSERT(void verifyStructure(const Index occupied = INVALID) const noexcept); @@ -141,9 +144,9 @@ class TaskListT { private: Index _vacantHead = 0; Index _vacantTail = 0; - Index _last = 0; - Index _count = 0; - Item _items[CAPACITY]; + Index _last = 0; + Index _count = 0; + Item _items[CAPACITY] {}; }; //------------------------------------------------------------------------------ diff --git a/development/hfsm2/detail/containers/task_list.inl b/development/hfsm2/detail/containers/task_list.inl index 91bc1ff..a41bdbd 100644 --- a/development/hfsm2/detail/containers/task_list.inl +++ b/development/hfsm2/detail/containers/task_list.inl @@ -5,6 +5,18 @@ namespace detail { //////////////////////////////////////////////////////////////////////////////// +template +HFSM2_CONSTEXPR(14) +void +TaskListT::clear() noexcept { + _vacantHead = 0; + _vacantTail = 0; + _last = 0; + _count = 0; +} + +//------------------------------------------------------------------------------ + template template HFSM2_CONSTEXPR(14) diff --git a/development/hfsm2/detail/root.hpp b/development/hfsm2/detail/root.hpp index 64f62b2..4f1e742 100644 --- a/development/hfsm2/detail/root.hpp +++ b/development/hfsm2/detail/root.hpp @@ -517,27 +517,6 @@ class R_ { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if HFSM2_SERIALIZATION_AVAILABLE() - - /// @brief Buffer for serialization - /// @see https://doc.hfsm.dev/user-guide/debugging-and-tools/serialization - /// @see HFSM2_ENABLE_SERIALIZATION - using SerialBuffer = typename Args::SerialBuffer; - - /// @brief Serialize FSM into 'buffer' - /// @param buffer 'SerialBuffer' to serialize to - /// @see HFSM2_ENABLE_SERIALIZATION - HFSM2_CONSTEXPR(14) void save( SerialBuffer& buffer) const noexcept; - - /// @brief De-serialize FSM from 'buffer' - /// @param buffer 'SerialBuffer' to de-serialize from - /// @see HFSM2_ENABLE_SERIALIZATION - HFSM2_CONSTEXPR(14) void load(const SerialBuffer& buffer) noexcept; - -#endif - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #if HFSM2_TRANSITION_HISTORY_AVAILABLE() /// @brief Get the list of transitions recorded during last 'update()' @@ -642,6 +621,11 @@ class R_ { HFSM2_CONSTEXPR(14) bool cancelledByGuards(const TransitionSets& currentTransitions, const TransitionSet& pendingTransitions) noexcept; +#if HFSM2_SERIALIZATION_AVAILABLE() + HFSM2_CONSTEXPR(14) void save(WriteStream& stream) const noexcept; + HFSM2_CONSTEXPR(14) void load( ReadStream& stream) noexcept; +#endif + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() HFSM2_CONSTEXPR(14) bool applyRequests(Control& control, const Transition* const transitions, @@ -701,6 +685,12 @@ class RV_ ::reset() noexcept { //------------------------------------------------------------------------------ -#if HFSM2_SERIALIZATION_AVAILABLE() - -template -HFSM2_CONSTEXPR(14) -void -R_::save(SerialBuffer& _buffer) const noexcept { - HFSM2_ASSERT(_core.registry.isActive()); - - WriteStream stream{_buffer}; - - // TODO: save _core.registry - // TODO: save _core.requests - // TODO: save _core.rng // HFSM2_IF_UTILITY_THEORY() - // TODO: save _core.planData // HFSM2_IF_PLANS() - // TODO: save _core.previousTransitions // HFSM2_IF_TRANSITION_HISTORY() - // TODO: save _activityHistory // HFSM2_IF_STRUCTURE_REPORT() - - _apex.deepSaveActive(_core.registry, stream); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -HFSM2_CONSTEXPR(14) -void -R_::load(const SerialBuffer& buffer) noexcept { - HFSM2_ASSERT(_core.registry.isActive()); - - _core.requests.clear(); - - TransitionSets emptyTransitions; - PlanControl control{_core, emptyTransitions}; - - _apex.deepExit(control); - - HFSM2_IF_TRANSITION_HISTORY(_core.transitionTargets.clear()); - HFSM2_IF_TRANSITION_HISTORY(_core.previousTransitions.clear()); - - _core.registry.clear(); - _core.requests.clear(); - - ReadStream stream{buffer}; - - // TODO: load _core.registry - // TODO: load _core.requests - // TODO: load _core.rng // HFSM2_IF_UTILITY_THEORY() - // TODO: load _core.planData // HFSM2_IF_PLANS() - // TODO: load _core.previousTransitions // HFSM2_IF_TRANSITION_HISTORY() - // TODO: load _activityHistory // HFSM2_IF_STRUCTURE_REPORT() - - _apex.deepLoadRequested(_core.registry, stream); - - _apex.deepEnter(control); -} - -#endif - -//------------------------------------------------------------------------------ - #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -520,6 +461,24 @@ R_::finalExit() noexcept { PlanControl control{_core, emptyTransitions}; _apex.deepExit(control); + + _core.registry.clear(); + _core.requests.clear(); + +#if HFSM2_PLANS_AVAILABLE() + _core.planData.clear(); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: _core.rng.clear(); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + _core.transitionTargets .clear(); + _core.previousTransitions.clear(); +#endif + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); } //------------------------------------------------------------------------------ @@ -685,6 +644,81 @@ R_::cancelledByGuards(const TransitionSets& currentTransitions, //------------------------------------------------------------------------------ +#if HFSM2_SERIALIZATION_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +R_::save(WriteStream& stream) const noexcept { + HFSM2_ASSERT(_core.registry.isActive()); + + _apex.deepSaveActive(_core.registry, stream); + + // TODO: save(stream, _core.requests); + +#if HFSM2_PLANS_AVAILABLE() + // TODO: save(stream, _core.planData); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: save(stream, _core.rng); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + // TODO: save(stream, _core.transitionTargets); + // TODO: save(stream, _core.previousTransitions); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + // TODO: save(stream, _activityHistory); +#endif +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +void +R_::load(ReadStream& stream) noexcept { + HFSM2_ASSERT(_core.registry.isActive()); + + _core.registry.clearRequests(); + _core.registry.compoResumable.clear(); + _apex.deepLoadRequested(_core.registry, stream); + + _core.requests.clear(); + // TODO: load(stream, _core.requests); + +#if HFSM2_PLANS_AVAILABLE() + _core.planData.clear(); + // TODO: load(stream, _core.planData); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: load(stream, _core.rng); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + _core.transitionTargets .clear(); + _core.previousTransitions.clear(); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + // TODO: load(stream, _activityHistory); +#endif + + TransitionSets emptyTransitions; + PlanControl control{_core, emptyTransitions}; + + _apex.deepChangeToRequested(control); + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); +} + +#endif + +//------------------------------------------------------------------------------ + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -875,8 +909,76 @@ RV_ +HFSM2_CONSTEXPR(14) +void +RV_, TA>::save(SerialBuffer& buffer) const noexcept { + WriteStream stream{buffer}; + + stream.template write<1>(1); + save(stream); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::load(const SerialBuffer& buffer) noexcept { + ReadStream stream{buffer}; + + if (HFSM2_CHECKED(stream.template read<1>())) + Base::load(stream); +} + +#endif + //////////////////////////////////////////////////////////////////////////////// +#if HFSM2_SERIALIZATION_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::save(SerialBuffer& buffer) const noexcept { + WriteStream stream{buffer}; + + if (isActive()) { + stream.template write<1>(1); + + save(stream); + } + else + stream.template write<1>(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::load(const SerialBuffer& buffer) noexcept { + ReadStream stream{buffer}; + + if (stream.template read<1>()) { + if (isActive()) + Base::load(stream); + else + loadEnter (stream); + } + else + if (isActive()) + finalExit(); +} + +#endif + +//------------------------------------------------------------------------------ + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -929,6 +1031,42 @@ RV_ +HFSM2_CONSTEXPR(14) +void +RV_, TA>::loadEnter(ReadStream& stream) noexcept { + HFSM2_ASSERT(_core.registry.empty()); + _apex.deepLoadRequested(_core.registry, stream); + + HFSM2_ASSERT(_core.requests.empty()); + +#if HFSM2_PLANS_AVAILABLE() + HFSM2_ASSERT(_core.planData.empty() == 0); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + HFSM2_ASSERT(_core.transitionTargets .empty()); + HFSM2_ASSERT(_core.previousTransitions.empty()); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + //HFSM2_ASSERT(_activityHistory.empty()); +#endif + + TransitionSets emptyTransitions; + PlanControl control{_core, emptyTransitions}; + + _apex.deepEnter(control); + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); +} + +#endif + //////////////////////////////////////////////////////////////////////////////// // COMMON diff --git a/development/hfsm2/detail/root/plan_data.hpp b/development/hfsm2/detail/root/plan_data.hpp index 148f271..856be99 100644 --- a/development/hfsm2/detail/root/plan_data.hpp +++ b/development/hfsm2/detail/root/plan_data.hpp @@ -30,9 +30,6 @@ struct Status final { HFSM2_CONSTEXPR(14) Status operator | (Status& lhs, const Status rhs) noexcept; HFSM2_CONSTEXPR(14) Status& operator |= (Status& lhs, const Status rhs) noexcept; -template <> -HFSM2_CONSTEXPR(11) Status filler() noexcept; - //////////////////////////////////////////////////////////////////////////////// #if HFSM2_PLANS_AVAILABLE() @@ -96,23 +93,23 @@ struct PlanDataT> final { - using StateList = TStateList; - using RegionList = TRegionList; - using Payload = TPayload; + using StateList = TStateList; + using RegionList = TRegionList; + using Payload = TPayload; static constexpr Long STATE_COUNT = StateList ::SIZE; static constexpr Long REGION_COUNT = RegionList::SIZE; static constexpr Long TASK_CAPACITY = NTaskCapacity; - using Task = TaskT; - using Tasks = TaskListT ; - using TaskLinks = StaticArrayT; - using Payloads = StaticArrayT; + using Task = TaskT ; + using Tasks = TaskListT ; + using TaskLinks = StaticArrayT; + using Payloads = StaticArrayT; - using TasksBounds = ArrayT ; - using TasksBits = BitArrayT < STATE_COUNT>; - using RegionBits = BitArrayT < REGION_COUNT>; - using RegionStatuses = StaticArrayT; + using TasksBounds = ArrayT ; + using TasksBits = BitArrayT < STATE_COUNT>; + using RegionBits = BitArrayT < REGION_COUNT>; + using RegionStatuses = StaticArrayT; Tasks tasks; TaskLinks taskLinks; @@ -130,6 +127,7 @@ struct PlanDataT> final { - using StateList = TStateList; - using RegionList = TRegionList; + using StateList = TStateList; + using RegionList = TRegionList; - static constexpr Long STATE_COUNT = StateList::SIZE; + static constexpr Long STATE_COUNT = StateList ::SIZE; static constexpr Long REGION_COUNT = RegionList::SIZE; static constexpr Long TASK_CAPACITY = NTaskCapacity; - using Task = TaskT; - using Tasks = TaskListT ; - using TaskLinks = StaticArrayT; + using Task = TaskT ; + using Tasks = TaskListT ; + using TaskLinks = StaticArrayT; - using TasksBounds = ArrayT ; - using TasksBits = BitArrayT < STATE_COUNT>; - using RegionBits = BitArrayT < REGION_COUNT>; - using RegionStatuses = StaticArrayT; + using TasksBounds = ArrayT ; + using TasksBits = BitArrayT < STATE_COUNT>; + using RegionBits = BitArrayT < REGION_COUNT>; + using RegionStatuses = StaticArrayT; Tasks tasks; TaskLinks taskLinks; @@ -191,6 +189,7 @@ struct PlanDataT -HFSM2_CONSTEXPR(11) -Status -filler() noexcept { - return Status{}; -} - //////////////////////////////////////////////////////////////////////////////// #if HFSM2_PLANS_AVAILABLE() @@ -103,6 +94,25 @@ PlanDataT +HFSM2_CONSTEXPR(14) +void +PlanDataT>::clear() noexcept { + tasks .clear(); + taskLinks .clear(); + taskPayloads .clear(); + payloadExists .clear(); + + tasksBounds .clear(); + tasksSuccesses.clear(); + tasksFailures .clear(); + planExists .clear(); + + clearRegionStatuses(); +} + //------------------------------------------------------------------------------ #if HFSM2_ASSERT_AVAILABLE() @@ -200,6 +210,23 @@ PlanDataT +HFSM2_CONSTEXPR(14) +void +PlanDataT>::clear() noexcept { + tasks .clear(); + taskLinks .clear(); + + tasksBounds .clear(); + tasksSuccesses.clear(); + tasksFailures .clear(); + planExists .clear(); + + clearRegionStatuses(); +} + //------------------------------------------------------------------------------ #if HFSM2_ASSERT_AVAILABLE() diff --git a/development/hfsm2/detail/root/registry.hpp b/development/hfsm2/detail/root/registry.hpp index 0ee9017..ea5db83 100644 --- a/development/hfsm2/detail/root/registry.hpp +++ b/development/hfsm2/detail/root/registry.hpp @@ -148,8 +148,10 @@ struct RegistryT +HFSM2_CONSTEXPR(11) +bool +RegistryT>::empty() const noexcept { + return compoRequested.empty() + && orthoRequested.empty() + && compoRemains .empty() + && compoActive .empty() + && compoResumable.empty(); +} + //////////////////////////////////////////////////////////////////////////////// } diff --git a/development/hfsm2/detail/root/registry_2.inl b/development/hfsm2/detail/root/registry_2.inl index cf4023f..4e20848 100644 --- a/development/hfsm2/detail/root/registry_2.inl +++ b/development/hfsm2/detail/root/registry_2.inl @@ -209,6 +209,19 @@ RegistryT +HFSM2_CONSTEXPR(11) +bool +RegistryT>::empty() const noexcept { + return compoRequested.empty() + && orthoRequested.empty() + && compoRemains .empty() + && compoActive .empty() + && compoResumable.empty(); +} + //////////////////////////////////////////////////////////////////////////////// } diff --git a/development/hfsm2/detail/shared/bit_stream.hpp b/development/hfsm2/detail/shared/bit_stream.hpp index e5dfcc7..f4ef714 100644 --- a/development/hfsm2/detail/shared/bit_stream.hpp +++ b/development/hfsm2/detail/shared/bit_stream.hpp @@ -25,15 +25,19 @@ class StreamBufferT { static constexpr Long BIT_CAPACITY = NBitCapacity; static constexpr Long BYTE_COUNT = contain(BIT_CAPACITY, 8u); - using Data = uint8_t[BYTE_COUNT]; + using StreamBuffer = StreamBufferT; + using Data = uint8_t [BYTE_COUNT ]; HFSM2_CONSTEXPR(14) void clear() noexcept { fill(_data, 0); } HFSM2_CONSTEXPR(14) Data& data() noexcept { return _data; } HFSM2_CONSTEXPR(11) const Data& data() const noexcept { return _data; } + HFSM2_CONSTEXPR(14) bool operator == (const StreamBuffer& s) const noexcept; + HFSM2_CONSTEXPR(14) bool operator != (const StreamBuffer& s) const noexcept; + private: - Data _data; + Data _data {}; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/development/hfsm2/detail/shared/bit_stream.inl b/development/hfsm2/detail/shared/bit_stream.inl index 872c080..d72dd4f 100644 --- a/development/hfsm2/detail/shared/bit_stream.inl +++ b/development/hfsm2/detail/shared/bit_stream.inl @@ -5,6 +5,32 @@ namespace detail { //////////////////////////////////////////////////////////////////////////////// +template +HFSM2_CONSTEXPR(14) +bool +StreamBufferT::operator == (const StreamBuffer& buffer) const noexcept { + for (hfsm2::Long i = 0; i < BYTE_COUNT; ++i) + if (_data[i] != buffer._data[i]) + return false; + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +bool +StreamBufferT::operator != (const StreamBuffer& buffer) const noexcept { + for (hfsm2::Long i = 0; i < BYTE_COUNT; ++i) + if (_data[i] != buffer._data[i]) + return true; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + template template HFSM2_CONSTEXPR(14) @@ -45,30 +71,29 @@ BitReadStreamT::read() noexcept { constexpr Short BIT_WIDTH = NBitWidth; static_assert(BIT_WIDTH > 0, "STATIC ASSERT"); - HFSM2_ASSERT(_cursor <= BIT_CAPACITY); + HFSM2_ASSERT(_cursor + BIT_WIDTH <= BIT_CAPACITY); using Item = UBitWidth; Item item = 0; - Short itemCursor = 0; - - for (Short itemWidth = BIT_WIDTH; itemWidth; ) - if (HFSM2_CHECKED(_cursor + itemWidth <= BIT_CAPACITY)) { - const Long byteIndex = _cursor >> 3; - const uint8_t& byte = _buffer._data[byteIndex]; - - const Short byteChunkStart = _cursor & 0x7; - const Short byteDataWidth = 8 - byteChunkStart; - const Short byteChunkWidth = min(byteDataWidth, itemWidth); - const Short byteChunkMask = (1 << byteChunkWidth) - 1; - const Item byteChunk = (byte >> byteChunkStart) & byteChunkMask; - const Item itemChunk = byteChunk << itemCursor; - - item |= itemChunk; - itemCursor += byteChunkWidth; - itemWidth -= byteChunkWidth; - _cursor += byteChunkWidth; - } + + for (Short itemCursor = 0, itemWidth = BIT_WIDTH; itemWidth; ) { + const Long byteIndex = _cursor >> 3; + const uint8_t& byte = _buffer._data[byteIndex]; + + const Short byteChunkStart = _cursor & 0x7; + const Short byteDataWidth = 8 - byteChunkStart; + const Short byteChunkWidth = min(byteDataWidth, itemWidth); + const Short byteChunkMask = (1 << byteChunkWidth) - 1; + + const Item byteChunk = (byte >> byteChunkStart) & byteChunkMask; + const Item itemChunk = byteChunk << itemCursor; + + item |= itemChunk; + itemCursor += byteChunkWidth; + itemWidth -= byteChunkWidth; + _cursor += byteChunkWidth; + } return item; } diff --git a/development/hfsm2/detail/structure/forward.hpp b/development/hfsm2/detail/structure/forward.hpp index 2c16347..75e30d6 100644 --- a/development/hfsm2/detail/structure/forward.hpp +++ b/development/hfsm2/detail/structure/forward.hpp @@ -370,6 +370,7 @@ struct RF_ final { #if HFSM2_SERIALIZATION_AVAILABLE() static constexpr Long ACTIVE_BITS = Apex::ACTIVE_BITS; static constexpr Long RESUMABLE_BITS = Apex::RESUMABLE_BITS; + static constexpr Long SERIAL_BITS = 1 + ACTIVE_BITS + RESUMABLE_BITS; #endif using Args = ArgsT; diff --git a/include/hfsm2/machine.hpp b/include/hfsm2/machine.hpp index 34d0a40..b9b4754 100644 --- a/include/hfsm2/machine.hpp +++ b/include/hfsm2/machine.hpp @@ -708,15 +708,19 @@ class StreamBufferT { static constexpr Long BIT_CAPACITY = NBitCapacity; static constexpr Long BYTE_COUNT = contain(BIT_CAPACITY, 8u); - using Data = uint8_t[BYTE_COUNT]; + using StreamBuffer = StreamBufferT; + using Data = uint8_t [BYTE_COUNT ]; HFSM2_CONSTEXPR(14) void clear() noexcept { fill(_data, 0); } HFSM2_CONSTEXPR(14) Data& data() noexcept { return _data; } HFSM2_CONSTEXPR(11) const Data& data() const noexcept { return _data; } + HFSM2_CONSTEXPR(14) bool operator == (const StreamBuffer& s) const noexcept; + HFSM2_CONSTEXPR(14) bool operator != (const StreamBuffer& s) const noexcept; + private: - Data _data; + Data _data {}; }; template @@ -780,6 +784,28 @@ class BitReadStreamT final { namespace hfsm2 { namespace detail { +template +HFSM2_CONSTEXPR(14) +bool +StreamBufferT::operator == (const StreamBuffer& buffer) const noexcept { + for (hfsm2::Long i = 0; i < BYTE_COUNT; ++i) + if (_data[i] != buffer._data[i]) + return false; + + return true; +} + +template +HFSM2_CONSTEXPR(14) +bool +StreamBufferT::operator != (const StreamBuffer& buffer) const noexcept { + for (hfsm2::Long i = 0; i < BYTE_COUNT; ++i) + if (_data[i] != buffer._data[i]) + return true; + + return false; +} + template template HFSM2_CONSTEXPR(14) @@ -818,30 +844,29 @@ BitReadStreamT::read() noexcept { constexpr Short BIT_WIDTH = NBitWidth; static_assert(BIT_WIDTH > 0, "STATIC ASSERT"); - HFSM2_ASSERT(_cursor <= BIT_CAPACITY); + HFSM2_ASSERT(_cursor + BIT_WIDTH <= BIT_CAPACITY); using Item = UBitWidth; Item item = 0; - Short itemCursor = 0; - - for (Short itemWidth = BIT_WIDTH; itemWidth; ) - if (HFSM2_CHECKED(_cursor + itemWidth <= BIT_CAPACITY)) { - const Long byteIndex = _cursor >> 3; - const uint8_t& byte = _buffer._data[byteIndex]; - - const Short byteChunkStart = _cursor & 0x7; - const Short byteDataWidth = 8 - byteChunkStart; - const Short byteChunkWidth = min(byteDataWidth, itemWidth); - const Short byteChunkMask = (1 << byteChunkWidth) - 1; - const Item byteChunk = (byte >> byteChunkStart) & byteChunkMask; - const Item itemChunk = byteChunk << itemCursor; - - item |= itemChunk; - itemCursor += byteChunkWidth; - itemWidth -= byteChunkWidth; - _cursor += byteChunkWidth; - } + + for (Short itemCursor = 0, itemWidth = BIT_WIDTH; itemWidth; ) { + const Long byteIndex = _cursor >> 3; + const uint8_t& byte = _buffer._data[byteIndex]; + + const Short byteChunkStart = _cursor & 0x7; + const Short byteDataWidth = 8 - byteChunkStart; + const Short byteChunkWidth = min(byteDataWidth, itemWidth); + const Short byteChunkMask = (1 << byteChunkWidth) - 1; + + const Item byteChunk = (byte >> byteChunkStart) & byteChunkMask; + const Item itemChunk = byteChunk << itemCursor; + + item |= itemChunk; + itemCursor += byteChunkWidth; + itemWidth -= byteChunkWidth; + _cursor += byteChunkWidth; + } return item; } @@ -2060,6 +2085,7 @@ class StaticArrayT final { HFSM2_CONSTEXPR(14) void fill(const Item filler) noexcept; HFSM2_CONSTEXPR(14) void clear() noexcept { fill(filler()); } + HFSM2_CONSTEXPR(14) bool empty() const noexcept; HFSM2_CONSTEXPR(14) Iterator begin() noexcept { return Iterator(*this, first()); } HFSM2_CONSTEXPR(11) CIterator begin() const noexcept { return CIterator(*this, first()); } @@ -2101,8 +2127,6 @@ class ArrayT final { static constexpr Index CAPACITY = NCapacity; public: - HFSM2_CONSTEXPR(14) void clear() noexcept { _count = 0; } - template HFSM2_CONSTEXPR(14) Index emplace(const TArgs &... args) noexcept; @@ -2115,7 +2139,10 @@ class ArrayT final { template HFSM2_CONSTEXPR(14) const Item& operator[] (const N index) const noexcept; - HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + + HFSM2_CONSTEXPR(14) void clear() noexcept { _count = 0; } + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return _count == 0; } template HFSM2_CONSTEXPR(14) ArrayT& operator += (const ArrayT& other) noexcept; @@ -2192,6 +2219,17 @@ StaticArrayT::fill(const Item filler) noexcept { item = filler; } +template +HFSM2_CONSTEXPR(14) +bool +StaticArrayT::empty() const noexcept { + for (const Item& item : _items) + if (item != filler()) + return false; + + return true; +} + template template HFSM2_CONSTEXPR(14) @@ -2267,7 +2305,7 @@ struct Units final { template class BitArrayT final { public: - using Index = UCapacity; + using Index = UCapacity; static constexpr Index CAPACITY = NCapacity; static constexpr Index UNIT_COUNT = contain(CAPACITY, 8); @@ -2344,6 +2382,8 @@ class BitArrayT final { template HFSM2_CONSTEXPR(14) void clear() noexcept; + HFSM2_CONSTEXPR(14) bool empty() const noexcept; + template HFSM2_CONSTEXPR(14) bool get (const TIndex index) const noexcept; @@ -2363,13 +2403,15 @@ class BitArrayT final { HFSM2_CONSTEXPR(14) CBits cbits(const Units& units) const noexcept; private: - uint8_t _storage[UNIT_COUNT]; + uint8_t _storage[UNIT_COUNT] {}; }; template <> class BitArrayT<0> final { public: HFSM2_CONSTEXPR(14) void clear() noexcept {} + + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return true; } }; } @@ -2542,6 +2584,17 @@ BitArrayT::clear() noexcept { unit = uint8_t{0}; } +template +HFSM2_CONSTEXPR(14) +bool +BitArrayT::empty() const noexcept { + for (const uint8_t& unit : _storage) + if (unit != uint8_t{0}) + return false; + + return true; +} + template template HFSM2_CONSTEXPR(14) @@ -2783,6 +2836,8 @@ class TaskListT { using Item = TaskT; public: + HFSM2_CONSTEXPR(14) void clear() noexcept; + template HFSM2_CONSTEXPR(14) Index emplace(TArgs&&... args) noexcept; @@ -2791,7 +2846,8 @@ class TaskListT { HFSM2_CONSTEXPR(14) Item& operator[] (const Index i) noexcept; HFSM2_CONSTEXPR(11) const Item& operator[] (const Index i) const noexcept; - HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } + HFSM2_CONSTEXPR(11) bool empty() const noexcept { return _count == 0; } private: HFSM2_IF_ASSERT(void verifyStructure(const Index occupied = INVALID) const noexcept); @@ -2799,9 +2855,9 @@ class TaskListT { private: Index _vacantHead = 0; Index _vacantTail = 0; - Index _last = 0; - Index _count = 0; - Item _items[CAPACITY]; + Index _last = 0; + Index _count = 0; + Item _items[CAPACITY] {}; }; template @@ -2816,6 +2872,16 @@ class TaskListT {}; namespace hfsm2 { namespace detail { +template +HFSM2_CONSTEXPR(14) +void +TaskListT::clear() noexcept { + _vacantHead = 0; + _vacantTail = 0; + _last = 0; + _count = 0; +} + template template HFSM2_CONSTEXPR(14) @@ -3120,8 +3186,10 @@ struct RegistryT +HFSM2_CONSTEXPR(11) +bool +RegistryT>::empty() const noexcept { + return compoRequested.empty() + && orthoRequested.empty() + && compoRemains .empty() + && compoActive .empty() + && compoResumable.empty(); +} + } } @@ -3668,6 +3749,17 @@ RegistryT +HFSM2_CONSTEXPR(11) +bool +RegistryT>::empty() const noexcept { + return compoRequested.empty() + && orthoRequested.empty() + && compoRemains .empty() + && compoActive .empty() + && compoResumable.empty(); +} + } } @@ -3711,6 +3803,13 @@ struct TaskLink final { Long next = INVALID_LONG; }; +template <> +HFSM2_CONSTEXPR(11) +TaskLink +filler() noexcept { + return TaskLink{}; +} + struct Bounds final { Long first = INVALID_LONG; Long last = INVALID_LONG; @@ -3757,23 +3856,23 @@ struct PlanDataT> final { - using StateList = TStateList; - using RegionList = TRegionList; - using Payload = TPayload; + using StateList = TStateList; + using RegionList = TRegionList; + using Payload = TPayload; static constexpr Long STATE_COUNT = StateList ::SIZE; static constexpr Long REGION_COUNT = RegionList::SIZE; static constexpr Long TASK_CAPACITY = NTaskCapacity; - using Task = TaskT; - using Tasks = TaskListT ; - using TaskLinks = StaticArrayT; - using Payloads = StaticArrayT; + using Task = TaskT ; + using Tasks = TaskListT ; + using TaskLinks = StaticArrayT; + using Payloads = StaticArrayT; - using TasksBounds = ArrayT ; - using TasksBits = BitArrayT < STATE_COUNT>; - using RegionBits = BitArrayT < REGION_COUNT>; - using RegionStatuses = StaticArrayT; + using TasksBounds = ArrayT ; + using TasksBits = BitArrayT < STATE_COUNT>; + using RegionBits = BitArrayT < REGION_COUNT>; + using RegionStatuses = StaticArrayT; Tasks tasks; TaskLinks taskLinks; @@ -3791,6 +3890,7 @@ struct PlanDataT> final { - using StateList = TStateList; - using RegionList = TRegionList; + using StateList = TStateList; + using RegionList = TRegionList; - static constexpr Long STATE_COUNT = StateList::SIZE; + static constexpr Long STATE_COUNT = StateList ::SIZE; static constexpr Long REGION_COUNT = RegionList::SIZE; static constexpr Long TASK_CAPACITY = NTaskCapacity; - using Task = TaskT; - using Tasks = TaskListT ; - using TaskLinks = StaticArrayT; + using Task = TaskT ; + using Tasks = TaskListT ; + using TaskLinks = StaticArrayT; - using TasksBounds = ArrayT ; - using TasksBits = BitArrayT < STATE_COUNT>; - using RegionBits = BitArrayT < REGION_COUNT>; - using RegionStatuses = StaticArrayT; + using TasksBounds = ArrayT ; + using TasksBits = BitArrayT < STATE_COUNT>; + using RegionBits = BitArrayT < REGION_COUNT>; + using RegionStatuses = StaticArrayT; Tasks tasks; TaskLinks taskLinks; @@ -3850,6 +3950,7 @@ struct PlanDataT +HFSM2_CONSTEXPR(14) +void +PlanDataT>::clear() noexcept { + tasks .clear(); + taskLinks .clear(); + taskPayloads .clear(); + payloadExists .clear(); + + tasksBounds .clear(); + tasksSuccesses.clear(); + tasksFailures .clear(); + planExists .clear(); + + clearRegionStatuses(); +} + #if HFSM2_ASSERT_AVAILABLE() template @@ -4098,6 +4218,21 @@ PlanDataT +HFSM2_CONSTEXPR(14) +void +PlanDataT>::clear() noexcept { + tasks .clear(); + taskLinks .clear(); + + tasksBounds .clear(); + tasksSuccesses.clear(); + tasksFailures .clear(); + planExists .clear(); + + clearRegionStatuses(); +} + #if HFSM2_ASSERT_AVAILABLE() template @@ -8502,6 +8637,7 @@ struct RF_ final { #if HFSM2_SERIALIZATION_AVAILABLE() static constexpr Long ACTIVE_BITS = Apex::ACTIVE_BITS; static constexpr Long RESUMABLE_BITS = Apex::RESUMABLE_BITS; + static constexpr Long SERIAL_BITS = 1 + ACTIVE_BITS + RESUMABLE_BITS; #endif using Args = ArgsT; @@ -13563,25 +13699,6 @@ class R_ { /// @brief Reset FSM to initial state (recursively 'exit()' currently active states, 'enter()' initial states) HFSM2_CONSTEXPR(14) void reset() noexcept; -#if HFSM2_SERIALIZATION_AVAILABLE() - - /// @brief Buffer for serialization - /// @see https://doc.hfsm.dev/user-guide/debugging-and-tools/serialization - /// @see HFSM2_ENABLE_SERIALIZATION - using SerialBuffer = typename Args::SerialBuffer; - - /// @brief Serialize FSM into 'buffer' - /// @param buffer 'SerialBuffer' to serialize to - /// @see HFSM2_ENABLE_SERIALIZATION - HFSM2_CONSTEXPR(14) void save( SerialBuffer& buffer) const noexcept; - - /// @brief De-serialize FSM from 'buffer' - /// @param buffer 'SerialBuffer' to de-serialize from - /// @see HFSM2_ENABLE_SERIALIZATION - HFSM2_CONSTEXPR(14) void load(const SerialBuffer& buffer) noexcept; - -#endif - #if HFSM2_TRANSITION_HISTORY_AVAILABLE() /// @brief Get the list of transitions recorded during last 'update()' @@ -13678,6 +13795,11 @@ class R_ { HFSM2_CONSTEXPR(14) bool cancelledByGuards(const TransitionSets& currentTransitions, const TransitionSet& pendingTransitions) noexcept; +#if HFSM2_SERIALIZATION_AVAILABLE() + HFSM2_CONSTEXPR(14) void save(WriteStream& stream) const noexcept; + HFSM2_CONSTEXPR(14) void load( ReadStream& stream) noexcept; +#endif + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() HFSM2_CONSTEXPR(14) bool applyRequests(Control& control, const Transition* const transitions, @@ -13734,6 +13856,12 @@ class RV_ ::reset() noexcept { _apex.deepEnter(control); } -#if HFSM2_SERIALIZATION_AVAILABLE() - -template -HFSM2_CONSTEXPR(14) -void -R_::save(SerialBuffer& _buffer) const noexcept { - HFSM2_ASSERT(_core.registry.isActive()); - - WriteStream stream{_buffer}; - - // TODO: save _core.registry - // TODO: save _core.requests - // TODO: save _core.rng // HFSM2_IF_UTILITY_THEORY() - // TODO: save _core.planData // HFSM2_IF_PLANS() - // TODO: save _core.previousTransitions // HFSM2_IF_TRANSITION_HISTORY() - // TODO: save _activityHistory // HFSM2_IF_STRUCTURE_REPORT() - - _apex.deepSaveActive(_core.registry, stream); -} - -template -HFSM2_CONSTEXPR(14) -void -R_::load(const SerialBuffer& buffer) noexcept { - HFSM2_ASSERT(_core.registry.isActive()); - - _core.requests.clear(); - - TransitionSets emptyTransitions; - PlanControl control{_core, emptyTransitions}; - - _apex.deepExit(control); - - HFSM2_IF_TRANSITION_HISTORY(_core.transitionTargets.clear()); - HFSM2_IF_TRANSITION_HISTORY(_core.previousTransitions.clear()); - - _core.registry.clear(); - _core.requests.clear(); - - ReadStream stream{buffer}; - - // TODO: load _core.registry - // TODO: load _core.requests - // TODO: load _core.rng // HFSM2_IF_UTILITY_THEORY() - // TODO: load _core.planData // HFSM2_IF_PLANS() - // TODO: load _core.previousTransitions // HFSM2_IF_TRANSITION_HISTORY() - // TODO: load _activityHistory // HFSM2_IF_STRUCTURE_REPORT() - - _apex.deepLoadRequested(_core.registry, stream); - - _apex.deepEnter(control); -} - -#endif - #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -14895,6 +15029,24 @@ R_::finalExit() noexcept { PlanControl control{_core, emptyTransitions}; _apex.deepExit(control); + + _core.registry.clear(); + _core.requests.clear(); + +#if HFSM2_PLANS_AVAILABLE() + _core.planData.clear(); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: _core.rng.clear(); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + _core.transitionTargets .clear(); + _core.previousTransitions.clear(); +#endif + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); } template @@ -15044,6 +15196,77 @@ R_::cancelledByGuards(const TransitionSets& currentTransitions, _apex.deepForwardEntryGuard(guardControl); } +#if HFSM2_SERIALIZATION_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +R_::save(WriteStream& stream) const noexcept { + HFSM2_ASSERT(_core.registry.isActive()); + + _apex.deepSaveActive(_core.registry, stream); + + // TODO: save(stream, _core.requests); + +#if HFSM2_PLANS_AVAILABLE() + // TODO: save(stream, _core.planData); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: save(stream, _core.rng); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + // TODO: save(stream, _core.transitionTargets); + // TODO: save(stream, _core.previousTransitions); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + // TODO: save(stream, _activityHistory); +#endif +} + +template +HFSM2_CONSTEXPR(14) +void +R_::load(ReadStream& stream) noexcept { + HFSM2_ASSERT(_core.registry.isActive()); + + _core.registry.clearRequests(); + _core.registry.compoResumable.clear(); + _apex.deepLoadRequested(_core.registry, stream); + + _core.requests.clear(); + // TODO: load(stream, _core.requests); + +#if HFSM2_PLANS_AVAILABLE() + _core.planData.clear(); + // TODO: load(stream, _core.planData); +#endif + +#if HFSM2_UTILITY_THEORY_AVAILABLE() + // TODO: load(stream, _core.rng); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + _core.transitionTargets .clear(); + _core.previousTransitions.clear(); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + // TODO: load(stream, _activityHistory); +#endif + + TransitionSets emptyTransitions; + PlanControl control{_core, emptyTransitions}; + + _apex.deepChangeToRequested(control); + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); +} + +#endif + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -15220,6 +15443,66 @@ RV_ +HFSM2_CONSTEXPR(14) +void +RV_, TA>::save(SerialBuffer& buffer) const noexcept { + WriteStream stream{buffer}; + + stream.template write<1>(1); + save(stream); +} + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::load(const SerialBuffer& buffer) noexcept { + ReadStream stream{buffer}; + + if (HFSM2_CHECKED(stream.template read<1>())) + Base::load(stream); +} + +#endif + +#if HFSM2_SERIALIZATION_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::save(SerialBuffer& buffer) const noexcept { + WriteStream stream{buffer}; + + if (isActive()) { + stream.template write<1>(1); + + save(stream); + } + else + stream.template write<1>(0); +} + +template +HFSM2_CONSTEXPR(14) +void +RV_, TA>::load(const SerialBuffer& buffer) noexcept { + ReadStream stream{buffer}; + + if (stream.template read<1>()) { + if (isActive()) + Base::load(stream); + else + loadEnter (stream); + } + else + if (isActive()) + finalExit(); +} + +#endif + #if HFSM2_TRANSITION_HISTORY_AVAILABLE() template @@ -15270,6 +15553,40 @@ RV_ +HFSM2_CONSTEXPR(14) +void +RV_, TA>::loadEnter(ReadStream& stream) noexcept { + HFSM2_ASSERT(_core.registry.empty()); + _apex.deepLoadRequested(_core.registry, stream); + + HFSM2_ASSERT(_core.requests.empty()); + +#if HFSM2_PLANS_AVAILABLE() + HFSM2_ASSERT(_core.planData.empty() == 0); +#endif + +#if HFSM2_TRANSITION_HISTORY_AVAILABLE() + HFSM2_ASSERT(_core.transitionTargets .empty()); + HFSM2_ASSERT(_core.previousTransitions.empty()); +#endif + +#if HFSM2_STRUCTURE_REPORT_AVAILABLE() + //HFSM2_ASSERT(_activityHistory.empty()); +#endif + + TransitionSets emptyTransitions; + PlanControl control{_core, emptyTransitions}; + + _apex.deepEnter(control); + + HFSM2_IF_STRUCTURE_REPORT(udpateActivity()); +} + +#endif + template HFSM2_CONSTEXPR(14) void diff --git a/test/shared/test_bit_array.cpp b/test/shared/test_bit_array.cpp index 7dcd19e..38c9a29 100644 --- a/test/shared/test_bit_array.cpp +++ b/test/shared/test_bit_array.cpp @@ -12,8 +12,7 @@ using Bits = typename BitArray::Bits; TEST_CASE("Shared.BitArrayT<>") { BitArray bitArray; - - bitArray.clear(); + REQUIRE(bitArray.empty()); WHEN("Static methods") { { @@ -68,6 +67,9 @@ TEST_CASE("Shared.BitArrayT<>") { REQUIRE(!bits.get<5>()); REQUIRE(!bits.get<6>()); } + + bitArray.clear(); + REQUIRE(bitArray.empty()); } WHEN("Dynamic methods") { @@ -123,6 +125,9 @@ TEST_CASE("Shared.BitArrayT<>") { REQUIRE(!bits.get(5)); REQUIRE(!bits.get(6)); } + + bitArray.clear(); + REQUIRE(bitArray.empty()); } } diff --git a/test/test_serialization.cpp b/test/test_serialization.cpp index 120b3ec..34e45b3 100644 --- a/test/test_serialization.cpp +++ b/test/test_serialization.cpp @@ -9,7 +9,10 @@ namespace test_serialization { //////////////////////////////////////////////////////////////////////////////// -using M = hfsm2::Machine; +using Config = hfsm2::Config + ::ManualActivation; + +using M = hfsm2::MachineT; //------------------------------------------------------------------------------ @@ -212,92 +215,165 @@ static_assert(server::FSM::Instance::Info::RESUMABLE_BITS == client::FSM::Instan //////////////////////////////////////////////////////////////////////////////// -TEST_CASE("FSM.Serialization") { - Logger logger; +void step1(server::FSM::Instance& authority, + client::FSM::Instance& replicated, + Logger& logger) +{ + server::FSM::Instance::SerialBuffer buffer; + authority .save(buffer); + replicated.load(buffer); + + logger.assertSequence({}); + + assertActive(authority, server::all, {}); + assertActive(replicated, client::all, {}); + + assertResumable(authority, server::all, {}); + assertResumable(replicated, client::all, {}); +} + +//------------------------------------------------------------------------------ + +void step2(server::FSM::Instance& authority, + client::FSM::Instance& replicated, + Logger& logger) +{ + authority.enter(); server::FSM::Instance::SerialBuffer buffer; + authority .save(buffer); + replicated.load(buffer); + + logger.assertSequence({ + { hfsm2::StateID{0}, Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + }); + + assertActive(authority, server::all, { + server::FSM::stateId(), + server::FSM::stateId(), + }); + + assertActive(replicated, client::all, { + client::FSM::stateId(), + client::FSM::stateId(), + }); + + assertResumable(authority, server::all, {}); + assertResumable(replicated, client::all, {}); +} - { - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//------------------------------------------------------------------------------ + +void step3(server::FSM::Instance& authority, + client::FSM::Instance& replicated) +{ + server::FSM::Instance::SerialBuffer serverBuffer; + server::FSM::Instance::SerialBuffer clientBuffer; + + authority .save(serverBuffer); + replicated.save(clientBuffer); + + REQUIRE (clientBuffer == serverBuffer); + REQUIRE_FALSE(clientBuffer != serverBuffer); +} + +//------------------------------------------------------------------------------ + +void step4(server::FSM::Instance& authority, + client::FSM::Instance& replicated, + Logger& logger) +{ + authority.immediateChangeTo(); + + server::FSM::Instance::SerialBuffer buffer; + authority .save(buffer); + replicated.load(buffer); + + logger.assertSequence({ + { client::FSM::stateId(), Event::Type::EXIT }, + { client::FSM::stateId(), Event::Type::EXIT }, + + { client::FSM::stateId(), Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + { client::FSM::stateId(), Event::Type::ENTER }, + }); + + assertActive(authority, server::all, { + server::FSM::stateId(), + server::FSM::stateId(), + server::FSM::stateId(), + server::FSM::stateId(), + server::FSM::stateId(), + }); + + assertActive(replicated, client::all, { + client::FSM::stateId(), + client::FSM::stateId(), + client::FSM::stateId(), + client::FSM::stateId(), + client::FSM::stateId(), + }); + + assertResumable(authority, server::all, { + server::FSM::stateId(), + server::FSM::stateId(), + }); + + assertResumable(replicated, client::all, { + client::FSM::stateId(), + client::FSM::stateId(), + }); +} +//------------------------------------------------------------------------------ + +void step5(server::FSM::Instance& authority, + client::FSM::Instance& replicated, + Logger& logger) +{ + authority.exit(); + + server::FSM::Instance::SerialBuffer buffer; + authority .save(buffer); + replicated.load(buffer); + + logger.assertSequence({ + { client::FSM::stateId(), Event::Type::EXIT }, + { client::FSM::stateId(), Event::Type::EXIT }, + { client::FSM::stateId(), Event::Type::EXIT }, + { client::FSM::stateId(), Event::Type::EXIT }, + { client::FSM::stateId(), Event::Type::EXIT }, + { hfsm2::StateID{0}, Event::Type::EXIT }, + }); + + assertActive(authority, server::all, {}); + assertActive(replicated, client::all, {}); + + assertResumable(authority, server::all, {}); + assertResumable(replicated, client::all, {}); +} + +//------------------------------------------------------------------------------ + +TEST_CASE("FSM.Serialization") { + Logger logger; + + { server::FSM::Instance authority; client::FSM::Instance replicated{&logger}; - { - logger.assertSequence({ - { hfsm2::StateID{0}, Event::Type::ENTRY_GUARD }, - { client::FSM::stateId(), Event::Type::ENTRY_GUARD }, - { client::FSM::stateId(), Event::Type::ENTRY_GUARD }, - - { hfsm2::StateID{0}, Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - }); - - assertActive(authority, server::all, { - server::FSM::stateId(), - server::FSM::stateId(), - }); - - assertActive(replicated, client::all, { - client::FSM::stateId(), - client::FSM::stateId(), - }); - - assertResumable(authority, server::all, {}); - - assertResumable(replicated, client::all, {}); - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - authority.immediateChangeTo(); - { - assertActive(authority, server::all, { - server::FSM::stateId(), - server::FSM::stateId(), - server::FSM::stateId(), - server::FSM::stateId(), - server::FSM::stateId(), - }); - - assertResumable(authority, server::all, { - server::FSM::stateId(), - server::FSM::stateId(), - }); - } - authority.save(buffer); - - replicated.load(buffer); - { - logger.assertSequence({ - { client::FSM::stateId(), Event::Type::EXIT }, - { client::FSM::stateId(), Event::Type::EXIT }, - { hfsm2::StateID{0}, Event::Type::EXIT }, - - { hfsm2::StateID{0}, Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - { client::FSM::stateId(), Event::Type::ENTER }, - }); - - assertActive(replicated, client::all, { - client::FSM::stateId(), - client::FSM::stateId(), - client::FSM::stateId(), - client::FSM::stateId(), - client::FSM::stateId(), - }); - - assertResumable(replicated, client::all, { - client::FSM::stateId(), - client::FSM::stateId(), - }); - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + step1(authority, replicated, logger); + step2(authority, replicated, logger); + step3(authority, replicated); + step4(authority, replicated, logger); + step5(authority, replicated, logger); } + + logger.assertSequence({}); } ////////////////////////////////////////////////////////////////////////////////