The user interface controls included with Inventor are adequate only for manipulating the Inventor scene itself. The design of NetSpy called for other user interface elements to allow for the interactive construction of scenes. The ViewKit class library, available on Silicon Graphics platforms, eases the construction of graphical user interfaces using the Motif widget set for the X Window system. This was the chosen method for the user interface impementation.
There are serveral levels of communication between the objects within NetSpy.
Figure 4-1: High level object communication.
Figure 1 shows the highest level of communication between the objects in NetSpy. The Application Object is an instance of the NetSpy class. This is an extension of the ViewKit class VkApp, of which an application using the toolkit must have one and only one instance. The Main Window object is an instance of the NetSpyWindow class, a derivative of the VkWindow class, another ViewKit class which controls the application's main window. NetSpy's menu is created by the Main Window object. Many of the menu selections lead to dialog boxes. These are handled as part of the User Interface objects. The Scene Definition object keeps track of all the information related to the visualization scene and the communication with the various collection services.
In terms of the scene displayed to the user, the communication between the computers being monitored (hosts) and the gadgets, the main object is the Scene Definition object. This is an instance of the SceneDef class. The following diagram shows the relationship between the Scene Definition object and the gadget and host objects.
Figure 4-2: Relationship between Scene Definition and gadgets and
hosts.
The Master Gadgets are instances of each type of gadget - there is one object for each type. When a user wishes to create a new shape or arranger gadget, a copy is made from one of the master objects. They are referenced by name: for example, "CityBlock" for the city block (grid) arranger object.
Each gadget in the scene is referenced by the Scene Definition object, as is each host from which statistics are being collected. The Connection objects provide the communication channel between the statistics and the gadgets.
The following diagram shows the class hierarchy:
Figure 4-3: Gadget class hierarchy
A scene is defined using a scene graph: a graph in which the nodes define certain graphical objects, transformations or other drawing properties. When Inventor renders a scene, the scene graph is traversed in depth first order and the resulting graphical display is built and rendered.
As a simple example, consider the following diagrams:
Figure 4-4: Inventor scene graph and resulting display
The text next to the nodes in the graph on the left are the Inventor node types (class names of objects). The SoSeparator node is the root of this particular scene graph. The SoMaterial node defines a drawing material (colour, surface reflectivity properties) and finally, the SoCone node defines the shape to be drawn.
If it is desired to move an object to a different location within a scene then a transform node must be placed within the graph before the shape in the traversal path. Some of the possible transforms include: SoTranslation, SoRotationXYZ and SoScale which, as their names suggest, perform translation, rotation and scale transformations, respectively.
The SoSeparator node allows the Inventor scene to be constructed as a number of components, each of which is created indepently. It is in this way that the scene graph is built in NetSpy. Each gadget object defines its own small part of the complete scene graph, each starting with a SoSeparator node. For example, the scene graph created by the Cube shape gadget is very similar to the example given above, except that a SoCube node is used instead of SoCone. An arranger gadget, under its own SoSeparator node, introduces the appropriate transform nodes to place the individual gadgets within the scene. The scene graph defined by the Cube gadget would be then be placed into the scene, as in the following diagram:
Figure 4-5: Scene graph constructed by gadgets
Programs that make use of PMCS use the PMAPI (Performance Metric Application Programming Interface). The function calls in this interface take care of the intricacies of network communication.
Similarly DPML-MCS defines a protocol for the transport of information across the network. This protocol is documented in Appendix B. The following diagram highlights the various objects in NetSpy and the DPML-MCS collection service that implement the network communicaton.
Figure 4-6: Implementation of collection service to gadget
communication.
NetSpy and the collection service share code for the Socket, Metric, Instance, and Value classes. A fellow honours student, Jamie Cameron, generously donated source code which I based the Socket and Server classes on. These classes implement a simple but reliable communication service using standard BSD socket system calls. The Server class provides default behaviour for creating a network service, such as the DPML-MCS collection service.
The Metric class defines the interface for specifying the format of the statistics. This includes their names, the individual instances and their values. A mechanism for translation into the DPML-MCS protocol for transmission, and for translation back again after reception, is similarly defined. Programmer's wishing to use DPML-MCS for sending additional statistics to NetSpy can use these classes to make the job easier, although they are not essential to conform to the protocol.
Figure 4-7: Scene navigation interface
The controls depicted in the above figure are provided by the SoXtExaminerViewer class, a member of the Open Inventor class library.
The menus and dialog boxes that make up the rest of the user interface were implemented using the ViewKit class library. However, it is somewhat incomplete, and in many cases the programmer is required to drop back to the Motif level to create user interface components such as buttons, lists, labels and text fields. To maintain consistency I extended the ViewKit class library so that it contained classes for creating the user interface components mentioned above.
Motif and ViewKit make heavy use of callback procedures. This is in keeping with the event driven nature of graphical user interface design. When the user takes some action an event is passed on to the application program. ViewKit or Inventor handles these events internally and then calls a callback function so that the application can regain control and take the appropriate action.
A good example of this is the pull-down menus. These menus are defined in the NetSpyWindow class, and callback routines are provided for each option in the menus. When the user makes a selection the appropriate callback is invoked - a method of NetSpyWindow in the case of the menus. In this way, when the user selects the "Add host..." option from the "Hosts" menu the addHostCallback() method is invoked, which in turn brings up the "Add New Host" dialog box.
The dialog boxes all make use of the classes in ViewKit derived from the VkDialogManager class. There is a separate class for each different dialog box in NetSpy, with an instance of each class created in the NetSpyWindow object. Most of these dialog boxes make heavy use of the Scene Definition object to obtain lists of the various elements in the current scene and the list of hosts from which statistics are currently collected. When the user selects the "OK" button on a dialog box an addition is typically made to the Scene Definition object. The following diagram illustrates the interaction between the various objects here.
Figure 4-8: Communication between user interface objects
As an example of the dialog boxes created for NetSpy, here is a screen shot of the "New shape..." dialog box:
Figure 4-9: "New shape..." dialog box
The components that make up the dialog box are each an object of a ViewKit, such as VkScolledList for the list of shape or arranger types, or VkRadioBox for the distributor type. The shape name field is a VkText object. Each of these classes implements a query method of the form get...() which is used to retrieve the appropriate value for that type of object. In the case of a VkScrolledList, for example, the method is getSelection() which returns the index to the currently selected item in that list.
Figure 4-10: Journey of a statistic to a gadget
Each Host object maintains a list of connections. A connection, an object of the Connection class, maintains a pointer to a statistic (instance of class Metric) and a dispatcher (instance of class Dispatch). When the host object retrieves new data from the collection service it updates all of the statistics. When this is done it gets the ConnectList to update all of the connections using the update() method.
When a Connection object is updated, using its update() method, it passes its statistic (Metric object) to its dispatcher. A dispatcher maintains a reference to a distributor gadget and the name of the degree to which this statistic is attched. When the dispatcher receives a metric it passes it on to the distributor object. As described in the section on gadgets, the distributor then dispenses the individual instances to each of its shape gadgets.
As evident from the above diagram, the distributor is passed a Metric, and the shape gadgets are passed a Value object. This is because a Metric contains a Value for each instance of that statistic collected from the host.