amaiorano / hsm Goto Github PK
View Code? Open in Web Editor NEWC++ framework library to simplify state-driven code
License: MIT License
C++ framework library to simplify state-driven code
License: MIT License
Example: A leveling system, which has a LEVELING state.
This LEVELING state can be started by a external LEVEL command. This will start actuators based on an MPU until the platform is level and state will return to IDLING
But the system also has an EXTEND_LEVEL command, which will raise the platform level as far a possible.
The EXTENDING_LEVEL state raises the platform by starting all actuators, but when its not level anymore, it will transition to state LEVELING. When its LEVEL again, it will continue raising the platform until MAX_LIMIT is reached.
So the LEVELING state can be started from IDLING state and from EXTENDING_LEVEL state and has to return to that state when the platform is level.
What is the best way to tell the LEVELING state to which state it has to transition when the platform is LEVEL?
I want to be able to access the current state of the statemachine through the owner, preferably with an enum value.
I could set this value in the OnEnter() method of every state, but I was wondering if there isn't an easier way to do this
How can I use this in a more object oriented fashion. Your book examples reference all the code concurrently in a single CPP avoiding having to forward declare a class. If I were to organize my states into .h and .cpp files respectively and try to forward declare a class it fails the static assertion checks. Example being
`
//in State_Idle.h
class State_Ready;
class State_Idle : public AbstractState{
}
//where AbstractState inherits hsm::State
//in State_Idle.cpp
//constructors, destructors, etc
#include State_Ready.h
//in State_Ready.h
class State_Ready: public AbstractState{
}
`
Thanks for your help.
hi,
i'm writing a game (my menu state), but i have this problem which i couldn't fix:
when i select the item, it is selected, but the next menu doesnt do well
here is my code:
states.cpp
``C++
#include "hsm.h"
#include "vars.h"
#include "functions.h"
#include "states.h"
void ExitState::OnEnter()
{
Tolk_Speak(L"thanks for playing", true);
s.fadeGlobalVolume(0.0, 2.0);
SDL_Delay(2000);
quit=true;
}
hsm::Transition ExitState::GetTransition()
{
return hsm::NoTransition();
}
void MainMenuState::OnEnter()
{
//initialize the main menu
mainmenu.load_menumove_from_memory((unsigned char*)cfl->getFile("menumove.ogg"), cfl->getFileSize("menumove.ogg"));
mainmenu.load_menuedge_from_memory((unsigned char*)cfl->getFile("menuedge.ogg"), cfl->getFileSize("menuedge.ogg"));
mainmenu.load_menuselect_from_memory((unsigned char*)cfl->getFile("menuselect.ogg"), cfl->getFileSize("menuselect.ogg"));
//play the mainmenu music
menumusic_handle=s.play(mainmusic);
//add things to menu
mainmenu.add(L"start game");
mainmenu.add(L"test speakers");
mainmenu.add(L"exit");
should_change=false;
item=mainmenu.run(L"use up and down arrow keys to navigate and press enter to activate the menu");
}
void MainMenuState::Update()
{
should_change=!mainmenu.is_running();
if(!should_change)
{
item=mainmenu.run(L"use up and down arrow keys to navigate and press enter to activate the menu");
}
}
hsm::Transition MainMenuState::GetTransition()
{
if(should_change)
{
if(mainmenu.get_item(item)==L"start game")
{
return hsm::SiblingTransition();
}
else if(mainmenu.get_item(item)==L"exit"||item<0)
{
return hsm::SiblingTransition();
}
else if(mainmenu.get_item(item)==L"test speakers")
{
s.stopAll();
return hsm::SiblingTransition();
}
}
else
{
return hsm::NoTransition();
}
return hsm::NoTransition();
}
void TestSpeakerState::OnEnter()
{
speakertesthandle=s.play(speakertest);
}
hsm::Transition TestSpeakerState::GetTransition()
{
if(s.isValidVoiceHandle(speakertesthandle))
{
SDL_Delay(2000);
return hsm::SiblingTransition();
}
return hsm::NoTransition();
}
void startgameState::OnEnter()
{
m.add(L"start a new game");
m.add(L"load a saved game");
m.add(L"delete a saved game");
m.add(L"go back to main menu");
should_change=false;
item=m.run(L"choose an option");
}
void startgameState::Update()
{
should_change=!m.is_running();
if(!should_change)
{
item=m.run(L"choose an option");
}
}
hsm::Transition startgameState::GetTransition()
{
if(should_change)
{
if(m.get_item(item)==L"go back to main menu")
{
return hsm::SiblingTransition();
}
}
else
{
return hsm::NoTransition();
}
return hsm::NoTransition();
}
`
states.h
``C++
#pragma once
#ifndef states_h
#define states_h
#include "hsm.h"
#include "menu.h"
//the menu state
class MainMenuState: public hsm::State
{
int item;
int menumusic_handle;
menu mainmenu;
bool should_change;
public:
hsm::Transition GetTransition();
void OnEnter();
void Update();
};
//test speaker state
class TestSpeakerState: public hsm::State
{
int speakertesthandle;
public:
void OnEnter();
hsm::Transition GetTransition();
};
//the exit state
class ExitState: public hsm::State
{
public:
hsm::Transition GetTransition();
void OnEnter();
};
class startgameState: public hsm::State
{
int item;
menu m;
bool should_change;
public:
void OnEnter();
void Update();
hsm::Transition GetTransition();
};
#endif //states_h
`
please tell me whats wrong with this state (without state, i could use my menu class correctly. but now i cant)
it can switch states, but the next state doesn't work
the exitState works as expected, just the start game state has problem which i couldnt fix
When trying to use this library I get the error:
error: 'GetStaticStateType' is not a member of 'MyStates::STATENAME'
for my specific implementation.
Any ideas what could be causing this? I am running c++11
Heres the example code from the wiki I am trying to use
(https://github.com/amaiorano/hsm/blob/master/samples/hsm_book_samples/source/ch3/inner_transition.cpp)
#include "../lib/hsm/include/hsm.h"
using namespace hsm;
class MyOwner
{
public:
MyOwner();
void UpdateStateMachine();
void Die() { mDead = true; }
void SetMove(bool enable) { mMove = enable; }
private:
bool IsDead() const { return mDead; }
bool PressedMove() const { return mMove; }
bool mDead;
bool mMove;
friend struct MyStates;
StateMachine mStateMachine;
};
struct MyStates
{
struct BaseState : StateWithOwner<MyOwner>
{
};
struct Alive : BaseState
{
virtual Transition GetTransition()
{
if (Owner().IsDead())
return SiblingTransition<Dead>();
return InnerEntryTransition<Locomotion>();
}
};
struct Dead : BaseState
{
virtual Transition GetTransition()
{
return NoTransition();
}
};
struct Locomotion : BaseState
{
virtual Transition GetTransition()
{
if (Owner().PressedMove())
return InnerTransition<Move>();
else
return InnerTransition<Stand>();
}
};
struct Stand : BaseState
{
};
struct Move : BaseState
{
};
};
MyOwner::MyOwner()
: mDead(false), mMove(false)
{
mStateMachine.Initialize<MyStates::Alive>(this);
mStateMachine.SetDebugInfo("TestHsm", TraceLevel::Basic);
}
void MyOwner::UpdateStateMachine()
{
mStateMachine.ProcessStateTransitions();
mStateMachine.UpdateStates();
}
int main()
{
MyOwner myOwner;
myOwner.UpdateStateMachine();
printf("Set Move = true\n");
myOwner.SetMove(true);
myOwner.UpdateStateMachine();
printf("Set Move = false\n");
myOwner.SetMove(false);
myOwner.UpdateStateMachine();
}
#ifdef __GNUC__
#define HSM_DEPRECATED(MESSAGE) __attribute__((deprecated("DEPRECATED: " MESSAGE)))
#elif defined(_MSC_VER)
#define HSM_DEPRECATED(MESSAGE) __declspec(deprecated("DEPRECATED: " MESSAGE))
#else
#pragma message("Implement HSM_DEPRECATED for this compiler")
#define HSM_DEPRECATED
#endif
Currently the library supports C++03, which is good for compatibility. But C++11/14 would allow for simplifying certain features (e.g. StateArgs), and remove some code that is now part of the standard (e.g. type traits). Maybe it's time to cut the cord on old compilers as most modern compilers (clang, gcc, msvc) implement most of C++11/14 today.
Currently HSM consists of 5 headers and a single cpp file; the latter is a pain because client code has to build it. Also, the directory structure separating the cpp and headers isn't great either.
If the entire library were just a single header file, hsm.h, it would be simpler to use.
VSNPRINTF(buffer + offset, sizeof(buffer), format, args);
should be:
VSNPRINTF(buffer + offset, sizeof(buffer) - offset - 1, format, args);
Is it possible to return a SiblingTransition from an inner state to a sibling of the parent state?
I have tried this, and it seems that the OnExit of the parent state is not executed
Moving ---- Idling
/ \
Opening Closing
So when returning SiblingTransition<Idling>
from state Opening
, the OnExit(
) of Opening
is executed, but the OnExit()
from Moving is not. Also when enabling TraceLevel::Diagnostic
you only see a Pop: Opening
and a Sibling: Idling
, missing the Pop:Moving
Is this a bug or I i'm doing something wrong? How can I solve this?
A large state machine results in many lines of code,so i want to put some sub states in other files,How can I achieve it
thanks
'ch5/cluster_root_clean_up.cpp.dot.png' doesn't show.
A large state machine results in many lines of code,so i want to put some sub states in other files,How can I achieve it
thanks
Not really sure what is going on here. Figured I'd bring it to your attention.
Traceback (most recent call last):
File "plotHsm.py", line 46, in <module>
sys.exit(main())
File "plotHsm.py", line 36, in main
ExecCommand(os.path.join(GetScriptPath(), 'hsmToDot.py') + ' ' + filespec + ' > ' + dotFile)
File "plotHsm.py", line 22, in ExecCommand
raise Exception("Command failed!")
Exception: Command failed!
However I can successfully generate the png if I do the steps manually
python hsmToDot.py ../samples/hsm_book_samples/source/ch4/state_args.cpp > myfile.dot
dot myfile.dot -Tpng -o myfile.png
Static code analysis ( sonarqube) report an error with this macro:
V1003: The macro 'HSM_ASSERT_MSG' is a dangerous expression.
The parameter 'msg' must be surrounded by parentheses.
Consider this case:
According to https://github.com/amaiorano/hsm/wiki/Chapter-3.-The-H-in-HSM#algorithm and based on my understanding, state stack is processed from the outermost state, e.g. state A. If A::GetTransition() returns SiblingTransition() or InnerTransition() and the current state is D, then D is popped from stack and has no chance to handle the event.
Pls correct me if I was wrong.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.