Transition and reaction difference in FSM?

Generally state machines are divided into Mealy and Moore:

formatting link

Once you get beyond that, there are hierarchical state machines, timed automata, etc. It depends on what you are trying to do with the model (generate code, verify properties, both, neither, etc.).

The goal of most modeling frameworks is to allow enough modeling richness that humans can understand the model, and allow enough independence that models can be reused in other systems. That first one is a losing battle--humans aren't good at understanding all possible behaviors of stateful systems.

Generally, all state machines are equivalent regardless of the modeling framework. There are algorithms to convert from various formalisms to various other formalisms.

As far as the construction of software, the absolute minimization of state is usually more important than the modeling framework. There is much to say in that department, but you didn't ask.

State machine design is very important in embedded systems.

------------------------------------------------------------ David T. Ashley ( snipped-for-privacy@e3ft.com)

formatting link
(Consulting Home Page)
formatting link
(Personal Home Page)
formatting link
(GPL Publications and Projects)

Reply to
David T. Ashley
Loading thread data ...

It appears to be implied by your requirement above that the software external to the FSM must know what state the FSM is in in order ro generate the proper events.

And here you lose me again.

1 - Events cause transitions 2 - Transistions change states 3 - Changes in state are annouced via events 4 - See step 1

No, it's a periodically executed action. It performs actions, I don't think I've ever done one that polled input.

A quick aside on timing. The transistions may in some abstract sense be instantaneous but in most of the state machines I write the transitions from state to state are 'clocked' in the sense they can take place only at certain regular intervals. This clocking is vital to the correct behaviour.

Let me provide a simplified example. Consider a motor control with the following inputs: - Forward (on or off) - Reverse (on or off) - Throttle (0 to Max)

From these and domain knowledge (which I leave out for brevity) we end up with a simple state chart to describe the drive control signal that will be sent to the drive control process.

State 1 - Stop Waiting for a proper direction signal. The throttle must be zero and both direction switches off before exiting this state. Only one transition allowed which is to neutral when the previous conditions are satisfied. On Entry Action -> DriveRequest = 0;

State 2 - Neutral Waiting for direction. Multiple possible transistions On Entry Action -> DriveRequest = 0; Tr A - ((Throttle > 0) || (Forward && Reverse)) Back to Stop. State 1 Tr B - (Forward && !Reverse) To Forward. State 3 Tr C - (!Forward && Reverse) To Reverse. State 4

State 3 - Forward. Multiple possible transistions During Action -> DriveRequest = Throttle; Tr A - (Forward && Reverse) To Stop. State 1 Tr B - (!Forward && Reverse) To Reverse. State 4 Tr C - (!Forward && !Reverse) To Neutral. State 2

State 4 - Reverse. Multiple possible transistions During Action -> DriveRequest = -Throttle; Tr A - (Forward && Reverse) To Stop. State 1 Tr B - (Forward && !Reverse) To Forward. State 3 Tr C - (!Forward && !Reverse) To Neutral. State 2

Now I suppose you could take each possible throttle setting as a separate state but... I would argue that the during action doesn't actually cause any change of state, it is essential to proper operation though.

Let me pose another simple example. Some states need to be reflected to the operator in an explicit fashion so for a state that needs this I might have the following: On Entry Action -> SetIndicatorLamp(); On Exit Action -> ClearIndicatorLamp();

Also, rather than setting a lamp this might be something essential to operation like turning on lubrication or cooling.

Now this could be simulated with multiple states and transitions but this introduces timing issues for clocked machines (there are workarounds) and expands the number of states for IMHO no apparent good reason.

Ahh, maybe I see. A during action doesn't imply a background execution process. It implies an action that takes place on each cycle of the state machine. See my note about clocked execution earlier.

Robert

--
Posted via a free Usenet account from http://www.teranews.com
Reply to
Robert Adsett

Responding to Adsett...

The software external to the FSM does not even have to know the FSM exists. The event is simply an announcement of something that happened externally. It is the job of the developer to determine who cares (i.e., which FSM) about what happened and address the event properly (e.g., at the level of a UML Interaction Diagram).

Your step (3) is essentially what I am talking about. When the state changes in a particular FSM, that manifests a change in the overall state of the solution. So the event generated in (3) is an announcement of that change.

However, the link between (3) and (1) is achieved by addressing that announcement to an FSM that cares about the change. That mapping is driven by the overall solution to the problem (e.g., algorithmic sequencing or data integrity issues for state variables) rather than any inherent expectation of what should happen next _in the action for (3)_.

Note the the underlying rules for FSAs stipulate that a state cannot know what the prior state was or the next state will be. So the action in (3) cannot anticipate what transition in (1) will be triggered (or even what FSM that transition lives in).

OK, but I think my same arguments apply so long as those activities occur /while/ the object in some state.

Typically that sort of periodic behavior is handled with timers. The timer generates an event that triggers a transition in some FSM and the action executes the periodic behavior. Thus each execution of the periodic action is triggered by an individual event and there is a transition of the FSM (albeit usually reflexive).

[Note that in OOA/D the timers, like event queues, are not even explicitly modeled. They are assumed to be implemented in some architectural subsystem.]

That's fine, but I think it is a separate issue that one handles with timers and explicit events/transitions in the FSM.

The Mealy/Moore issue is about the ambiguity of what state the FSM is in while the action is executing. It doesn't make any difference because we have other rules, such as an event queue only popping one event at a time, that ensure the ambiguity is transparent in practice. IOW, the infrastructure around the FSM ensures the software behaves exactly _as if_ the action execution were instantaneous.

I am reluctant to pull a Topmind and start critiquing the FSM for reasons unrelated to the point in hand (using "during actions"). Instead, let just say I think I would do it differently... B-)

Apropos of my point above, I think I would want an FSM where one of the events signaled a change in throttle because changing the throttle setting seems like it it quite important to running a motor. I am also suspicious that the notions of 'forward' and 'reverse' can be completely captured in object attributes rather than FSM states. That's because the direction and throttle settings are really just register writes of values. IOW, I think When one is allowed to change direction and throttle is more important to the solution than the specific values. So I would probably abstract an object like:

[Motor]
  • direction
  • currentThrottleSetting

with an FSM something like (assuming the motor must stop before changing direction when running):

E3:ThrottleChange +-------+ | | | V [ThrottleChanged] >I have a similar problem with exit actions. If they do something

Not necessarily. I think a lot depends upon where one allocates the intelligence in the overall solution. One can imagine

| | E1:toggle V [IndicatorChanged] >As it happens I can accept exit actions in an OO context because object

Then I'm now confused. B-) What do you mean by "each cycle of the state machine"? In particular, what is actually going on in your FSM where you have:

During Action -> DriveRequest = Throttle

How does the During Action get invoked if there is no event triggering a transition? (I assumed it was some asynchronous mechanism like a callback method being invoked, but that invocation would be independent of the FSM cycle.) What exactly is a Drive Request? (I assumed it was some attribute.) What does During Action actually do? (I assumed something more than just setting the DriveRequest attribute, since that would just be an orthogonal knowledge setter.) How do you ensure that this During Action is not invoked when the FSM is in some other state?

************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

Beg pardon? 'One is required to "follow" those transitions in the software external to the FSM'

OK, but that appears to be inconsistent with 'events arrive when the FSM is in the proper current state'. Maybe you meant something different but that reads to me as the events the FSM recieves depends on the state it is in, and the 'required to follow' reads as the external SW is reponsible for ensuring that the proper filtering and sorting is done to ensure the proper arrival of the events.

Um, yes, of course. But what has that to do with the original statement?

Methinks we have a language gap of some sort.

So there's more than one FSM involved? That changes things. If you are generating events in one FSM to be consumed in other separate FSMs then in that case all you are saying is the output of an FSM is useful.

I'm confused again. What mapping are you talking about?

Say what?

Yes it could be simulated by a reflexive transistion. If it makes you any happier think of it as a reflexive transition that skips any on- entry or on-exit actions. I see no way that it is any different. It just leaves the diagram a little cleaner.

Not at all, that would clutter up the FSM with timers on every transition.

One of the issues with that is the throttle changes continuously. This sort of state machine usually executes one cycle per control loop. A throttle changed event would fire every cycle. It's a lot simpler to just view it as a continuous input.

Ultimately everything can be completely captured with attributes and calculated as needed. A state machine just captures some of that information in the state. The key is whether the state machine captures the intent in a simpler and/or more robust fashion. Of course many of those constructs will just be state machines developed in a more opaque fashion.

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.

Forward and reverse are distinct, it's tempting to treat them identically but there is usually an assymetry in speed or some other associated behaviour. You could treat forward and reverse the same but IME it's easier to disentangle them here rather than later. And once you treat them separately they really are distinct states. There is duplication only in the degenerate case.

There is not a lot of distinction visible in this example between neutral and stop but they are different. Neutral is a normal operating state but stop is a fault state from which the operator is required to return to a specified starting setup before continuing. I'll try to make the distinction clearer in the modified example.

Hmm, I don't think I was clear enough here. Actually I wasn't clear at all, my apologies. This FSM describes the evaluation of the control signals not the motor control. The actual motor control is done elsewhere.

Hmm, I'm not sure it does. It looks closer to my neutral state. but I think I simplified my neutral state too much.

Purists didn't much like Dirac deltas when they were introduced either. That didn't prevent everybody else from using them to advantage though. Not that I'm claiming any of this approaches the usefulness of a delta function though. :)

?? The transitions are still conditional, they still rely on the events occuring. I see no difference between your conditions and the conditions I used other than a layer of syntactic sugar. You are also missing start sequencing and the direction interlock both of which are critical.

This reworking partly works as long as the directions are symmetrical but it also moves a fair piece of the logic outside the state machine making it less clear what is happening. You have removed the during action but at the expense of implementing it elsewhere. It's now more difficult to tell if the result is correct.

It's certainly possible to implement FSMs without during actions. It's also possible to implement procedural code without for, while or do loops. That doesn't mean the constructs are not useful though.

Doesn't that add another state machine where previously there was one? It also changes an absolute set/clear to a toggle. Something I'd be reluctant to do without a lot bigger benefit than getting rid of exit actions.

Caareful about your assumptions ;). It's just meant to be an assignment statement. The value is then fed on to the actual motor control which could be another state machine or a D/A setting to another controller entirely or ....

I don't understand the question, how could it get invoked if you were in another state?

Let me revise the example a bit to include asymmetry in the directions as would be typical. I'll also walk through a couple of iterations so you can get an idea of how the during actions work.

State 1 - Stop Waiting for a proper direction signal. The throttle must be zero and both direction switches off before exiting this state. Only one transition allowed which is to neutral when the previous conditions are satisfied. On Entry Action -> DriveRequest = 0; State 2 - Neutral Waiting for direction. Multiple possible transistions On Entry Action -> DriveRequest = 0; Tr A - (Forward && Reverse) Back to Stop. State 1 Tr B - (Forward && !Reverse) To Forward. State 3 Tr C - (!Forward && Reverse) To Reverse. State 4 State 3 - Forward. Multiple possible transistions During Action -> DriveRequest = Throttle; Tr A - (Forward && Reverse) To Stop. State 1 Tr B - (!Forward && Reverse) To Reverse. State 4 Tr C - (!Forward && !Reverse) To Neutral. State 2 State 4 - Reverse. Multiple possible transistions On Entry Action -> SetReverseAlarm(); During Action -> DriveRequest = -Throttle/2; On Exit Action -> ClearReverseAlarm(); Tr A - (Forward && Reverse) To Stop. State 1 Tr B - (Forward && !Reverse) To Forward. State 3 Tr C - (!Forward && !Reverse) To Neutral. State 2

Note 1: The reverse alarm setup is more naive than would actually be used (by a fair amount) but it keeps the example simpler.

Note 2: I've modified the neutral state to more properly reflect the difference between it and the stop state. In a less naive example the neutral state would be a little more complex.

The statechart is implemented as a function, each call check for valid transitions and performs them before returning. The values checked and returned will be in the argument list, I'll leave the details out.

Now to walk through. The state machine is already initialized to be in state 1

Time=t0 Conditions - Forward and Reverse are false, throttle is 0, DriveRequest is 0 (from entering state 1 some time previously) call to statechart state changes to state 2 DriveRequest is set to 0 returns

Time=t1 Conditions - Forward is false, Reverse is true, throttle is 0 call to statechart state changes to state 4 DriveRequest is set to 0 call to SetReverseAlarm() returns

Time=t2 Conditions - Forward is false, Reverse is true, throttle is 500 call to statechart DriveRequest is set to -250 returns

Time=t3 Conditions - Forward is false, Reverse is true, throttle is 490 call to statechart DriveRequest is set to -245 returns

Time=t4 Conditions - Forward is true, Reverse is false, throttle is 310 call to statechart call to ClearReverseAlarm() state changes to state 3 DriveRequest is set to 310 returns

etc...

And of course this could be implemented in just about any language C, ASM, ST or even as a method in an OO language.

Robert

--
Posted via a free Usenet account from http://www.teranews.com
Reply to
Robert Adsett

Responding to Adsett...

It wasn't until I got to the end that I realized most of this thread is a tempest in a teapot and we have been talking past on another. You might want to skip to the end an read my closing comments first.

In addition, I am suspicious that you are not developing in an OO environment despite posting to comp.object (where I live).

As I explained in the description of applying DbC, the developer can decide where events go /after/ designing the object FSMs. So one does not need to know anything about that when designing an individual object FSM. Hence the individual object FSM is independent of context and does not need to know the receiving FSM even exists.

What I was referring to with the "follow" remark was that object FSMs are not designed in a vacuum. Objects are abstracted from the problem space with one eye on the problem to be solved. That is, the object abstraction is tailored to the needs of the overall solution by determining what knowledge and behavior responsibilities must be abstracted to solve the problem in hand.

The OO paradigm provides methodological guidance for how to do problem space abstraction (e.g., defining logically indivisible behavior responsibilities) that ensures that once the object is properly abstracted, an asynchronous behavior communication model will Just Work if one uses object state machines.

For example, handshaking is an ancient notion in R-T/E. So the traditional way of designing interacting FSMs was to define handshaking protocols for synchronization and then build the FSMs around those protocols. But what those protocols were really doing is providing a systematic approach to ensuring a functional granularity that was detailed enough so that one could connect the flow of control dots with messages. The OO paradigm essentially already provides that when doing problem space abstraction.

You lubrication vs. cooling example demonstrates that. The OO paradigm dictates that they are distinct responsibilities that need to be encapsulated separately and decoupled. That leads to associating each one with its own state so they are triggered by different messages. In turn, that guarantees a granularity such that one can synchronize the flow of control by connecting the dots with individual events using DbC (with a little serialization help from the event queue).

I submit that is the only way a rigorous DbC approach to flow of control can work. The event cannot depend on what is going to happen; it can only depend on what it is announcing that the sender did.

As for the synchronization with current state, that is what DbC provides in the OO paradigm (or handshaking protocols in non-OO paradigms).

I was responding to your assertion:

"It appears to be implied by your requirement above that the software external to the FSM must know what state the FSM is in in order to generate the proper events."

I was trying to explain why that inference is not valid.

Purists would argue that self-directed events are illegal; that all the events triggering transitions must be generated outside the FSM. In addition, the events must be synchronized in some manner with the inherent sequencing rules of the FSM transitions. So there are almost always at least two FSMs. In an OO application if FSMs are used at all, then every object having behavior responsibilities will have an FSM. (In translation methodologies the use of FSMs is mandatory for all applications.)

The mapping between the event generated in (3) and what transition it triggers in (1).

In an FSA the state action can only process the input alphabet. IOW, the only thing the state action can understand about context is the provided alphabet.

You can only do that with a Mealy model because the action depends on the transition rather than the state. As I indicated, I think the Moore model is better suited to the OO paradigm. Since Moore and Mealy models are convertible, though, one could always convert the Mealy to a Moore (albeit possibly at the cost of an extra state). So one can get to the same place without "during actions".

My argument here is that if the "during action" is triggered by something other than a transition, then one is asking for trouble because one cannot rely on the usual infrastructures like event queues to ensure data and referential integrity "for free".

The timers aren't in the FSM. They are architectural mechanisms that are orthogonal to the FSM. In an OOA/D model they wouldn't even appear explicitly; they would be manifested in external events that trigger the transitions in the FSM.

In addition, one only uses the timer to provide the sort of periodic triggering that you described. Then the event condition raised is based on some duration of clock time elapsing. That is completely independent of the dynamic sequencing within the solution. So one needs an orthogonal mechanism like timers to deal with it.

You mentioned "throttle settings". To me that implies a finite number of discrete values. And in your example below you employ discrete values.

I am also unsure what you mean by 'cycle' and 'control loop' here.

In any event, I don't see how you can view it as continuous changes unless there is a direct hardware link between the physical throttle control and the motor -- in which case the software is irrelevant. Software is digital, which means that it is inherently discrete. In the end a software controller for hardware can only read and write hardware registers at discrete intervals. So one can only emulate analog (continuous) hardware processes with some variant of time slicing and that infrastructure needs to be built into the software explicitly.

[I've done hardware controllers for analog devices and it is a real pain. The hardware has to provide the D-to-A conversion. Even then, trying to provide the required granularity is tough to do. So much so that I would be very tempted to put the throttle's continuous emulation (i.e., the infrastructure needed to slice it) in its own object with its own FSM and leave stuff like direction changing to somebody else who is looking at the Big Picture.]

My issue is: why can't the incoming signal be changeDirection rather than an explicit Forward/Reverse couplet? The only problem space rule that seems likely to be relevant is that the motor needs to be in Neutral when the direction is changed. The direction itself is just a data element that the hardware interprets. The fact that it is stored in a hardware register rather than a memory location doesn't prevent it from being viewed as an object knowledge attribute that some other object sets.

OK.

I agree, that is a big difference. B-) Alas, without more words around what "evaluation" means in your problem context, I can't speculate on how I would eliminate the "during action".

There are no conditional events for my Motor abstraction. The events are raised in other objects that understand when and why, say, a direction needs to be changed. It is up to the developer to connect the flow of control dots via DbC to provide the necessary synchronization unconditionally.

For example, Let's say object A has an action, doIt, that issues the E3:changeDirection event. The precondition for transitioning to the A::doIt state is that Motor is in the Stopped state (neutral now). That condition is announced by generating, say, the E52:inNeutral event when the Motor enters that state. The developer then matches the Motor::neutral postcondition to the A::doIt precondition and addresses that event to A's FSM. That handshaking synchronizes the FSMs.

I don't buy that argument because one is managing complexity through separating concerns and decomposing functionality into more manageable chunks. Providing better maintainability, of which comprehensibility and verifying correctness are important aspects, is the raison d'etre of the OO paradigm. Providing simple, highly cohesive, and context-independent objects is crucial to achieving that. In addition, DbC provides a quite formal and rigorous technique for ensuring correctness.

However, I think the real issue with "during actions" is that they invite problems because they violate the underlying FSA rules. If the "during action" does anything at all useful to the solution, it is actually changing the state of the solution, as manifested by the FSM state. So long as that can happen at any time while the FSM is in a given state, one has opened a Pandora's Box of data and referential integrity problems that are far worse that any delegation of responsibilities among multiple objects. IMO, a "during action" completely trashes the primary value of using FSMs in the first place: providing a disciplined framework for execution.

I don't think so. I am replacing your state having entry and exit actions with state that has only one entry action. The difference is that instead of two different events in sequence to turn ON and then turn OFF (one transitioning to the state and the other transitioning out of it), one sends two toggle events that both transition to it.

[ed. Here is where we really diverge. It becomes clear later that what you meant by "during action" and what I inferred were two very different things.]

But then it is nothing but a knowledge setter and has no business being in an FSM. One uses object FSMs to describe behavior responsibilities. Knowledge and behavior responsibilities are completely different worlds because knowledge accessors are always synchronous in OOA/D while FSMs are inherently asynchronous.

By your description the "during action" is an action that is invoked while the containing FSM is in a particular state. My understanding of "during" is that the action gets invoked at any arbitrary time within the time interval that the FSM is in that state.

Since it is not triggered by an FSM event, someone external to the FSM must invoke it. That invocation mechanism is orthogonal to the FSM invocation mechanism (triggering transitions). Therefore it would seem possible that whoever that is could be invoking the action asynchronously without regard to or knowledge of the current FSM state. So my question is: how do you make sure that external entity only invokes the method when the FSM is in a particular state?

OK. AFAICT, this a function takes arguments of {isForward, isReverse, throttle} and uses them to set the value of DriveRequest and, optionally, invoke two other functions for ReverseAlarm. To do that it needs to understand the history of prior invocations, which is summarized in a value for current state.

If so, then I don't see any "during action". There is only one "event" and it is always the call to the function. The only thing that changes are the values in the data packet. That "event" is dispatched to the appropriate "call to statechart" method for each state based on the value of current state and the data packet values. Then the value of DriveRequest is always set on entry to the current state using the rules and policies that prevail for that state. IOW, this seems to be an ordinary Moore FSM with only entry actions (i.e., the individual "call to statechart" method for each state).

All you are doing is including the STT dispatch mechanism that would usually be in external supporting infrastructure (i.e., the event queue or static class-level lookup) in the function call. The state actions are the individual methods for "call to statechart". The body of those methods, including setting DriveRequest, is executed all at once on entry to the state.

BTW, I would argue that this is not an FSM at all. It is simply a single synchronous service that uses a state variable to record history. That is, "current state" really represents a state variable for context that is computed from the prior context variable value and the current input values. The calls to ReverseAlarm are just ordinary conditional message generation based on the state variable values. IOW,

new context value = F(last context value, isForward, isReverse, throttle).

is a single deterministic computation. Because it depends upon the input alphabet values rather than just {event ID, current state}, it isn't a true FSM.

I could easily implement your function as a single procedure without distinct "action" methods. [That's not a criticism, BTW. Using FSM-like reasoning and structure provides good discipline. I am just arguing that (a) it isn't a true FSM because current state is computed from the input alphabet and (b) it would be an ordinary Moore FSM even if it were.]

************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

Sigh I had a detailed reponse but it just got lost. Maybe we're better off :)

If you check you'll see it's cross posted to c.a.e. Why the original poster crosposted this way I don't know but I've been using this discussion to try to learn from your perspective. I figure the sig I'm adding may be appropriate though.

Um, it's not vs, They were separate examples.

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.

Agreed, in fact in some cases it's not a direct input.

OK, seems an odd way to view it to me but I can see the equivalency.

OK, although I suspect the denizens of c.a.e would recognize both as state machines. I could be wrong on that though the participants cover quite a broad spectrum.

I've done it that way myself. I switched to an explicit state appraoch for the reasons you suggested. Also to make it easier to explain andverify the logic with people who are not familiar with progrmming languages. A visual statechart is a little more obvious, unfortuanately it's only a little.

I have enjoyed this BTW, hopefully it's not been too much of an annoyance to you or anyone else.

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."
Reply to
Robert Adsett

Responding to Adsett...

?? You combined them for one state condition. I was using 'versus' to argue against that precisely because of the apples/oranges thing.

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".

Thus in your example one could change the function arguments to {directionIsToggled, throttleSetting}. Then the function logic checks directionIsToggled and the currentDirection state variable's value to decide what to do. The result would be just as deterministic; only the IF conditions would change.

Hmmm. I thought I was just mapping what you provided, so let me expand on my analysis of the example...

By your description (i.e., the statechart is implemented as a single function) the function call is the event message to the FSM from the external world. Since there is only one function call and it always has the same data packet, there is only one event that triggers all transitions. That's unusual for practical FSMs but not illegal; one only /needs/ different events if there are multiple unconditional transitions out of a state. (In your example multiple exiting transitions is handled by conditional events.)

In your example, you identified a "call to statechart" whose body did different things depending on what the state was. I assumed those were the methods for individual state or transition actions since all the business rules seemed to be contained in them. Those methods mapped uniquely to states since you associated each one with a state number. In addition, there were no other methods doing anything (other than the transition dispatch in the function itself).

Those methods seem to be the only FSM actions in the example AND they were explicitly associated with a particular state AND they were called after a transition had been selected but before the 'current state' was set. So they had to be executed while entering the state. Consequently the FSM has to be a simple Moore FSM.

Since DriveRequest is set in the body of those actions rather than in some other method, I don't see any "during action". That is, Drive Request can only be set if an FSM event is consumed (i.e., the statechart function is called) AND it is only set after a transition is selected to a specific state AND the action seems to be a Moore action. So I don't see any other interpretation.

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. IOW, you are combining the event queue and STT in the single function that initially consumes the event.

In addition, the dispatch is based upon the value of 'current state' and the values of the input alphabet (event data packet). In a true FSM the input alphabet is irrelevant and the dispatch is based the value of 'current state' and the event ID. That is, conditional transitions are an overlay to a pure FSM that explicitly employs state variables, which don't exist for FSAs.

FWIW, with regard to conditional transitions, I am a purist and don't regard them as true FSMs for two reasons. One is that they aren't necessary, as my model for Motor demonstrated. In three decades of mucking with FSMs I've never used one. (That is the main thing I meant when I said I would do the FSM "differently".) IOW, I see value in keeping the notation simple even if is occasionally causes the developer some slight inconvenience.

Back in the '80s there was an OO guru who showed up at conferences with papers proposing enhancements to the OOA/D notations. Invariably these were special cases of existing notation mechanisms that were tied to specific problem semantics. His enhancements were elegant and they made the problem space intent quite clear. But the OOP would be implemented exactly the same way with or without his enhancements. That is, the existing, more generic notational elements were just as complete, precise, and unambiguous in specifying what the application had to do.

To me he was missing the point of having an OOA/D notation. In the end one is specifying what the application needs to do, so correct specification is pretty much a given. OTOH, an OOA/D notation deals with a complex conceptual subject matter so correct OOA/D models aren't all that easy to construct. It is generally recognized that simplicity aids communication and has value in managing complexity. Thus one wants a notation that is simple and easy to understand _in order to get the specification right_ because one does not want to be distracted by the complexity the notation itself. So one doesn't want several different ways of specifying the same thing that can cause confusion.

A classic example was COBOL. COBOL was universally vilified because it was verbose and yet until the RAD IDEs and infrastructures of the '90s the vast majority of the world's software was written in COBOL. That was despite the fact that languages like PL/I and C offered lots of elegant ways to solve the same basic problems. Why? Because it was simple. The language was verbose only because of the number of keystrokes needed to type in its constructs. But the constructs themselves were quite unambiguous and remarkably simple semantically compared to languages like C or PL/I. So COBOL allowed the big IT shops to employ acres of entry level developers while shops using tricky languages like PL/I and C had to hire experts and there are a lot more journeyman developers than experts.

[Grace Hopper is still one of my heroes because she got it so close to right in the 2nd 3GL ever invented. Tough lady, too; she made Admiral in an era when Navy women were supposed to be secretaries and nurses!]

The second reason has to do with the scope of data integrity. When a state action messes with state variables the scope is well defined as the method boundary. In the OO paradigm this is further augmented because methods are self-contained and messages rarely have data packets since methods are expected to access the data they need synchronously on an as-needed basis. Under those conditions the method boundary is a very convenient scope for managing data integrity, especially in concurrent environments (i.e., providing blocking semaphores, pausing threads, etc. is easy because of the bounded scope). [It also helps that in stack-based hardware architectures one gets some scope management "for free".]

However, when selecting the transition to a state depends on state variable values, one is in another ballpark because accessing those variables is outside the scope of the target state action. In effect one needs to manage two different scopes for data integrity while consuming an event and one of them is not conveniently bounded by the call stack.

As a translationist I am especially sensitive to this because a full code generator needs to optimize around this sort of thing. If the scope of data integrity is not limited to the scope of a single method, it tends to significantly impede any optimization. I submit that the same complexity that impedes optimization is also a potential problem for future maintenance.

[Commercial code generators tend to ignore the problem if they support conditional transitions in UML Statecharts. IOW, they assume the developer will make sure the tested state variable is correct in all circumstances where an event can be consumed. But that just puts the optimization problem in the developer's lap and adds the resolution of nonfunctional requirements to the developer's problems.]

I'm retired now so I've got time on my hands. B-) In addition, after many moons in the business I find that I have an oversupply of opinions that I need to get rid of anyway, so I welcome the opportunity.

************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

No, at least that was not the intent. I was presenting them as examples of behaviours that could be tightly coupled to a machines operating state.

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.

You need more information.

I'm not disagreeing it just seems an odd viewpoint.

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.

STT?

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.

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);

You've lost me completely. The data passed in is valid for the call. Sounds like it's bounded by the call stack to me.

If you are referring to the inputs then how is that any different from any other function or method call? Indeed unless you are passing in the values via reference how would the issue arise at all?

In that case, glad to take up your excess time :)

Robert

--
Posted via a free Usenet account from http://www.teranews.com
Reply to
Robert Adsett

Davy escribió:

IIRC, reaction is the name given by Rhapsody's creators to internal transitions. An internal transition is one that causes an action without leaving a state.

Yes.

Reply to
Ignacio G.T.

Responding to Adsett...

The operative word is "a" [machine's operating state]. B-) In the context I was arguing that /because/ they were quite different things they should not be associated with the same state as entry and exit actions.

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:

| | E1:prepare V [Prepared] | | E2:start V [Running] | | E3:cool V [Cooled]

To do that surgery must be performed to the [Prepared] state implementation.

Now suppose we recognized cooling as a distinct suite of rules and policies in the /original/ state machine:

| | E1:prepare V [Prepared] | | E3:cool V [Cooled] | | E2:start V [Running]

To convert to the new state machine for the new requirements, all we have to do is update the STT and, perhaps, change where the E3 event is generated. IOW, we can implement the change at the message flow of control level without touching the [Prepared] state implementation.

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.

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 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. 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. (Typically the exception handler is going to have to do other things, like flushing the event queue, that the FSM itself should not be concerned with.) However, this is getting pretty far OT.

I guess it depends on where the observer is standing...

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 action as a direct result of consuming the event that triggered going to the state.

State Transition Table. 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? This is a very basic acronym for FSMs and almost all FSMs are implemented using one for efficiency. 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. Personally I regard the GoF State pattern for FSMs as a misuse of the pattern, but let's not go there.]

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. It just did it with different conditions (states), events, and state variables.

I think the condition is only one step removed. 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).

Having the condition decoupled from the action is actually an advantage for maintenance. (One can argue that the entire OO paradigm is designed around the separation of message and method to eliminate the hierarchical dependencies associated with procedural imperatives.) So long as the state actions are cohesive and self-contained, one can easily connect the flow of control dots via DbC using event generation.

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. 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.

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.

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. 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 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.

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}.

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. 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/.

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.

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.

Sorry, I was thinking about the more common situation where conditional transitions are based upon the values of persistent state variables rather than values in the event data packet.

[I am biased because in OO applications one rarely sees data in event data packets. Data integrity is easier to manage if the method accesses attribute data directly on an as-needed basis since there is potentially an arbitrary delay between when an event is generated and when it is consumed in the asynchronous execution model. So pretty much the only time one sees data packets is for situations like sensor readings where all the values need to be taken from the same time slice.] ************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

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.

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.

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.

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.

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.

Almost always :)

I understand. I'm just suggesting that like a for loop is a glorified goto, the syntactic sugar is useful.

Ah, I thought I'd seen the acronym before.

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.

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.

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.

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.

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.

Exactly as in the case of using a simple conditional check.

And how are the constraints I'm using less invariant than events?

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.

OK so why wouldn't conditions based on those same values be well constrained?

No more than events do.

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.

Who's modifying transistions dynamically?

Or just as likely both. The state machine then has to discard the irrelevant events.

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.

?? 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.

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.

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."
Reply to
Robert Adsett

Responding to Adsett...

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.

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.]

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.

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.

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.

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.

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. But the last 200 or so messages were triggered by the notion of having a "during action". B-)

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.)

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-)]

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 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-)

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.

You forgot the last step: select a transition. That is equivalent to generating an event below.

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 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 problem lies in who owns the condition check (more precisely, who owns which pieces of a compound condition check), per the point immediately above.

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 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.

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.

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.

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.

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.

You are any time you modify the state variable used to select a conditional transition during the life of the FSM.

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_.

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 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.

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-)

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 snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

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.

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.

I thought I'd already pointed out that I wasn't considering them dependent, that they were two different examples.

Absolutely, but I don't see that creating an additional state machine is any more error prone later rather than sooner.

OK, but this example is identical to the lubrication example you are objecting to.

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.

Yes

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.

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.

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.

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.

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.

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.

They are, however, the wrong ones.

That's exactly what it is doing, responding to changes in the application state by switching states.

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.

That's what it's doing.

I also indicated you got there from start. And in neither case does it indicate there is anything wrong with the software.

That's just what's happening here.

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.

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.

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.

You missed some behaviour.

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.

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.

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.

Which may make sense (probably does) in another environment, here it just obscure the logic and adds overhead.

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.

They are, into condition and action :)

Methinks we are dealing with differnt issues of scale.

So do the events.

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.

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.

It is so limited. It's limited to the single call of the function doing the statechart evaluation.

The same is true of the conditionals I've used.

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.

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.

And with the conditionals one always knows what path will be taken. You seem to be arguing against something that isn't occurring.

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.

So would the events in your case.

The events also bleed the same context in.

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.

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.

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.

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.

And?

Of course.

So an event occurred/a condition was met.

??? I think you are envisioning a different problem set.

That's a repeat of the first line.

The conditions to enter the state must be true or you can't enter the state. That much does seem obvious.

What? I'm evaluating as the check on the transistion. You are evaluating to generate events. I don't see a difference.

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.

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.

Condition and action are different. Well yes.

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."
Reply to
Robert Adsett

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.

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.

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.

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 transition event itself.)

[I am beginning to suspect the real issue in this subdiscussion is application partitioning. More on that below.]

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.

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.

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.

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.]

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.

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...

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 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.]

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.

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 it would be more obvious what the nature of the problem is for conditional transitions.

I don't think the conditions and what they map to are at issue. To me the issue is where they should be evaluated.

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.)]

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.

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 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.

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.

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.]

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 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.)

But they are still tightly coupled in the same subject matter (module, object, problem space abstraction, or whatever).

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.

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.]

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.

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.

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...

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.

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.

In this case it is also given that you have event in hand; whatever transition is followed must be triggered by an event.

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.

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 transition A event Y with transition B event Z with 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.

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.

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.

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).

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.

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.

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 transitions.

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.

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 snipped-for-privacy@pathfindermda.com Pathfinder Solutions

formatting link
blog:
formatting link
"Model-Based Translation: The Next Step in Agile Development". Email snipped-for-privacy@pathfindermda.com for your copy. Pathfinder is hiring:
formatting link
(888)OOA-PATH

Reply to
H. S. Lahman

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."
Reply to
Robert Adsett

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.