The state of an agent is a user-defined set of variables in the agent class whose values are saved whenever the agent is moved or swapped or a configuration containing that agent is saved. The only restriction on the types that can be used in a state is that the classes must implement java's Serializable interface. The state is saved via java's serialization process, but the exact format of that storage is up to the programmer; furthermore, if necessary, the programmer can override the serialization
Connect the two agents in Module Manager. Set each of them to run on the computer you're running your NetController and to use the AgentServer. Save the configuration as waveform.cfg and execute the system. You'll notice that the square wave it produces has a frequency of 2Hz and a duty cycle of 50%. While these parameters are editable in the source code, it's annoying to have to recompile the agent just to do so. Furthermore, that's not much help if a user only has the class file. So we'll make these two parameters part of the agent's state.
Let's not worry yet about the editing part and look at what's just necessary to make those variables part of the state.
The first required method we need to add is dumpState(). It
takes each of the internal data members that are part of the
state (as determined by the programmer) and places them within a
java Object (in this case an array of Objects, which is fine
since an array is a subclass of Object). This function is used
to save the state of the agent.
/**********************************************************************
serializes my state into an object.
@param none
@return Object holding the state
@exception none
**********************************************************************/
public Object dumpState(){
Object state[]=new Object[2];
state[0]=new Float(this.frequency);
state[1]=new Float(this.dutyCycle);
return (state);
} /* dumpState() */
The second required method is processInternalState().
This method is used to restore the state of the agent. It takes
the Object produced by dumpState(), pulls each of
the data members out, and sets the internal data members of the
agent accordingly.
/**********************************************************************
takes a dumped object and loads it into my state.
@param none
@return true == OK, false == bad.
@exception none
**********************************************************************/
public boolean processInternalState( Object statearr ){
boolean retval=true;
float period;
Object[] state;
System.out.println(getID()+" is processing internal state...");
if (statearr!=null){
state=(Object[])statearr;
}
else{
state=(Object[])getDefaultState();
}
try{
this.frequency=((Float)state[0]).floatValue();
this.dutyCycle=((Float)state[1]).floatValue();
period=1000.0f/frequency;
this.periodHigh=period*this.dutyCycle;
this.periodLow=period*(1-this.dutyCycle);
}catch(Exception e){
retval=false;
}
return (retval);
} /* processInternalState() */
We also add a third method, getDefaultState(), that is used if the retrieval
of the state fails or is null (as it often is initially if a state editor
is not used in Module Manager). It is not strictly necessary
here but is very helpful to give a user an idea of the format of the state
and also is used by the default state editor we'll be using later.
/*******************
returns an object that can be used as the default state
@param none
@return Object containing state
@exception none
*********************************/
public Object getDefaultState(){
Object[] state=new Object[2];
state[0]=new Float(1.0f);
state[1]=new Float(0.5f);
return state;
}
Additionally, we need to remove from allocateInternals() the
lines of code initializing the variables that will now be part of the state.
**********************************************************************
Description: allocates data internals.
@param none
@return nada
@exception none
**********************************************************************/
protected void allocateInternals(){
this.level=false;
} /* allocateInternals() */
Side note: I personally tend to store state as an array of Objects. One reason for this is that is compatible with the default state editor we'll encounter later. But there is no restriction to this.You can store your state however you like, as long as it is a valid java Object.
It also should be noted that the order in which
the setup functions are called is initialize(), allocateInternals(), processInternalState(), and initOutputVals().
This means that we didn't actually have to clear out
the contents of allocateInternals(),
since they would be overridden in
processInternalState(),
but it is much better practice to allocate only
variables that have no dependence on the state in allocateInternals(), since it is conceivable that the order those functions are called could
be changed in the future.
Recompile your agents. Now go back to ModuleManager. Launch waveform.cfg. If you've done everything right, it should behave exactly the same as it did the last time, except now you'll notice in NetController that at the beginning of execution it prints the statement we included that the agent is processing its internal state. That's our clue that we've set up the state correctly. But since this agent does nothing to edit its own state, this isn't very impressive yet. That's where the state editor comes in.
| Prev | Next |
Copyright 2000, Carnegie Mellon University
This page written by Jonathan
Jackson
Last modified: Thu May 18 19:11:32 EDT 2000