12.3. How to Customize Probe Displays

Why might one want to customize the display? Well, frankly, the default probe display may look ugly. It may include lots of variables the user does not want to see. There are some instance variables, such as C arrays, that cannot be probed, and so their inclusion in a probe display is uninformative. Swarm is designed to allow the user to pick and choose which variables ought to be included in the display. There are a number of strategies for customizing, one of the standard strategiew uses an object called probeLibrary.

Like probeDisplayManager, the probeLibrary is a global object provided by the Swarm kernel. Customization is achieved by writing code that communicates back and forth from objects to the probeLibrary. In short, the programmer "checks out" a unique, shared copy of a Probe/ProbeMap from the probeLibrary object (of class ProbeLibrary) provided by the kernel. By shared we mean that a similar request made at a different point in the code, will return a reference to the very same probe instance.

Here is a skeleton example of the commands that can create a customized probe display using this approach.


Example 12-1. Generating a probeMap

To generate a probeMap for an instance of the class Agent called agent, which consists of two fields: one VarProbe for the instance variable someIVar and one MessageProbe for the message someMessage, use the following:

probeMap = [EmptyProbeMap createBegin: self];
[probeMap setProbedClass: [self class]];
probeMap = [probeMap createEnd];

[probeMap addProbe: [probeLibrary getProbeForVariable: "someIVar"
inClass: [agent class]]];
[probeMap addProbe: [probeLibrary getProbeForMessage: "someMessage" 
inClass: [agent class] setHideResult: 1]];

[probeLibrary setProbeMap: probeMap For: [agent class]];

Don't forget to execute the CREATE_PROBE_DISPLAY for this object in the observer swarm.


In the file HeatbugModelSwarm.m, one can find a fully fleshed out example of these steps.

probeMap = [EmptyProbeMap createBegin: aZone];
[probeMap setProbedClass: [self class]];
probeMap = [probeMap createEnd];

[probeMap addProbe: [probeLibrary getProbeForVariable: "numBugs"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "diffuseConstant"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "worldXSize"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "worldYSize"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "minIdealTemp"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "maxIdealTemp"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "minOutputHeat"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "maxOutputHeat"
      			    inClass: [self class]]]; 
[probeMap addProbe: [probeLibrary getProbeForVariable: "evaporationRate"
      			    inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForMessage: 
                                    "toggleRandomizedOrder" 
                                  inClass: [self class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "randomMoveProbability"
      			    inClass: [self class]]];
[probeMap addProbe: [[probeLibrary getProbeForMessage: "addHeatbug:"
      		     inClass: [self class]]
      		setHideResult: 1]];

[probeLibrary setProbeMap: probeMap For: [self class]];

As in the generic example, in the heatbugs case the user follows a three step procedure that puts a customized probe map in place of the standard "variable probes only" default.

  1. A new object called probeMap is created. The probeMap is an instance of the the Swarm class EmptyProbeMap. Next we customize the probe map and pass it to the display mechanism.

  2. One-by-one, tell probeMap to add probes for individual variables and messages, and those probes are retrieved from the probeLibrary.

  3. Tell the probeLibrary that, when it creates a probe display for this object, to use this special objectprobeMap rather than the default.

It is important to understand how this customization fits in with the default probe map. The probeDisplayManager creates a widget for every object that you tell it to. Unless you tell it otherwise, it assumes that the widget for every object is be based on the "default probemap" which includes probes for all instance variables, no message probes. If you alter the probeMap by the methods we have been discussing, you are replacing the generic "has it all" probeMap with a customized probeMap. If you right-click on the object name button in a customized probeMap, what pops up is a probe display based on the complete probeMap specification, a map in which all variables and methods are listed.

The procedure outlined above is clear and methodical. It is also open to different kinds of customization. If there is no need for customization of individual probes, there is a "shortcut" that can be used to get most of this work done. Swarm has a class called CustomProbeMap. The CustomProbeMap can create the probeMap and fill it with the desired probes. Here is an example as it would appear in the heatbugs model.


Example 12-2. Non-verbose probeMap creation

probeMap = [CustomProbeMap create: aZone forClass: [self class]
                              withIdentifiers: "numbugs",
                              "diffuseConstant", "worldXSize",
                              "worldYSize","minIdealTemp",
                              "maxIdealTemp","minOutputHeat",
                              "maxOutputHeat", "evaporationRate",
                              "toggleRandomizedOrder" 
                              "randomMoveProbability",":",
                              "addHeatbug:", NULL];

  [probeLibrary setProbeMap: probeMap For: [self class]];

   

The last argument to the method create:forClass:withIdentifiers: is basically a set of character strings that are strung together and used inside the Swarm library to do the work of creating the individual probes. The colon separates the variable probes from the message probes. Notice the inclusion of NULL at the end of the input, which signals the end of the input to the CustomProbeMap.

This method will not allow customization of individual probes, so the setHideResult:1 command that appears in the heatbugs example cannot be included. In order to get specialized probes of that sort, we could break this into two steps, one that creates the probeMap with the default probes and then another which adds the special probes. Consider this:

probeMap = [CustomProbeMap create: aZone forClass: [self class]
                              withIdentifiers: "numbugs",
                              "diffuseConstant", "worldXSize",
                              "worldYSize","minIdealTemp",
                              "maxIdealTemp","minOutputHeat",
                              "maxOutputHeat", "evaporationRate",
                              "toggleRandomizedOrder" 
                              "randomMoveProbability",
                               NULL];
   [probeMap addProbe: [[probeLibrary getProbeForMessage: "addHeatbug:"
			     inClass: [self class]]
		        	setHideResult: 1]];

   [probeLibrary setProbeMap: probeMap For: [self class]];