Design pattern: refresh-based or event-based?

I tried to start a thread

formatting link
on comp.lang.c, without good success. I think I haven't described the problem very well and the guys on clc aren't very interested in those kind of problems. Maybe this group is better. I try to re-describe my question.

Suppose you have a complex dashboard with tenth of LEDs, gauges, values, labels and so on. It could be a full-futured high-resolution TFT display. The values shown on the dashboard come from remote controllers connected through a bus. I think it's a very common scenario in my systems.

When I design the firmware of the dashboard controller, I always have many doubts on the design better to choose: refresh-based or event-based?

Refresh-based is very simple: every 20ms, 100ms, 500ms or 1s a refresh function is called. This function redraws *everything* on the dashboard based on the current values collected on the bus. The thing I don't like with this approach is that it could be time consuming. If the values change slowly, most probably only 1-5 values really change since the last refresh. Why do I redraw everything? I could improve this by defining a variable with the old displayed value for each graphical element:

bool ledX_displayed; if (ledX_displayed != ledX_currentvalue()) { ledX_displayed = ledX_currentvalue(); ledX_refresh(); }

However this method uses a double RAM space (current values and displayed values) and the code isn't so clear.

Another approach is event-based. IMHO, it is very attractive and elegant, because it remembers state-machines. The drawing operations are performed only when necessary.

int X, Y, ...; void bus_rx(const char *msg, size_t msglen) { int new_value; ... new_value = /* X value */ if (X != new_value) { X = new_value; event_handler(X_CHANGED); } ... } void event_handler(enum EVENT_TYPE evtype) { if (X_CHANGED == evtype) ledX_refresh(); else ... }

The event X_CHANGED is triggered by the bus task: when a new value for X is received, it is compared with the current value and, if it is different, the event is triggered (a callback is called).

Even this design patter have some drawbacks. IMHO it is dangerous and error-prone, because you could ignore a certain sequence of events that brings to a wrong state (you will have a wrong value displayed on the dashboard). The startup must be designed with great care. Should a "virtual startup event" be triggered during initialization for each value, so the current/init value is correctly displayed? How to distinguish a "virtual startup event" from a "normal event"? It's important, if I have to make soma actions only if a "normal event" occurs.

What is you preferred method?

Reply to
pozz
Loading thread data ...

In addition to startup, you should have a method of refreshing the whole screen on request (e.g. refresh button) in case of the actual display and the background information are out of synch, eg. due to EMC issues with some garbage on screen.

A clear screen operation followed by the startup time full drawing should do that. Thus the display problems could be handled without restarting the whole system to clear out some garbage on display.

Reply to
upsidedown

The refresh-based design pattern solves automatically this problem, because it continuously redraws the full screen.

Reply to
pozz

It's not really anything more than remembering the current state of each

*individual* display item. I.e., it is many "simple" state machines with a common "conditional": if value is no longer what it was previously, then display new value and remember this value.

So, you're keeping a copy of the "currently displayed" value in "numeric form" as well as in "graphical form".

You can force a "change" simply by ensuring the initial state for every displayed value is "UNDEFINED". So, the first value that you receive for each displayed value is seen as "different" and forces that portion of the display to be repainted.

Your initialization routine simply sends an "update display" event to every registered "display item" (widget, control, etc. -- whatever you want to call them). Part of that process for each display item is a means of displaying "UNDEFINED".

This depends on whether parameters are routinely *reported* (from the "field") or, only when they change.

If the latter, then how do you know what the initial value to "display" should be (in your refresh scenario)? In the former, you are effectively continuously "refreshing" the display -- but at a rate determined by each "control/widget" and the frequency of its reporting event(s).

This can work for or against you. E.g., if a parameter is being reported at a very high frequency, then you are needlessly repainting the display faster than the user can perceive its changes. (i.e., you'd have to add an intermediary filter to present values that "make sense" from the many values coming in).

OTOH, you can choose to update certain portions of the display at different rates. E.g., engine temperature changes much more slowly than vehicular velocity; why repaint the temperature gauge if you know it isn't changing very quickly??

You also have to have a mechanism for ensuring that messages aren't lost; AND, identifying when they *have* been lost -- perhaps causing you to manually query each parameter and synthesize corresponding "events" (i.e., as if the parameters had been

*reported* instead of *queried*).

For example, you may want to demultiplex incoming events to device specific "handlers". Each of these can then impose whatever sanity checks they deem necessary on the *data* AND the *reporting rate*! So, if the "temperature handler" doesn't get a report/event indicating current engine temperature in X seconds (where X is greater than the reporting interval that the *design* was supposed to satisfy), then *it* (the temperature handler) can schedule a "temperature query". Eventually, that query will be passed to the appropriate subsystem and, if all is well, a "temperature event" will be returned.

This will percolate through the demultiplexor to the "temperature handler" (which is the entity that effectively requested it!) and be processed as if it had arrived in the course of normal operation.

If, OTOH, no such message arrives, the temperature handler knowws that something is broke and can take corrective actions (signal "check engine", log an error code to black box, place a reduced limit on the maximum engine speed that can be commanded, etc.)

As well as updating the display, as appropriate!

Reply to
Don Y

Am 21.07.2015 um 10:02 schrieb pozz:

Well, you did post this to c.a.embedded, where we usually assume that some degree of real-time constraint will be in place. In the case of your display update that means the system almost certainly _must_ be able to handle the case where everything _does_ change, anyway, and without breaking the timing constraints. So if you must be able to do it sometimes, it may well be better to just do it all the time, if only to reduce the variation of CPU load caused by it, and to increase overall predictability of the system's behaviour.

The fact that it's a lot easier to do it that way comes an additional bonus.

Reply to
Hans-Bernhard Bröker

Usually interfaces like you describe are implemented with a UI toolkit running under a window system, and those are typically event-based, to use your terminology. Maybe you want to consider using such a toolkit rather than rolling your own. They might seem like code bloat, but there is a huge amount of detail that goes into writing one that can put up good looking UI's.

If you're making a consumer-facing product then you should probably also allocate significant development schedule to tweaking the UI after its basics are working. It takes a lot of iterated small improvements to make a UI really attractive, and that in turn has a big effect on product success (look at how much effort Apple puts into this).

Reply to
Paul Rubin

What does this mean ??

Reply to
hamilton

s.b. "tens of LEDs" -- or something along those lines. (?)

Reply to
Don Y

You do not have to pick either this or that. You can do both. The way I do it is refresh based on an event - at a reasonable rate, say no more than 30 times a second - and do a complete refresh after some time - say, 10 seconds - expires even if there are no events.

Thus you get the best of both worlds at a negligible "over refresh" cost.

Dimiter

------------------------------------------------------ Dimiter Popoff, TGI

formatting link

------------------------------------------------------

formatting link

Reply to
Dimiter_Popoff

It depends a lot on the subtleties of the particular situation. I'll consider the technical issues first - what do you get from the sensors? I note that you have already described them as having their own controllers and connected via a bus of some description so from our perspective they are intelligent units. How do they communicate with the (I presume vehicular) dashboard? Do they generate an interrupt when they record a change or wait to be polled? Can they report their values on demand or do you need to begin a sample process and read the result some time later? What do they report? A raw figure or is it cleaned up first via e.g. some form or hysteresis or majority voting to eliminate noise and unnecessary fluctuations? This is stuff that flavours your thought processes before you even start.

Next consider the instruments themselves and how they are used. In a car the key instruments are probably the speedo and the tachometer and you will probably want to update them as near instantly as you can manage - several times a second at least. On the other hand it doesn't matter if you only update the odometer every few seconds or the fuel gauge every minute.

I'd also consider the nature of the output - if I was designing for a traditional dash where each indicator is independent I would by and large keep each controller equally independent - it potentially allows for smaller, cheaper chips (albeit more of them) but it does simplify the design and analysis of each. They are also potentially more reusable, i.e. the next time I need a speedo for another dash I can copy the design over. Of course, that approach doesn't work for a glass dashboard where everything needs to be unified on the same display.

All other things being equal I suspect my preferred approach for the one big system model you seem to be envisioning would be what I assume you mean by the "refresh based approach" - a big main loop that polls and updates everything in turn. If I then find the key instruments are not being updated quickly enough I may then introduce a system of numbered passes through that loop so that the important stuff gets done every time but only some the less important stuff gets done in turn each pass through the loop.

I'd try to avoid an interrupt based ("event driven") approach if I could help it, and if I couldn't I'd aim for a hybrid approach where as little as possible interrupts and the rest is polled. Interrupts introduce their own complications, especially when there are multiple sources that can trigger at almost the same time, making them more difficult to write and more difficult to properly test.

--
Andrew Smallshaw 
andrews@sdf.lonestar.org
Reply to
Andrew Smallshaw

Wait is this an actual physical dashboard, as opposed to some kind of UI displayed on a screen (often metaphorically called a dashboard)? Hmm.

Event driven doesn't necessarily mean interrupts. There can be some kind of message system, or simple polling of the different data sources, dropping change notifications into a queue that the UI event loop consumes. This is a very traditional way to write a UI and is pretty safe from the usual hazards of concurrency and interrupts.

Reply to
Paul Rubin

So you use a refresh-based (in my terminology, it's a refresh triggered by a regular timer event) method.

Ok, this is a good hint. Anyway this solution is for another issue, the possibility to have wrong pixels on the display (EMC?).

My original question regards the regular refresh of values on the GUI: triggered by a timer (every 100ms) or by an event (when a value really needs to be updated)?

Reply to
pozz

I got your point, but HMIs are't usually hard real-time systems. I could accept a short delay in opening a new screen (completely redraw of the display, think of opening an application on Windows), but I'd prefer to have a responsive interface after this time.

If I don't have a powerful graphical CPU, one solution to improve responsiveness could be to update on the display only the values that really change. Mostly if most of them change very slowly.

Indeed many times I use a refresh-based mechanism.

Reply to
pozz

I use emWin graphical libraries, but I think the situation is similar to other similar toolkit.

Even with these libraries, you can choose an event-based or a refresh-based approach. Yes, they are usually event-based, but this is limited to the GUI: the user press a button and an event is trigger (a callback is called).

Anyway the programmer can choose to update values on the display at a regular rate or when an event (from the field/bus, not from the GUI) is triggered.

I know... I know.

Reply to
pozz

IMHO these questions (and answers) are very important, but not for my original discussion. For each situation you describe, you have a lower-level layer that takes care to collect data from the field. A request/answer is needed to have the last updated engine temperature? It's the lower-level layer that takes care to trigger this at a regular rate. Does the remote controller sends a message with the updated value at a regular rate? The lower-layer level receive and save it in RAM for the GUI (and whatever else). Should the graphical system make some filtering on collected data from the bus? It's the lower-level layer that should take care of this.

At last, I have a clean and updated "image"/status of the remote peripherals in RAM. They are ready-to-use (from the HMI higher-layer) values. And now you can choose whatever you want: refresh the HMI at a regular basis or triggered by an event fired from the lower-level layer when the value really changes.

I'm thinking about a system similar to a "glass dashboard", a regular display. It isn't stricly related to automotive. Think of a building-automation system and the wall-mounted display that shows the situation of the sensors spread over the house (temperatures, human presence, and so on). Another application is an industrial automation system where the operator has a big display with a graphical representation of the current/updated status of the system.

Indeed this is my last decision, after some thinking. However, the event-based approach is simpler (I'll explain in another post in this thread).

As Paul Rubin said, even-based doesn't mean hardware interrupt. Anyway I agree with you, they can introduce complications... but in some cases are useful.

Reply to
pozz

Il 21/07/2015 10:02, pozz ha scritto: > [...]

I forgot one situation where event-based is simpler and mor immediate than refresh-based approach.

Suppose you need to show a small popup in the front of other graphical elements for some seconds when the engine is started (or other event in the system under control). Popups are graphical elements, so they are included in the HMI management.

In the refresh-based method, I have a refresh callback. IMHO it's not immediate to code this kind of popup:

int old_engine_status; void refresh(void) { ... ... update every graphical element ... if ((engine_status() != old_engine_status)) { old_engine_status = engine_status(); if (old_engine_status) { popup_show("Engine is turned ON"); } } ... }

I need an additional variable (the old status). And there is another drawback. What happens if the engine is turned ON and OFF very fast inside a refresh window? There's a (small, not zero) probability that the engine change isn't captured by the refresh function if it is called immediately before the ON and immediately after the OFF.

With event-based approach:

void engine_event(void) { update_engine_hmi(); if (engine_status()) popup_show("Engine is turned ON"); } }

I don't need the old status variable and the code is cleaner. Moreover, I don't risk to loose any event.

Reply to
pozz

Usually GUI's are drawn from a frame buffer at some fixed frequency like

60 hz. Window systems are usually event-based and write to the frame buffer asynchronously from the refresh. 10 hz updates could look jerky to the user.
Reply to
Paul Rubin

I've never used emWin (it does look nice,

formatting link
but I've used various other GUI toolkits and they were all event based. Dealing with refresh within the application sounds like a 1970's video game.

These days if you have a 4" TFT touch screen and a fairly powerful embedded Linux system running X windows and a Python application underneath, the hardware cost will be dominated by the screen rather than the computer. So unless this is a super high volume application or you have special hardware constraints, you should probably use high level tools rather than dealing with the level of detail you're asking about.

Reply to
Paul Rubin

How exactly should this be implemented ? Assume there are more than one event in 1/30 seconds, you should do the full refresh after the _last_ event, not after the first event.

One way would be to scan every 30 ms for any potential signal source, make some internal update for any active events and at the end of the scan, check if there was any active things during the scan and only then, decide if a full refresh is needed.

This is definitively recommend, just check how Windows will have incorrect screens (usually correctable by clicking on a window) and Windows has been out for decades.

Reply to
upsidedown

Don't confuse the low-level refresh of the display pixels and the refresh/updating of the values shown on the display.

The first rate should be higher (60Hz or more) to show smoothly animations, blinking and the like. The second rate could be lower.

Reply to
pozz

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.