file | type | explanation |
---|---|---|
device.properties | O | Configuration file |
libgebiec.a | GO | GEB IEC 61131 support binary (static) library |
gebiec.h | G | GEB IEC 61131 standard libraries headers (implemented in libgebiec.a) |
gebtime.h | G | GEB specific time related functions (implemented in libgebiec.a) |
geblib.h | G | GEB IEC 61131 support functions (implemented in libgebiec.a) |
gebdebug.h | G | GEB specific debug related functions (implemented in libgebiec.a) |
gebdbgprot.h | G | GEB specific debug comm protocol functions (implemented in libgebiec.a) |
geb.h | G | GEB public header files: interface to the device code. |
gebarch.h | Os | Architecture/compiler dependent definitions (types and macros) |
gebdrv.h | G | GEB gral mechanisms for var mapping (directly represented variables) |
deviceio.c | Os | Device specific i/o code, variables mapping, time functions |
gebud.h | Os | "User defined" functions: native libraries, code supplied by OEM |
main.c | Os | Executable entry point, main loop. |
build.bat | Os | Script for building binary (calls C compiler) |
transfer.bat | Os | Script for transfering binary (only relevant if local build and remote run) |
run.bat | Os | Script for local or remote running/debugging (if supported) |
debugcomm.h | G | Low level communication for debugging (declaration) |
debugcomm.c | Os | Low level communication for debugging (if supported) |
More than one device can be declared in one device directory, in extra device_XXX.properties configuration files. The device name is specified in the configuration file, is should be unique inside an installation.
The name of OEM provided source code files are arbitrary; more device specific code can be added, as well as header files and binary libraries.
Changes in the device configuration should be reloaded automatically by the IDE, eventually they might require a restart.
#starts a comment.
For details about device.properties config file properties and their syntax, see any of the included files.
You should also edit the declaration of the range of directly represented (mapped) variables for this device. Eg:
# mapped addresses : format according to norms (page 37) . we allow ranges # # format of keys: ioaddress.ID.range ioaddress.ID.comment # where ID is an arbitrary (unique) alphanumeric identifier, and 'comment' is optional # files in filesystem - for demonstration only ioaddress.f1.range = %IW51.1-16 ioaddress.f1.comment = Input Word (16 bits) in file # ioaddress.m1.range=%IX1.1-16 ioaddress.m1.comment=MODBUS DIGITAL COIL TABLEIn that example, that line means the device knows how to map any AT variable with %MW10.1. as prefix. You can add more lines to add another ranges or individual addresses.
4.a Variable mappings: directly represented (AT) variables
In the simplest implementation, you define (typically in gebarch.h) the constant
#define USE_DRVarModuleSimpleand then you just need to code in (say) deviceio.c these functions from gebdrv.h:
void geb_drv_set_int8(char d,char w,int ad0,int ad1,int ad2,int ad3,int8_t val); int8_t geb_drv_get_int8(char d,char w,int ad0,int ad1,int ad2,int ad3); void geb_drv_set_int16(char d,char w,int ad0,int ad1,int ad2,int ad3,int16_t val); int16_t geb_drv_get_int16(char d,char w,int ad0,int ad1,int ad2,int ad3); void geb_drv_set_int32(char d,char w,int ad0,int ad1,int ad2,int ad3,int32_t val); int32_t geb_drv_get_int32(char d,char w,int ad0,int ad1,int ad2,int ad3); void geb_drv_set_float(char d,char w,int ad0,int ad1,int ad2,int ad3,float val); float geb_drv_get_float(char d,char w,int ad0,int ad1,int ad2,int ad3);This assumes a simple address scheme: any AT address is split into (up to) four numeric fields, which correspond to ad0,ad1,ad2,ad3 arguments above. The first two letters after the % character are passed as arguments d=directionand w=width; these is merely informative, most of the semantics of these characters is already taken into account on upper layers, so typically this can be ignored. For example reading a variable %MW10.2.4 it result in a calll to `geb_drv_get_int16('M', 'W', 10, 2, 4, 0)`
If not need more flexible mappings (more datatypes, or other addressing schemes) you can comment out the line #define USE_DRVarModuleSimple and provide your own implementation of the functions defined in gebdrv.h
See the deviceio.c in the included devices for examples and templates.
NOTE: the behaviour of the AT mapping has changed in version 3. Now all the AT variables in the project are allocated in a global heap, and they are read/written from/to IO devices by calling the above functions only at the start/end of the execution cycle. This implies that now IO reading/writing is not performed each time an AT var is accesed, but only before and after the cycle execution. This also implies that POUs execution works with a snapshot of the IO vars, there are no asynchronous changes due to IO inputs varying during execution - as it should be.
4.b Variable persistence: RETAIN variables
The device must implement these three functions:
/* loads data from disk, flash, etc - this will be called by Geb - returns negative if error, 0 if no data */ int readRetainData(unsigned char *buffer, int len); /* saves retain data - returns negative if error */ int writeRetainData(unsigned char * buffer,int len); /* delete all persisted data from flash/disk - to force a cold reboot - this should be called in case of fatal error */ void deleteRetainData(void);Typically, these will simply read and write the bytes from FLASH memory or hard disk, if available. Examples are provided. There's no need to add extra intelligence -to check for changes, for example-, the GEB generated code will call these functions only when necessary.
4.c Time related functions
Because this is highly device specific, we define a simple time API via these straightforward functions (declared in geb.h), that the device manufacturer must implement:
/* Gets the current time counter read, in ticks (not necessarily a real time clock) The meaning of the fields is totally arbitrary, they are opaque placeholders, to be interpreted by the device code itself in the function dev_elapsed_time() */ void dev_time(int32_t *hi,uint32_t *lo); /* Gets the real time elapsed since the timestamp (data stored from a previous dev_time call) till now. The implementation should decide on the time unit and inform it in the 'unit' argument. unit= 0=usec (10e-6 secs) 1=msec (10e-3 secs - RECOMMENDED) 2: seconds unit=-1: not implemented */ int32_t dev_elapsed_time(int32_t ts_hi,uint32_t ts_lo, int *unit); /* Gets current calendar time - optional - month 1=january - fill all with zero if not implemented */ void dev_datetime(int16_t *year,int16_t *mon,int16_t *mday, int16_t *hour, int16_t *min,int16_t * sec,int16_t *msec); /* Tries to sleep usecs micro-seconds - usually a resolution of milliseconds is enough. Returns the usecs slept. */ int dev_usleep(int usec);Again, examples are provided - see the sample devices deviceio.c files.
4.d Low level communication for debugger
If the device supports on-hardware debugging, debugcomm.c should implement the functions declared in debugcomm.h. The example provided in debugcomm_sample.c should work for any environment that supports TCP/IP sockets API (Linux and Windows included).
6.a Implement global special functions
The OEM code must implement a few functions defined and commented in geb.h.
/** * Handle an event, for example writing to stdout or to a log file. * There are several different event types * 0: Fatal error (this should probably call deleteRetainData() to force a cold reboot) * 1: Error (non fatal) * 2: Warning / Info * 3: Debug */ void gebEvent(const char* s, int type); /** * Handler for out-of-range errors. Tyically implemented in main.c, this should trigger a fatal error. */ void outOfRange(long v, long vmin, long vmax, const char *msg);
6.b Implement main loop, invoking hooks
The main execution code and execution loop should call the following function at the appropiate points:
/* hook for performing actions in different points of the execution cycle */ #define GEB_WHERE_STARTING 0 /* prior to entering the main loop */ #define GEB_WHERE_LOOP 1 /* inside loop, not running pou - this could be called very often */ #define GEB_WHERE_CYCLE_START 2 /* prior to execution of group of programs (cycle start) */ #define GEB_WHERE_CYCLE_END 3 /* after execution of group of programs (cycle end) */ #define GEB_WHERE_END_OF_PROGRAM 4 /* inside program, just before ending*/ #define GEB_WHERE_END_OF_FUNC_FB 5 /* inside funciton or FB (not program) pou, just before ending */ #define GEB_WHERE_ENDING 6 /* exiting main loop */ #define GEB_WHERE_SUSPENSION 10 /* debugging: suspended (called repeteadly) */ void geb_hook(int where,bool debugmode);See example in common_f.c
6.c Implement hook function
Typically you should copy the geb_hook in common_f.c 6.d Implement main function
It's important to call follow the pattern in example main.c at start:
gebInit(); /* should be called early, even before parsing command line args */ if(! parseArgs(argc,argv)) return 1;The parsing of arguments should support the standard GEB arguments (see againg example in common_f.c) in order to support run/debug from the IDE.