I'm looking for some general
Optimization
Correctness
Extensibility
advice on my current C++ Hierarchical State Machine implementation.
Sample
variable isMicOn = false
variable areSpeakersOn = false
variable stream = false
state recording
{
//override block for state recording
isMicOn = true //here, only isMicOn is true
//end override block for state recording
}
state playback
{
//override block for state playback
areSpeakersOn = true //here, only areSpeakersOn = true
//end override block for state playback
state alsoStreamToRemoteIp
{
//override block for state alsoStreamToRemoteIp
stream = true //here, both areSpeakersOn = true and stream = true
//end override block for state alsoStreamToRemoteIp
}
}
goToState(recording)
goToState(playback)
goToState(playback.alsoStreamToRemoteIp)
Implementation
Currently, the HSM is implemented as a tree structure where each state can have a variable number of states as children.
Each state contains a variable number of "override" blocks (in a std::map) that override base values. At the root state, the state machine has a set of variables (functions, properties...) initialized to some default values. Each time we enter a child state, a list of "overrides" define variable and values that should replace the variables and values of the same name in the parent state. Updated original for clarity.
Referencing variables
At runtime, the current states are stored on a stack.
Every time a variable is referenced, a downwards stack walk is performed looking for the highest override, or in the case of no overrides, the default value.
Switching states
Each time a single state frame is switched to, the state is pushed onto a stack.
Each time a state is switched to, I trace a tree descension that takes me from the current state to the root state. Then I do a tree descension from the target state to the root state until I see the current trace matches the previous trace. I declare an intersection at where those 2 traces meet. Then, to switch to the target state, I descend from the source, popping state frames from the stack until I reach the intersection point. Then I ascend to the target node and push state frames onto the stack.
So for the code sample above
Execution trace for state switch
Source state = recording
Target State = alsoStreamToRemoteIp
descension from source = recording->root (trace = [root])
descension from target = alsoStreamToRemoteIp->playback->root (trace = [playback, root])
Intersects at root.
To switch from recording to alsoStreamToRemoteIp,
Pop "recording" from the stack (and call its exit function... not defined here).
Push "playback" onto the stack (and call the enter function).
Push "alsoStreamToRemoteIp" onto the stack (and call the the enter function).
Two things:
1: For most cases just represent the state of your program as a Model, and interact with it directly or through the MVC pattern.
2: If you really need a FSM, i.e. you want to randomly make a bunch of actions to your model, only some of which are allowed at certain times. Then....
Still keep the state of your program in a Model (or multiple Models depending on decomposition and complexity) and represent states and transitions like.
class State:
def __init__(self):
self.neighbors = {}
Where neighbors contains a dictionary of of {Action: State}, so that you can do something like
someAction.execute() # Actions manipulate the model (use classes or lambdas)
currentState = currentState.neighbors[someAction]
Or even cooler, have an infinite loop randomly selecting an action from the neighbors, executing it, and moving state indefinitely. It's a great way to test your program.
I'm not sure I follow all the details here. However, it seems that you are describing an FSM (finite state machine) implementation where you have multiple state machines. Sometimes, when a particular event (E1) occurs in a particular state (S1) of FSM F1, you need to enter a new FSM (call it F2) to simplify the processing overall).
If that's the case, then when E1 occurs in S1, you need to invoke an action routine that takes over the event reading and implements the F2 FSM. When invoked, it starts processing in the start state of F2, and handles the relevant sub-events. When it reaches its end state, the interpreter for F2 finishes. It might return some information to the F1 action routine that was suspended while F2 ran, and the next state in F1 may be affected by that.
The rest of your description - stuff like 'override blocks' - won't make much sense to people without access to your implementation.
Related
AnyLogic (version 8.7.5) OverheadCrane is working well in automatic mode.
I use the moveByCrane logic blocks to move other agents from one location to another.
But I'd like to modify the behaviour just a little. After the crane has finished its move, I'd like it to lift the hook up out of the way.
There is a facility for operating cranes manually: the CraneProgram. In the crane's "On bridge state change" action, I use the following code to lift the hook up.
traceln("crane.onStateChange() "+newState);
if (newState == OverheadCraneBridgeState.IDLE) {
traceln(" Raising hook");
CraneProgram cp = new CraneProgram();
cp.moveHook(8, METER);
bridge.moveByProgram(cp);
}
This works nicely, except that the crane proceeds through MOVING_UNLOADED state and ends up in the WAITING state, and in that state it will not automatically work on any agents that are subsequently available to be moved.
The documentation describes WAITING as:
The bridge is seized by the agent, but this agent is currently moving through any blocks that are not SeizeCrane, MoveByCrane, or ReleaseCrane blocks. Also includes cases where the bridge in manual mode remains at target, executes a programmed delay command, or has finished the program and waits for the next command.
Is there a way to tell the crane that there will be no "next command", and it should switch from manual mode back to automatic mode?
Advice from AnyLogic support, received with thanks:
In this case I advise you to use “moveTo(…)” function of a bridge instead of using “CraneProgram” class. This function has “remainsAsTarget” argument, that allows you to use the required behavior when it reaches the destination.
Essence of solution:
if (newState == OverheadCraneBridgeState.IDLE && !movedHookUp) {
Point newPosition = new Point(endNode.getX(),endNode.getY(),endNode.getZ()+toPixels(8, LengthUnits.METER))
bridge.moveTo(newPoint, //destination
8, // safety height
false); // remainsAtTarget - see documentation
movedHookUp = true;
}
See moveTo function in the bridge documentation.
In a turn-based combat game, all actions that characters can perform inherit from this class. It started off looking like this:
using ActionPtr = std::shared_ptr<Action>;
class Action {
public:
Action(Entity actor, std::uint32_t time) { ... }
virtual ActionPtr execute() = 0;
...
std::uint32_t time; // time that the action will occur. For e.g 10 if it occurs in tick 10 of combat
Entity actor; // the character performing the action
};
Upon entering combat, everyone gets a DecideAction that allows characters to decide what to do (either bring up menus for the player, or the AI choosing actions).
The combat system's maintains a timeline of actions; sorted by the time that actions should occur and the speed of actors to break ties. Its update function does something like this:
sortTimeline();
ActionPtr followUpAction = timeline.front()->execute();
timeline.removeFront();
timeline.push_back(followUpAction);
For example, the AiDecideAction executing returns the follow-up action (i.e what that entity chose to do). An attack action's execution would return a decide action set to happen after attack-dependent cooldowns. These actions get added and processed in the next tick.
Since actions can create other actions, I'm finding that I have to push all the possible info that actions may require to execute to the Action class, the root of the inheritance hierarchy. This is so that it can properly construct follow-up actions. For e.g
AttackAction::execute() {
...
return ActionPtr(new AiDecide(actor, nextActionTime, allies, enemies);
// allow this entity to decide what to do after attacking
}
The attack action needs access to allies and enemies just to supply it to constructors; so I ended up modifying the constructor of Action to take in all possible info that derived actions may require to try solving this problem.
Is there a better way of doing this? I'm wondering if inheritance is the right way to model this problem. I've had to modify all actions several times so that information required for constructing other actions is present, which isn't ideal.
We have a data set that grows while the application is processing the data set. After a long discussion we have come to the decision that we do not want blocking or asynchronous APIs at this time, and we will periodically query our data store.
We thought of two options to design an API for querying our storage:
A query method returns a snapshot of the data and a flag indicating weather we might have more data. When we finish iterating over the last returned snapshot, we query again to get another snapshot for the rest of the data.
A query method returns a "live" iterator over the data, and when this iterator advances it returns one of the following options: Data is available, No more data, Might have more data.
We are using C++ and we borrowed the .NET style enumerator API for reasons which are out of scope for this question. Here is some code to demonstrate the two options. Which option would you prefer?
/* ======== FIRST OPTION ============== */
// similar to the familier .NET enumerator.
class IFooEnumerator
{
// true --> A data element may be accessed using the Current() method
// false --> End of sequence. Calling Current() is an invalid operation.
virtual bool MoveNext() = 0;
virtual Foo Current() const = 0;
virtual ~IFooEnumerator() {}
};
enum class Availability
{
EndOfData,
MightHaveMoreData,
};
class IDataProvider
{
// Query params allow specifying the ID of the starting element. Here is the intended usage pattern:
// 1. Call GetFoo() without specifying a starting point.
// 2. Process all elements returned by IFooEnumerator until it ends.
// 3. Check the availability.
// 3.1 MightHaveMoreDataLater --> Invoke GetFoo() again after some time by specifying the last processed element as the starting point
// and repeat steps (2) and (3)
// 3.2 EndOfData --> The data set will not grow any more and we know that we have finished processing.
virtual std::tuple<std::unique_ptr<IFooEnumerator>, Availability> GetFoo(query-params) = 0;
};
/* ====== SECOND OPTION ====== */
enum class Availability
{
HasData,
MightHaveMoreData,
EndOfData,
};
class IGrowingFooEnumerator
{
// HasData:
// We might access the current data element by invoking Current()
// EndOfData:
// The data set has finished growing and no more data elements will arrive later
// MightHaveMoreData:
// The data set will grow and we need to continue calling MoveNext() periodically (preferably after a short delay)
// until we get a "HasData" or "EndOfData" result.
virtual Availability MoveNext() = 0;
virtual Foo Current() const = 0;
virtual ~IFooEnumerator() {}
};
class IDataProvider
{
std::unique_ptr<IGrowingFooEnumerator> GetFoo(query-params) = 0;
};
Update
Given the current answers, I have some clarification. The debate is mainly over the interface - its expressiveness and intuitiveness in representing queries for a growing data-set that at some point in time will stop growing. The implementation of both interfaces is possible without race conditions (at-least we believe so) because of the following properties:
The 1st option can be implemented correctly if the pair of the iterator + the flag represent a snapshot of the system at the time of querying. Getting snapshot semantics is a non-issue, as we use database transactions.
The 2nd option can be implemented given a correct implementation of the 1st option. The "MoveNext()" of the 2nd option will, internally, use something like the 1st option and re-issue the query if needed.
The data-set can change from "Might have more data" to "End of data", but not vice versa. So if we, wrongly, return "Might have more data" because of a race condition, we just get a small performance overhead because we need to query again, and the next time we will receive "End of data".
"Invoke GetFoo() again after some time by specifying the last processed element as the starting point"
How are you planning to do that? If it's using the earlier-returned IFooEnumerator, then functionally the two options are equivalent. Otherwise, letting the caller destroy the "enumerator" then however-long afterwards call GetFoo() to continue iteration means you're losing your ability to monitor the client's ongoing interest in the query results. It might be that right now you have no need for that, but I think it's poor design to exclude the ability to track state throughout the overall result processing.
It really depends on many things whether the overall system will at all work (not going into details about your actual implementation):
No matter how you twist it, there will be a race condition between checking for "Is there more data" and more data being added to the system. Which means that it's possibly pointless to try to capture the last few data items?
You probably need to limit the number of repeated runs for "is there more data", or you could end up in an endless loop of "new data came in while processing the last lot".
How easy it is to know if data has been updated - if all the updates are "new items" with new ID's that are sequentially higher, you can simply query "Is there data above X", where X is your last ID. But if you are, for example, counting how many items in the data has property Y set to value A, and data may be updated anywhere in the database at the time (e.g. a database of where taxis are at present, that gets updated via GPS every few seconds and has thousands of cars, it may be hard to determine which cars have had updates since last time you read the database).
As to your implementation, in option 2, I'm not sure what you mean by the MightHaveMoreData state - either it has, or it hasn't, right? Repeated polling for more data is a bad design in this case - given that you will never be able to say 100% certain that there hasn't been "new data" provided in the time it took from fetching the last data until it was processed and acted on (displayed, used to buy shares on the stock market, stopped the train or whatever it is that you want to do once you have processed your new data).
Read-write lock could help. Many readers have simultaneous access to data set, and only one writer.
The idea is simple:
-when you need read-only access, reader uses "read-block", which could be shared with other reads and exclusive with writers;
-when you need write access, writer uses write-lock which is exclusive for both readers and writers;
Given a System that contains two components, A and B, and
The System starts up A and B concurrently. Now A can go through states {A.Starting, A.Ready}, and B can be in states {B.Starting, B.DoingX, B.DoingY}. (Events to transition between A's and B's states are named accordingly: B.doingx => B goes to B.DoingX etc...)
I want to model that
While A is in A.Starting, or B is in B.Starting, the System is "Starting"
The System is in state "DoingX" when A is in A.Ready and B is in B.DoingX
The System is in state "DoingY" when A is in A.Ready and B is in B.DoingY
If I'm not mistaken, the fork/join pseudo-states could be used here.
But do these model elements have the declarative semantics of the composed state mentioned above? Is there another way to model this?
(Note: the diagrams are from http://yuml.me)
Why don't you just pull these apart? Here's another idea on how you could model it (assuming I understood it correctly) :
a state "Starting", that contains the states you refer to as A.Starting and B.Starting in parallel regions (you can use fork/joins here, or just rely on the default behavior of all regions being activated when "Starting" state is entered)
another state "Doing" that contains a region with your "A.Ready" state and another parallel region, that contains the two states "B.DoingX" and "B.DoingY".
If you really need to have an overall "DoingX" state, then you may have to create two states that correspond to A.Ready.
Anyways, on a broader perspective: I believe your point of view is a little bit off here, when you say that the "System is in state ...". Rather, the system modeled by such a state machine is in a set of states. So normally, I would be perfectly happy to say that "the system is currently in A.Ready and B.DoingX".
Maybe all you need is a change of terminology. What about this:
The system is in configuration "DoingX" when A.Ready and B.DoingX states are active ?
In response to the comment: Yes, this is standard, here's the corresponding part from the superstructure specification (version 2.4 beta):
In a hierarchical state machine more than one state can be active at the same time. [...] the current active “state” is actually represented by a set of trees of states
starting with the top-most states of the root regions down to the innermost active substate. We refer to such a state tree as
a state configuration.
I made a class that has an asynchronous OpenWebPage() function. Once you call OpenWebPage(someUrl), a handler gets called - OnPageLoad(reply). I have been using a global variable called lastAction to take care of stuff once a page is loaded - handler checks what is the lastAction and calls an appropriate function. For example:
this->lastAction == "homepage";
this->OpenWebPage("http://www.hardwarebase.net");
void OnPageLoad(reply)
{
if(this->lastAction == "homepage")
{
this->lastAction = "login";
this->Login(); // POSTs a form and OnPageLoad gets called again
}
else if(this->lastAction == "login")
{
this->PostLogin(); // Checks did we log in properly, sets lastAction as new topic and goes to new topic URL
}
else if(this->lastAction == "new topic")
{
this->WriteTopic(); // Does some more stuff ... you get the point
}
}
Now, this is rather hard to write and keep track of when we have a large number of "actions". When I was doing stuff in Python (synchronously) it was much easier, like:
OpenWebPage("http://hardwarebase.net") // Stores the loaded page HTML in self.page
OpenWebpage("http://hardwarebase.net/login", {"user": username, "pw": password}) // POSTs a form
if(self.page == ...): // now do some more checks etc.
// do something more
Imagine now that I have a queue class which holds the actions: homepage, login, new topic. How am I supposed to execute all those actions (in proper order, one after one!) via the asynchronous callback? The first example is totally hard-coded obviously.
I hope you understand my question, because frankly I fear this is the worst question ever written :x
P.S. All this is done in Qt.
You are inviting all manner of bugs if you try and use a single member variable to maintain state for an arbitrary number of asynchronous operations, which is what you describe above. There is no way for you to determine the order that the OpenWebPage calls complete, so there's also no way to associate the value of lastAction at any given time with any specific operation.
There are a number of ways to solve this, e.g.:
Encapsulate web page loading in an immutable class that processes one page per instance
Return an object from OpenWebPage which tracks progress and stores the operation's state
Fire a signal when an operation completes and attach the operation's context to the signal
You need to add "return" statement in the end of every "if" branch: in your code, all "if" branches are executed in the first OnPageLoad call.
Generally, asynchronous state mamangment is always more complicated that synchronous. Consider replacing lastAction type with enumeration. Also, if OnPageLoad thread context is arbitrary, you need to synchronize access to global variables.