Now we can proceed on our journey with the confidence that we have done everything possible to extract information from our customer. The customer in turn should have some confidence that the project about to be received will make him or her truly thankful.
So, onward.
Progress was rapid, but now changes are having effects on the stability of the software, plus the customer design review threw up a few surprises . Anyway code-fix, code-fix, code-fix. |
Now we shall concentrate on adding functionality to the framework of components that we've identified, or that our pattern has identified for us. For the sake of simplicity, let's assume that the framework of components produced from the first principles agrees closely with the framework defined by the patterns. |
Looking closer at the hardware pattern, we've already defined the top-level commands for the component, so now we'll go deeper. Each hardware action is a combination of Control, Drive, and Read. There may be no need for all of them, but at least one will be required. For instance, a simple thermometer would only have the Read component, whereas a scanning thermometer would have a combination of Control and Read.
Here's the implementation for this project and a reminder of the actions for this component.
It transpires that the power is switched on to all the units at the start of the test and removed at the conclusion of testing. This means that two extra hardware actions are required to apply and remove power to the units.
So, here are highlights from the source code.
Action | Description |
---|---|
UnderRange | Throws or passes error |
Initialize System | Sets up all the hardware components |
Switch STDs In | Connects the standard units to the selected test position |
Reset Switches | Clear connections |
Read Unit Status | Read the selected unit's Status from the serial port |
Read Unit Serial Number | Read the selected unit's Serial Number from the serial port |
Read Unit Part Number | Read the selected unit's Part Number from the serial port |
Read Unit Software Version | Read the selected unit's Software Version from the serial port |
Read Chan1 Reading | Read the selected unit's reading for channel 1 from the serial port |
Read Chan2 Reading | Read the selected unit's reading for channel 2 from the serial port |
Get Chan1 An Out | Measure the selected unit's channel 1 analog output |
Get Chan2 An Out | Measure the selected unit's channel 2 analog output |
Get Chan1 Display Reading | Get the display reading for channel 1 |
Get Chan2 Display Reading | Get the display reading for channel 2 |
Power On | Apply power supply to units under test |
Power Off | Remove power supply from units under test |
OverRange | As UnderRange |
Looking at Figure 9.14 you can see that Test Position is a Strict Type Def. enumerated type that has already been defined for the prototype and is used throughout the software. Hardware Actions is a new Strict Type Def. enumerated type that has all the actions that were defined earlier.
Hardware Actions has been set to "required" as a precondition, and a standard wiring pattern has been defined, as shown in Figure 9.15 and 9.16. All components will follow this pattern where possible.
The block diagram looks like that shown in Figures 9.17. The first time through the hardware is initialized .
The UI screen control is sent a message describing each action that has been taken by the hardware component. The hardware action is then implemented. The actions that talk to the serial ports are all pretty repetitive and involve sending the message and the unit position for that message to the read component. This will then do all the communicating and return a string or numeric value. The measure action is a bit more involved; it requires a bit of switching to select the unit under test.
Clearly demonstrated here is the self-documenting nature of LCOD ”you can see exactly what each component is being asked to do.
Programming the Get Chan2 An Out action was simply a matter of duplicating the Get Chan1 An Out case and changing the command to the control component as shown in Figure 9.18. The Power On and Power Off commands use the drive component and simply set the power supply volts to 24 or 0 as shown in Figure 9.19.
The hierarchy in Figure 9.20 shows the Control>>Drive>>Read pattern. Below the generic Switch, DMM, and PSU components would be the manufacturers or your own drivers. For continuity we tend to rewrite them as components as well.
It is envisioned that the measurement and signal lines are switched from the units under test to the computer and measurement equipment.
Action | Description |
---|---|
UnderRange | Throws or passes error |
Init Control System | This initializes the control hardware, getting and setting identifiers and setting initial conditions |
Reset Switches | Set all switches open |
Switch Standards In | Switches the standards into the chosen position |
Switch DMM to Chan1 | Switches the DMM to channel 1 of the chosen position |
Switch DMM to Chan2 | Switches the DMM to channel 2 of the chosen position |
OverRange | As UnderRange |
Here's the implementation.
So regarding Figure 9.21 we will define Control Commands as a Strict Type Def. enumerated type and use Test Position as in the hardware component.
As we go to lower levels of abstraction we can see the commands becoming more oriented to the hardware. This is demonstrated by the states in Figures 9.22 and 9.23 that are now less identifiable as part of a test system and more specific to switching systems.
Drive is used for signal injection, PSU setting, and similar actions. For this project there is a requirement to switch on the power supply to the Widgetometer.
Action | Description |
---|---|
UnderRange | Throws or passes error |
Init Drive System | This initializes the drive hardware |
Set PSU Volts | Sets the power supply volts current |
Get PSU Volts | Returns the power supply volts |
Get PSU Current | Returns the power supply |
Clear PSU | Sets the power supply to a safe level, 0 Vdc |
OverRange | As UnderRange |
The front panel, as shown in Figure 9.24, is a command Strict Type Def. enumerated type, with just the voltages in and results out.
The rest of the states are the same, as shown in Figure 9.25, with corresponding messages to the PSU component.
There will be a multimeter for analog input measurement. There will also be the commands to talk to the serial port. Finally, we'll put the visual inspection stuff in here as well.
Action | Description |
---|---|
UnderRange | Throws or passes error |
Initialize Meas System | This initializes the measurement hardware |
Read Unit Status | Read the selected unit's Status from the serial port |
Read Unit Serial Number | Read the selected unit's Serial Number from the serial port |
Read Unit Part Number | Read the selected unit's Part Number from the serial port |
Read Unit Software Version | Read the selected unit's Software Version from the serial port |
Read Chan1 Reading | Read the selected unit's channel 1 reading from the serial port |
Read Chan2 Reading | Read the selected unit's channel 2 reading from the serial port |
Get Chan1 Display Reading | Get the display reading for channel 1 |
Get Chan2 Display Reading | Get the display reading for channel 2 |
Measure Analog Out | Returns the dc volts from the DMM |
OverRange | As UnderRange |
Let's quickly look at some of the implementation of these actions.
Figure 9.26 shows the Read component front panel, where once again we use the ubiquitous Test Position Strict Type Def. enumerated type, and as before we define a new Read Commands Strict Type Def. enumerated type with the commands defined earlier.
Nothing too tricky about this. The read component merely calls the visual, serial, or DMM components and returns their responses as shown in Figures 9.27 and 9.28.
The only other action of note is the Initialize command, which is shown in Figure 9.29.
Finally, you need a test executive, so that is discussed next .
The array of hardware actions drives the For Loop for each active test position. In reality, the results would go to a file or a database. The more observant reader will have looked at Figure 9.30 and noticed that the array "Tests" is an array of constants. Correct! Although they are dynamic Strict Type Defs., they are indeed constants and would be better placed in a file.
If you see a constant String or Number anywhere in your code, pull it out into a configuration file. We use a pattern for this that takes advantage of the flexibility of enumerated types.
Make a copy of the Prototype Software Directory on your hard drive and rename it Widgetometer Software Directory. Now you need to copy the configuration directories over to where the program resides. So copy the following directories to your Widgetometer Software Directory:
....\Patterns\Section Keyed Data Handling Pattern\1 Displays
....\Patterns\Section Keyed Data Handling Pattern\2 System
If this has been done correctly, you will find that the following message will be displayed as shown in Figure 9.31.
Select [Yes to A ll].
Rename the directories to suit as shown in Figure 9.32.
Open Widgetometer User Interface.vi and find any lost VIs. Widgetometer User Interface loses Widgetometer Enter Data.vi and 2 button dialog.vi. Then select File>>Save With Options>>changed VIs from the menu and save everything.
Without closing Widgetometer User Interface.vi, open Config ini.vi and as before find any lost VIs or Controls. This is usually just Datamanager.ctl. Once again select File>>Save With Options>>changed VIs from the menu.
Rename Config ini.vi to Widgetometer Config ini.vi, and update the icon to reflect the new program.
Now we need to open the configuration file when the program starts and load all the configuration data. In Widgetometer User Interface.vi you will find UI Initialize.vi, open it up and put . . .\\2 System\2 Data Handling\1 File Management\1 File Handler\File Handler.vi into it, and put in the file path to the configuration file as shown in Figure 9.33.
Also put in the two data manager VIs. They can be found in . . .\\2 System\2 Data Handling\Config Data (or copied from the hierarchy diagram). Pop up on the command input and select "Load All".
Now drop Widgetometer Config.ini into the case for Button 3 in Widgetometer User Interface.vi diagram (run state), as illustrated in Figure 9.34.
You can optionally change the names of the data manager files and customize the icons to your individual requirements. What you now have is the capability to create, load, and save constants in a reasonably secure location. Defining these constants is as simple as updating an enumerated type control.
Let's do a couple as an example. Let's remove the example parameters and add a new parameter called System Name . We've changed the data manager names to Widgetometer System Config Data Man.vi and Widgetometer System Config Data Man2.vi.
Double-click on the parameter control on Widgetometer System Config Data Man.vi front panel to bring up the control editor (if you haven't got this option set you'll have to open the control in longhand by highlighting the control and selecting customize from the menus ). Figure 9.35 shows the resulting screen.
Remove the example parameters and replace the last one (you won't be able to remove it) with System Name.
To run the new set up you will have to ensure that the directory for the Config.ini file exists (as pointed to in UI Initialize.vi). The ini file will be automatically created when the program is first run.
Now run Widgetometer User Interface.vi and select Button 3 (after it moans about there being no file). The Config.ini dialog should be displayed. You will notice that there is now a key called System Name.
Other constants we could pull out of the code at this stage are:
Power Supply On Volts
Power Supply Off Volts
Serial Port Settings (baud rate, handshaking protocols and data, and start and stop bit sizes)
All you do then is drop the Widgetometer System Config Data Man.vi into the relevant hardware VI and wire it as shown in Figure 9.36.
Finally, the array of Strict Type Def. enumerated types used to drive the Test Executive should also be generated from the configuration file.
In keeping with the components within components theme we have created an error handling component. This component can be set to a mode where it complains loudly (throws dialogs) or logs errors discretely. In development mode you want it to complain loudly, and in run mode you want it to store the errors in a file. This component is called Error Control.vi and comes with both the User Interface Pattern and the Config.ini pattern.
Information hiding makes the error handling decision-making process a little simpler. Lower-level private components can pass errors up to their peer components. These errors can then be handled by the top-level components. The following diagram, as shown in Figure 9.37, illustrates the process.
The program executive could also have an error state that handles the errors and warnings in an elegant manner.
The program executive was discussed earlier, but what are the advantages of doing it like this? It is actually quite easy to list the different states of a system. From a hardware perspective, most test systems have very few states. These states are then defined by the hardware component. So using state machines has simplified the definition of the problem.
The program executive is usually just as simple to define. The ones in Figure 9.38 describe a common pattern.
Another common state machine is the Test Executive. The one used in this system takes advantage of the fact that the hardware component models all the states that the test system has. It takes an array of these states and sticks them into a For Loop, creating a sequence of states that defines the test. For extra flexibility this array of constants should be pulled out of the code and stored in a file.
The state machine structure also helps when changing code. In the Widgetometer case if we wanted to define and add a completely new test, we would simply update the hardware component and add the new hardware actions to the array in the Widgetometer test executive.
What have we reused and how much effort has it saved us?
Patterns allow us to reuse design and also enable us to reuse more of our code.
For this project the following patterns were used:
UI Controller-Message Queue Pattern
Section Keyed Data Handling Pattern
Control>> Drive>>Read Pattern
It's all right, we're not going to list them all. A quick count tells us that without including any of the hardware, we have reused nearly 70% of the code in some shape or form.
Reuse can sometimes have implications on the design, since you can end up changing your design so that you can reuse existing code. This shouldn't be the case with LCOD because the patterns are purely a way of emulating the patterns observed in all systems when you start using components in your code. Strict Type Defs. then allow you to modify the pattern to suit your design.
Also, what new code can you reuse in future systems? For a start, you can pluck out the lower-level hardware VIs.
If you are in a multideveloper environment you will find the following checklists useful for broadcasting the standards expected for a job. They are also quite good as reminders, even if you're working on your own. We like checklists!
Derek's Software Co.
|
Top |