|
|
|
This is a brief intro to the current implementation which supports only the SDR1000 and has basic receive functions operational. The target architecture which extends and enhances this function is described here. Node descriptionThere are three nodes in the system at present. Two of these are pure C, built as C-Nodes in the erlang terminology. They use the erlang ei library as opposed to the erl-interface library, ei is the later and preferred library. To the outside world these look like erlang nodes in that a native erlang node can't tell the difference. The two C-Nodes are the application and user interface. Now it is quite possible to write the application in pure erlang and that is on the wish-list. The user interface however is always going to be at least partially in some other language. The switcher node is native erlang and no more than about 50 lines of code in total. Switcher (erlink-sw)The switcher has several responsibilities. I indicated earlier that it could operate in several modes to cater for different types of client nodes. In truth I have not implemented all that yet but it does queue and operate in a request mode. When the switcher is started it spawns a process to act as a register and gives it a well known global name so everyone can talk to it. When other nodes start they register with this process passing several pieces of information such as their own node details and preferred mode of operation. When the registration process receives a request it spawns a gateway process to manage the client node. The response to the request is the address of this gateway process and you can see this announced in the screenshot. The gateway then enters a listen loop for requests from the node it is handling. When a node sends a request it includes the destination. Now at the moment this destination is a node class such as the user interface class and the receiving gateway passes this request off to the gateway(s) responsible for the destination class where the message is queued until the destination node requests the next message. Registered clients, which can be erlang processes or nodes will have a class name, indicating a class of node such as a UI or part of a UI such as a display, the application, a CAT node, a DSP etc etc. which is used in the destination address. Several nodes can have the same class address, for example there could be more than one user interface active, so both would receive the message. In addition when displays are introduced they will definitely be separate nodes and there could be multiple of them active, maybe on different machines. It would be essential that any update for a display was sent to all active displays so a single class name would be essential. Also one does want to add all the possible address to the command line as nodes are added to the system. The class system is just a stop-gap and will not be adequate for the things I want to do. There needs to be a proper routing function otherwise many nodes will receive a lot of messages they can't handle and will simply discard them (wasteful and not very elegant). This needs a design exercise before I can understand exactly what the requirements are and the possible solutions. User Interface (erlink-gtkmain)The current user interface is built in GTK+ using Glade. It consists of very little more than Glade itself auto-creates. Each event proc that is dropped by the designer is augmented with code that creates a data packet consisting of the event and any associated values from the interface widget such as a frequency digit etc. This packet of data is sent to the erlang interface code (which is going to be pretty similar for all C-Nodes) where it is encoded in the erlang binary data format and sent to its gateway. Periodically (using the GTK idle loop hooks) it checks for updates with the gateway. Updates received are then decoded and the widgets updated with the new values. This includes things like creating new windows (e.g. another instance of a watch) and changing the enabled (called sensitivity) state of controls. The encoding and decoding functionality is essentially the same for any UI implementation, just the final update of the controls would be different. It is thus easy to turn out new interface pieces in different languages. The operation of the UI is akin to a dumb browser web application. The UI *never* updates itself, if it did the scheme of being able to have separate pieces of the UI on different nodes and different machines all interacting as if they were the same executable would not work. Application (erlink-sm)To coin a phrase, 'the magic happens here'. Its not very magic though. The implementation of the application is realized as a finite state machine. There are plenty of texts on the subject so if interested a quick Google will give you more information that you can handle. A state machine can be a pretty simple implementation - and mine is - no hierarchical state, nesting or history here because it does not call for it at the moment. Essentially the machine has a single memory, the state it is currently in. Here is a small segment from the so called state-event matrix.
The general idea here is that a single state variable takes away all the 'if then but what if' logic because it only ever allows valid things to be done. In computer speak it is deterministic and much used in control systems for that reason. Each and every action function (the second field in the event) follows exactly the same path so the boiler-plate is set and it's a case of filling in the blanks. Another advantage compared to just coding up the logic is that it's very easy to change the behaviour by adding and removing events and states in the table. Architecture SchematicA slightly more detailed look at the construction.
The same three nodes are shown but with the register and gateways as described above. The main reason for this diagram is to show what's hanging off the application. Lets say straight away that the DSP and HW Control should be separate nodes and that is fairly high on the todo list. In the actions that invoke the DSP there is some conditional compilations that take one of the paths shown. These stacks should be quite familiar to anyone who has played with both OS's. Of course it should not take much to cover OS-X as well but I don't have an OS-X box. The one thing that needs some explanation is the Linux Startup component. This is a piece of Python that uses a Python wrapper over Jack. It performs the start-up and initialisation to a running state of DttSP and Jack and also stops and starts the audio stream, so Poweron/Poweroff does the same with the audio on Linux and Windows. This process is also a good candidate for a separate node.
|