Advanced Usage Guide

ProbeMap design

When designing a ProbeMap for a given (subclass of) SwarmObject, inclusion of instance variables or messages defined in the super class might be desirable. The normal ProbeMap design code might look like (this code was taken from the tutorial app called "hello-world"):

probeMap = [CustomProbeMap createBegin: [self getZone]];
[probeMap setProbedClass: [person class]];
probeMap = [probeMap createEnd];
[probeMap addProbe: [probeLibrary getProbeForVariable: "room"
  				 inClass: [person class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "party"
  				 inClass: [person class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "name"
  				 inClass: [person class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "stillhere"
  				 inClass: [person class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "listOfFriends"
  				 inClass: [person class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "myColor"
  				 inClass: [person class]]];
[probeLibrary setProbeMap: probeMap For: [person class]];
[probeDisplayManager createProbeDisplayFor: person];

where room, party, name, stillhere, listOfFriends, and myColor are instance variables declared in the interface to the Person subclass. And Person is a subclass of Agent2d, which is a subclass of SwarmObject.

Now let's say that there are two variables declared in Agent2d that you want to put into this custom probe in addition to the ones you've picked out of Person. Call them x and y. The way to add them to the probeMap is to add the following two lines of code to the above.

[probeMap addProbe: [probeLibrary getProbeForVariable: "x"
  				 inClass: [Agent2d class]]];
[probeMap addProbe: [probeLibrary getProbeForVariable: "y"
					 inClass: [Agent2d class]]];

And that's it! The two superclass-declared variables, which are, in fact, instance variables of the instance of the subclass, are now included in the probe.

In addition, a convenience message has been added to the CustomProbeMap interface to compress the above rather cluttered mechanism into one message. This convenience message can be used in the usual case where a ProbeMap will consist of variables and messages from the same class. For example, the first part of the custom probe creation above can be shortened to:

probeMap = [CustomProbeMap create: [self getZone] forClass: [person class]
      withIdentifiers: "room", "party", "name", "stillhere",
                       "listOfFriends", "myColor", NULL];

And if the user wanted messages in the probe as well, it could be extended to:

probeMap = [CustomProbeMap create: [self getZone] 
                          forClass: [person class]
                   withIdentifiers: "room", "party", "name",
                       "stillhere", "listOfFriends", "myColor", 
                       ":",
                       "setWorld:Room:Party:",
                       "setPerson:Topic_array:ShowSpeech:",
                       NULL];

At present, this message doesn't search the superclasses for the message names listed here. But, that will soon be rectified.

ActivityControl Issues

It is completely reasonable to assume that explicit control can be had over all the activities in a simulation. However, at present, this control is limited because the context in which an activity runs determines how it behaves. To understand how an ActivityControl is to be used, we will have to explore the behavior of activities in context. (For a more complete explanation of the behavior of activities, see the activity library documentation.)

There are two ways to get an activity started, one can activate the activity in nil or in some other activity. So called "top-level" activities are activated in nil and are intended to be independent of the sophisticated scheduling activity that dictates the execution of actions in any other context in the simulation. I.e. the only activities that should be activated in nil are those sets of events that are expected to preserve the same behavior no matter what goes on in any other part of the simulation.

The other type of activity, those activated in some other activity, is intended to be an integral part of its owner activity. However, this doesn't mean that it must depend on the outcome of actions happening in the owner activity. In fact, an ActionPlan can be designated as containing actions that are capable of being processed in parallel, via code like the following:

[anActionPlan setDefaultOrder: Concurrent];

But these activities are still intended to be meshed with their owner activities. In other words, they are part and parcel of the same model or simulation.

Now, the operational effect of activating an activity in nil is that it will not be meshed with the rest of the Swarm activity structure. This gives the user (or process) complete control over the execution of that activity. A run on that activity will run either to completion or until a stop flag is set by a sequence of events purely internal to that activity. Or, one can stop it from the outside with a message from something like an ActivityControl.

What all this means is that, while one can attach an ActivityControl to any activity, only the "top-level" activities (those having been activated in nil) are going to respond well to it. Any sub-activity will respond half-heartedly, if at all. For example, in the Mousetrap demo distributed with Swarm, an ActivityControl has been placed on both the ObserverSwarm and the ModelSwarm activities. Now, if one sends a run message to the ActivityControl that is attached to the observerSwarm's activity, the entire model continues to run to completion, unless the user sends a stop message. However, if the sim is stopped at some point, a run message to the modelSwarm's activity will have no effect at all. (Note: If you do this via the activity controllers, you see the currentTime variable get updated; but, the actual run message to the activity, itself, has no effect.)

So, the rule of thumb, for the present, is to attach ActivityControl objects only to "top-level" activities, like the ObserverSwarm.