From GridLAB-D Wiki
Jump to: navigation, search

 DEPRECATED  The metronome example is not longer used.

The purpose of this document is to concisely explain the design and implementation of the Metronome class that is used as an example in the Beginners Guide To The GridLAB-D Core. This also serves as a quick reference for developing your own classes.

The Concept

A distinct need for an object is present before anyone works on a class. Whether a system needs particular functions that don't fit into the existing classes, or following a textbook example, the concept for a class is the first thing that is worked on. Write a little section on what the class is supposed to do, on a napkin or in comments in a stub file, for what the class is supposed to do, what states it has, what variables it stores, and if it needs to interface with other objects or modules.

The Metronome class is supposed to duplicate the behavior of a metronome. A metronome is "instrument designed to mark exact time by a regularly repeated tick", according to Merriam-Webster. To mimic this, we need to be able to store the rate at which the metronome ticks, and the last time it ticked. To model a more traditional metronome, it will also alternate between going "tick" and "tock". Finally, we need a way of telling our object to stop, as the spring will wind down (or the batteries will die), so we limit the number of ticks to a maximum count.

A Bit Of Pseudocode

At this point, we have enough to sketch out the C++ class for the Metronome. We need a rate, we need the sound that was made, and we need the time that it last ticked. The C++ file might look a bit like

   class Metronome {
       enum sound {tick, tock};
       int last_time;
       int rate;
       int count;
   }

Having sketched out what it contains, let‘s write a quick snippet for how it works. GridLAB-D wants work that happens in “real-time” for the simulator to occur in the sync() function, so consider

   int sync(int t0, int t1)
   {
       /* if it has been rate seconds since the last tick */
           /* tick! */
           /* advance the time we last ticked */
           /* decrement tick count */
       /* if count == 0, we never tick */
       /* otherwise return the next time to tick */
       return 0;
   }

One of the things we need to ask is "which step should this take place in?" since we can have the state change occur as a presync, sync, or postsync operation. Assuming that this state change is independent of other objects, and there may be other objects dependent on accurately witnessing the current state, we’ll put it in the presync.

Returning The Next Time

Most of the prior example was fairly straightforward, but the t0, t1, and return value are somewhat abstract. The inputs t0 and t1 are the timestamps that the core is advancing from and that it is advancing to. The return value should be the time the object will next change state, and would like the core to advance to next.

The inputs define the time length for the object to update across, important for values such as power and solar thermal input. The return value must only refer to when its internal state, and it must eventually reach a steady state answer and return TS_NEVER. With the metronome, the next state change occurs at a fixed interval of last_time + rate. Accordingly, we return this value while the metronome is still running, which is while count is a positive value.

Class Registration and Publishing Variables

Having established what our object does, what it has, and when it needs to update its internal state, we’re very ready to call the core functions that will register the class. These functions end up in the C++ class constructor, and we need to call the constructor in the module init function. Accordingly, we have

   metronome::metronome(MODULE *mod){
       if(oclass == NULL){
           oclass = gl_register_class(mod, “metronome”, PC_PRETOPDOWN);
           if(oclass == NULL)
               GL_THROW(“unable to register metronome class”);
           /* publish variables */
           /* set defaults */
           ...
       }
   }

and in the module init.cpp file…

   EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[]){
       ...
       new metronome(module);
       ...
       return metronome::oclass;
   }

This will call the constructor for the class when the module is initialized and register the class with the core for the first time the constructor is called. The core now has a metronome class registered, even if it doesn’t have any variables published. Going back to our pseudocode class definition, we have an enumerated sound, and int values for the rate, last tick time, and count of ticks. Of these, we want to be able to read the sound that the metronome last made, set how fast it is moving, and set how many ticks it will make. All three of these need to be published using gl_publish_variable(). This will expand our metronome constructor with

   ...
   /* publish variables */
   if(gl_publish_variable( oclass, /* from gl_register_class */
       PT_int32, “rate”, PADDR(rate),
       PT_int32, “count”, PADDR(count),
       PT_enumeration, “sound”, PADDR(sound),
           PT_KEYWORD, “tick”, M_TICK,
           PT_KEYWORD, “tock”, M_TOCK,
       NULL) < 1){GL_THROW(“unable to publish metronome”);}
   /* set defaults */
   ...

The PADDR macro returns an offset pointer to the variable's location within the class struct. The constants M_TICK and M_TOCK need to be defined somewhere, preferably as the integers 0 and 1, so as to avoid using magic numbers.

Creating and Initializing the Metronome

When we create an object from the model file, we get a set of default values, a create function, and an init function. With our metronome, the default values will be to have a wound-down metronome with a fast tick speed, and will have never ticked before. To model this, we set the defaults in the constructor to

   ...
   /* set defaults */
   rate = 1;
   count = 0;
   sound = M_TICK;
   last_time = 0;
   defaults = this;
   ...

This defaults pointer is a static class variable, out of the GridLAB-D class basecode. It comes into play in the create function, which usually looks like

   int metronome::create(void)
   {
       memcpy(this,defaults,sizeof(metronome));
       return 1;
   }

Create is called by the loader once the object header and object class data block have been allocated with malloc(). Anything that goes into create should rely solely on class-internal properties, since the object file is still being loaded. The init function is used for both setting model-dependent values, and for reality-checking the values in the object. In our case,

   int metronome::init(OBJECT *parent)
   {
       if(rate < 0){
           gl_error(“Metronome object has negative interval!”);
           return 0; /* failure */
   }
   if(count < 0){
       gl_error(“Metronome object has negative tick count!”);
       return 0; /* failure */
   }
   /* start the metronome at the initialized clock */
       last_time = gl_global_find("clock")->prop->addr;
   return 1;
   }

This takes a somewhat simplistic approach to the initial conditions. It would also be reasonable to reset the interval to a positive number, or to reset the count to zero, which both are valid conditions.

Filling Out Sync

The core now knows that the Metronome class is there, and that it updates during the presync stage. We know what we want the Metronome to do during the sync calls. When we fill them in, we get

   int metronome::presync(TIMESTAMP t0, TIMESTAMP t1)
   {
       if(count <= 0){ /* if we’re done, we’re done*/
           return TS_NEVER;
   }
   /* if it’s time to tick */
   if(last_time + rate == t1){
       sound = (sound == M_TICK ? M_TOCK : M_TICK);
       count--;
       last_time += rate;
   }
   /* next time to tick, whether or not we did this time */
   return last_time + rate;
   }

Creating the Metronome

All the code for the metronome has been written, and the next step is to write the object into a model file. In a text or .glm file, we write

   module metronome;
   object metronome {
       name Metronome25;
       count 40;
       rate 25;
   }
   object metronome {
       name OddMetronome;
       count 20;
       rate 30;
       sound tock; # default is “tick”
   }
   object metronome {
       name EmptyMetronome;
   }

All three of these entries will create valid metronome objects. The first one will tick/tock forty times every 25 seconds, the second one will tick/tock twenty times every thirty seconds while starting on tock instead of tick, and the third one will contain only the default values.