Note, this paper doesn't cover hardware. It's assumed there's an object that speaks to the LED hardware and can make it change colors. The design of the serial protocols needed to make this happen may be complicated and interesting, but are not covered here.
Another note, there aren't any diagrams or code. Just a discussion. The fun parts are left as an exercise :-)
LEDs seem simple. You just make them flash colors. What could be simpler? I go by the saying if someone says something is simple they are lying. This is so true for LEDs.
Of course it does depend on the system. If a system just has to blink green when everything is going fine or amber when things are going OK then it should be fairly simple (oh oh, i'm lying again). LEDs however always grow more complex as every nuance of a system's operation must be shown in the LEDs. LEDs are the facial expressions of a device.
People in the field find expressive LEDs very useful as will advanced users. Good LED desing can prevent the use of more intrusive testing equipment and possibly prevent a service call or two. The real reason LED displays get complicated is people can see LEDs which compels them to make changes. If people couldn't see LEDs they would never change :-)
From a programmers perspective your design will start simple and clean. When this or that happens show this or that colour. Then the special cases start getting added. Don't display this if that and the other thing happened. Eventually your original clean design breaks, it can't handle the added complexity. In defense against the inevitable you may want to consider the following approach which is pretty flexible, simple, and robust.
Pretty self evident. The more LEDs you have the more information you can display. As more LEDs drives the per unit cost higher this isn't usually the approach used.
Instead of red and green you could add yellow making for 3 colors. The more patterns you can display the more information you can display. Rememeber no color is an option so even if you have 2 colors you have 3 states that can be displayed. More colours also cost more money.
States are differentiated by a cycle of colours. For exmaple, {red, green} represent one state and {green, red} another. A pattern may be one color displaying solid, one color blinking, no color, 2 colours in sequence, 3 colours in sequence, and so on. Blinking is accomplished by showing a colour for a period of time and then showing no colour for a period of time.
What patterns to choose are up to you. It's best to discuss the colour scheme with people from the field as they will have a good idea of what's important. There are some conventions it seems. Green is good. Amber is not so good. Patterns for important states should be kept short so they are identifiable. Longer patterns are hard to identify.
The time a color displays can vary. Two patterns may have the same color sequence, but the time spent displaying each colour can vary. How long to display a colour takes a while a to tune. It may be too fast or too slow. Users will let you know. It may also depend on the priority your Led Agent runs at. If it's a low priority then trying to implement fast colour transitions won't work.
Factor out absolute time entirely. Make a fundamental time unit for LEDs in terms of which other times are defined. Let's make it 1/4 second for this example. Then we'll define two display periods: short and long. A short time period needs to be distinguishable from a long period. Let's make a short time period (s) 1 time unit and a long time period (l) as 4 time units. A factor of 4 should visually differentiate a short from long colour display.
The advantage of this design is you can change the time unit withought changing any other code. You can easily define more display levels if you think people can distinguish them. And you can structure your code so that you don't need to start and stop timers. Just have one timer run at the fundamental time unit and use an iteration count to track how long a color is displayed.
A pattern is now described: {red(s), green(l)}, which means show red for a short duration followed by green in a long duration.
A pattern can be displayed a fixed number of times or an infinite number of times until interrupted. To indicate repetitions: {red(s), green(l), none(s)} x 2. Infinite is the default.
The Led Agent is driven by events and only events. MIB requests are considered a type of event. No part of the system touches the LEDs except for the Led Agent. Decoupling LED management from the rest of the system makes for a very robust and easily modifiable system. Modules in the system only care about emiting the events they are responsible for, they are not concerned at all about how those events are reflected in the LEDs. Led Agent can make radical changes without any other part of the system caring. It's all in the events.
The problem with this approach is Led Agent grew very complex as it eventually incorperated parts of every other state machine in the system. Don't make this mistake. Assume other modules in the system will only emit events when they should. There's no reason to validate events using a state machine. LEDs should only reflect events in the system.
This was a fundamentally wrong approach and took some time to get out of my head. The problem with that approach was every thing became a special case. For example, some events should only be cleared by another event of a specific type. Other events are transient and should only be displayed for a while. Some event type should cancel the current saved event and become the new current event. And on and on. It becomes very difficult to incorperate any kind of complexity with a current state/saved state approach.
The solution is to introduce the idea of event categories and priority. Always display the event associated with the highest priority event category.
A transient event displays on the LEDs for a set sequence and then goes away. Its purpose is to tell someone something interesting happened. But it's not interesting enough to display forever. Transient has the highest priority because we always want to see them when they occur. They don't usually last for long so other event categories are not obscured. If a transient event is currently displaying then all new transient events are dropped, unless of course you have way to display multiple transient events.
A locked category event is displayed until a clearing event arrives. For example, if a line test starts you want the LED to display that the test is happening. When the test is done an event is sent indicating that fact and the Led Agent then displays the next event. Locked category events are exlusive. Only one can displayed at a time. Other locked category events are ignored. Of course, if you can handle more than one locked category display then modify accordingly.
A preempt category event is a major failure causing all other categories to stop and reset. For example, if your network connection goes down any locked category becomes meaningless. Of course a network down event should have been emitted so all modules could clean up. You may or may not have preempt category events, but they are possible.
A stable category event indicates a phase of the system that may last for some time. For example, booting is a phase that may be considered stable. Stable category events are preempted by any other stable category event. So booting may be followed by initialization which is followed by something else. Stable events may have clearing events. Your last stable event may be cleared leaving no current stable event until some stable event occurs later. The priority based mechanism allows categories not to have current events.
Default maps to the underlying state of the system: online, offline, error, or whatever. It is the lowest priority category and should be displayed if there's nothing else to display. A default event must always exist. It is the event displayed when there is no other higher priority event.
You may want a Sequence object that has:
The event category the event belongs to.
Events that come into the system may have event numbers that are in their own name space and are probably not usable as array indexes. So you may want to create a parallel numbering system that identifies each event such that you can index into an array of sequences and quickly get the descriptor for that sequence. It may seem kind of a waste but it makes the logic simpler in the end.
A sequence contains a colour sequence and the number of time units to display each colour.
How many cycles should the color sequence be run through before it has completed. Allow for an infinite number of cycles.
The current Sequence object is the natural place to maintain indexes for the current colour being displayed, the current time index being displayed, and the current cycle being displayed. The LED paint code can use the indexes to decide what to do next on each timer fire. If the last colour of the last cycle has displayed then reset this sequence and calculate the next highest priority event category to display.
Since all the information on what to display is in the Sequence object then it makes sense for the sequence object to know how to paint itself on the LEDs. It can use a LED hardware object to accomplish the painting.
An incoming event can clear an existing event category.
An incoming event can clear a particular event in a particular event category.
© Copyright 1995-1997. Todd Hoff. All rights reserved.