Re: Lahman, how ya doing?



Responding to Hansen...

Bottom line: one has to think about the simulation as-if it were real-time when dealing with synchronization, delays, etc. The closer the simulation structure reflects the real-time structure, the easier that will be. For example, the real-time system is proactively triggered by timer events. So don't have the tasks poll a timer and then make a decision about what to do. Construct the software so it reacts explicitly and unequivocally to the events that the timer generates just like the real system. Then you isolate the problem of generating the "real-time" events as a simulation implementation problem for the Timer object rather than as part of emulation abstractions.


There's also the matter of what resolution is needed. In the real system, sometimes an event like adjusting the control is more than 1/60 second late. But not 7/60 seconds late. And the thermal time constant of the target is around 14 seconds, much longer than any delays in the control loop. The data I've taken fits well to a model second-order control system, and in general I've seen no reason to believe that processing lags have any interesting effect, so I think the ideal computer approximation will do.

The actual system is a real-time hardware control system. As such, its software will be event-based and the software processing will mostly be done via interacting state machines. There will be <at least one> event queue manager program unit and a Timer program unit that gets ticks from the system clock and generates events to other program units. That's just basic R-T/E design.


My point in this context is that the closer your simulation models that actual software, the better and more robust it will be. In fact, if that controller is well-formed OO you should be able to reuse much of the software in the feedback loop with, at most, only cosmetic changes (e.g., the Thermometer object reads an attribute in the simulation rather than an instrument hardware register).

There are only two things that make your simulation fundamentally different than the real system: (A) you must calculate the value of temperature that the thermometer "reads" and that computation may trash real-time absolute simulation because of its complexity; and (B) instead of writing to the heater, beam, shutter, and other hardware on the other end of the feedback loop you need to provide inputs for the calculation in (A) for the next cycle, which also may take time that would trash a real-time simulation. IOW, everything between reading the hardware and writing the hardware should be exactly the same; what you are actually simulating is the hardware itself.

The finite execution time for the simulation infrastructure computations probably means you can't do an absolute real-time simulation. Somehow you have to spread out the events to accommodate those computations. One way is to simply scale the time in the Timer (e.g., 50 clock ticks = 1 simulation (event) clock ticks). Another way is to not use the system clock at all and just trigger the next simulation clock tick with an event to the Timer that is generated when all the processing for a single <simulation> clock tick is done.

Either way, that is just a matter of how you implement the Timer for the simulation. That should be completely transparent to all the rest of the software. Thus you have to ensure is that the events will be generated by the Timer in _exactly the same order_ that they would be in the real-time controller timer. All of your calculations on temperature samples, statistics, filtering, smoothing, etc. should be completely indifferent to the way the events are generated. As far as they are concerned they are being done in real-time to the system clock. IOW, all of you simulation software -- except the hardware emulation itself -- /is/ a real-time simulation and it should work exactly like it does in the real controller. That's what I meant about the problem being a real-time simulation; it should work exactly the same way, down to being event-based.

I also need jobs that do task 1 at time interval 1, and task 2 at time interval 2. But it won't be hard to add a

time.add_task(&c3, 5, 20);

At the risk of being picky, doesn't this do the /same/ task twice?


No. In the system I want to simulate, there's a sampling time of, say, one second, and an averaging time of, say, 60 seconds. Once per second it takes a data point and calculates a running total and total^2, and once per 60 seconds it calculates and reports an average and standard deviation, and zeroes the totals.

OK, I thought the tasks would be in different objects. So the c3 object has a state machine like(?):


                    E2:sixty ticks
+--------> [Sampled] --------------> [Statistics Computed]
|            |    ^                          |
|            |    |                          |
+------------+    +--------------------------+
E1:one tick               E1:one tick

and all you are doing is registering it to receive both the E1 and E2 events from the Time object. If so, then it might be simpler to just register via

time.add_task(&c3, 1);
time.add_task(&c3, 60);

since the Time object shouldn't care about duplicate handles; it just needs to know where to send the indicated event. Then you don't a special implementation in Time for this case.


Yes. Except it has to know that the first line refers to a sample and the second line refers to an averaging. I can think of not-so-clean ways around that, like a third parameter that specifies which task, or a counter internal to the object which then wouldn't need a trigger for the second event. Or maybe make every event a double even with a third parameter 0=off by default. I can't think of a satisfactorily clean way.

It doesn't matter. What you are telling the Timer is that it should generate the E1 event every 1 second and send it to &c3. It should also generate the E60 event every 60 seconds and send it <serendipitously> to &c3. The Timer does not and should not know anything about the semantics of how the events will be interpreted by the receivers, who the receivers are, or why the receivers care about the events.


As a practical matter you probably want better naming conventions for the events and you would parameterize that as well. B-) IOW, you register the receiver, the event to generate, AND the tick count interval.

You kind of get that for free if you use a single event queue and popping an event doesn't return in the event queue manager until the task is completed. If, though, the timer event is only one of several events that may be issued to complete tasks, one needs a different mechanism.


What I've done so far, I don't think you could really call an event queue. Each task keeps a wait_time and a next_event, and executes and updates when polled with a time >= next_event. Since it's a simulation, I don't think it's really sensible to have an event queue. It's not going to be waiting for a user to decide to click the mouse or anything, except externally to the simulation.

If you have a timer at all it is easier to make it event-based. Then you don't have to write any conditional code or have history attributes. The serialization is essentially free, as in the 1, 60, and 7/60 event case above. At most you may have to have a prioritization scheme. The Timer will be a simpler and more generic as well. And the event queue manager will be entirely reusable (the target &cN is passed in the event).


I guess I'm not sure whether I'm event based. I gave a little more detail elsewhere, but as I have it set up now, when timer.add_task(&c1,5) is called, a Task internal to Timer (the user doesn't directly interact with it) is created, and is given the Block pointer and the timing information. Every time timer ticks, each Task is told the current time. The Task decides whether to trigger. When it triggers, it sends the Block the elapsed time since the last trigger, e.g. ptr->evolve(dt).

This is exactly what I am arguing against. The controller is /inherently/ event-based. Think of it as a major R-T/E design pattern. Decades of pain and suffering have determined that the best way to implement this sort of hardware controller is making it event-based. In that sort of design the sequencing of operations is determined by the order in which events are generated and who consumes them. Your simulation software should emulate that.


The recipient of an event does its thing immediately with no debate; consuming the event is the trigger. Doing that thing was properly placed in the overall processing sequence by the ordering of the events so there is no decision for the receiver to make.

In this particular case making it event-based actually makes the simulation a lot easier. You describe a significant infrastructure in each Task just to decide if it should do something. That goes away completely if Timer generates events and the Tasks just respond. All of the decision making is done in registering the events with the timer because that is where the ordering can be defined as well (i.e., Timer generates events for the same time tick in the order that they were registered). That also makes it trivially easy to change the order of processing -- you just reorganize the registration statements. So here the event-based view is much cleaner, simpler, and easier to maintain.


That sort of seems event based since each Task is really just sitting there waiting for a kick in the pants, and each Block is sitting there waiting for a kick. But I guess I don't know the language very well. The timer still calls each task, one by one. I suppose it's not really an event queue, anyway.

Event-based processing is a methodological mindset; its about the way one thinks of collaborations. One defines the overall solution sequence by determining where events should be generated and where they go. The receivers have no explicit decision making; they just execute their responsibility when triggered by an event.


The corollary is that events are announcements (I'm Done) and they are sent to whoever cares about that (i.e., has a response for the announcement). That decouples what the sender is doing from what the receiver does. That happens to be one of the key things that distinguishes OO methodology from procedural or functional methodologies where sequences are chained together by imperatives (Do This). It just so happens that the rules of finite state machines coincide very nicely with fundamental OO practice.

Consider the other side of the coin. Suppose one has designed with object state machines and an event queue manager. If there is nothing inherently asynchronous in the problem (which there doesn't appear to be in your case), one could do a pure synchronous implementation at the 3GL level. To do that all one needs to do is replace push calls to the event queue manager with direct method calls to the state action that would respond to the event. That eliminates the event queue manager and if one looks at the resulting 3GL code it might be difficult to tell the OOA/D had been done with state machines at all, much less that event-based processing had been used.

One could provide the methodological discipline for decoupling, encapsulation, etc., etc. without using event-based processing or state machines. But then the onus is on the developer to avoid falling into the Do This procedural/functional paradigm that tends to create implementation dependencies. If one does the OOA/D with an asynchronous communication model, one is forced to Do The Right Thing. That's why all the translation methodologies that do full code generation from OOA models /always/ use object state machines and event-based processing in their models even when it isn't an R-T/E subject matter.

In your case, the subject matter /is/ R-T/E so you have even more reason to think about the problem solution in event-based terms.

Note that one can even change the role of the Timer from doling out events based on clock ticks to a arbitrator. Suppose you have an Activity Diagram with multiple dependencies among tasks like

Task1       task2      task3
|           |          |
+----+ +----+          |
     | |               |
    =====              |
      |                |
    task4              |
      |                |
      +-------+ +------+
              | |
             =====
               |
             task5

Now the Timer can manage a suite of semaphores for the tasks. It starts off with one task, say task2 and sends it a trigger event. When task2 completes, it sends back an I'm done event to Timer. Timer then sets the task2 semaphore and and checks its rules (each task has a bitmap of semaphores that must be set for it to execute). It selects some task whose prerequisites are done, say task3. And so on... When all the semaphores are set, Timer clears them and starts over.]


Not that I don't appreciate the insight; I'm reading it and thinking of things to do with it. But I'm looking at a system more like

                beam
                  |
heater --- thermal block 1 --- heat link --- thermal block 2 --- He bath
                  |
              thermometer

There isn't a time sequence of events, every part of the system evolves simultaneously. Heat goes from the heater to thermal block 1, heat goes from thermal block 1 to the heat link and to block 2, the temperature and resistance of the thermometer change as the heat load changes, all at the same time. So at a given instant of simulation time I just freeze the state and determine how much heat goes into and out of block 1, how much heat goes into and out of block 2, how much does the temperature of the thermometer change, etc.

That's fine, but I don't think it is relevant to the sampling timer issues. B-) As I understand it you are computing what those actual transfers to the He bath would be in order to simulate what the thermometer would actually see (i.e., the simulated temperature will be the from the net residual heat in thermal block 1 after the heat flow is computed). IOW, you don't care about the propagation delays on the right because all you want is the heat out of the 1st thermal block.


The simple way to march those calculations is to just daisy-chain them.


Daisy-chaining makes me nervous. I'm not really sure what you mean by it.

Remember I am talking about the calculations you add that emulate the hardware, particularly the computation of the temperature value. Since they are mathematically defined by thermodynamics much more general than the context of this problem, I would be inclined to put them in realized code that I did procedurally or functionally outside of the OO development (e.g., in a function library or a reusable package). Procedural/functional techniques are more intuitive and tend to be more terse than OO solutions, especially for algorithmic processing. Since the algorithms don't change there is no maintainability issue once one gets them working. In that world literal, hierarchical daisy-chaining of imperatives is the normal operating mode. B-)


From a more philosophical perspective, all software solutions are daisy-chained. The tricky part is to do it without creating implementation level dependencies, which is the OO goal. The tools for that are things like encapsulation, implementation hiding, peer-to-peer collaboration, and separation of message and method in OOA/D. So in OOA/D we daisy chain by sending messages from one object ot another on a peer-to-peer basis. If one looks at a UML Sequence Diagram, that is exactly what it describes -- connecting the behavior dots in time via passing messages. (Note that events are pure messages.) However, in OOA/D defining collaboration messages to connect the dots is usually the last thing we do (after we define objects, responsibilities, relationships, state machines, behaviors, etc.) while in procedural/functional development message and response are not separated at all.

If you feel the computations are complicated enough to warrant distributing over multiple objects and you want to do an OO solution for those computations, then I would suggest tucking it in a subsystem where you can address that problem alone. In that subsystem you may or may not choose to use events for communication. But that subsystem would still just return a temperature value when it was done and that would be encapsulated in a single method in your software the simulates the control system.

I imagine Blocks representing elements of the system, and they can be connected together in any sort of pattern, with loops and interconnections all over the place. Change any little thing and it's hard to maintain one iteration. Or maybe you mean the blocks are just arranged in a queue, and my code

  for (ptr = list.begin(); ptr != list.end(); ptr++)
    (*ptr)->timeis(t);

would be replaced by

  ptr = list.begin();
  (*ptr)->timeis(t);	// rely on first object to tickle second, etc.

This is close to the event-based approach. The most rigorous approach to state machine design is a variation on DbC. To execute a state action some precondition must be satisfied. That precondition will prevail as a result of executing some other state action. IOW, the precondition will match the postcondition for executing the other action. So once the state machines are designed, one could look at each action's precondition and find the action whose postcondition matches. One then generates the event in the action with the matching postcondition. Thus we "daisy-chain" the overall solution flow of control by "walking" the chain of precondition/postcondition matches to define the events.


[Things are complicated in OO development because there is another form of "state": state variables or attributes. So the precondition for executing and action is compound. There is usually some other action that must execute -- the traditional algorithmic sequence. But any attributes that the action reads must also be current. So the precondition also includes conditions on data integrity. That is usually the tricky part in connecting the event dots and sometimes we have to deviate from the "use case" view of successive steps to make sure the data has been modified properly as well.]

But back to this particular context, I was envisioning a traditional procedural functional decomposition Do This hierarchy that was explicitly synchronous.


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

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions  -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH



.



Relevant Pages

  • Re: Lahman, how ya doing?
    ... >real-time when dealing with synchronization, delays, etc. ... >the simulation structure reflects the real-time structure, ... >triggered by timer events. ... Heat goes from the heater to thermal block 1, ...
    (comp.object)
  • Re: Lahman, how ya doing?
    ... There will be event queue manager program unit and a Timer program unit that gets ticks from the system clock and generates events to other program units. ... My point in this context is that the closer your simulation models that actual software, the better and more robust it will be. ... Another way is to not use the system clock at all and just trigger the next simulation clock tick with an event to the Timer that is generated when all the processing for a single clock tick is done. ...
    (comp.object)
  • Re: Lahman, how ya doing?
    ... >> target is around 14 seconds, much longer than any delays in the control ... >queue manager program unit and a Timer program unit that gets ticks from ... the Thermometer object reads an attribute in the simulation ... executes at a particular time, ...
    (comp.object)
  • Re: Havent done anything real with OOP yet.
    ... >> separate timer. ... >We are back to the issue of simulation strategy definition. ... Sampling time is a property of the controller; ... >objects do nothing different in response to their triggers either way. ...
    (comp.object)
  • Re: Lahman, how ya doing?
    ... A time tick in the simulation can occur at arbitrary intervals in real time to accommodate software processing. ... the rules and policies for what /must/ happen and the order in which it must happen on a given tick are exactly the same for a simulation tick as for a real-time tick. ... Heat goes from the heater to thermal block 1, heat goes from thermal block 1 to the heat link and to block 2, the temperature and resistance of the thermometer change as the heat load changes, all at the same time. ...
    (comp.object)