1. IntroductionThe Operator Interface is a programmable application interface for TestStand. TestStand provides ActiveX components that allow developers to build an application that will interact with the TestStand engine. The components are separated into visible controls and manager controls. The visible controls consist of components that facilitate user interaction with a test management system. These include custom controls such as the SequenceView and ReportView, as well as standard controls including buttons, list boxes, and menus. The manager controls are ActiveX objects that handle communication between the TestStand engine and the visible controls. User commands, issued through the visible controls, are addressed by the manager controls. The manager controls include the Application Manager, the SequenceFileView Manager, and the ExecutionView Manager. Each manager control acts as an administrator for different types of interaction. For instance, the SequenceFileView Manager owns a SequenceView visible control. The user is able to see and interact with the SequenceView control. Any user interaction is then handled by the manager controls, and the appropriate update is made to the data in the TestStand engine.
Figure 1. The Full Featured OI implemented in LabVIEW.
Each visible control is capable of acting as an interface for the user to execute commands. Commands are handled by the relevant manager control, which then updates the dataset in the current instantiation of the TestStand engine. For example, a user can choose a specific sequence from the Sequence pull down menu. The manager will respond to the user selection and update the sequence in the SequenceView.
In the Full Featured OI, a context menu appears when the user right clicks on the SequenceView control. The context menu provides the option to change the run mode or run the step in interactive execution mode. Each of these options is a command that is issued to the SequenceView manager. The SequenceView manager handles the appropriate response to the command. If the user chooses the "Skip" command from the context menu, the SequenceView manager will update the step run mode in the TestStand engine. The result is visible under the "Flow Properties" column of the SequenceView.
Figure 2. The Standard SequenceView Context Menu.
There are cases where the developer may need to offer additional functionality to the operator. This tutorial demonstrates how to modify the Full Featured Operator Interface by adding a custom command to the Sequence File View context menu. Custom commands allow the developer to provide additional services to the operator. The standard Full Featured OI prohibits the user from changing the limits of a Numeric Limit Test step type. The context menu of the SequenceView is replaced with a customized menu including a custom command that allows the user to edit the limits of a Numeric Limit Test step. In order to implement this functionality, this tutorial will cover event callbacks, configuring a custom command, and finally implementing a custom command.
2. Event Callbacks
Each callback is registered with an ActiveX control. Registering an event callback tells the control which function to execute when an event occurs. The LabVIEW Full Featured OI registers callback VIs in "Full OI - Configure Event Callbacks.vi". ActiveX controls, including the manager and visible controls, are assigned callback VIs for specific events. For example, the OI registers callback VIs for the "ExitApplication", "DisplayExecution", and "DisplaySequenceFile" events. The registered VIs are called when the corresponding events are triggered.
Events are registered using the Register Event Callback VI found in the ActiveX palette of LabVIEW. The Register Event Callback VI accepts three terminals including the event, the callback VI reference, and the user parameter. The ActiveX control reference is wired into the event terminal. A drop down selection is populated based on the available events for a specific control. The selectable events will change from control to control. A static VI reference is wired into the VI reference terminal. The static reference points to the callback VI that is called when an event occurs. Any additional data is passed in the user parameter terminal. For instance, a reference to an external ActiveX control may be used in the callback VI. The DisplayExecution event requires a reference to the ExecutionViewManager to update the ExecutionView. The user parameter terminal expects a variant data type. The variant data type is an ActiveX type that is generic. Any data type is convertible to and from a variant data type. Figure 4 shows the event callback registration from the LabVIEW Full Featured OI. The Register Event Callback nodes are chained together. Each control event is assigned a specific callback VI.
Figure 4. Register Event Callback VIs.
3. Building a Custom Context Menu
Figure 5. The Default SequenceView Context Menu.
After extracting the Application Manager control reference, the NewCommands method is invoked. NewCommands generates a collection where the developer can insert commands. Using the reference to the Commands object, the InsertKind method is invoked. This inserts one or more commands into the Commands collection. The first parameter of the InsertKind method is cmdKind. The cmdKind terminal expects an enumeration of different command types prefixed with "CommandKind_". For the default SequenceView, the "CommandKind_DefaultSequenceViewContextMenu_Set" is inserted into the Commands collection. This enumeration inserts several commands including the Run Mode command set, the Breakpoint command set, and the Interactive Execution command set. The entire set is placed into the Commands collection. Next, the InsertIntoWin32Menu function is invoked. Using the menuHandle extracted from the Event data, the Commands collection is used to build the menu. The menu is then displayed by the SequenceView control. When the user selects a specific command from the menu, the command is executed and the SequenceView is then updated. Tip: For help on specific ActiveX methods and properties, right click on any Invoke Node or Invoke Node, then select "Help For Method/Property".
Creating a Custom Command
- Before the Commands collection is created with the NewCommands Invoke Node, insert an Invoke Node on the block diagram. Wire the Application Manager reference to the reference terminal. Select GetCommand from the list of methods for the Application Manager invocation. Right-click the cmdKind terminal, and select Create -> Constant from the context menu. This generates an enumeration constant of different command kinds. Select CommandKind_Custom from the enumeration drop down select. The GetCommand instantiates a new Command object of a specified command kind.
- Place an Invoke Node on the block diagram and wire the Command reference from the GetCommand Invoke Node to the reference terminal. Expand the Invoke Node so that the Enabled, Visible, UserData, and UserObject properties are shown. Right click on the Invoke Node and select Change All to Write. This ensures that we are setting the properties. Wire true constants into the Enabled and Visible properties. This allows the user to see and select this command. The UserData and UserObject are variant data properties. The developer can assign any data values to these properties. When the command is executed, the data is extracted and used by the command function. Wire a numeric constant into the UserData property. The numeric value is a unique identifier for the command. This identifier is used to determine which command to execute when the custom command type is called. Wire the SequenceFileView Manager reference into the UserObject terminal. The SequenceFileView Manager is needed to edit and update the numeric limits of a step. Connect the error wire.
- Place an Invoke Node on the block diagram and wire the Command reference from the previous Command Invoke Node to the reference terminal. Select SetDisplayName from the list of Command methods. Create a string constant and wire it into the val terminal. This method sets the string that will be displayed in the context menu. Connect the error wire.
- Wire the error out terminal to the error in terminal of the NewCommands Invoke Node. Figure 6 shows the section of the block diagram for building a custom command.
Figure 6. Creating a Custom Command
Building a Custom Context Menu
- The NewCommands Invoke Node should already be on the front panel. Make sure the error wire from the SetDisplayName Invoke Node is wired into the input error terminal of the NewCommands Invoke Node. This ensures that the ActiveX calls are executed in the proper order.
- Replace the default InsertKind Invoke Node. Place two Invoke Nodes on the block diagram and wire the Commands collection object from the NewCommands Invoke Node to the reference terminal. Create a constant from the command kind terminals. Choose CommandKind_RunModeSkip for the first Invoke Node and CommandKind_RunModeNormal for the second Invoke Node. Wire a numeric constant into the insertBefore terminal with the value -1 in both nodes. This inserts the command after the last available slot in the Commands collection. Assigning a value above -1 will insert the command before that index in the Collections collection. Wire an empty string constant into the currentMenuName and allMenuNames terminals for both nodes. These terminals are used for Menu Bar items. Connect the error terminals together. These nodes provide the user with ability to set the run mode of a step. The developer has the ability to provide any command from the CommandKinds list within the context menu.
- Place an Invoke Node on the block diagram and wire the Commands collection to the reference terminal. Select Wire from the list of Commands methods. Wire the Command reference output from the SetDisplayName Invoke Node to the Item terminal of the Insert node. Wire a numeric constant with -1 into the insertBefore terminal. Connect the error terminals. This node places our custom command into the Commands collection. Figure 7 shows the section of the block diagram for building the Collections object.
- Wire the reference out terminal of the Insert Invoke Node to the InsertIntoWin32Menu Invoke Node. This call builds the menu from the Collections command.
- Place a close reference VI onto the block diagram between the InsertIntoWin32Menu and the existing close reference VI. Wire the Command reference from the SetDisplayName Invoke Node to the new close reference VI. This ensures that we close out the reference of the newly created command object. Wire through the error terminals to make sure the VIs are executed in the proper order. Figure 8 shows the code for Inserting the Commands collection into the menu.
Figure 7. Building a Custom Context Menu
Figure 8. Inserting the Commands Collection into the Menu
4. Handling Custom Commands
Registering the PostCommandExecute Callback VI
- Extend the Register Event Callback to show an additional terminal set.
- Wire the Application Manager reference node into the Event terminal. This provides a node to capture Application Manager events.
- Select PostCommandExecute from the list of events. The PostCommandExecute callback is invoked after the execution of any command, including custom commands.
- Right click on the VI Ref terminal and choose Create Callback VI. LabVIEW will automatically generate a callback VI for handling the PostCommandExecute event. The proper input terminals are automatically provided within the VI. Figure 9 shows the modified Register Event Callback node.
Figure 9. Modifying the Register Event Callback Node in the Full OI - Configure Event Callbacks VI.
5. Implementing a Custom Command
Extracting Data from the Command Object
- Wire the Event Data terminal into an Unbundle by Name VI. Extract the Command reference from the cluster.
- Place an Invoke Node on the block diagram. Wire the Command reference into the Invoke Node. Extend the Invoke Node so that four properties are extracted from the Command reference. Select Kind, UserData, UserObject, and SequenceFileViewMgr as the properties to extract from the Invoke Node. Kind refers to the type of command. UserData and UserObject contain any data that is assigned to the Command object when the Command is created.
- Place a case structure on the block diagram. Wire the Kind numeric into the Case selector. Change the case selection to the 3. CommandKind_Custom is an enumeration that maps to the number 3. The PostCommandExecute callback is executed every time a command is invoked. The case structure ensures that only the custom commands are handled in this VI.
- Place a Variant to Data VI on the block diagram inside the case structure. Wire the UserData into the variant terminal of a Variant to Data VI. Wire an I32 representation numeric into the type terminal of the Variant to Data VI. The numeric extracted from this VI is the unique identifier assigned to the custom Command when the command is created. In this tutorial, only one custom command is created. However, using an identifier number, the developer is able to implement multiple custom commands.
- Place a Variant to Data VI on the block diagram inside the case structure. Wire the UserObject into the variant terminal of the Variant to Data VI. Wire the SequenceFileViewMgr from the Command Invoke Node into the type terminal of the Variant to Data VI. This extracts the reference to the Sequence File View Manager control. The control reference is required to access Numeric Limit Test step information.
- Wire through the error terminals. Figure 10 shows the code to extract the Command object data from the Event Data parameter.
Figure 10. Extracting the Command object from the Event Data.
Checking the Command Type
- Place a case structure within the command kind case structure. Wire the output terminal of the UserData Variant to Data VI into the case selector terminal. Change the case number to 61455. This is the unique identifier number assigned when the command is created. This ensures that the callback code only reacts to the proper custom command call. Additionally, it allows developers to extend the OI with additional custom command types.
- Place an Invoke Node inside the nested case structure. Wire the Sequence File View Manager control reference to the reference terminal of the Invoke Node. Extract the SelectedSteps property from the Invoke Node. SelectedSteps returns a collection of steps that are currently selected in the SequenceView control. Connect the error terminals.
- Place an Invoke Node to the right of the Sequence File View Manager Invoke Node. Wire the SelectedSteps reference to the reference terminal of the Invoke Node. Select Item from the method list of the Invoke Node. Wire a constant with the value 0 into the itemIndex parameter. This extracts the first step from the collection of selected steps. Item returns a Step object. Connect the error terminals.
- Place an Invoke Node to the right of the SelectedSteps Invoke Node. Wire the Item reference to the reference terminal of the Invoke Node. Extract the StepType property from the Step Invoke Node. The StepType object is used to determine what type of step is currently selected. Connect the error terminals.
- Place an Invoke Node to the right of the Step Invoke Node. Wire the StepType reference to the reference terminal of the Invoke Node. Extract the Name property from the StepType Invoke Node. Connect the error terminals.
- Place an Equal? VI on the block diagram to the right of the StepType Invoke Node. Create a string constant and wire it into the bottom terminal of the Equal? VI. Place "NumericLimitTest" into the string constant. Wire the Name property into the top terminal of the Equal? VI. This ensures that the Numeric Limit dialog is only displayed for Numeric Limit Test step types. Figure 11 shows the completed code for checking the step type of the selected step.
Figure 11. Checking the Step Type
Executing the Edit Limits Substep
- Place a Case Structure to the right of the Equal? VI. Wire the boolean output terminal of the Equal? VI into the case selector terminal of the Case Structure. This ensures that the Edit Limits dialog is only shown for Numeric Limit Test step types.
- Place an Invoke Node inside the true case of the case structure. Wire the reference from the Item terminal in the SelectedSteps Invoke Node into the reference terminal of the Invoke Node. Select ExecuteSubstep from the list of methods of the Step Invoke Node. Wire a numeric constant with the value of zero into the substepIndex terminal. Steps are often made up of multiple substeps. The first substep of the Numeric Limit Test step type is the Edit Limits substep. This is the step that displays the Edit Limits dialog when the developer selects to edit limits from within the Sequence Editor. The Step Invoke Node is executing the substep when the user selects this command from the SequenceView context menu. Wire through the error terminals.
- Place an Invoke Node to the right of the Step Invoke Node. Wire the Sequence File View Manager reference to the Invoke Node reference terminal. Select Refresh from the list of methods in the Sequence File View Manager Invoke Node. This causes the SequenceView to update with the modified limits. Connect the error terminals.
- Place three Close Reference VIs outside the Step Type Case Structure. Wire the output error terminal of the Sequence File View Manager Invoke Node to the first Close Reference VI. Select the False case of the Step Type Case Structure. Connect the input error tunnel to the output error tunnel on the case structure. Figure 12 shows the False case block diagram.
- Wire the output reference terminal from the StepType Invoke Node to the first Close Reference VI. Wire the output terminal from the Step Invoke Node to the second Close Reference VI. Wire the output reference terminal from the SelectedSteps Invoke Node to the last Close Reference VI. Wire through the error terminals. The references that were opened in this VI must be closed to avoid memory leaks. Figure 11 shows the finished block diagram.
Figure 12. Executing the Edit Limits Substep.
Figure 13. The False Case of the Numeric Limit Step Type Case Structure
TestStand UI Controls API Reference Help Topics
- ApplicationMgr, SequenceFileViewMgr
- CommandsKinds Enumeration
- PostCommandExecute, PreCommandExecute