This component of the Polly system consists of a piece of software built from a number of modules all written in C. It includes its own simple operating system which is non-preemptive and based on message passing, and each area of functionality has its own module which receives and acts on messages.
1. Platform History
The original server software ran under DOS on a PC and performed reliably for many years (Including a period of over 500 days of continuous running which was only ended when I accidentally unplugged the wrong mains plug!)
In 2006 I ported the program to run on Linux, again on a PC.
In 2013 the platform was changed to a Raspberry Pi.
2. The User Interface
This is one aspect of the Polly system where its long history counts against it, and the user interface has a very "retro" style and its inner workings make porting to other interfaces (such as a smartphone) difficult.
Back in 1993 the system ran on an old 8086-based PC which didn't even support VGA if I recall correctly. I used the standard 25 rows of eighty characters terminal display. Fortunately colour was available. In order to get reasonable performance the software wrote directly to the memory on the display card, bypassing the BIOS. This was changed to use BIOS calls when I moved to a more powerful (386 I think) platform, and when porting to Linux the BIOS calls were replaced by use of the ncurses library.
So the user is presented with a colourful but crude screen with a command prompt at the bottom, and all interaction consists of typing a command and seeing the results displayed.
2.1 Headless Operation
During the DOS era, the user interface was displayed on the monitor of the PC running the server software, which became mildly inconvenient when this was relegated to the airing cupboard!
Once I had moved to Linux, it became possible to have the display remote from the server. In a clever (or was it just luck?) bit of design during the early development of the software, I had limited the control of what is on the screen to a very small number of procedure calls, so it was fairly simple to make these procedures send messages over a socket connection to a display client, which in turn performed the same action that the procedure used to do.
I decided to allow more than one display client to connect simultaneously, but this is not a multi-user system, as all displays show exactly the same thing, to the extent that if one user types a command other users can see it being echoed in the command line. If two users type at the same time you can imagine what happens!
3. Message Passing
The individual modules within the Polly software communicate with each other using two methods. Sometimes, for the sake of efficiency, a procedure call is used but usually communication is in the form of messages. Each message has a name, a destination module, and zero or more bytes of contents. Messages are queued by the operating system and the message at the front of the queue is passed to the appropriate module for processing.
To give a simple example, a motion sensor detects motion in a room and via one of the input/output methods advises the hardware module. Configuration data for the hardware is used to translate this signal into a "logical input" value. The hardware module then queues a message for the general control module. The message says, for example, "input n has changed to 1". When this message reaches the front of the queue the operating system will pass it to the general control module. Here a decision is made as to what action should be taken. If the house is occupied and it is dark outside and the light in the room where the motion sensor is located is off then obviously we need to turn the light on. The general control module queues a message for the hardware module saying "Change output m to 1". When this message in turn reaches the front of the queue it will be passed to the hardware module which will determine which piece of hardware deals with this output and will arrange for the output to be changed, causing the lamp to come on. Obviously this all takes a lot longer to describe than it does to actually happen, and we are talking about a fraction of a second for the whole process to be completed. Although I have written "when the message reaches the front of the queue", in practice this will normally be next time the queue is examined, because there is rarely more than one message in the queue.
If procedure calls are more efficient, why not use them for all communication? If we look at the example above we can see the hardware module would call the general control module which would then call the hardware module. While it is certainly possible to design the software to handle recursion I took the decision that it would be easier to ban it. Using message passing also keeps the modules thoroughly isolated from each other, and if a difficult bug is being investigated the code has a debug mode where all messages are recorded in a log file as they are passed.
4. Module Structure
Each module is responsible for a specific function, such as central heating control, audio system control, etc. etc. The code of each module includes as a minimum a procedure which accepts messages passed from the operating system queue.
4.1 Module User Interface
If the module requires a user interface (Almost all do.) then this is activated by the user entering the module name into the main screen. The OS Module interprets this command and sends an Activate UI message to the module. The module then takes over control of the display and writes whatever it requires on the screen. It also advises the OS that subsequent typing by the user should be passed to this module, so it can act on user commands. One of these commands will be QUIT and when the user types this the module will relinquish control of the display and advise the OS it is no longer interested in the user's input.
Within this structure the module can have more than one page of display, with commands to change between the pages.
4.2 Module Configuration
Each module will have zero or more configuration files stored on disc. These fall into two types as shown below. Of course, the exact requirements vary for each module.
4.2.1 Fixed Configuration
These configuration files are edited outside of the program, and are read when the system starts up. This is obviously less than ideal but is acceptable for data which will change rarely, or for data which will probably only change when new software is being added. For example, a list of ETHPIC nodes and locations to configure the ETHPIC module: If I add a new ETHPIC node I am going to need new software to deal with the new node, so the system will inevitably be restarted to introduce the new software.
4.2.2 User Configuration
More changeable data (What time the heating comes on, etc. etc.) can be altered by the user from within the program, and is recorded in configuration files so that it is preserved if the system is restarted.
On the original 1993 platform, the hard disc (20 MB, and pre-dating the IDE interface if I remember correctly.) was excruciatingly slow, and I had to take special measures to allow for the fact that saving a configuration file caused the whole system to stop for half a second or so! Because of this, modules written at that time require the user to enter a command to save their configuration changes. More recent modules are less "efficient" and write to disc as soon as each change is entered.
4.2.3 Unsecured Configuration
This is a third category of configuration settings where the information is not stored on disc, so in the rare event that the system is restarted, the setting will be lost. For example, if the heating boost function is selected the central heating will be turned up for a short period of time. If the system is rebooted the boost will be cancelled. Obviously it would be possible to record the boost and its end time in a configuration file and re-apply it after a restart, but in a system where the designer is also the (lazy) coder, compromises have to be made!