Embed Linux in Monitoring and Control Systems

Your VMC class will inherit from QmainWindow, so your constructor will be defined in VMC.cpp as shown here:


 VMC::VMC() : QMainWindow()
 {
 //declare a central widget to host our screen:
 QWidget* gCentralWidget = new QWidget(this);
 setCentralWidget(gCentralWidget);
//Set fonts, colors, geometry, etc
       - - - -
//Declare an object to hold the screen features:
ScreenC cScreenLayout = new ScreenC(); 
//Lastly, breathe life into the application
cHeartBeat = new QTimer (this);
connect(cHeartBeat, SIGNAL(timeout()), this,\
                          SLOT(slotPaintScreen()));
cHeartBeat->setSingleShot(false);
cHeartBeat->start(50); //milliseconds/20Hz
}

That is an abridged view of the constructor, but the actual code isn't much longer. The connected routine slotPaintScreen() will be activated on a 50 millisecond interval by the timer overflow. It too is brief:


//Fetch loop characters gathered by COMthread
SensorLoopService(); 
//Update the display
cScreenLayout->Update(); 
//Redraw the screen
update();

(Again abridged because this is a story about how to do it rather than how to code it.)

The central portion of Figure 1 shows the cascade of object creation that will embody the VMC application. Notice the declaration of a ScreenC object by the VMC constructor and the update of that object at a 20Hz rate.

The ScreenC class constructor simply declares a ScreenItemC object for each entity that appears on the screen. A typical declaration is:


pF[i] = new ScreenItemC(xOff,yOff,xSiz,ySiz,\
                  MEAS_TACHOMETER, 0, "Tach");

Here you define the location and size and name the object type of each on-screen object. At update time "update" is simply relayed to its children like this:


//Update screen features
for (i=0; i<cNumberFeatures; i++)
{
 pF[i]->Update();
}

The ScreenItemC class constructor is responsible for the look of items on the screen. In this application, a ScreenItemC item consists of two QLabel objects placed one above the other so as to appear to be a single instrument. The form of a QLabel declaration is:


QLabel cReading = new QLabel(gCentralWidget, Qt::FramelessWindowHint);

The instrument displays of Figure 2 are pretty "plain Jane". It is here in the ScreenItemC class code where you can fancy it up. The ScreenItemC constructor also declares a MeasureC object. That object's update routine returns the data that the ScreenItemC object places on the screen:


MeasureC cMeasure = new MeasureC(MEAS_TACHOMETER);

The MeasureC class is where the hardware interface is described. HIP address, register numbers and scale factors are defined. For example:


case MEAS_TACHOMETER:
{
fScale = 27648000.0;  //29.75Hz -> 1788rpm
      // RPM = fScale / binary from loop + fOffset
fOffset = 0;
rule = MEAS_RULE_RECIPROCAL_TACHO;
DeviceId = NODE_E;                  //Loop device id
DevicePort = P_IC_PERIOD_2;  //Sensor on device
//Create a sensor for the measurement
pSens = new SensorC(MEAS_TACHOMETER,\
    DeviceId, DevicePort, fScale, Offset, rule);
break;
}

Notice the declaration above of a SensorC object. At update time, that SensorC object will fetch its most recent raw reading from the loop buffer, scale that and return the result to its MeasureC parent, which will relay that back to its ScreenItemC parent, which will display that result on the screen. The MeasureC items that represent a keypad key will declare a ContrtolC object here. The ControlC object will use its own SensorC object to inquire of the loop if its key is the most recently pressed. ControlC objects also run device-specific code (like timing a blinker, for example). The ControlC object may place commands on the loop as necessary. The ControlC update routine will return 1 or 0 depending on whether its control target has changed state or not. That return flows back up the cascade to its grandparent ScreenItemC object and then is reflected on the display.

This cascade of object creation ends with SensorC objects that return the result of their previous request to the loop and issue a new data request at each update time. As ControlC objects may place commands on the loop at their whim, the loop will have a mixture of independent commands circulating that must be resolved back to their originator. When a command is issued to the loop, the issuer of that command also inserts into a class visible circular buffer a pointer to itself.

As mentioned above, slotPaintScreen() will call SensorLoopService() at each update time. SensorLoopService() extracts characters that have been placed into the loop receive buffer by the gCOMgo thread. Mutexes are used here to prevent interference by other threads. SensorLoopService() parses the characters as it fetches data from the buffer, and when it has detected a complete valid message, it places the four data bytes into a location pointed to by the pointer mentioned above. This data will be returned up the cascade at the next update time.

Here it is in fewer words: the update event cascades down from the ScreenC object to multiple SensorC objects that bounce parameter states back up to ScreenItemC objects that paint those states on the screen. The left panel of Figure 1 depicts this.

Linux Environment Considerations

Some kinks that Linux throws in include the screen saver that defaults active, but is bad news in a monitoring application. To turn it off, go to System Settings→Power Management and disable all Screen Energy Saving options. Another issue is automatic software updates. It is my consideration that if something works, don't screw around with the operating environment, as software updates do. The safest way to suppress updating is by staying off the Internet while your application is active. Another way is to disable updates by software control. To do so, go to the Application Launcher (lower left on the desktop), and start the System Settings from Favorites, go to Software Management and left-click the wrench icon at the upper-right edge. Select Settings from its menu. In the General Settings page, set the Check for updates menu to Never, and "Apply" that. Also, go to /etc/yum/pluginconf.d/refresh-packagekit.conf and set enabled to 0 (disable update). For me it was just too easy to switch off the Wi-Fi when I wanted a stable environment, so I can't give you other advice here.

To claim credit as being an "embedded" application, this system should come up with the power—that is, without login or any other user input required to make it go. To kill the login, go to /etc/kde/kdm/kdmrc and set AutoLoginAgain=true and AutoLoginUser=YourUserName. To bring up your application with system start up, go to ~/.kde/Autostart and place an executable script there like this:


#!/bin/bash
cd /home/YourUserName/projects/VMC/build
./VMC

Serendipitously, this will not bring up multiple instances of the application if it was active when you last powered down, and you have your system set to restore the previous session at power up.

With a man in the loop, the VMC application is not time-critical at all and may take its share of CPU time whenever it is offered. There is a lot of other stuff in a Linux system that also wants CPU time (look at ps -A). If your application is time-critical with predetermined response times at close tolerances between events, this scheme will not work for you. However, if you have a few milliseconds here and there to spare, Linux will host your monitoring and control applications with a reasonably small level of effort and good reliability.

______________________

Rick Brown is a US Navy veteran, holds a BSEE granted in 1970 by the University of Florida, developed atmospheric research instruments as a faculty member of the University of Nevada System, and consulted in the private sector as a developer.