Building a GridLAB module


Detailed Description

A GridLAB module must be a Windows DLL or a Linux SO.

The compile options that are typically required are

For example the command-line options used by the climate module is

	compiler: /Od /I "../core" /I "../test/cppunit2/include"
		/D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL"
		/D "_CRT_SECURE_NO_DEPRECATE" /D "_WINDLL" /D "_MBCS"
		/Gm /EHsc /RTC1 /MDd /Fo"Win32\Debug\climate\"
		/Fd"Win32\Debug\climate\vc80.pdb" /W3 /nologo /c /Wp64
		/ZI /TP /errorReport:prompt

	linker: /OUT:"Win32\Debug\climate.dll" /INCREMENTAL /NOLOGO
	/LIBPATH:"..\test\cppunit2\lib" /DLL /MANIFEST
	/MANIFESTFILE:"Win32\Debug\climate\climate.dll.intermediate.manifest"
	/DEBUG /PDB:"c:\projects\GridlabD\source\VS2005\Win32\Debug\climate.pdb"
	/SUBSYSTEM:WINDOWS /MACHINE:X86 /ERRORREPORT:PROMPT cppunitd.lib
	kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib
	advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
	

The main.cpp file contains the code to load and unload both Windows DLL and Linux shared-object libraries:

    // the version can be used to make sure the right module is loaded
    #define MAJOR 0 // TODO: set the major version of your module here
    #define MINOR 0 // TODO: set the minor version of your module here

    #define DLMAIN
    #include <stdlib.h>
    #include "gridlabd.h"
    EXPORT int do_kill(void*);
    EXPORT int major=MAJOR, minor=MINOR;

    #ifdef WIN32
    #define WIN32_LEAN_AND_MEAN     // Exclude rarely-used stuff from Windows headers
    #include <windows.h>
    BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID)
    {
        switch (ul_reason_for_call)
        {
            case DLL_PROCESS_ATTACH:
            case DLL_THREAD_ATTACH:
                break;
            case DLL_THREAD_DETACH:
            case DLL_PROCESS_DETACH:
                do_kill(hModule);
                break;
        }
        return TRUE;
    }
    #else // !WIN32
    CDECL int dllinit() __attribute__((constructor));
    CDECL int dllkill() __attribute__((destructor));
    CDECL int dllinit() { return 0;}
    CDECL int dllkill() { do_kill(NULL);}
    #endif // !WIN32

The init.cpp file contains the code needed to initialize a module after it is loaded:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include "gridlabd.h"
    #include "myclass.h"
    EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[])
    {
        // set the GridLAB core API callback table
        callback = fntable;

        // TODO: register each object class by creating its first instance
        new myclass(module);

        // always return the first class registered
        return myclass::oclass;
    }
    CDECL int do_kill()
    {
        // if anything needs to be deleted or freed, this is a good time to do it
        return 0;
    }

You can also implement

 EXPORT int check(void) 
to allow the core to request a check of the objects that are implemented by the module. This is particularly useful to perform topology checks for network models.

If you implement the

 EXPORT int import_file(char *file) 
this will permit users to use the import command in the model definition.

If you implement the

 EXPORT int save(char *file) 
this will permit users to request saves in formats other than .glm or .xml.

The

 EXPORT int setvar(char *varname, char *value) 
and the
 EXPORT void* getvar(char *varname, char *value, unsigned int size) 
are implemented when you wish to allow the user to alter any of the module's global variables. See network/varmap.c for an example.

The

 EXPORT int module_test(TEST_CALLBACKS *callbacks,int argc, char* argv[]) 
function implements the module's unit test code. See "Unit testing" for more information.

The

 EXPORT MODULE *subload(char *modname, MODULE **pMod, CLASS **cptr, int argc, char **argv) 
function is used to load modules written in foreign languages. Look at the gldjava project for examples of how this is used. This function needs to manually set the function pointers for any classes registered by subload-ed modules. A module subload is triggered with "module X::Y".

The implementation of each class will require two files for each object class be included in your module's source code. The header file will usually include the following:

    #ifndef _MYCLASS_H
    #define _MYCLASS_H
    #include "gridlabd.h"
    class myclass {
    public:
        // TODO: add your published variables here using GridLAB types (see PROPERTY)
        double myDouble; // just an example
    private:
        // TODO: add any unpublished variables here (any type is ok)
        double *pDouble; // just an example
    public:
        static CLASS *oclass;
        static myclass *defaults;
    public:
        myclass(MODULE *module);
        int create(void);
        int init(OBJECT *parent);
        TIMESTAMP sync(TIMESTAMP t0, TIMESTAMP t1);
    };
    #endif

The implementation file should include the following:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include "myclass.h"
    CLASS *myclass::oclass = NULL;
    myclass *myclass::defaults = NULL;
    myclass::myclass(MODULE *module)
    {
        if (oclass==NULL)
        {
            oclass = gl_register_class(module,"myclass",PC_BOTTOMUP);
            if (gl_publish_variable(oclass,
                // TODO: publish your variables here
                PT_double, "my_double", PADDR(myDouble), // just an example
                NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
            defaults = this;
            // TODO: initialize the default values that apply to all objects of this class
            myDouble = 1.23; // just an example
            pDouble = NULL; // just an example
        }
    }
    void myclass::create(void)
    {
        memcpy(this,defaults,sizeof(*this));
        // TODO: initialize the defaults values that do not depend on relatioships with other objects
    }
    int myclass::init(OBJECT *parent)
    {
        // find the data in the parent object
        myclass *pMyClass = OBJECTDATA(parent,myclass); // just an example

        // TODO: initialize the default values that depend on relationships with other objects
        pDouble = &(pMyClass->double); // just an example

        // return 1 on success, 0 on failure
        return 1;
    }
    TIMESTAMP myclass::sync(TIMESTAMP t0, TIMESTAMP t1)
    {
        // TODO: update the state of the object
        myDouble = myDouble*1.01; // just an example

        // TODO: compute time to next state change
        return (TIMESTAMP)(t1 + myDouble/TS_SECOND); // just an example
    }

    // IMPLEMENTATION OF CORE LINKAGE

    EXPORT int create_myclass(OBJECT **obj, 
                              OBJECT *parent) 
    {
        *obj = gl_create_object(myclass::oclass,sizeof(myclass));
        if (*obj!=NULL)
        {
            myclass *my = OBJECTDATA(*obj,myclass);
            gl_set_parent(*obj,parent);
            my->create();
            return 1;
        }
        return 0;
    }

    EXPORT int init_myclass(OBJECT *obj, 
                            OBJECT *parent) 
    {
        if (obj!=NULL)
        {
            myclass *my = OBJECTDATA(obj,myclass);
            return my->init(parent);
        }
        return 0;
    }

    EXPORT TIMESTAMP sync_myclass(OBJECT *obj, 
                                  TIMESTAMP t1) 
    {
        TIMESTAMP t2 = OBJECTDATA(obj,myclass)->sync(obj->clock,t1);
        obj->clock = t1;
        return t2;
    }

There are a number of useful extended capabilities that can be added. These include

 int notify_myclass(OBJECT *obj, NOTIFYMODULE msg) 
can be implemented to receive notification messages before and after a variable is changed by the core (NM_PREUPDATE / NM_POSTUPDATE) and when the core needs the module to reset (NM_RESET)

 int isa_myclass(OBJECT *obj, char *classname) 
can be implemented to allow the core to discover whether an object is a subtype of another class.

 int plc_myclass(OBJECT *obj, TIMESTAMP t1) 
can be implemented to create default PLC code that can be overridden by attaching a child plc object.

 EXPORT int recalc_myclass(OBJECT *obj) 
can be implemented to create a recalculation triggered based on changes to properties made through object_set_value_by_addr() and related functions. A property can be made to trigger recalculation calls by adding PT_FLAGS, PF_RECALC after the property publish specification, e.g.,
 (gl_publish_variable(oclass, PT_double, "resistance[ohm]", PADDR(resistance), PT_FLAGS, PF_RECALC, NULL); 
will cause recalc() to be called after resistance is changed. The recalc calls occur right before the PLC code is run during sync() events.

A Linux GridLAB module must be a shared object library.


Defines

#define _STR(x)   #x
#define DLLOAD(P)   dlopen(P,RTLD_LAZY)
#define DLSYM(H, S)   dlsym(H,S)
#define LIBEXT   .so
#define LIBPREFIX   "lib"
#define STR(x)   _STR(x)

Typedefs

typedef MODULE *(*) LOADER (const char *, int, char *[])
 Load a runtime module.

Functions

void dlload_error (const char *filename)
int get_exe_path (char *buf, int len, void *mod)
int module_check (MODULE *mod)
int module_checkall (void)
int module_cmdargs (int argc, char **argv)
int module_dumpall (void)
MODULEmodule_find (char *modname)
int module_get_exe_path (char *buf, int len)
MODULEmodule_get_first (void)
int module_get_path (char *buf, int len, MODULE *mod)
void * module_getvar (MODULE *mod, char *varname, char *value, unsigned int size)
double * module_getvar_addr (MODULE *mod, char *varname)
int module_import (MODULE *mod, char *filename)
void module_libinfo (char *module_name)
MODULEmodule_load (const char *file, int argc, char *argv[])
int module_save (MODULE *mod, char *filename)
int module_saveall (FILE *fp)
int module_saveall_xml (FILE *fp)
int module_saveall_xml_old (FILE *fp)
int module_saveobj_xml (FILE *fp, MODULE *mod)
int module_setvar (MODULE *mod, char *varname, char *value)

Variables

MODULEfirst_module = NULL
MODULElast_module = NULL
int64 lock_count
int64 lock_spin


Typedef Documentation

typedef MODULE*(*) LOADER(const char *, int, char *[])

Load a runtime module.

Returns:
a pointer to the MODULE structure NULL on failure, errno set to:
  • ENOEXEC to indicate init() not defined in module
  • EINVAL to indicate call to init failed
  • ENOENT to indicate class not defined by module

Definition at line 555 of file module.c.


Function Documentation

MODULE* module_load ( const char *  file,
int  argc,
char *  argv[] 
)

Parameters:
file  module filename, searches PATH
argc  count of arguments in argv
argv  arguments passed from the command line

Definition at line 556 of file module.c.

References class_get_first_class(), class_get_last_class(), init(), last_module, load_java_module(), load_python_module(), module_find(), module_load(), s_class_list::next, s_module_list::next, s_module_list::oclass, output_error(), output_verbose(), and s_module_list::subload.

Referenced by cmex_module(), module_libinfo(), module_load(), and output_xsd().

int module_saveobj_xml ( FILE *  fp,
MODULE mod 
)

< the stream to write to

Definition at line 860 of file module.c.

References s_class_list::module, s_object_list::name, s_object_list::next, object_get_first(), and s_object_list::oclass.

Referenced by module_saveall_xml().


GridLAB-DTM Version 1.0
An open-source project initiated by the US Department of Energy