sml
sml copied to clipboard
Issue transition from an innerstate to other states
Hi, I have pasted the code below for a motor control statemachine.
auto conditionalSwitchToControl = [](const auto& evt,auto& sm,auto& deps,auto& subs)
{
if(sml::aux::get<controllerInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)->emergencyStatus())
{
sml::aux::get<smlTimer_Impl&,decltype(deps)>(deps).reload();
}
else
{
if(sml::aux::get<dispatchInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)
->hasdeferredEventArrived())
{
sml::aux::get<dispatchInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)->setDeferredEvent(false);
auto temp = sml::aux::get<dispatchInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)
->getdeferredEvent();
if(std::is_same_v<sml::aux::remove_reference_t<decltype(temp)>,_event::enableControl>)
{
sm.process_event(_event::enableControl{},deps,subs);
}
else if(std::is_same_v<sml::aux::remove_reference_t<decltype(temp)>,_event::start>)
{
sm.process_event(_event::start{},deps,subs);
}
}
else
{
sm.process_event(_event::reInit{},deps,subs);
}
}
};
namespace guard {
auto emergencyON = [](const auto& evt,auto& sm,auto& deps,auto& subs)-> bool
{
return sml::aux::get<controllerInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)->emergencyStatus();
};
auto paramsReceived = [](const auto& evt,auto& sm,auto& deps,auto& subs) -> bool
{
return sml::aux::get<controllerInterface*,sml::aux::remove_reference_t<decltype(deps)>>(deps)->paramsInited();
};
}
struct _state::Halt {
auto operator()(){
using namespace sml;
return make_transition_table(
*"halted"_s + on_entry<_>/[](dispatchInterface* dis,smlTimer_Impl& timer)
{
dis->setHalt(true);
dis->updateState(STATE::HALT);
timer.init(1000);
},
"halted"_s + event<_event::timeout>/conditionalSwitchToControl,
"halted"_s + on_exit<_>/[](dispatchInterface* dis) {dis->setHalt(false);}
);
}
};
struct _state::openLoopControl {
auto operator()() {
using namespace sml;
return make_transition_table(
*"init2"_s + on_entry<_>/[](controllerInterface* iface,dispatchInterface* dis){iface->initialise();dis->updateState(STATE::OPENLOOPCONTROL);},
"init2"_s + event<_event::cmd>[!guard::emergencyON] = "drive"_s,
"drive"_s + event<_event::cmd>[!guard::emergencyON]/[](controllerInterface* iface) {iface->setpointUpdate();iface->drive();},
"drive"_s + on_entry<_>/[](controllerInterface* iface,smlTimer_Impl& timer){iface->setpointUpdate();iface->drive();timer.init(5);},
"drive"_s + event<_event::timeout>/[](smlTimer_Impl& timer,controllerInterface* iface){iface->feedback();timer.reload();},
"drive"_s + event<_event::brake>/[](controllerInterface* iface) {iface->setPointZero();},
"drive"_s + event<_event::emergency>/[](controllerInterface* iface,dispatchInterface* dis)
{iface->stop();dis->updateState(STATE::HALT);} = state<_state::Halt>
);
}
};
struct _state::control {
auto operator()() {
using namespace sml;
return make_transition_table(
*"init"_s + on_entry<_>/[](controllerInterface* iface,dispatchInterface* dis) {iface->initialise(); dis->updateState(STATE::CONTROL);},
"init"_s + event<_event::cmd>[!guard::emergencyON] = "controlActive"_s,
"controlActive"_s + on_entry<_>/[](smlTimer_Impl& timer,dispatchInterface* dispatch){timer.init(2);dispatch->updateState(STATE::CONTROLACTIVE);},
"controlActive"_s + event<_event::timeout>/[](controllerInterface* iface,smlTimer_Impl& timer){iface->update();timer.reload();},
"controlActive"_s + event<_event::brake>/[](controllerInterface* iface) {iface->setPointZero()},
"controlActive"_s + event<_event::emergency>/[](controllerInterface* iface,dispatchInterface* dis)
{iface->stop();dis->updateState(STATE::HALT);} = state<_state::Halt>
);
}
};
struct _state::idle {
auto operator()() {
using namespace sml;
return make_transition_table(
*"S1"_s + on_entry<_>/[](dispatchInterface* dis,controllerInterface* iface)
{
dis->updateState(STATE::IDLE);
if(!iface->paramsInited())
dis->dispatchParamsRequest();
},
"S1"_s + event<_event::enableControl>[guard::paramsReceived] = state<_state::control>,
"S1"_s + event<_event::enableControl>[!guard::paramsReceived]/[](dispatchInterface* dis){dis->dispatchParamsRequest();},
"S1"_s + event<_event::start>[!guard::paramsReceived]/[](dispatchInterface* dis){dis->dispatchParamsRequest();},
"S1"_s + event<_event::start>[guard::paramsReceived] = state<_state::openLoopControl>,
"S1"_s + event<_event::emergency> = state<_state::Halt>,
state<_state::openLoopControl> + event<_event::enableControl>[!guard::emergencyON]/[](smlTimer_Impl& timer){timer.reset();}
= state<_state::control>,
state<_state::control> + event<_event::start>[!guard::emergencyON]/[](smlTimer_Impl& timer){timer.reset();}
= state<_state::openLoopControl>,
state<_state::Halt> + event<_event::enableControl>[!guard::emergencyON] = state<_state::control>,
state<_state::Halt> + event<_event::start>[!guard::emergencyON] = state<_state::openLoopControl>,
state<_state::Halt> + event<_event::reInit>[!guard::emergencyON] = "S1"_s
);
}
};
The statemachine works as intended and the behaviour is correct when testing on x86 but when i run the same on an stm32 microcontroller (arm-none-eabi-gcc 7.3.1). The behaviour changes a little i.e. upon processing of the event<reInit>
or event<start>
or event<enableControl>
an external transition from _state::halt to the relevant state is expected but doesnt happen. The transition happens on x86. I hope i have properly followed the UML semantics for statemachine transitions and that there are no illegal transitions.
I faced a similar issue when going from state<openLoopControl>
or state<control>
to state<Halt>
. The event _event::emergency would not trigger a state transition to _state::Halt
. This was because the definition of sub_sm _state::Halt
was after the definiton of state<openLoopControl>
and state<control>
. Moving the definition above fixed the issue. However i havent faced similiar issues on x86 tests.
Encountered the same erroneous behaviour on an armv7 processor.
On x86 do you also compile using gcc with the same version ? It might be due to some compiler shenanigans for ARM platform or some gcc errors