EmbeddedRelated.com
Forums

Transition and reaction difference in FSM?

Started by Davy December 24, 2006
In article <TCSmh.10$5g.7@trndny01>, H. S. Lahman says...
> In a software FSM a state represents a condition where a unique set of > rules and policies prevails. A corollary is that the rules and policies > in that set will be cohesive. I believe this is important because it > can affect subsequent maintenance. Suppose we originally have > > | > | E1:prepare > V > [Prepared] > | > | E2:start > V > [Running] > > where the [Prepared] state has an entry action to lubricate and an exit > action to start cooling. > > Now consider what happens when requirements change such that cooling is > triggered by a temperature sensor. Now cooling is triggered after the > motor is running rather than before. So one might introduce a new state > and transition:
I'd use a different state machine in that case. Since it would be truly independant of the machine operating state. For the kind of state dependance I'm referring to this kind of change would be rare. It would require changing the machine, a much more daunting task than modifying the state machine. Even if it is likely the simplicity of viewing the behaviour is a single state machine is, in this case, an advantage. Once the behaviour is no longer dependant on the state the multiplicitive increase in states alone makes a separate state machine attractive. More to the point it matches the problem domain. Also it's not as if creating an additional state machine at that point is difficult, it's a small addition to the work needed to add the new temperature input. One thing to remember is we are not dealing with a general purpose library here, these behaviours tend to be specific to the application environment and if a behaviour is tightly coupled to a state it's very likely to remain so. The most recent occaision I've had reason for using that coupled on- entry/on-exit construct was for flagging a fault state. On entry to the fault state the flag would be set, on exit it would be cleared. You could do the same thing by performing the action on the transitions instead but since there could be multiple entry and exit transistions it was clearer to use the entry/exit actions. Syntatic sugar sure, but useful. You could also use events to announce the entry and exit and then implement another state machine to set the flag but other than increasing the size of the code and the execution time I don't see much effect.
> >>>>>The incoming signals can indicate forward, reverse, neutral or be > >>>>>invalid. The throttle can independantly be anywhere in its range. In a > >>>>>complete example it could also be out of range but I did want a simple > >>>>>example :) I also don't deal here with finite allowable tme to switch > >>>>>directions, deadman switches and anti-tiedown logic :) > >>>>> > >>>>>As a result of these the output could be forward, reverse, neutral or > >>>>>stop. > >>>> > >>>>My issue is: why can't the incoming signal be changeDirection rather > >>>>than an explicit Forward/Reverse couplet? > >>> > >>> > >>>I suppose they could be but you would then still have to figure out > >>>which direction it had changed to. I suppose you could generate > >>>different events for each possibility but I think we've lost the > >>>advantage at that point. > >> > >>If one captures current direction in a state variable rather than in an > >>FSM state, then all one has is a toggle that is triggered by a single > >>event with no data packet. So I don't see a need for "different events". > > > > > > Remember the incoming direction signal has four possible states. A > > simple toggle won't work since from any specific diection indication you > > could get any of the remaining three. So either you provide different > > events for all possibilities or you pass in the new direction signal > > information. > > I didn't want to go here, but this was one of the things I was referring > to when I said I would do things differently; but it has nothing to do > with FSMs per se. In your example the Forward and Reverse data are > serving double duty. Certain combinations (both ON or both OFF) have > additional semantics unrelated to direction.
I think you've misunderstood the problem. The fact that direction consists of four states is a consequence of the user interface not an internal program design decision. This state machine is using the signals from the user interface to determine the the signals that will be fed to the actual motor control. Whether neutral is considered a direction could be debated, personally I think it's a good abstraction in the domain I use it in. It certainly is distinct from forward or reverse and must be considered in any place you consider forward and reverse. In any case all four states are present, necessary and have to be dealt with.
> That is very similar to return values being "normal" data when positive > and error codes when negative. Such semantic double duty can lead to > problems during maintenance (e.g., when requirements change and make > some negative values "normal" data or when your criteria for stopping > changes). IOW, separation of concerns applies to data domains are well > as behaviors.
I can understand why you would think that. It's not really the case though. In that sense it's more akin to input validation. I am with you on the separation of normal data from error codes. The trouble is all four states are normal in a very real sense. Even if you were to decide to treat the both directions selected as a separate event you would still have three direction states.
> > I also have a problem with using them to trigger a transition to the > [Stop] state, which you said was an error state. Since they are input > alphabet values (i.e., part of the event data packet), what is being > checked is that consistent data is being provided.
As I said that's at least half the point of the state machine. It's not checking for consistent data per se. The data is perfectly consistent but something has caused an input that forces a return to stop. Note that in a complete example more and different kinds of conditions can cause a return to stop state.
> If one is using DbC > for design, that should be a responsibility of whoever generates the > event. Thus it is a precondition of consuming the event that the data > in the data packet be consistent. IOW, when using DbC any assertions > made about the input data are checking the correctness of the software, > not the consistency of the data. > > When the software becomes broken, that's a job for exception handling. > One might still need a [Stop] state for recovery, but getting there is a > job for the exception handler rather than FSM design.
Getting to stop is a function of working software not broken software. Responding to faults is something the software must do and do correctly. Think deadman switch or e-stop.
> >>>OK, seems an odd way to view it to me but I can see the equivalency. > >> > >>Hmmm. I thought I was just mapping what you provided, so let me expand > >>on my analysis of the example... > > > > > > I'm not disagreeing it just seems an odd viewpoint. > > I guess it depends on where the observer is standing...
Almost always :)
> > OK. The separation into On-entry/during/on-exit (and transition actions > > too) actions is still useful though. The code generator then picks up > > the appropriate combination for each transistion. That would otherwise > > have to be done by the designer. The computer is much better at > > consistently doing that than I am. > > My point here is that in your example I don't see any separation at all. > That is, I don't see a "during action". All I see is a simple Moore > FSM. The value of DriveRequest is always set in that <entry> action as > a direct result of consuming the event that triggered going to the state.
I understand. I'm just suggesting that like a for loop is a glorified goto, the syntactic sugar is useful.
> >>The only thing I see as unusual in the example is the way that way > >>transitions were selected. It is done within the statechart function > >>rather than in a spearate event queue or through a static class STT > >>lookup table. > > > > > > STT? > > State Transition Table.
Ah, I thought I'd seen the acronym before.
> There are two forms. In one the table cells > are the event ID and the rows/columns are [current state, new state]. > That is the one commonly seen in theoretical discussions. In the other > the cells are new state and the rows/columns are [current state, event > ID]. In practice the second form is used in software FSMs but the cells > are pointer-to-function for the specific state action. Then the event > queue can dispatch directly to the action when an event is popped. > > No offense intended, but how often have you used FSMs?
Quite some time now. We just have a different educational background that's all and since the code generator I've used for the last dozen years or so doesn't use the construct or the acronymn is not near the top of list of acronyms I recall. We do use the same language on occasion to mean rather differnt things.
> This is a very > basic acronym for FSMs and almost all FSMs are implemented using one for > efficiency.
Actually a very common technique in embedded systems is a table of function pointers. One that a client has me maintain (I didn't design it) uses a list of pointers to define possible transitions for each state. Each transistion is defined by a pointer to condition function, a pointer to an action function to be executed if the condition function returns true and a pointer to the next state if the transistion is taken (the condition returns true). If the next state is null the state doesn't change. And what happens? You can probably guess at his point. The conditions functions actually perform actions in some cases.
> I was already suspicious that you don't use a lot because > you used the "statechart function" to implement the event with embedded > processing for the conditional event dispatch. I had never seen anyone > do that before. B-) BTW, the two most common ways for implementing > software FSMs are the GoF State pattern (where the STT is used to > dynamically instantiate the relationship between Context and State) and > utilizing a reusable event queue infrastructure. > > [All commercial full code generators employ the event queue > infrastructure.
Not all of them, the one I use doesn't. It uses that function call. A lot of state machines I've seen use some variation on that. Some use tables of function pointers, some use other constructs. There are actually reasons you might not want tables of constant values in some embedded systems.
> > I suppose you could convert all the conditional checks by running them > > externally and producing event ids for each one. Seems a lot of work > > just get get a abstract ID though. The other disadvantage I see with > > that is the actual condition is now several steps removed from the > > transition it causes. > > Not necessarily. Recall my model for Motor. It was actually somewhat > simpler than your model. Its actions could do everything you said were > done in your FSM actions and its transitions enforced the same > sequencing rules that yours did.
No it didn't. It failed on two accounts. It never returned to stop and it didn't have any forward/reverse assymetry (admittedly a later clarification). It didn't even have a stop state, just a neutral state. It also excluded the event generation.
> It just did it with different > conditions (states), events, and state variables. > > I think the condition is only one step removed.
Instead of call statechart check condition you have check condition generate event call statechart I count an extra step. More if you include the fact you are now check more conditions than necessary for a given state.
> The DbC precondition > for executing any state action exactly matches the postcondition of some > other action (i.e., the one where the event is generated to announce > that its postcondition prevails).
Exactly as in the case of using a simple conditional check.
> > So far I don't see the abstract IDs as any simpler than the conditional > > check. Actually I don't see that they are not fundamentally identical > > unless you have something running around changing values behind the > > state machines back which would be bad design practice IMO. > > > > Why is > > if( a < DFG ) { > > QueueEvent(queue, QW); > > } > > if( b == GHJZZ ) { > > QueueEvent(queue, QE); > > } > > StateChart( queue, out); > > > > superior to > > StateChart( a, b, out); > > There are several reasons. From an aesthetic perspective the FSM > provides /invariant/ constraints on /relative/ sequencing. They should > be invariant relative to the external context.
And how are the constraints I'm using less invariant than events?
> However, 'a' and 'b' are > external context variables. If one moves the conditional dispatch into > the FSM, one is making the FSM context-dependent and increases the > probability of maintenance problems.
I don't see that. I'm not saying there aren't cases where events are a better represenation, I think there are. It's just in the cases I usually deal with using conditional checks is simpler and more obviously correct.
> There are two ways the FSM can utilize context variables. The FSM > actions can access them parametrically for a computation within a state > action. That usage is well constrained with respect to scope.
OK so why wouldn't conditions based on those same values be well constrained?
> The other thing they can affect is the sequencing of actions within the > FSM. That is essentially what conditional events do; they use context > variables to change the structure of the FSM on the fly.
No more than events do.
> To me that is > kind of like an assigned GOTO in FORTRAN; it opens all sorts of > opportunities for unexpected side affects. IOW when one changes the > data semantics of the state variables one not only needs to worry about > what one computes from the data but which formula one uses to do the > computation.
That's sort of the point. The result depends on the state you are in. If you don't compute it there you have to have knowledge of which state the state machine is in externally when you compute it. As far as I'm concerned that opens the inner workings of the state machine to external view too much. The during actions express that clearly and directly.
> That is an issue of separation of concerns. The FSM structure is > inherently static. Modifying transitions dynamically adds another > dimension of complexity to the FSM structure and that is probably not a > good idea.
Who's modifying transistions dynamically?
> One way that is manifested is in the notion of OO announcement messages > (I'm Done) compared to procedural imperative messages (Do This). The > event being generated announces a change in condition /outside/ the FSM. > That is, the event itself is announcing something happening > externally. As it happens that depends upon 'a' and 'b'. But it > announces a compound condition that also includes whatever the caller > did. IOW, the condition being announced is {(a < DFG), caller completed > its part of the solution} OR {(b == GHJZZ), caller completed its part of > the solution}.
Or just as likely both. The state machine then has to discard the irrelevant events.
> More importantly, it includes a context decision that the IF statements > themselves define. Those IF statements implement problem space rules > and policies to determine exactly What should be announced.
Beg pardon? I think you are envisioning an entirely different kind of problem than I am. The state machine must be aware of all the inputs (or events) anything else would be unsafe. The only way anything external to the state machine could filter the events/data to a subset would be if it were aware of the state it was in.
> The FSM > only responds to the announcement. If one moves the IF statements into > the FSM itself, one is moving that external context decision into the > FSM. That external context decision probably isn't any of the FSM's > business; it should just respond to the decision's /result/.
?? That external context is the entire reason for the state machine to exist. Having said that the external context may be abstracted or idealized. The direction input could be a single multivalued input rather than a pair of flags as a for instance.
> Finally, when managing complexity it is wise to isolate related rules > and policies via encapsulation. It is highly unlikely that the rules > and policies that determine what event to generate will be logically > related to the rules and policies that the FSM exists to resolve. So if > one moves those rules and policies into the FSM, one is trashing the > cohesion of the FSM.
While there is a lot to encapsulation and isolation it is quite possible to take it too far. Data and actions that are tightly coupled should not be separated.
> The bottom line is that the software will be more maintainable if one > decouples message and method, separates concerns, and encapsulates rules > and policies in a cohesive fashion. Superficially both your examples do > the same thing. But devil is in the details and from the perspective of > maintainability there are potentially huge differences when one gets to > the details.
Actually, encapsulation is a big reason for choosing the form I use in the example rather than an event queue. All of the logic for determining the requested drive signal is isolated to the state machine. Robert -- From the Divided by a Common Language File (Edited to protect the guilty) ME - "I'd like to get Price and delivery for connector Part # XXXXX" Dist./Rep - "$X.XX Lead time 37 days" ME - "Anything we can do about lead time? 37 days seems a bit high." Dist./Rep - "that is the lead time given because our stock is live.... we currently have stock." -- Posted via a free Usenet account from http://www.teranews.com
Responding to Adsett...

>>In a software FSM a state represents a condition where a unique set of >>rules and policies prevails. A corollary is that the rules and policies >>in that set will be cohesive. I believe this is important because it >>can affect subsequent maintenance. Suppose we originally have >> >> | >> | E1:prepare >> V >>[Prepared] >> | >> | E2:start >> V >>[Running] >> >>where the [Prepared] state has an entry action to lubricate and an exit >>action to start cooling. >> >>Now consider what happens when requirements change such that cooling is >>triggered by a temperature sensor. Now cooling is triggered after the >>motor is running rather than before. So one might introduce a new state >>and transition: > > > I'd use a different state machine in that case. Since it would be truly > independant of the machine operating state. For the kind of state > dependance I'm referring to this kind of change would be rare. It would > require changing the machine, a much more daunting task than modifying > the state machine. Even if it is likely the simplicity of viewing the > behaviour is a single state machine is, in this case, an advantage. > Once the behaviour is no longer dependant on the state the > multiplicitive increase in states alone makes a separate state machine > attractive. More to the point it matches the problem domain.
I was assuming that lubricating, starting, and cooling were all hardware facilities. That is, the rules and policies for controlling each one involved the same register reads and writes whether one decided to cool as part of startup or based on running temperature. IOW, the code in the actions would be exactly the same. As far as mapping the the problem space is concerned, it seems to me that placing lubricating and cooling as entry/exit actions in a single state like [Prepared] is constructing the FSM around a preconceived view of the solution rather than the invariants of the problem space. Once one accepts the notion that controlling lubrication is a quite different responsibility than controlling cooling, it seems reasonable to associate those unique rules and policies with their own states. When the motor is lubricated it is in a state where it is ready to run. When the motor is cooled it is in a state where the the cooling mechanisms are activated. And when it has been started it is a state where it has been powered up. Those conditions all seem quite natural in the problem space and they are intrinsic states of the Motor that do not depend on the order in which the hardware elements are controlled. That ordering can then be expressed in terms of transitions between those states. So if one isolated those actions with the problem space states, one could reorder the sequence by simply rearranging the transitions when requirements provided a different order. IOW, there is no need for new states; they were already in the problem space and one has simply reordered their sequence.
> > Also it's not as if creating an additional state machine at that point > is difficult, it's a small addition to the work needed to add the new > temperature input. One thing to remember is we are not dealing with a > general purpose library here, these behaviours tend to be specific to > the application environment and if a behaviour is tightly coupled to a > state it's very likely to remain so.
Any time one touches the software there is an opportunity for breaking it by inserting a defect. The probability of inserting a defect tends to increase for data changes -> static structure changes -> behaviors. So a major goal of modern software development (and the OO paradigm in particular) is to minimize the number of places one touches the software to implement changes while making changes in places that are less likely to become broken by the change. In this case I would much rather use the same states and actions than add a state even though the action could be cut & pasted. I don't want to touch the actions in any way if I can avoid it. I don't want to substantially change the static structure by adding states if I can avoid it. If it were feasible to do the change in configuration data without touching the transitions, I would do that instead. [I have a great horror story about about a one-line cut&paste of a single call that resulted in a Group VP being berated for half an hour on the phone by an irate customer CEO. As a developer, one does not want that kind of visibility. I use the OO paradigm because I want to minimize the possibility of that kind of visibility -- even for changes that "aren't that difficult". Isolating lubrication and cooling in their own problem space states does that; artificially making them entry and exit actions in the same state because that's more convenient for the original solution doesn't.]
> The most recent occaision I've had reason for using that coupled on- > entry/on-exit construct was for flagging a fault state. On entry to the > fault state the flag would be set, on exit it would be cleared. You > could do the same thing by performing the action on the transitions > instead but since there could be multiple entry and exit transistions it > was clearer to use the entry/exit actions. Syntatic sugar sure, but > useful. You could also use events to announce the entry and exit and > then implement another state machine to set the flag but other than > increasing the size of the code and the execution time I don't see much > effect.
As you may recall, I already said I don't have that big a problem with exit actions per se on a case-by-case basis. I just feel one should understand the trade-off for maintenance risk and that trade-off rarely comes out in favor of exit actions.
>>>>>>>The incoming signals can indicate forward, reverse, neutral or be >>>>>>>invalid. The throttle can independantly be anywhere in its range. In a >>>>>>>complete example it could also be out of range but I did want a simple >>>>>>>example :) I also don't deal here with finite allowable tme to switch >>>>>>>directions, deadman switches and anti-tiedown logic :) >>>>>>> >>>>>>>As a result of these the output could be forward, reverse, neutral or >>>>>>>stop. >>>>>> >>>>>>My issue is: why can't the incoming signal be changeDirection rather >>>>>>than an explicit Forward/Reverse couplet? >>>>> >>>>> >>>>>I suppose they could be but you would then still have to figure out >>>>>which direction it had changed to. I suppose you could generate >>>>>different events for each possibility but I think we've lost the >>>>>advantage at that point. >>>> >>>>If one captures current direction in a state variable rather than in an >>>>FSM state, then all one has is a toggle that is triggered by a single >>>>event with no data packet. So I don't see a need for "different events". >>> >>> >>>Remember the incoming direction signal has four possible states. A >>>simple toggle won't work since from any specific diection indication you >>>could get any of the remaining three. So either you provide different >>>events for all possibilities or you pass in the new direction signal >>>information. >> >>I didn't want to go here, but this was one of the things I was referring >>to when I said I would do things differently; but it has nothing to do >>with FSMs per se. In your example the Forward and Reverse data are >>serving double duty. Certain combinations (both ON or both OFF) have >>additional semantics unrelated to direction. > > > I think you've misunderstood the problem. The fact that direction > consists of four states is a consequence of the user interface not an > internal program design decision. This state machine is using the > signals from the user interface to determine the the signals that will > be fed to the actual motor control.
I can't buy that. The UI is a separate subsystem whose job is to convert the user view to the problem solution view. Using ON/ON and OFF/OFF combinations in the interface between the UI and the problem solution is a matter of interface design. The interfaces to the problem solution's subsystems are under the developer's control. Even if the UI wants to export ON/ON and OFF/OFF, each subsystem has its own two-way interface and one can deal the problem in the glue code between them. So my pushback would be to fix those interfaces.
>>That is very similar to return values being "normal" data when positive >>and error codes when negative. Such semantic double duty can lead to >>problems during maintenance (e.g., when requirements change and make >>some negative values "normal" data or when your criteria for stopping >>changes). IOW, separation of concerns applies to data domains are well >>as behaviors. > > > I can understand why you would think that. It's not really the case > though. In that sense it's more akin to input validation. > > I am with you on the separation of normal data from error codes. The > trouble is all four states are normal in a very real sense. Even if you > were to decide to treat the both directions selected as a separate event > you would still have three direction states.
I still think it's still double duty; its is just spread over two data domains. Forward ON; Reverse OFF. Redundantly specifies forward operation Forward OFF; Reverse ON. Redundantly specifies reverse operation Forward ON; Reverse ON. An input error. Forward OFF; Reverse OFF. The motor goes to neutral. Clearly fielding an input error in the UI has nothing to do with the notions of forward and reverse operation. That's the UI's job and shouldn't even be an issue in the controller context except, possibly, as a DbC assertion for the interface to the UI. That is pretty much exactly what using negative numbers for error codes on return values does. Similarly, the notion of neutral really isn't the same as NOT forward AND NOT reverse. That is manifested in the redundancy when specifying when the motor really is operating in forward or reverse. What is needed is to separate the semantics of direction vs. running, as in: Forward ON. Specifies forward operation, if running Forward OFF. Specifies reverse operation, if running Powered ON. Specifies motor is powered Powered OFF. Specifies motor is not powered There are still two state variables and four combinations of values. But the semantics have been properly decoupled in the variable data domains so that they can be assigned independently.
>>I also have a problem with using them to trigger a transition to the >>[Stop] state, which you said was an error state. Since they are input >>alphabet values (i.e., part of the event data packet), what is being >>checked is that consistent data is being provided. > > > As I said that's at least half the point of the state machine. It's not > checking for consistent data per se. The data is perfectly consistent > but something has caused an input that forces a return to stop. Note > that in a complete example more and different kinds of conditions can > cause a return to stop state.
You indicated [Stop] was an error state. That's fine. But getting there should be triggered by an event that is raised explicitly by detecting the error condition _outside the FSM_. The FSM /responds/ to changes in the application state by switching states. IOW, generating the error event may be conditional based on state variables, but there is nothing conditional about the FSM's transition once that condition prevails. In this case I think the UI should check the input values and generate the appropriate event if you really want the motor to respond that way. If some other external situation also triggers a reset to the error state, whoever detects that condition should generate the appropriate event. It isn't up the the FSM to detect error conditions in the state of the application (e.g., user input errors); the FSM just responds appropriately when such conditions prevail by consuming the triggering event.
>>If one is using DbC >>for design, that should be a responsibility of whoever generates the >>event. Thus it is a precondition of consuming the event that the data >>in the data packet be consistent. IOW, when using DbC any assertions >>made about the input data are checking the correctness of the software, >>not the consistency of the data. >> >>When the software becomes broken, that's a job for exception handling. >>One might still need a [Stop] state for recovery, but getting there is a >>job for the exception handler rather than FSM design. > > > Getting to stop is a function of working software not broken software. > Responding to faults is something the software must do and do correctly. > Think deadman switch or e-stop.
You indicated that going to the Stop state was the result of inconsistent user input data for the directions and that is the case I am addressing. I don't care how many other ways there may be to get to the Stop state; I am only interested in the FSM structure around this path. I submit that such an error should have been detected by the UI so that it never reaches the FSM. In that case, any DbC check by the FSM is a check on the correctness of the software in not allowing such an inconsistency to reach it. If there is a reason for the motor to go to the stop state when the user supplies inconsistent data, that is a quite different thing. In that situation the UI should generate an event to announce the inconsistency and the FSM should respond to that event by unconditionally transitioning to the Stop state.
>>>OK. The separation into On-entry/during/on-exit (and transition actions >>>too) actions is still useful though. The code generator then picks up >>>the appropriate combination for each transistion. That would otherwise >>>have to be done by the designer. The computer is much better at >>>consistently doing that than I am. >> >>My point here is that in your example I don't see any separation at all. >> That is, I don't see a "during action". All I see is a simple Moore >>FSM. The value of DriveRequest is always set in that <entry> action as >>a direct result of consuming the event that triggered going to the state. > > > I understand. I'm just suggesting that like a for loop is a glorified > goto, the syntactic sugar is useful.
OK. But the last 200 or so messages were triggered by the notion of having a "during action". B-)
>>There are two forms. In one the table cells >>are the event ID and the rows/columns are [current state, new state]. >>That is the one commonly seen in theoretical discussions. In the other >>the cells are new state and the rows/columns are [current state, event >>ID]. In practice the second form is used in software FSMs but the cells >>are pointer-to-function for the specific state action. Then the event >>queue can dispatch directly to the action when an event is popped. >> >>No offense intended, but how often have you used FSMs? > > > Quite some time now. We just have a different educational background > that's all and since the code generator I've used for the last dozen > years or so doesn't use the construct or the acronymn is not near the > top of list of acronyms I recall. We do use the same language on > occasion to mean rather differnt things. > > >>This is a very >>basic acronym for FSMs and almost all FSMs are implemented using one for >>efficiency. > > > Actually a very common technique in embedded systems is a table of > function pointers. One that a client has me maintain (I didn't design > it) uses a list of pointers to define possible transitions for each > state. Each transistion is defined by a pointer to condition function, > a pointer to an action function to be executed if the condition function > returns true and a pointer to the next state if the transistion is taken > (the condition returns true). If the next state is null the state > doesn't change. And what happens? You can probably guess at his point. > The conditions functions actually perform actions in some cases.
That table of function pointers is basically the second form of STT that I mentioned above. The condition function is just the sort of indirection overhead one gets into if one supports the /possibility/ of conditional relationships. If one doesn't use conditional relationships the table just contains the action pointers. (Of course if one also might have exit actions, one needs yet another table.)
>>[All commercial full code generators employ the event queue >>infrastructure. > > > Not all of them, the one I use doesn't. It uses that function call. A > lot of state machines I've seen use some variation on that. Some use > tables of function pointers, some use other constructs. There are > actually reasons you might not want tables of constant values in some > embedded systems.
Just out of curiosity, which one is that? I thought I knew all the full code generators for UML (Pathfinder is one of the vendors). [BTW, PathMATE happens to support both conditional transitions and exit actions. It uses event queues and STT dispatch for any asynchronous implementation. PathMATE's primary market niche is high performance R-T/E, which might say something about the performance merits. B-)]
>>>I suppose you could convert all the conditional checks by running them >>>externally and producing event ids for each one. Seems a lot of work >>>just get get a abstract ID though. The other disadvantage I see with >>>that is the actual condition is now several steps removed from the >>>transition it causes. >> >>Not necessarily. Recall my model for Motor. It was actually somewhat >>simpler than your model. Its actions could do everything you said were >>done in your FSM actions and its transitions enforced the same >>sequencing rules that yours did. > > > No it didn't. It failed on two accounts. It never returned to stop and > it didn't have any forward/reverse assymetry (admittedly a later > clarification). It didn't even have a stop state, just a neutral state.
That's because in your original FSM description Stop didn't do anything except set DriveRequest to 0, which was what Neutral did. When I changed the state variable and other semantics it was no longer needed. It did return to Neutral under all the same <substantive> conditions that you originally specified (or I inferred). Now you can argue that your original FSM was designed your way because of other, unmentioned requirements. But that's a different FSM. My counter is that if those requirements were mentioned and your full FSM was drawn, I could probably still come up with a simpler version -- if for no other reason than eliminating the conditional transitions. B-)
> > It also excluded the event generation.
I am not sure what this means. You FSM didn't have any event generation either; it just selected transitions given an event. Do you mean I didn't have conditional transitions? If so, that came with changing the semantics of the state variables and events.
>>It just did it with different >>conditions (states), events, and state variables. >> >>I think the condition is only one step removed. > > > Instead of > call statechart > check condition
You forgot the last step: select a transition. That is equivalent to generating an event below.
> you have > check condition > generate event > call statechart > > I count an extra step. More if you include the fact you are now check > more conditions than necessary for a given state.
The point is that somebody always detects a condition and announces it with an event. The FSM always responds to that announcement event by transitioning to a new state and executing the associated action. That is about as peer-to-peer as the collaboration can get without bleeding cohesion between the FSM and the external environment. When has a condition check that determines the transition inside the FSM, one is bleeding cohesion because the FSM is doing part of the job that the <external> event generator should be doing. Part of the detection (the trigger event) is outside the FSM and part is inside (selecting a transition). IOW, detecting condition changes is one thing while responding to them is quite another and I think those two concerns should be separated.
>> The DbC precondition >>for executing any state action exactly matches the postcondition of some >>other action (i.e., the one where the event is generated to announce >>that its postcondition prevails). > > > Exactly as in the case of using a simple conditional check.
The problem lies in who owns the condition check (more precisely, who owns which pieces of a compound condition check), per the point immediately above.
>>>So far I don't see the abstract IDs as any simpler than the conditional >>>check. Actually I don't see that they are not fundamentally identical >>>unless you have something running around changing values behind the >>>state machines back which would be bad design practice IMO. >>> >>>Why is >>> if( a < DFG ) { >>> QueueEvent(queue, QW); >>> } >>> if( b == GHJZZ ) { >>> QueueEvent(queue, QE); >>> } >>> StateChart( queue, out); >>> >>>superior to >>> StateChart( a, b, out); >> >>There are several reasons. From an aesthetic perspective the FSM >>provides /invariant/ constraints on /relative/ sequencing. They should >>be invariant relative to the external context. > > > And how are the constraints I'm using less invariant than events?
They depend upon specific state variable values. The FSM doesn't care about the specific values; it only cares that /some/ condition announced by QW or QE exists. The sequencing constraints in the FSM define intrinsic constraints on when the state actions can be performed relative to one another without regard to solution context. The constraints on 'a' and 'b' represent a mapping of intrinsic FSM sequencing constraints to specific context semantics in the overall problem solution. That mapping to solution specifics is none of the FSM's concern. Suppose there is another object with a method that does something like: if (x > ABC) QueueEvent (queue, QW) if (y != LMNOP) QueueEvent (queue, QE) StatrChart (queue, out) Here have have exactly the same events triggering the same actions in the same FSM. But the local context semantics is quite different. If one moves the conditions on 'a', 'b', 'x', and 'y' into transition conditions within the FSM, one starts to lose the significance of the local context in the complexity of the <now compound> conditions on the transitions. OTOH, if one keeps the two sets of conditions encapsulated in different objects each context is much clearer. (Each is isolated and each is explicitly linked to the broader semantic contexts where DFG and ABC are meaningful.) More important, when only one of those detailed contexts changes, one just touches that context. The other context and the FSM remains untouched as it still responds to QW and QE in exactly the same way. IN addition, one can add an entirely new context to collaborate with the same FSM without touching the FSM.
>> However, 'a' and 'b' are >>external context variables. If one moves the conditional dispatch into >>the FSM, one is making the FSM context-dependent and increases the >>probability of maintenance problems. > > > I don't see that. I'm not saying there aren't cases where events are a > better represenation, I think there are. It's just in the cases I > usually deal with using conditional checks is simpler and more obviously > correct.
The condition checks are going to be exactly the same in either case. The issue is about who should own them. I submit that since the specific conditions represent a mapping of the intrinsic (invariant) internal FSM sequencing constraints into the semantics of specific problem contexts, those conditions should be owned by someone outside the FSM who understands the local context.
>>There are two ways the FSM can utilize context variables. The FSM >>actions can access them parametrically for a computation within a state >>action. That usage is well constrained with respect to scope. > > > OK so why wouldn't conditions based on those same values be well > constrained?
I am talking about data integrity scope here. One can manage things like thread pausing and whatnot easier if scope is defined by a single procedure. Similarly, one can focus on the design issues better if scope of data access is limited to single method scope.
>>The other thing they can affect is the sequencing of actions within the >>FSM. That is essentially what conditional events do; they use context >>variables to change the structure of the FSM on the fly. > > > No more than events do.
Very much more so! If there are no conditional transitions the structure and sequencing of the FSM for the incoming events is completely invariant. It depends solely on {event ID, current state}. No matter how many times that tuple is processed, the path is exactly the same each time. However, if there are conditional transitions the structure is effectively modified. Instead of one possible path for a given event and current state there are multiple paths that depend upon {event ID, current state, state var1, state var2, ...}. Thus the structure of the FSM itself is potentially different each time that {event ID, current state, ...} tuple is processed. To put it another way, if one drew the FSM as if only one condition existed, it would have a different structure than if one drew it for only one of the other conditions.
>>To me that is >>kind of like an assigned GOTO in FORTRAN; it opens all sorts of >>opportunities for unexpected side affects. IOW when one changes the >>data semantics of the state variables one not only needs to worry about >>what one computes from the data but which formula one uses to do the >>computation. > > > That's sort of the point. The result depends on the state you are in. > If you don't compute it there you have to have knowledge of which state > the state machine is in externally when you compute it. As far as I'm > concerned that opens the inner workings of the state machine to external > view too much. The during actions express that clearly and directly.
When there are no conditional transitions, one /always/ knows exactly what path will be taken for a given {event ID, current state} tuple, regardless of what might be happening in the overall solution. As soon as one starts to make the path variable, one has an assigned GOTO where the assignment is done outside the FSM. Those state variables one is using to select transitions are defined and assigned outside the FSM. They represent external context. So one is bleeding that context into the FSM, which makes the FSM more complex. It also splits up compound conditions between whoever generates the triggering event and the FSM itself. (Somebody still has to know it is time to generate an event to the FSM.) However, if one encapsulates all the semantics of the local solution context all in one place, one has good separation of concerns and that aids in managing the complexity. The condition for transition to a particular state has to be detected somewhere. To me the issue is whether one should encapsulate those detection rules and policies in one place that logically should understand them or split them up over two objects, one of which don't have a logical need to know about the context.
>>That is an issue of separation of concerns. The FSM structure is >>inherently static. Modifying transitions dynamically adds another >>dimension of complexity to the FSM structure and that is probably not a >>good idea. > > > Who's modifying transistions dynamically?
You are any time you modify the state variable used to select a conditional transition during the life of the FSM.
>>One way that is manifested is in the notion of OO announcement messages >>(I'm Done) compared to procedural imperative messages (Do This). The >>event being generated announces a change in condition /outside/ the FSM. >> That is, the event itself is announcing something happening >>externally. As it happens that depends upon 'a' and 'b'. But it >>announces a compound condition that also includes whatever the caller >>did. IOW, the condition being announced is {(a < DFG), caller completed >>its part of the solution} OR {(b == GHJZZ), caller completed its part of >>the solution}. > > > Or just as likely both. The state machine then has to discard the > irrelevant events.
I think ignoring events in the STT is a quite different thing than conditional transitions. There is no law that requires one to do something in response to raising a condition, so it is valid to ignore certain events when the FSM is in a particular state. The issue here is choosing a transition path from among alternatives _when the event cannot be ignored_.
>>More importantly, it includes a context decision that the IF statements >>themselves define. Those IF statements implement problem space rules >>and policies to determine exactly What should be announced. > > > Beg pardon? I think you are envisioning an entirely different kind of > problem than I am. The state machine must be aware of all the inputs > (or events) anything else would be unsafe. The only way anything > external to the state machine could filter the events/data to a subset > would be if it were aware of the state it was in.
The FSM only needs to be aware of events and the relevant input alphabet for a transition that is processed by the corresponding action. That's all an FSM does: it responds to events by processing the the input alphabet. My point here is when you select a transition you are defining a particular state for the FSM (i.e., the target state for the selected transition). The precondition for entering that state is a compound condition composed of: - the postcondition of the action the generated the trigger event - any data integrity constraints on state variables to be accessed by the action - the conditions used to select the transition. IOW, the conditions used to select the transition are necessarily part of the precondition for entering the target state. If they did not evaluate TRUE, transitioning to that state would be inappropriate. The primary issue of this whole subdiscussion is whether that precondition should be evaluated in one place or in two places.
>>The FSM >>only responds to the announcement. If one moves the IF statements into >>the FSM itself, one is moving that external context decision into the >>FSM. That external context decision probably isn't any of the FSM's >>business; it should just respond to the decision's /result/. > > > ?? That external context is the entire reason for the state machine to > exist. Having said that the external context may be abstracted or > idealized. The direction input could be a single multivalued input > rather than a pair of flags as a for instance.
The state variables being tested /are/ the external context. Forward and Reverse or 'a' and 'b' in your examples have their values assigned based upon conditions that exist outside the FSM. They have semantics that is defined in terms of that external context. I am arguing that such external context should be evaluated externally (preferably in one place) and an appropriate event generated to the FSM based on that evaluation. If one always encapsulates the rules and policies for detecting state changes that way in a place that logically understands the context semantics, one never needs conditional events in the receiver FSMs.
>>Finally, when managing complexity it is wise to isolate related rules >>and policies via encapsulation. It is highly unlikely that the rules >>and policies that determine what event to generate will be logically >>related to the rules and policies that the FSM exists to resolve. So if >>one moves those rules and policies into the FSM, one is trashing the >>cohesion of the FSM. > > > While there is a lot to encapsulation and isolation it is quite possible > to take it too far. Data and actions that are tightly coupled should > not be separated.
True. That's why one needs a design methodology to draw the lines in the sand. The OO paradigm draws lines in the sand based on problem space abstraction while Structured Programming drew them based upon functional decomposition. But I would still argue that the rules and policies for detecting state changes are going to be quite different than those that govern the response to state changes, regardless of what methodology one uses. B-)
>>The bottom line is that the software will be more maintainable if one >>decouples message and method, separates concerns, and encapsulates rules >>and policies in a cohesive fashion. Superficially both your examples do >>the same thing. But devil is in the details and from the perspective of >>maintainability there are potentially huge differences when one gets to >>the details. > > > Actually, encapsulation is a big reason for choosing the form I use in > the example rather than an event queue. All of the logic for > determining the requested drive signal is isolated to the state machine.
And this is a core disagreement. To me conditional transitions represent lack of cohesion and poor separation of concerns. ************* There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl@pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info@pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
In article <IYfnh.5273$SQ1.4549@trnddc03>, H. S. Lahman says...
> Responding to Adsett... > > >>In a software FSM a state represents a condition where a unique set of > >>rules and policies prevails. A corollary is that the rules and policies > >>in that set will be cohesive. I believe this is important because it > >>can affect subsequent maintenance. Suppose we originally have > >> > >> | > >> | E1:prepare > >> V > >>[Prepared] > >> | > >> | E2:start > >> V > >>[Running] > >> > >>where the [Prepared] state has an entry action to lubricate and an exit > >>action to start cooling. > >> > >>Now consider what happens when requirements change such that cooling is > >>triggered by a temperature sensor. Now cooling is triggered after the > >>motor is running rather than before. So one might introduce a new state > >>and transition: > > > > > > I'd use a different state machine in that case. Since it would be truly > > independant of the machine operating state. For the kind of state > > dependance I'm referring to this kind of change would be rare. It would > > require changing the machine, a much more daunting task than modifying > > the state machine. Even if it is likely the simplicity of viewing the > > behaviour is a single state machine is, in this case, an advantage. > > Once the behaviour is no longer dependant on the state the > > multiplicitive increase in states alone makes a separate state machine > > attractive. More to the point it matches the problem domain. > > I was assuming that lubricating, starting, and cooling were all hardware > facilities. That is, the rules and policies for controlling each one > involved the same register reads and writes whether one decided to cool > as part of startup or based on running temperature. IOW, the code in > the actions would be exactly the same.
I expect it would be, but adding temperature sensing would not be a small task. That's my point here, for many of these choices to use on- entry/on-exit actions in this fashion the hardware limitations have already restricted the possibles places to perform the action to places where the machine state is well known. Adding a separate state machine just adds a level of indirection, reducing clarity. Having a state machine whose only purpose is to turn something on when another state machine enters a state and to turn it off again when it exits that state seems a little over done. As I said it the machine is change dto be independant of the operating state (such as to become dependent on the temperature) then it's time to create a separate state machine.
> As far as mapping the the problem space is concerned, it seems to me > that placing lubricating and cooling as entry/exit actions in a single > state like [Prepared] is constructing the FSM around a preconceived view > of the solution rather than the invariants of the problem space.
You still misunderstand. Lubrication on would be the on-entry action of [running], lubrication off would be the on-exit action of [running]. Cooling doesn't get involved anywhere, it was a separate example.
> Once > one accepts the notion that controlling lubrication is a quite different > responsibility than controlling cooling,
I thought I'd already pointed out that I wasn't considering them dependent, that they were two different examples.
> > Also it's not as if creating an additional state machine at that point > > is difficult, it's a small addition to the work needed to add the new > > temperature input. One thing to remember is we are not dealing with a > > general purpose library here, these behaviours tend to be specific to > > the application environment and if a behaviour is tightly coupled to a > > state it's very likely to remain so. > > Any time one touches the software there is an opportunity for breaking > it by inserting a defect.
Absolutely, but I don't see that creating an additional state machine is any more error prone later rather than sooner.
> > The most recent occaision I've had reason for using that coupled on- > > entry/on-exit construct was for flagging a fault state. On entry to the > > fault state the flag would be set, on exit it would be cleared. You > > could do the same thing by performing the action on the transitions > > instead but since there could be multiple entry and exit transistions it > > was clearer to use the entry/exit actions. Syntatic sugar sure, but > > useful. You could also use events to announce the entry and exit and > > then implement another state machine to set the flag but other than > > increasing the size of the code and the execution time I don't see much > > effect. > > As you may recall, I already said I don't have that big a problem with > exit actions per se on a case-by-case basis. I just feel one should > understand the trade-off for maintenance risk and that trade-off rarely > comes out in favor of exit actions.
OK, but this example is identical to the lubrication example you are objecting to.
> >>>Remember the incoming direction signal has four possible states. A > >>>simple toggle won't work since from any specific diection indication you > >>>could get any of the remaining three. So either you provide different > >>>events for all possibilities or you pass in the new direction signal > >>>information. > >> > >>I didn't want to go here, but this was one of the things I was referring > >>to when I said I would do things differently; but it has nothing to do > >>with FSMs per se. In your example the Forward and Reverse data are > >>serving double duty. Certain combinations (both ON or both OFF) have > >>additional semantics unrelated to direction. > > > > > > I think you've misunderstood the problem. The fact that direction > > consists of four states is a consequence of the user interface not an > > internal program design decision. This state machine is using the > > signals from the user interface to determine the the signals that will > > be fed to the actual motor control. > > I can't buy that. The UI is a separate subsystem whose job is to > convert the user view to the problem solution view. Using ON/ON and > OFF/OFF combinations in the interface between the UI and the problem > solution is a matter of interface design. The interfaces to the problem > solution's subsystems are under the developer's control. Even if the UI > wants to export ON/ON and OFF/OFF, each subsystem has its own two-way > interface and one can deal the problem in the glue code between them. > So my pushback would be to fix those interfaces.
As I said I think you've misunderstood the problem. This example does not simply receive information from the UI it provides the proper behaviour for the UI. It is providing the safety interlocks. And the input neutral is definitely distinct from forward and reverse.
> >>That is very similar to return values being "normal" data when positive > >>and error codes when negative. Such semantic double duty can lead to > >>problems during maintenance (e.g., when requirements change and make > >>some negative values "normal" data or when your criteria for stopping > >>changes). IOW, separation of concerns applies to data domains are well > >>as behaviors. > > > > > > I can understand why you would think that. It's not really the case > > though. In that sense it's more akin to input validation. > > > > I am with you on the separation of normal data from error codes. The > > trouble is all four states are normal in a very real sense. Even if you > > were to decide to treat the both directions selected as a separate event > > you would still have three direction states. > > I still think it's still double duty; its is just spread over two data > domains. > > Forward ON; Reverse OFF. Redundantly specifies forward operation > Forward OFF; Reverse ON. Redundantly specifies reverse operation
Yes
> Forward ON; Reverse ON. An input error.
Sort of. As I said think deadman switch or e-stop as well. I won't object if you want to consider it separately though.
> Forward OFF; Reverse OFF. The motor goes to neutral.
No. The input state goes to stop, what the motor does is beyond this example. Typically the motor will indeed follow, probably to a neutral state or something similar but there are likely to be lags. Don't get too hung up on this. Remember I said this was a simplified example. This input although it exists serves also to stand-in for all the other possible ways you get back to the stop state.
> Clearly fielding an input error in the UI has nothing to do with the > notions of forward and reverse operation.
That's true, but it does have to do with what the requested motor output should be. As I said in the previous message look at it as e-stop or deadman, it's not an input error in the sense the user should retype in a value, it's an error in the sense that a safety sequence has been violated. The input state MUST (it's a requirement) go back to the stop state and go through the start sequencing before it can re-enter neutral and select a direction.
> That's the UI's job and > shouldn't even be an issue in the controller context except, possibly, > as a DbC assertion for the interface to the UI.
No. It is very much the job of this routine. This is where it's determined that the operator has satisfied the required conditions for proceeding. The actual motor control is decoupled from this as it should be. If it helps, consider it part of the UI, after all it's about ensuring the operator has satisfied the input requirements and then generating the appropriate output to go to the motor control.
> Similarly, the notion of neutral really isn't the same as NOT forward > AND NOT reverse. That is manifested in the redundancy when specifying > when the motor really is operating in forward or reverse. What is > needed is to separate the semantics of direction vs. running, as in: > > Forward ON. Specifies forward operation, if running > Forward OFF. Specifies reverse operation, if running
And neutral which is different. A motor can be powered but not in either forward and reverse. If you don't believe me try operating an automobile without using neutral. More to the point since this state machine does not deal with the motor but the input to the motor, neutral is a distinct input state. The operator can distinctly specify neutral and will.
> Powered ON. Specifies motor is powered > Powered OFF. Specifies motor is not powered
Generally if the motor (controller) is not powered you won't even get this far. Error checking during startup should ensure that. Also you are confusing the motor state with the input state this state machine is providing. The motors own control and states are separate from this state machine.
> There are still two state variables and four combinations of values.
They are, however, the wrong ones.
> >>I also have a problem with using them to trigger a transition to the > >>[Stop] state, which you said was an error state. Since they are input > >>alphabet values (i.e., part of the event data packet), what is being > >>checked is that consistent data is being provided. > > > > > > As I said that's at least half the point of the state machine. It's not > > checking for consistent data per se. The data is perfectly consistent > > but something has caused an input that forces a return to stop. Note > > that in a complete example more and different kinds of conditions can > > cause a return to stop state. > > You indicated [Stop] was an error state. That's fine. But getting > there should be triggered by an event that is raised explicitly by > detecting the error condition _outside the FSM_. The FSM /responds/ to > changes in the application state by switching states.
That's exactly what it is doing, responding to changes in the application state by switching states.
> IOW, generating the error event may be conditional based on state > variables, but there is nothing conditional about the FSM's transition > once that condition prevails. In this case I think the UI should check > the input values and generate the appropriate event if you really want > the motor to respond that way.
Remember this is not the motor control statechart. It's the statechart that is using the inputs to determine what signals to the motor control will be.
> If some other external situation also > triggers a reset to the error state, whoever detects that condition > should generate the appropriate event. It isn't up the the FSM to > detect error conditions in the state of the application (e.g., user > input errors); the FSM just responds appropriately when such conditions > prevail by consuming the triggering event.
That's what it's doing.
> > >>If one is using DbC > >>for design, that should be a responsibility of whoever generates the > >>event. Thus it is a precondition of consuming the event that the data > >>in the data packet be consistent. IOW, when using DbC any assertions > >>made about the input data are checking the correctness of the software, > >>not the consistency of the data. > >> > >>When the software becomes broken, that's a job for exception handling. > >>One might still need a [Stop] state for recovery, but getting there is a > >>job for the exception handler rather than FSM design. > > > > > > Getting to stop is a function of working software not broken software. > > Responding to faults is something the software must do and do correctly. > > Think deadman switch or e-stop. > > You indicated that going to the Stop state was the result of > inconsistent user input data for the directions and that is the case I > am addressing.
I also indicated you got there from start. And in neither case does it indicate there is anything wrong with the software.
> I don't care how many other ways there may be to get to > the Stop state; I am only interested in the FSM structure around this > path. I submit that such an error should have been detected by the UI > so that it never reaches the FSM.
That's just what's happening here.
> > Actually a very common technique in embedded systems is a table of > > function pointers. One that a client has me maintain (I didn't design > > it) uses a list of pointers to define possible transitions for each > > state. Each transistion is defined by a pointer to condition function, > > a pointer to an action function to be executed if the condition function > > returns true and a pointer to the next state if the transistion is taken > > (the condition returns true). If the next state is null the state > > doesn't change. And what happens? You can probably guess at his point. > > The conditions functions actually perform actions in some cases. > > That table of function pointers is basically the second form of STT that > I mentioned above. The condition function is just the sort of > indirection overhead one gets into if one supports the /possibility/ of > conditional relationships. If one doesn't use conditional relationships > the table just contains the action pointers. (Of course if one also > might have exit actions, one needs yet another table.)
Note also thst the condition functions map very cleanly to the conditions I use in the example I've been using. All that's happened is each conditions have been wrapped up in a function. Of course my conditions don't perform actions. I wasn't commenting on the existence of the condition function except to note it had been used to perform actions. Without that it would have bee quite a clean implementation even if it ends up scatterd all over God's half acre. If you don't have the tools to let you draw and generate code from a statechart though it not a bad approach. I'd really have liked to not seen actions in the condition functions though, it makes it a lot harder to follow.
> > >>[All commercial full code generators employ the event queue > >>infrastructure. > > > > > > Not all of them, the one I use doesn't. It uses that function call. A > > lot of state machines I've seen use some variation on that. Some use > > tables of function pointers, some use other constructs. There are > > actually reasons you might not want tables of constant values in some > > embedded systems. > > Just out of curiosity, which one is that?
BetterState, WindRiver sells it, although I'm not sure they sell it separately anymore. They acquired it as part of their purchase of ISI. ISI bought it from R-Active concepts. I believe it was developed by Doron Drusinsky as part of his graduate work so there may well be some papers out there related to it. The original versions included support for Verilog and VHDL generators, although I never had the need so I didn't purchase and use them.
> >>>I suppose you could convert all the conditional checks by running them > >>>externally and producing event ids for each one. Seems a lot of work > >>>just get get a abstract ID though. The other disadvantage I see with > >>>that is the actual condition is now several steps removed from the > >>>transition it causes. > >> > >>Not necessarily. Recall my model for Motor. It was actually somewhat > >>simpler than your model. Its actions could do everything you said were > >>done in your FSM actions and its transitions enforced the same > >>sequencing rules that yours did. > > > > > > No it didn't. It failed on two accounts. It never returned to stop and > > it didn't have any forward/reverse assymetry (admittedly a later > > clarification). It didn't even have a stop state, just a neutral state. > > That's because in your original FSM description Stop didn't do anything > except set DriveRequest to 0, which was what Neutral did.
No, I went back and double checked the revised/clarified version I used just to be sure. To exit [stop] to [Neutral] direction must be neutral and throttle must be 0. To exit [neutral] a direction must be selected but it is not necessary to have the throttle at zero. This enforces the sequencing that both throttle and direction indicators must be in a certain state before starting.
> When I > changed the state variable and other semantics it was no longer needed. > It did return to Neutral under all the same <substantive> conditions > that you originally specified (or I inferred).
You missed some behaviour.
> Now you can argue that your original FSM was designed your way because > of other, unmentioned requirements.
Nope, not my argument. Just an aside mentioning that a complete model include more ways to get there. Meant to indicate that the direction indication was acting as a proxy for what would be more possible transistions in a complete model. You're getting hung up on terminology rather than duplicating the behaviour.
> > It also excluded the event generation. > > I am not sure what this means. You FSM didn't have any event generation > either; it just selected transitions given an event. Do you mean I > didn't have conditional transitions? If so, that came with changing the > semantics of the state variables and events.
I mean you don't have the full logic available. The full logic includes the causes of the events as well as their effects. Without that it is impossible to tell whether the state machine provides the required behaviour.
> > >>It just did it with different > >>conditions (states), events, and state variables. > >> > >>I think the condition is only one step removed. > > > > > > Instead of > > call statechart > > check condition > > You forgot the last step: select a transition. That is equivalent to > generating an event below.
OK so add select a transition to the below as well. Once an event has been generated you still have to match it with the specific transistion.
> > > you have > > check condition > > generate event > > call statechart > > > > I count an extra step. More if you include the fact you are now check > > more conditions than necessary for a given state. > > The point is that somebody always detects a condition and announces it > with an event. The FSM always responds to that announcement event by > transitioning to a new state and executing the associated action. That > is about as peer-to-peer as the collaboration can get without bleeding > cohesion between the FSM and the external environment.
Which may make sense (probably does) in another environment, here it just obscure the logic and adds overhead.
> When has a condition check that determines the transition inside the > FSM, one is bleeding cohesion because the FSM is doing part of the job > that the <external> event generator should be doing. Part of the > detection (the trigger event) is outside the FSM and part is inside > (selecting a transition).
Sorry, trigger event? There are no trigger events outside the state machine unless maybe you are referring to the fact that the state machine is clocked? I don't see it making a practical difference.
> IOW, detecting condition changes is one thing > while responding to them is quite another and I think those two concerns > should be separated.
They are, into condition and action :)
> >> The DbC precondition > >>for executing any state action exactly matches the postcondition of some > >>other action (i.e., the one where the event is generated to announce > >>that its postcondition prevails). > > > > > > Exactly as in the case of using a simple conditional check. > > The problem lies in who owns the condition check (more precisely, who > owns which pieces of a compound condition check), per the point > immediately above.
Methinks we are dealing with differnt issues of scale.
> >>>So far I don't see the abstract IDs as any simpler than the conditional > >>>check. Actually I don't see that they are not fundamentally identical > >>>unless you have something running around changing values behind the > >>>state machines back which would be bad design practice IMO. > >>> > >>>Why is > >>> if( a < DFG ) { > >>> QueueEvent(queue, QW); > >>> } > >>> if( b == GHJZZ ) { > >>> QueueEvent(queue, QE); > >>> } > >>> StateChart( queue, out); > >>> > >>>superior to > >>> StateChart( a, b, out); > >> > >>There are several reasons. From an aesthetic perspective the FSM > >>provides /invariant/ constraints on /relative/ sequencing. They should > >>be invariant relative to the external context. > > > > > > And how are the constraints I'm using less invariant than events? > > They depend upon specific state variable values.
So do the events.
> The FSM doesn't care > about the specific values; it only cares that /some/ condition announced > by QW or QE exists.
So the causes of the transitions is obscured by generating events. I can see this as an advantage in some systems but not in the example I'm using. If the events are named something convienient like forward I don't see any difference from checking the direction flags.
> The sequencing constraints in the FSM define > intrinsic constraints on when the state actions can be performed > relative to one another without regard to solution context. The > constraints on 'a' and 'b' represent a mapping of intrinsic FSM > sequencing constraints to specific context semantics in the overall > problem solution. That mapping to solution specifics is none of the > FSM's concern. > > Suppose there is another object with a method that does something like: > > if (x > ABC) > QueueEvent (queue, QW) > if (y != LMNOP) > QueueEvent (queue, QE) > StatrChart (queue, out)
Keep in mind how constrained the system is. These signals have a very narrow effect. The chance of the signals being used by another context approaches zero. The chance of the of the same conditions being used in another context is even smaller. I've never seen either happen. Now you could have a system with multiple 'throttles' controlling multiple motors but that just means you have a state chart instance for each.
> > OK so why wouldn't conditions based on those same values be well > > constrained? > > I am talking about data integrity scope here. One can manage things > like thread pausing and whatnot easier if scope is defined by a single > procedure. Similarly, one can focus on the design issues better if > scope of data access is limited to single method scope.
It is so limited. It's limited to the single call of the function doing the statechart evaluation.
> >>The other thing they can affect is the sequencing of actions within the > >>FSM. That is essentially what conditional events do; they use context > >>variables to change the structure of the FSM on the fly. > > > > > > No more than events do. > > Very much more so! If there are no conditional transitions the > structure and sequencing of the FSM for the incoming events is > completely invariant. It depends solely on {event ID, current state}. > No matter how many times that tuple is processed, the path is exactly > the same each time.
The same is true of the conditionals I've used.
> However, if there are conditional transitions the structure is > effectively modified. Instead of one possible path for a given event > and current state there are multiple paths that depend upon {event ID, > current state, state var1, state var2, ...}. Thus the structure of the > FSM itself is potentially different each time that {event ID, current > state, ...} tuple is processed.
No more than it would be if you used the (event ID, current state) approach. Different events or conditions cause different transistions to occur. That's rather the point isn't it? All that happens is the conditions get turned into events.
> To put it another way, if one drew the > FSM as if only one condition existed, it would have a different > structure than if one drew it for only one of the other conditions.
That last sentence is sort of obvious. If you draw a statechart for one event only it's different than if you draw it for another event only. I don't see how that's particularly useful though.
> >>To me that is > >>kind of like an assigned GOTO in FORTRAN; it opens all sorts of > >>opportunities for unexpected side affects. IOW when one changes the > >>data semantics of the state variables one not only needs to worry about > >>what one computes from the data but which formula one uses to do the > >>computation. > > > > > > That's sort of the point. The result depends on the state you are in. > > If you don't compute it there you have to have knowledge of which state > > the state machine is in externally when you compute it. As far as I'm > > concerned that opens the inner workings of the state machine to external > > view too much. The during actions express that clearly and directly. > > When there are no conditional transitions, one /always/ knows exactly > what path will be taken for a given {event ID, current state} tuple, > regardless of what might be happening in the overall solution.
And with the conditionals one always knows what path will be taken. You seem to be arguing against something that isn't occurring.
> As soon > as one starts to make the path variable, one has an assigned GOTO where > the assignment is done outside the FSM.
The path isn't variable, at least not more so than using events. If you are in state A and b is true then you take transition C. if b is true then generate event B If you are in state A and event B occurs take transistion C I see no difference in the result of the above approaches.
> Those state variables one is using to select transitions are defined and > assigned outside the FSM.
So would the events in your case.
> They represent external context. So one is > bleeding that context into the FSM, which makes the FSM more complex.
The events also bleed the same context in.
> It also splits up compound conditions between whoever generates the > triggering event and the FSM itself. (Somebody still has to know it is > time to generate an event to the FSM.)
The same will be true in the event case. It is still necessary for the state machine to be clocked. This clocking BTW comes for free since the inputs arrive in a synchronous fashion.
> >>That is an issue of separation of concerns. The FSM structure is > >>inherently static. Modifying transitions dynamically adds another > >>dimension of complexity to the FSM structure and that is probably not a > >>good idea. > > > > > > Who's modifying transistions dynamically? > > You are any time you modify the state variable used to select a > conditional transition during the life of the FSM.
What are you talking about? The state machine doesn't modify anything it uses to make a decision. Sure the inputs will vary on each cycle but surely with events the events would be expected to change on each call as well.
> >>One way that is manifested is in the notion of OO announcement messages > >>(I'm Done) compared to procedural imperative messages (Do This). The > >>event being generated announces a change in condition /outside/ the FSM. > >> That is, the event itself is announcing something happening > >>externally. As it happens that depends upon 'a' and 'b'. But it > >>announces a compound condition that also includes whatever the caller > >>did. IOW, the condition being announced is {(a < DFG), caller completed > >>its part of the solution} OR {(b == GHJZZ), caller completed its part of > >>the solution}. > > > > > > Or just as likely both. The state machine then has to discard the > > irrelevant events. > > I think ignoring events in the STT is a quite different thing than > conditional transitions.
But it does become a question of efficiency. I've become used to having more than 16K of RAM available but I'm still not inclined to waste it by providing a queue most of the elements of which are used to buffer items that will be generated and then immediately thrown away. That's w/o mentioning the extra time taken.
> There is no law that requires one to do > something in response to raising a condition, so it is valid to ignore > certain events when the FSM is in a particular state. > > The issue here is choosing a transition path from among alternatives > _when the event cannot be ignored_.
What? Choose via condition or choose via condition transformed in an event, I see no difference in the end result. I do see the condition code adding a layer of complexity.
> >>More importantly, it includes a context decision that the IF statements > >>themselves define. Those IF statements implement problem space rules > >>and policies to determine exactly What should be announced. > > > > > > Beg pardon? I think you are envisioning an entirely different kind of > > problem than I am. The state machine must be aware of all the inputs > > (or events) anything else would be unsafe. The only way anything > > external to the state machine could filter the events/data to a subset > > would be if it were aware of the state it was in. > > The FSM only needs to be aware of events and the relevant input alphabet > for a transition that is processed by the corresponding action. That's > all an FSM does: it responds to events by processing the the input alphabet.
And?
> My point here is when you select a transition you are defining a > particular state for the FSM (i.e., the target state for the selected > transition).
Of course.
> The precondition for entering that state is a compound > condition composed of: > > - the postcondition of the action the generated the trigger event
So an event occurred/a condition was met.
> - any data integrity constraints on state variables to be accessed by > the action
??? I think you are envisioning a different problem set.
> - the conditions used to select the transition.
That's a repeat of the first line.
> > IOW, the conditions used to select the transition are necessarily part > of the precondition for entering the target state. If they did not > evaluate TRUE, transitioning to that state would be inappropriate.
The conditions to enter the state must be true or you can't enter the state. That much does seem obvious.
> The primary issue of this whole subdiscussion is whether that > precondition should be evaluated in one place or in two places.
What? I'm evaluating as the check on the transistion. You are evaluating to generate events. I don't see a difference.
> > >>The FSM > >>only responds to the announcement. If one moves the IF statements into > >>the FSM itself, one is moving that external context decision into the > >>FSM. That external context decision probably isn't any of the FSM's > >>business; it should just respond to the decision's /result/. > > > > > > ?? That external context is the entire reason for the state machine to > > exist. Having said that the external context may be abstracted or > > idealized. The direction input could be a single multivalued input > > rather than a pair of flags as a for instance. > > The state variables being tested /are/ the external context. Forward > and Reverse or 'a' and 'b' in your examples have their values assigned > based upon conditions that exist outside the FSM.
Yes and that's the entire reason for the state machine to exist is to evaluate what state to be in as a result of those values.
> They have semantics > that is defined in terms of that external context. > > I am arguing that such external context should be evaluated externally > (preferably in one place) and an appropriate event generated to the FSM > based on that evaluation. If one always encapsulates the rules and > policies for detecting state changes that way in a place that logically > understands the context semantics, one never needs conditional events in > the receiver FSMs.
You've just called them events instead and now to unserstand the state machine you have to have the state chart on one page and the definition of all the events on another. It'll certainly work but for the context I'm using it in it does seem pointless complexity.
> > >>Finally, when managing complexity it is wise to isolate related rules > >>and policies via encapsulation. It is highly unlikely that the rules > >>and policies that determine what event to generate will be logically > >>related to the rules and policies that the FSM exists to resolve. So if > >>one moves those rules and policies into the FSM, one is trashing the > >>cohesion of the FSM. > > > > > > While there is a lot to encapsulation and isolation it is quite possible > > to take it too far. Data and actions that are tightly coupled should > > not be separated. > > True. That's why one needs a design methodology to draw the lines in > the sand. The OO paradigm draws lines in the sand based on problem > space abstraction while Structured Programming drew them based upon > functional decomposition. > > But I would still argue that the rules and policies for detecting state > changes are going to be quite different than those that govern the > response to state changes, regardless of what methodology one uses. B-)
Condition and action are different. Well yes.
> >>The bottom line is that the software will be more maintainable if one > >>decouples message and method, separates concerns, and encapsulates rules > >>and policies in a cohesive fashion. Superficially both your examples do > >>the same thing. But devil is in the details and from the perspective of > >>maintainability there are potentially huge differences when one gets to > >>the details. > > > > > > Actually, encapsulation is a big reason for choosing the form I use in > > the example rather than an event queue. All of the logic for > > determining the requested drive signal is isolated to the state machine. > > And this is a core disagreement. To me conditional transitions > represent lack of cohesion and poor separation of concerns.
I don't think we are going to agree on this. I can see it's utility in larger problems, especially when a state machine gets reused with different sources for it's events but that is just not the case here. We have at least reached the point where we understand during actions. I hope we can reach the point where we have something similar on the state machine structure. I expect we will always disagree on events/conditions. Robert -- From the Divided by a Common Language File (Edited to protect the guilty) ME - "I'd like to get Price and delivery for connector Part # XXXXX" Dist./Rep - "$X.XX Lead time 37 days" ME - "Anything we can do about lead time? 37 days seems a bit high." Dist./Rep - "that is the lead time given because our stock is live.... we currently have stock." -- Posted via a free Usenet account from http://www.teranews.com
Responding to Adsett...

We are repeating ourselves, which is a sure sign of talking past each 
other.  I took one more pass at clarifying my position, but I think we 
are close to the point where we need to agree to disagree.

>>As far as mapping the the problem space is concerned, it seems to me >>that placing lubricating and cooling as entry/exit actions in a single >>state like [Prepared] is constructing the FSM around a preconceived view >>of the solution rather than the invariants of the problem space. > > > You still misunderstand. Lubrication on would be the on-entry action of > [running], lubrication off would be the on-exit action of [running]. > Cooling doesn't get involved anywhere, it was a separate example.
OK, I guess I misunderstood. I thought you provided lubrication and cooling as examples of why one would need entry and exit actions. However, my point here still stands. I think the same sort of logic still applies to any entry/exit action couplet. If they represent rules and policies that are different enough to justify encapsulation in separate actions AND they are invoked in different contexts (e.g., different times by different events) AND some relative sequencing rules apply, then I think they should be associated with different states to provide maximum maintenance flexibility.
>>>The most recent occaision I've had reason for using that coupled on- >>>entry/on-exit construct was for flagging a fault state. On entry to the >>>fault state the flag would be set, on exit it would be cleared. You >>>could do the same thing by performing the action on the transitions >>>instead but since there could be multiple entry and exit transistions it >>>was clearer to use the entry/exit actions. Syntatic sugar sure, but >>>useful. You could also use events to announce the entry and exit and >>>then implement another state machine to set the flag but other than >>>increasing the size of the code and the execution time I don't see much >>>effect. >> >>As you may recall, I already said I don't have that big a problem with >>exit actions per se on a case-by-case basis. I just feel one should >>understand the trade-off for maintenance risk and that trade-off rarely >>comes out in favor of exit actions. > > > OK, but this example is identical to the lubrication example you are > objecting to.
Then I'm confused about which example you are talking about here. I thought you were talking here about the FSM example you provided. That was a simple Moore model with no exit actions.
>>>I think you've misunderstood the problem. The fact that direction >>>consists of four states is a consequence of the user interface not an >>>internal program design decision. This state machine is using the >>>signals from the user interface to determine the the signals that will >>>be fed to the actual motor control. >> >>I can't buy that. The UI is a separate subsystem whose job is to >>convert the user view to the problem solution view. Using ON/ON and >>OFF/OFF combinations in the interface between the UI and the problem >>solution is a matter of interface design. The interfaces to the problem >>solution's subsystems are under the developer's control. Even if the UI >>wants to export ON/ON and OFF/OFF, each subsystem has its own two-way >>interface and one can deal the problem in the glue code between them. >>So my pushback would be to fix those interfaces. > > > As I said I think you've misunderstood the problem. This example does > not simply receive information from the UI it provides the proper > behaviour for the UI. It is providing the safety interlocks. And the > input neutral is definitely distinct from forward and reverse.
You said that the ON/ON combination was a user input error that triggered a transition to the Stop state. I am arguing that input errors should be detected in the UI, not the problem solution. So that combination should never get to the FSM in the problem solution if the UI software is working correctly. (Unless there is some need for the system to respond to input errors, in which case the UI should generate the appropriate <unconditional> transition event itself.) [I am beginning to suspect the real issue in this subdiscussion is application partitioning. More on that below.]
>>>>That is very similar to return values being "normal" data when positive >>>>and error codes when negative. Such semantic double duty can lead to >>>>problems during maintenance (e.g., when requirements change and make >>>>some negative values "normal" data or when your criteria for stopping >>>>changes). IOW, separation of concerns applies to data domains are well >>>>as behaviors. >>> >>> >>>I can understand why you would think that. It's not really the case >>>though. In that sense it's more akin to input validation. >>> >>>I am with you on the separation of normal data from error codes. The >>>trouble is all four states are normal in a very real sense. Even if you >>>were to decide to treat the both directions selected as a separate event >>>you would still have three direction states. >> >>I still think it's still double duty; its is just spread over two data >>domains. >> >>Forward ON; Reverse OFF. Redundantly specifies forward operation >>Forward OFF; Reverse ON. Redundantly specifies reverse operation > > > Yes > > >>Forward ON; Reverse ON. An input error. > > > Sort of. As I said think deadman switch or e-stop as well. I won't > object if you want to consider it separately though.
At one point you said you were checking user input. I think things like deadman's switches are quite different. Ultimately they are hardware interrupts that would normally be manifested as unique external events addressed to the FSM. In that case, the FSM's response would be unconditional.
>>Forward OFF; Reverse OFF. The motor goes to neutral. > > > No. The input state goes to stop, what the motor does is beyond this > example. Typically the motor will indeed follow, probably to a neutral > state or something similar but there are likely to be lags.
In the FSM you provided States 3 and 4 both had, "Tr C - (!Forward && !Reverse) To Neutral. State 2" Those states only went the State 1 (Stop) in the ON/ON situation.
> Don't get too hung up on this. Remember I said this was a simplified > example. This input although it exists serves also to stand-in for all > the other possible ways you get back to the stop state.
I can only discuss what you give me. I think we need to drop this subdiscussion because I am too confused about what the requirements are.
>>That's the UI's job and >>shouldn't even be an issue in the controller context except, possibly, >>as a DbC assertion for the interface to the UI. > > > No. It is very much the job of this routine. This is where it's > determined that the operator has satisfied the required conditions for > proceeding. The actual motor control is decoupled from this as it > should be. > > If it helps, consider it part of the UI, after all it's about ensuring > the operator has satisfied the input requirements and then generating > the appropriate output to go to the motor control.
I think there are some megathinker application partitioning issues here. Basically you can get external input from three sources: an interactive user; from other software, through a programmatic API; or from the hardware, through network ports, interrupts, etc.. An interactive UI is encapsulated in a subsystem because the controller doesn't care what the communication paradigm is (GUI, browser, smoke signals, whatever). In that subsystem one abstracts to the paradigm in hand (e.g., Window/Control objects for a GUI, Page/Section objects for a browser). At that level of abstraction the problem semantics is expressed as simply text data values for window and control labels. If there is a consistency constraint on the input values, it is expressed in terms of values. Thus one checks if the value for Control X and the value for Control Y are both ON in a GUI. Thus the semantics of what "ON" means and what Control X and Control Y represent is not relevant. So if one were going to check for consistency in the UI, I don't think one would use an FSM that that had all the problem space semantics that your FSM has. OTOH, one should provide simplistic value-based rules for consistency that the GUI subsystem can check. [The classic model for this is a RAD IDE like Access where consistency checks for input form fields are specified purely in terms of field data domains.] A programmatic API is essentially a "smart" interface. It could be just a Facade class or it could be a full subsystem depending on the how many syntactic mismatches there are between the client software's view of the functionality and that of the service. (Subsystem interfaces are two-way explicitly so one can provide that sort of glue to resolve syntactic mismatches when applying large scale reuse.) However, a programmatic API represents a client/service DbC contract. That contract would require that the client provide consistent values for Forward and Reverse. So, like the interactive UI, bad combinations should never get to the interface. If the service needs to be informed that the inconsistency exits on the client side, then there should be a unique interface message to announce that. That's because the semantics of the existence of an inconsistency is quite different than the semantics of Forward/Reverse operation. If the application is a hardware controller, then processing hardware signals is part of its job. However, one needs to convert hardware register data into something the software understands. The natural way to do that is though event messages when the hardware does a write. One may have a subsystem whose sole job in life is to provide that conversion, which can be very handy for hiding things like bitsheet changes (e.g., ECOs) from the controller's invariant logic. In the end any asynchronous hardware signal like a deadman's throttle being released will be manifested directly as a unique event before it ever gets to the FSM. The FSM will then have an unconditional transition to deal with it. Bottom line: if the ON/ON combination is provided by a user, then any consistency errors should be dealt with in the UI using the UI semantics. Similarly, for a programmatic API, they should be dealt with by the client. In either case, if the controller needs to know that the inconsistency exists, that should be dealt with through a different interface message with unique semantics. Finally, if such input comes from the hardware it will already be cast as an event that FSM must respond to unconditionally. [BTW, there is a category on Application PArtitioning in my blog that you may find interesting.]
>>Similarly, the notion of neutral really isn't the same as NOT forward >>AND NOT reverse. That is manifested in the redundancy when specifying >>when the motor really is operating in forward or reverse. What is >>needed is to separate the semantics of direction vs. running, as in: >> >>Forward ON. Specifies forward operation, if running >>Forward OFF. Specifies reverse operation, if running > > > And neutral which is different. A motor can be powered but not in > either forward and reverse. If you don't believe me try operating an > automobile without using neutral.
That neutral is different is exactly my point. That's why using Forward/Reverse to convey that is forcing the directional semantics to do double duty.
> > More to the point since this state machine does not deal with the motor > but the input to the motor, neutral is a distinct input state. The > operator can distinctly specify neutral and will.
That's fine. I submit, though, that neutral is different than forward or reverse so one needs to convey that with separate semantics than value of Forward/Reverse. My problem here is with the semantics of the data. Another way to handle it would be with a data domain with different semantics: OperatingMode ON. Specifies forward operation OperatingMode OFF. Specifies reverse operation OperatingMode IDLE. Specifies neutral operation I am not fond of that because neutral really isn't just another flavor of direction. So one would get in trouble if, like a hand drill, one can set forward/reverse once and start/stop the motor many times without re-specifying direction. So...
>>Powered ON. Specifies motor is powered >>Powered OFF. Specifies motor is not powered > > > Generally if the motor (controller) is not powered you won't even get > this far. Error checking during startup should ensure that. Also you > are confusing the motor state with the input state this state machine is > providing. The motors own control and states are separate from this > state machine.
What I am seeking here is a semantics that properly decouples the direction from what the motor is doing. So if one needs to be careful about neutral vs. stopped, I would prefer something like: OperatingMode POWERED. The motor is running in some direction OperatingMode NEUTRAL. The motor is unpowered OperatingMode STOPPED. The motor is reset One achieves the neutral vs. stopped distinction but that is fully decoupled from the semantics of forward/reverse. I don't think it matters whether the FSM is the actual motor itself or some preprocessor for motor commands. The preprocessor still has to capture the same rules and policies that govern how the motor operates. IOW, for a preprocessor, all one needs to do is substitute "motor should be" for "motor is" in the above descriptions.
>>>>I also have a problem with using them to trigger a transition to the >>>>[Stop] state, which you said was an error state. Since they are input >>>>alphabet values (i.e., part of the event data packet), what is being >>>>checked is that consistent data is being provided. >>> >>> >>>As I said that's at least half the point of the state machine. It's not >>>checking for consistent data per se. The data is perfectly consistent >>>but something has caused an input that forces a return to stop. Note >>>that in a complete example more and different kinds of conditions can >>>cause a return to stop state. >> >>You indicated [Stop] was an error state. That's fine. But getting >>there should be triggered by an event that is raised explicitly by >>detecting the error condition _outside the FSM_. The FSM /responds/ to >>changes in the application state by switching states. > > > That's exactly what it is doing, responding to changes in the > application state by switching states.
I have to strongly disagree. The rules for detecting the error are the same, but where they are applied is quite different. You have including the error detection logic in the transition conditions rather than outside FSM. I am arguing is that there should be a specific E1:Stop event that is generated outside FSM based upon those conditions. Then in the FSM there should be unconditional transitions from State 2, State 3, and State 4 to State 1 that are all triggered when that particular event is received. IOW, whoever understands how to detect an error should have all the information in hand and Just Do It. Then the FSM responds directly to the announcement that an error exists. [BTW, I am not making this stuff up. B-) I can honestly say that I have /never/ seen a state machine where moving to an error state was not unconditionally triggered by an explicit external event. I think that's because detecting errors is something that one wants to do as close to where they occur as possible.]
>>If some other external situation also >>triggers a reset to the error state, whoever detects that condition >>should generate the appropriate event. It isn't up the the FSM to >>detect error conditions in the state of the application (e.g., user >>input errors); the FSM just responds appropriately when such conditions >>prevail by consuming the triggering event. > > > That's what it's doing.
Picture me jumping up and down, Yoda-like, screaming at the monitor: No it isn't! B-) The condition detection (i.e., identifying the ON/ON combination) is in your FSM as you select a transition. A conditional transition is a fundamental structural element of the FSM itself. IOW, the FSM is analyzing the context and making a decision about what to do next rather than simply responding to a change in context. This is really an important difference. Detecting condition changes and generating events is about determining WHEN a behavior response is needed in the overall solution context. FSMs are about WHAT intrinsic behavior responses a particular subject matter is responsible for providing. In my view that is a very important distinction. If we can't agree that it is an important distinction, we aren't going to get anywhere with the rest of this discussion.
>>>>If one is using DbC >>>>for design, that should be a responsibility of whoever generates the >>>>event. Thus it is a precondition of consuming the event that the data >>>>in the data packet be consistent. IOW, when using DbC any assertions >>>>made about the input data are checking the correctness of the software, >>>>not the consistency of the data. >>>> >>>>When the software becomes broken, that's a job for exception handling. >>>>One might still need a [Stop] state for recovery, but getting there is a >>>>job for the exception handler rather than FSM design. >>> >>> >>>Getting to stop is a function of working software not broken software. >>>Responding to faults is something the software must do and do correctly. >>>Think deadman switch or e-stop. >> >>You indicated that going to the Stop state was the result of >>inconsistent user input data for the directions and that is the case I >>am addressing. > > > I also indicated you got there from start. And in neither case does it > indicate there is anything wrong with the software.
Fine, but in this context I am only talking about the ON/ON combination. That is special because it is an error condition. My objections here are that: (a) the Forward/Reverse data domains are serving double duty; (b) user input errors should be detected in the UI; and (c) any error should be detected as close to where it occurs as possible. Only the first is an issue for the other transition conditions. I focused on the error because <I hoped> it would be more obvious what the nature of the problem is for conditional transitions.
>>>Actually a very common technique in embedded systems is a table of >>>function pointers. One that a client has me maintain (I didn't design >>>it) uses a list of pointers to define possible transitions for each >>>state. Each transistion is defined by a pointer to condition function, >>>a pointer to an action function to be executed if the condition function >>>returns true and a pointer to the next state if the transistion is taken >>>(the condition returns true). If the next state is null the state >>>doesn't change. And what happens? You can probably guess at his point. >>>The conditions functions actually perform actions in some cases. >> >>That table of function pointers is basically the second form of STT that >>I mentioned above. The condition function is just the sort of >>indirection overhead one gets into if one supports the /possibility/ of >>conditional relationships. If one doesn't use conditional relationships >>the table just contains the action pointers. (Of course if one also >>might have exit actions, one needs yet another table.) > > > Note also thst the condition functions map very cleanly to the > conditions I use in the example I've been using. All that's happened is > each conditions have been wrapped up in a function. Of course my > conditions don't perform actions.
I don't think the conditions and what they map to are at issue. To me the issue is where they should be evaluated.
>>>>[All commercial full code generators employ the event queue >>>>infrastructure. >>> >>> >>>Not all of them, the one I use doesn't. It uses that function call. A >>>lot of state machines I've seen use some variation on that. Some use >>>tables of function pointers, some use other constructs. There are >>>actually reasons you might not want tables of constant values in some >>>embedded systems. >> >>Just out of curiosity, which one is that? > > > BetterState, WindRiver sells it, although I'm not sure they sell it > separately anymore. They acquired it as part of their purchase of ISI. > ISI bought it from R-Active concepts.
OK. That's not a full code generator; it just deals with individual FSMs. In my world (translation) a code generator produces a full 3GL or Assembly application generated from a UML OOA model, including computing space optimization. IOW, UML with an abstract action language is used as a 4GL. [FYI, most translation code generators can also produce system documentation (e.g., in MS Word) from the same OOA model. The solutions are also portable; they can generate proper code for any supported computing environment, including target languages, without modification of the model. All translation tools also provide a model simulator so the OOA model can actually be executed for validation prior to producing 3GL or Assembly code. (It is SOP to run the same test cases for functional requirements against the model as one runs against the executable; only the test harness changes.)]
>>>>>I suppose you could convert all the conditional checks by running them >>>>>externally and producing event ids for each one. Seems a lot of work >>>>>just get get a abstract ID though. The other disadvantage I see with >>>>>that is the actual condition is now several steps removed from the >>>>>transition it causes. >>>> >>>>Not necessarily. Recall my model for Motor. It was actually somewhat >>>>simpler than your model. Its actions could do everything you said were >>>>done in your FSM actions and its transitions enforced the same >>>>sequencing rules that yours did. >>> >>> >>>No it didn't. It failed on two accounts. It never returned to stop and >>>it didn't have any forward/reverse assymetry (admittedly a later >>>clarification). It didn't even have a stop state, just a neutral state. >> >>That's because in your original FSM description Stop didn't do anything >>except set DriveRequest to 0, which was what Neutral did. > > > No, I went back and double checked the revised/clarified version I used > just to be sure. To exit [stop] to [Neutral] direction must be neutral > and throttle must be 0. To exit [neutral] a direction must be selected > but it is not necessary to have the throttle at zero. This enforces the > sequencing that both throttle and direction indicators must be in a > certain state before starting.
That's the condition for selecting the transition. That has nothing to do with the object's behavior responsibilities. I was talking about what the state action actually did (i.e., the rules and policies that apply when the state condition prevails). With my revised transitions and state variables those conditions weren't part of the FSM.
>>>It also excluded the event generation. >> >>I am not sure what this means. You FSM didn't have any event generation >>either; it just selected transitions given an event. Do you mean I >>didn't have conditional transitions? If so, that came with changing the >>semantics of the state variables and events. > > > I mean you don't have the full logic available. The full logic includes > the causes of the events as well as their effects. Without that it is > impossible to tell whether the state machine provides the required > behaviour.
Then I have to disagree strongly with the second sentence. The causes of events should have nothing to do with the FSM structure. Events are supposed to be generated outside the FSM. Therefore the conditions associated with generating the events (i.e., why they are generated) should be external to the FSM. FSMs respond to changes in external conditions, they don't define those changes. In fact, this is substantially what I have against conditional transitions -- they effectively move the definition of the cause of the state change into the FSM, which trashes cohesion and separation of concerns. Ignoring FSMs, consider the following pseudo code procedures: computeBenefits () foreach benefit if (employeeType == hourly) computeHourlyBenefit (benefit, employeeBaseSalary) else .... computeHourlyBenefit (benefit, employeeBaseSalary) if (employeeBaseSalary < 5000) return switch benefit // compute benefit using employeeBaseSalary Even a die hard procedural developer from the '70s would probably realize there was something wrong here about the way responsibilities were allocated and would rewrite this as: computeBenefits () foreach benefit if ((employeeType == hourly) && (employeeBaseSalary >= 5000)) computeHourlyBenefit (benefit, employeeBaseSalary) else .... computeHourlyBenefit (benefit, employeeBaseSalary) switch benefit // compute benefit using employeeBaseSalary That's because the decision about whether the benefit should be computed needs to be evaluated in one place at a higher level of abstraction than /within/ computeHourlyBenefit. IOW, computeHourlyBenefit's job in life should be limited to the computation and determining whether the computation should be done at all is left as a job for a different trade union. Then when requirements change so that the rules change for whether a benefit should be computed, one only has to touch the code in one place. Conditional events essentially do they same thing as splitting up the condition in the first version. They move <part of> the definition of what changed in the outside world into the FSM. The FSM should only provide behaviors and enforce intrinsic relative sequencing rules among those behaviors; it should not be concerned with why and how the outside conditions changed.
>>>>It just did it with different >>>>conditions (states), events, and state variables. >>>> >>>>I think the condition is only one step removed. >>> >>> >>>Instead of >>> call statechart >>> check condition >> >>You forgot the last step: select a transition. That is equivalent to >>generating an event below. > > > OK so add select a transition to the below as well. Once an event has > been generated you still have to match it with the specific transistion.
That's not necessary in my version. When the check is positive the right event (QW or QE) is generated. The transition it triggers in the FSM will be unconditional so there is no selection there.
>>>you have >>> check condition >>> generate event >>> call statechart >>> >>>I count an extra step. More if you include the fact you are now check >>>more conditions than necessary for a given state. >> >>The point is that somebody always detects a condition and announces it >>with an event. The FSM always responds to that announcement event by >>transitioning to a new state and executing the associated action. That >>is about as peer-to-peer as the collaboration can get without bleeding >>cohesion between the FSM and the external environment. > > > Which may make sense (probably does) in another environment, here it > just obscure the logic and adds overhead.
What overhead is added? The checks are, at worst, exactly the same. In effect one has: Your caller: IF (time to generate event) generateEvent (Forward, Reverse, Throttle) and in your FSM: IF (Forward && Reverse) dispathTable[current state](Forward, Reverse, Throttle); ELSE ... while in my caller: IF ((time to generate event) && Forward && Reverse) generateEvent (QW, Forward, Reverse, Throttle) ELSE ... and in my FSM: dispatchTable[current state, event ID](Forward, Reverse, Throttle); The only difference is where the IF checks are located. [Note that you actually have an extra indirection because in your FSM you need to invoke both statechart to select a transition and the individual stateN action while I go directly to the action. Worse, that indirection exists whether the transitions are conditional or not (unless you write each FSM without any reuse).] As far as obscuring the logic is concerned, I'm afraid we are on different planets here. To me separating (time to generate event) and the conditions on Forward/Reverse makes it much more difficult to grok what is going on in the collaboration. [I use "(time to generate event)" as a placeholder for whatever condition causes someone in your software to call your "Statechart function". IOW, it defines whatever the caller did that justifies generating an event.]
>>When has a condition check that determines the transition inside the >>FSM, one is bleeding cohesion because the FSM is doing part of the job >>that the <external> event generator should be doing. Part of the >>detection (the trigger event) is outside the FSM and part is inside >>(selecting a transition). > > > Sorry, trigger event? There are no trigger events outside the state > machine unless maybe you are referring to the fact that the state > machine is clocked? I don't see it making a practical difference.
Your statechart function's signature is the event that your FSM responds to. When that function is invoked, somebody outside the FSM is generating an event. That means some condition has changed in the world outside the FSM and invoking the statechart function announces that change. Your FSM responds to that event by transitioning to a new state. Because you use conditional transitions, the proper transition to navigate must be selected. But whichever one is selected, it is triggered by the "statechart function" event. Corollary: that is the /only/ event your FSM responds to, regardless of what transition is navigated. If we were to draw the FSM graphically as an STD every transition would have the same event ID attached to it. Note that including the dispatch mechanisms explicitly in the FSM further obscures what is actually going on. (Not to mention having to reinvent the wheel with each FSM by supplying the same dispatch infrastructure.) That pretty much hides the fact that your FSM uses conditional events in a manner that reduces the triggering events to exactly one. In a normal FSM there would be multiple events, each one associated with a different causative context. I think attaching those events to <unconditional> transitions would greatly clarify how the FSM relates to external context. (It took me awhile to even realize it was a simple Moore machine because it was such an unconventional representation.)
>>IOW, detecting condition changes is one thing >>while responding to them is quite another and I think those two concerns >>should be separated. > > > They are, into condition and action :)
But they are still tightly coupled in the same subject matter (module, object, problem space abstraction, or whatever).
>>>>>So far I don't see the abstract IDs as any simpler than the conditional >>>>>check. Actually I don't see that they are not fundamentally identical >>>>>unless you have something running around changing values behind the >>>>>state machines back which would be bad design practice IMO. >>>>> >>>>>Why is >>>>> if( a < DFG ) { >>>>> QueueEvent(queue, QW); >>>>> } >>>>> if( b == GHJZZ ) { >>>>> QueueEvent(queue, QE); >>>>> } >>>>> StateChart( queue, out); >>>>> >>>>>superior to >>>>> StateChart( a, b, out); >>>> >>>>There are several reasons. From an aesthetic perspective the FSM >>>>provides /invariant/ constraints on /relative/ sequencing. They should >>>>be invariant relative to the external context. >>> >>> >>>And how are the constraints I'm using less invariant than events? >> >>They depend upon specific state variable values. > > > So do the events.
EXACTLY! Events announce external changes in the state of the application. Events are external inputs to an FSM, not part of its internal its definition. One maps events to transitions in order to link the fundamental FSM structure to the outside world. And one can do that after the FSM is designed. However, conditional events make specific state variable values part of the FSM structure.
>>The FSM doesn't care >>about the specific values; it only cares that /some/ condition announced >>by QW or QE exists. > > > So the causes of the transitions is obscured by generating events. I > can see this as an advantage in some systems but not in the example I'm > using. If the events are named something convienient like forward I > don't see any difference from checking the direction flags.
Actually, how one names events is an interesting and ongoing debate. I happen to belong to the camp that names events for the causative changes in the environment. That is, I name events for the external situation rather than the receiving FSM semantics. That links the events very specifically to the causes. Let's assume some events are provided from a winch operator. To that operator the proper perspective might be 'wind' and 'unwind'. For a crane operator the proper semantics might be 'up' and 'down'. It is up to the software to map that into 'forward' and 'reverse' for the motor powering the winch. However, the motor is going to respond in exactly the same way regardless of what the events are named (i.e., what the context semantics is) so long as they are associated with the right transitions. In a case like this I would find it more useful to attach E1:wind and E2:unwind to the transitions because that provides a better mapping to the rest of the software context. Since associating an event with a transition is all about mapping to the external context, that seems quite reasonable. [There are other advantages to naming events by the generating context. For example, allowing the same event from a given object to be broadcast to multiple receiving FSMs having different semantics. That's a lot cleaner from the event generation side than generating several different events that may have different semantics for the various receivers when the generator shouldn't know who is receiving the events, much less their semantics in that context.]
>>The sequencing constraints in the FSM define >>intrinsic constraints on when the state actions can be performed >>relative to one another without regard to solution context. The >>constraints on 'a' and 'b' represent a mapping of intrinsic FSM >>sequencing constraints to specific context semantics in the overall >>problem solution. That mapping to solution specifics is none of the >>FSM's concern. >> >>Suppose there is another object with a method that does something like: >> >>if (x > ABC) >> QueueEvent (queue, QW) >>if (y != LMNOP) >> QueueEvent (queue, QE) >>StatrChart (queue, out) > > > Keep in mind how constrained the system is. These signals have a very > narrow effect. The chance of the signals being used by another context > approaches zero. The chance of the of the same conditions being used in > another context is even smaller. I've never seen either happen.
My point here is just to demonstrate why it is a good idea to separate the concerns. It is difficult to anticipate what changes to requirements the future will bring. Rather than trying to be prescient, it is better to ignore the future and build for today. But one should do so in an manner that can be readily maintained in case do things change; IOW, one builds for generic change. Separating concerns and encapsulating related rules and policies is a very useful tool for providing that sort of generic maintainability.
>>>OK so why wouldn't conditions based on those same values be well >>>constrained? >> >>I am talking about data integrity scope here. One can manage things >>like thread pausing and whatnot easier if scope is defined by a single >>procedure. Similarly, one can focus on the design issues better if >>scope of data access is limited to single method scope. > > > It is so limited. It's limited to the single call of the function doing > the statechart evaluation.
No, in your FSM there are two calls. The transition is selected in your "statechart function". That's the scope where the condition variables are accessed. But the actual state action is another method that is called from the statechart function ("call state action"). Consider what happens if the state variables in the transition conditions aren't in the event data packet (as they usually would not be in an OO context). It would be possible for the values to change after the transition was selected but before the specific state action was invoked. Now the action might access a data structure that was deleted as part of the context of changing the state variables. One can prevent that fairly easily, but one needs to worry about it explicitly because the relevant scope spans multiple procedures.
>>>>The other thing they can affect is the sequencing of actions within the >>>>FSM. That is essentially what conditional events do; they use context >>>>variables to change the structure of the FSM on the fly. >>> >>> >>>No more than events do. >> >>Very much more so! If there are no conditional transitions the >>structure and sequencing of the FSM for the incoming events is >>completely invariant. It depends solely on {event ID, current state}. >>No matter how many times that tuple is processed, the path is exactly >>the same each time. > > > The same is true of the conditionals I've used. > > >>However, if there are conditional transitions the structure is >>effectively modified. Instead of one possible path for a given event >>and current state there are multiple paths that depend upon {event ID, >>current state, state var1, state var2, ...}. Thus the structure of the >>FSM itself is potentially different each time that {event ID, current >>state, ...} tuple is processed. > > > No more than it would be if you used the (event ID, current state) > approach. Different events or conditions cause different transistions > to occur. That's rather the point isn't it? > > All that happens is the conditions get turned into events.
I honestly don't know how to respond here. If there are conditional transitions, then the FSM structure itself potentially changes each time a state variable in the condition changes. It is a different state machine with different transitions...
>>To put it another way, if one drew the >>FSM as if only one condition existed, it would have a different >>structure than if one drew it for only one of the other conditions. > > > That last sentence is sort of obvious. If you draw a statechart for one > event only it's different than if you draw it for another event only. I > don't see how that's particularly useful though.
That's the point! B-( But the event isn't changing. I could draw three FSMs for your example, one for each combination of the state variable values, and use the same event on the transition in each one. What is changing is the way the states are linked through transitions (i.e., the fundamental structure of the FSM). IOW, the same event just triggers a transition to a different state in each FSM. If the transitions are unconditional, there is only one set of paths through the FSM for a given set of events. When you introduce state variables and conditional transitions one actually has multiple different "pure" FSMs with unconditional transitions and one essentially chooses which FSM to use as the "current" FSM based upon the state variable values. The same events are input but they trigger different paths. It is exactly like an assigned GOTO in FORTRAN. The graph of the flow of control changes depending on how the label is assigned. Once a particular graph is selected it remains that way until one makes a new label assignment to the GOTO, regardless of how many times it is traversed. But when one changes the label one now has a new graph. The flow of control graph in the FORTRAN program is exactly analogous to a "pure" FSM with unconditional events. The label assignment to change the graph is exactly analogous to modifying the values of the condition state variables in the FSM.
>>>>To me that is >>>>kind of like an assigned GOTO in FORTRAN; it opens all sorts of >>>>opportunities for unexpected side affects. IOW when one changes the >>>>data semantics of the state variables one not only needs to worry about >>>>what one computes from the data but which formula one uses to do the >>>>computation. >>> >>> >>>That's sort of the point. The result depends on the state you are in. >>>If you don't compute it there you have to have knowledge of which state >>>the state machine is in externally when you compute it. As far as I'm >>>concerned that opens the inner workings of the state machine to external >>>view too much. The during actions express that clearly and directly. >> >>When there are no conditional transitions, one /always/ knows exactly >>what path will be taken for a given {event ID, current state} tuple, >>regardless of what might be happening in the overall solution. > > > And with the conditionals one always knows what path will be taken. You > seem to be arguing against something that isn't occurring.
Au contraire. You only know that if you know what the current values of the condition variables are. That is pretty much the point. The state variables in the conditions are assigned dynamically outside the FSM so there is no way to predict what the new state will be for a given current state and a given event ID by simply looking at the FSM structure. You must know the dynamic context outside the FSM at run-time to determine the actual path a given set of events will traverse.
> > >>As soon >>as one starts to make the path variable, one has an assigned GOTO where >>the assignment is done outside the FSM. > > > The path isn't variable, at least not more so than using events. > > If you are in state A and b is true then you take transition C.
In this case it is also given that you have event in hand; whatever transition is followed must be triggered by an event.
> > if b is true then generate event B > If you are in state A and event B occurs take transistion C > > I see no difference in the result of the above approaches.
In the first case I cannot look at the STD with the given event when the current state is A and determine what the new state will be. To determine what the new state will be I must also know what the value of 'b' is. That value is dynamic. In the second case I know that I will always follow the C transition to the same state when whenever the current state is A and the event in hand is B. I can determine that just be glancing at the STD. That's because the new state is determined statically by the FSM structure. There is a huge difference in the relative complexity of the FSM operation for the two cases when the uncertainty of dynamic state variable dispatches in introduced in the first case -- just like introducing label assignments to GOTOs in FORTRAN. The moral equivalent of instant spaghetti code.
>>>>That is an issue of separation of concerns. The FSM structure is >>>>inherently static. Modifying transitions dynamically adds another >>>>dimension of complexity to the FSM structure and that is probably not a >>>>good idea. >>> >>> >>>Who's modifying transistions dynamically? >> >>You are any time you modify the state variable used to select a >>conditional transition during the life of the FSM. > > > What are you talking about? The state machine doesn't modify anything > it uses to make a decision. Sure the inputs will vary on each cycle but > surely with events the events would be expected to change on each call > as well.
The events don't change. When a given context prevails externally the same event is always generated. The current value of the state variable essentially identifies a particular unconditional transition for the event to trigger. Changing the state variable substitutes a different unconditional transition for the event to trigger. What I think you are referring to is the difference between a generic event whose condition does not include all the condition details and a specific event whose condition does include the condition details. Your "Statechart function" is a generic event whose full condition has not been specified. It's condition is simply "(time to generate event)". It only becomes fully specified when one includes the state variable values used to select a specific transition. Once those state variables are specified, one has a specific event that corresponds exactly to one transition choice. In your example the condition on your "statechart function" event is simply the criteria that indicates it is time to generate some sort of event for the motor. For a given current state that event is associated with /each/ of the exit transitions from that state; it can trigger any of those transitions. Each exit transition has a particular state variable condition associated with it. generic event -> condition: (time to generate event) select transition A -> condition: Forward && Reverse select transition B -> condition: !Forward && Reverse select transition C -> condition: !Forward && !Reverse That is exactly the same as: event X -> condition: (time to generate event) && Forward && Reverse event Y -> condition: (time to generate event) && !Forward && Reverse event Z -> condition: (time to generate event) && !Forward && !Reverse where I associate: event X with <unconditional> transition A event Y with <unconditional> transition B event Z with <unconditional> transition C. The only difference lies in whether the condition to be evaluated is split between the point where the condition is raised and the FSM or entirely localized where the condition is raised. However, that distinction has profound affects on maintainability and cohesion. That is manifested in a much simpler FSM achieved at the cost of a more complex condition for generating the event. And the complexity of the condition is mitigated by the fact that all the rules and policies describing the cause of the event are located in one place and that place is where the cause originates.
>>>>One way that is manifested is in the notion of OO announcement messages >>>>(I'm Done) compared to procedural imperative messages (Do This). The >>>>event being generated announces a change in condition /outside/ the FSM. >>>> That is, the event itself is announcing something happening >>>>externally. As it happens that depends upon 'a' and 'b'. But it >>>>announces a compound condition that also includes whatever the caller >>>>did. IOW, the condition being announced is {(a < DFG), caller completed >>>>its part of the solution} OR {(b == GHJZZ), caller completed its part of >>>>the solution}. >>> >>> >>>Or just as likely both. The state machine then has to discard the >>>irrelevant events. >> >>I think ignoring events in the STT is a quite different thing than >>conditional transitions. > > > But it does become a question of efficiency. I've become used to having > more than 16K of RAM available but I'm still not inclined to waste it by > providing a queue most of the elements of which are used to buffer > items that will be generated and then immediately thrown away. That's > w/o mentioning the extra time taken.
This is yet another OT issue. I disagree; the net size penalty of an event queue is minor. It certainly pales in comparison to the size of the redundant infrastructure code built into every one of your FSMs. However, given our existing disagreements I don't want to go here and open up new ones. So let's agree to disagree on this.
>>There is no law that requires one to do >>something in response to raising a condition, so it is valid to ignore >>certain events when the FSM is in a particular state. >> >>The issue here is choosing a transition path from among alternatives >>_when the event cannot be ignored_. > > > What? Choose via condition or choose via condition transformed in an > event, I see no difference in the end result. I do see the condition > code adding a layer of complexity.
All I am saying is that ignoring events is completely orthogonal to conditional transitions. Semantically they are different; the mechanisms for handling them are different; it's apples & oranges. let's not get side tracked on ignoring events.
>>>>More importantly, it includes a context decision that the IF statements >>>>themselves define. Those IF statements implement problem space rules >>>>and policies to determine exactly What should be announced. >>> >>> >>>Beg pardon? I think you are envisioning an entirely different kind of >>>problem than I am. The state machine must be aware of all the inputs >>>(or events) anything else would be unsafe. The only way anything >>>external to the state machine could filter the events/data to a subset >>>would be if it were aware of the state it was in. >> >>The FSM only needs to be aware of events and the relevant input alphabet >>for a transition that is processed by the corresponding action. That's >>all an FSM does: it responds to events by processing the the input alphabet. > > > And? > > >>My point here is when you select a transition you are defining a >>particular state for the FSM (i.e., the target state for the selected >>transition). > > > Of course. > > >>The precondition for entering that state is a compound >>condition composed of: >> >>- the postcondition of the action the generated the trigger event > > > So an event occurred/a condition was met.
>
>>- any data integrity constraints on state variables to be accessed by >>the action > > > ??? I think you are envisioning a different problem set.
The state action usually accessed state variable data. There will be conditions on the timeliness of that data (i.e., it has been properly updated).
>>- the conditions used to select the transition. > > > That's a repeat of the first line.
I don't think so. Whoever generates the event (invokes your "statechart function") has done something that changed the state of the application. That something is characterized by the postcondition of the caller method (i.e., the first line). The event is announcing that postcondition prevails. That postcondition is quite different than the specific conditions that determine which transition to navigate.
>>IOW, the conditions used to select the transition are necessarily part >>of the precondition for entering the target state. If they did not >>evaluate TRUE, transitioning to that state would be inappropriate. > > > The conditions to enter the state must be true or you can't enter the > state. That much does seem obvious. > > >>The primary issue of this whole subdiscussion is whether that >>precondition should be evaluated in one place or in two places. > > > What? I'm evaluating as the check on the transistion. You are > evaluating to generate events. I don't see a difference.
That is only part of the condition. The other part is the condition that raises your generic event. Whoever generates the event has done something to change the state the the application and whatever that is must take place before the precondition of the action is satisfied. That is what I keep referring to above as "(time to generate event)" for lack of more detailed requirements. As I see it, the fundamental issue here is splitting up those clauses of the compound precondition between the caller's postcondition and the FSM's transition conditions. I think that is a very bad idea, which is why I never use conditional transitions.
>>>>The FSM >>>>only responds to the announcement. If one moves the IF statements into >>>>the FSM itself, one is moving that external context decision into the >>>>FSM. That external context decision probably isn't any of the FSM's >>>>business; it should just respond to the decision's /result/. >>> >>> >>>?? That external context is the entire reason for the state machine to >>>exist. Having said that the external context may be abstracted or >>>idealized. The direction input could be a single multivalued input >>>rather than a pair of flags as a for instance. >> >>The state variables being tested /are/ the external context. Forward >>and Reverse or 'a' and 'b' in your examples have their values assigned >>based upon conditions that exist outside the FSM. > > > Yes and that's the entire reason for the state machine to exist is to > evaluate what state to be in as a result of those values.
Once again, picture me jumping up an down screaming at the monitor: no, No, NO! B-) Events are raised by context outside the FSM. All the FSM does is respond to the change in condition. It is not the FSM's job to evaluate whether a change in condition has taken place or analyze the nature of that change to determine what to do. It is the developer's job to provide the mapping of the FSM structure to the external context by assigning events to <unconditional> transitions.
>>They have semantics >>that is defined in terms of that external context. >> >>I am arguing that such external context should be evaluated externally >>(preferably in one place) and an appropriate event generated to the FSM >>based on that evaluation. If one always encapsulates the rules and >>policies for detecting state changes that way in a place that logically >>understands the context semantics, one never needs conditional events in >>the receiver FSMs. > > > You've just called them events instead and now to unserstand the state > machine you have to have the state chart on one page and the definition > of all the events on another. It'll certainly work but for the context > I'm using it in it does seem pointless complexity.
The event just announces the change in condition. Detecting that change in condition requires an evaluation that applies particular rules and policies. The FSM describes intrinsic object behavior that _responds to changes in external condition_. The FSM doesn't need to know and shouldn't know what the criteria were for detecting the change; it just needs to know the change occurred and the event tells it that. Mapping the event to a transition allows the developer to link the intrinsic FSM behaviors to the external context. But that can be done after the FSM has been designed. What pointless complexity?!? The FSM without conditional events will clearly be simpler, if for no other reason than the lack of conditions, and it will certainly be easier to understand the intrinsic sequencing constraints without worrying about the dynamics of state variable changes. The rules and policies for detecting state changes still need to be encoded, but if the change detection is separated from the behavior responses one can focus on each much more easily.
>>>>Finally, when managing complexity it is wise to isolate related rules >>>>and policies via encapsulation. It is highly unlikely that the rules >>>>and policies that determine what event to generate will be logically >>>>related to the rules and policies that the FSM exists to resolve. So if >>>>one moves those rules and policies into the FSM, one is trashing the >>>>cohesion of the FSM. >>> >>> >>>While there is a lot to encapsulation and isolation it is quite possible >>>to take it too far. Data and actions that are tightly coupled should >>>not be separated. >> >>True. That's why one needs a design methodology to draw the lines in >>the sand. The OO paradigm draws lines in the sand based on problem >>space abstraction while Structured Programming drew them based upon >>functional decomposition. >> >>But I would still argue that the rules and policies for detecting state >>changes are going to be quite different than those that govern the >>response to state changes, regardless of what methodology one uses. B-) > > > Condition and action are different. Well yes.
If you have apples and oranges, putting them in different bins will tend to make it easier to manage them. ************* There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl@pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info@pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
In article <lCWnh.1255$Ul4.124@trnddc05>, H. S. Lahman says...
> Responding to Adsett... > > We are repeating ourselves, which is a sure sign of talking past each > other. I took one more pass at clarifying my position, but I think we > are close to the point where we need to agree to disagree.
You are quite right. And on that note I'll not reply to your points. I promise I will further consider your points and over time I may even accept more of them :) I don't think they are completely invalid BTW. FWIW I've had a few of the jumping up and down and shouting at the screen episodes myself during this discussion. :) I would be surprised if everyone else hadn't tuned out long ago though. Maybe our paths will cross again. I wish you well Robert -- From the Divided by a Common Language File (Edited to protect the guilty) ME - "I'd like to get Price and delivery for connector Part # XXXXX" Dist./Rep - "$X.XX Lead time 37 days" ME - "Anything we can do about lead time? 37 days seems a bit high." Dist./Rep - "that is the lead time given because our stock is live.... we currently have stock." -- Posted via a free Usenet account from http://www.teranews.com