diff --git a/hipadaba.c b/hipadaba.c new file mode 100644 index 00000000..3ec67b0f --- /dev/null +++ b/hipadaba.c @@ -0,0 +1,815 @@ +/*------------------------------------------------------------------------- + The hierarchical parameter database code. For more information, see + hipadaba.h + + copyright: GPL + + Mark Koennecke, June 2006 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "hipadaba.h" + +#define ABS(x) (x < 0 ? -(x) : (x)) +#define HDBMAGICK 77119900 +/*================== internal functions ===================================*/ +void DeleteCallbackChain(pHdbCallback root){ + pHdbCallback current = NULL, thisEntry; + + current = root; + while(current != NULL){ + if(current->killFunc != NULL){ + current->killFunc(current->userData); + } + thisEntry = current; + current = (pHdbCallback)current->next; + free(thisEntry); + } +} +/*-----------------------------------------------------------------------*/ +static void DeleteNodeData(pHdb node){ + pHdb tmp = NULL; + + if(node == NULL){ + return; + } + DeleteCallbackChain(node->writeCallbacks); + DeleteCallbackChain(node->updateCallbacks); + DeleteCallbackChain(node->readCallbacks); + + if(node->name != NULL){ + free(node->name); + } + ReleaseHdbValue(&node->value); + node->magic = 000000; + + while(node->child != NULL){ + tmp = node->child; + node->child = node->child->next; + DeleteNodeData(tmp); + } + free(node); +} +/*------------------------------------------------------------------------*/ +void RemoveHdbNodeFromParent(pHdb node){ + pHdb parent = NULL; + pHdb current = NULL; + + parent = node->mama; + if(parent != NULL){ + if(parent->child == node){ + parent->child = node->next; + return; + } + current = parent->child; + while(current->next != node){ + current = current->next; + } + current->next = current->next->next; + } +} +/*-----------------------------------------------------------------------*/ +static void RemoveCallbackNode(pHdbCallback victim){ + if(victim->previous != NULL) { + victim->previous->next = victim->next; + } + if(victim->next != NULL){ + victim->next->previous = victim->previous; + } + if(victim->killFunc != NULL){ + victim->killFunc(victim->userData); + } + free(victim); +} +/*----------------------------------------------------------------------- + * This code is ugly: the problem is fixing up the start of the chain. + * Think about it and improve + * ----------------------------------------------------------------------*/ +static pHdbCallback DeleteForID(pHdbCallback root, int id){ + pHdbCallback current = root; + pHdbCallback tmp = NULL; + pHdbCallback result = NULL; + + if(root == NULL){ + return NULL; + } + + /* + * delete at the start of the chain + */ + result = root; + while(result->id == id){ + tmp = result; + result = result->next; + RemoveCallbackNode(tmp); + if(result == NULL){ + return NULL; + } + } + + /* + * delete nodes in the middle of the chain + */ + current = result; + while(current != NULL){ + if(current->id == id){ + tmp = current; + current = (pHdbCallback)current->next; + RemoveCallbackNode(tmp); + } else { + current = (pHdbCallback)current->next; + } + } + return result; +} +/*-----------------------------------------------------------------------*/ +static pHdbCallback DeleteForInternalID(pHdbCallback root, int id){ + pHdbCallback current = root; + pHdbCallback tmp = NULL; + pHdbCallback result = NULL; + + if(root == NULL){ + return NULL; + } + + /* + * delete at the start of the chain + */ + result = root; + while(result->internalID == id){ + tmp = result; + result = result->next; + if(tmp->killFunc != NULL){ + tmp->killFunc(tmp->userData); + } + free(tmp); + if(result == NULL){ + return NULL; + } + } + + /* + * delete nodes in the middle of the chain + */ + current = result; + while(current != NULL){ + if(current->internalID == id){ + if(current->next != NULL){ + current->next->previous = current->previous; + } + if(current->previous != NULL){ + current->previous->next = current->next; + } + tmp = current; + current = (pHdbCallback)current->next; + if(tmp->killFunc != NULL){ + tmp->killFunc(tmp->userData); + } + free(tmp); + } else { + current = (pHdbCallback)current->next; + } + } + return result; +} +/*-------------------------------------------------------------------------*/ +static int InvokeCallbackChain(pHdbCallback root, pHdb node, + void *callData, hdbValue v){ + pHdbCallback current = root; + int status; + + while(current != NULL){ + status = current->userCallback(current->userData,callData, + node,v); + if(status != 1){ + return status; + } + current = current->next; + } + return 1; +} +/*----------------------------------------------------------------------*/ +char *hdbTrim(char *str) +{ + char *ibuf = str, *obuf = str; + int i = 0, cnt = 0; + + /* + ** Trap NULL + */ + + if (str) + { + /* + ** Remove leading spaces (from RMLEAD.C) + */ + + for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf) + ; + if (str != ibuf) + memmove(str, ibuf, ibuf - str); + + /* + ** Collapse embedded spaces (from LV1WS.C) + */ + + while (*ibuf) + { + if (isspace(*ibuf) && cnt) + ibuf++; + else + { + if (!isspace(*ibuf)) + cnt = 0; + else + { + *ibuf = ' '; + cnt = 1; + } + obuf[i++] = *ibuf++; + } + } + obuf[i] = '\0'; + + /* + ** Remove trailing spaces (from RMTRAIL.C) + */ + + while (--i >= 0) + { + if (!isspace(obuf[i])) + break; + } + obuf[++i] = '\0'; + } + return str; +} +/*------------------------------------------------------------------------*/ +static pHdb locateChild(pHdb root, char *name){ + pHdb current = NULL; + + current = root->child; + while(current != NULL){ + if(strcmp(current->name,name) == 0){ + return current; + } + current = current->next; + } + return NULL; +} +/*================= data functions ========================================*/ +hdbValue makeHdbValue(int datatype, int length){ + hdbValue val; + + memset(&val,0,sizeof(hdbValue)); + val.dataType = datatype; + + switch(datatype){ + case HIPINTAR: + case HIPINTVARAR: + val.arrayLength = length; + val.v.intArray = malloc(length*sizeof(long)); + if(val.v.intArray != NULL){ + memset(val.v.intArray,0,length*sizeof(long)); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + val.arrayLength = length; + val.v.floatArray = malloc(length*sizeof(double)); + if(val.v.floatArray != NULL){ + memset(val.v.floatArray,0,length*sizeof(double)); + } + break; + case HIPTEXT: + val.v.text = strdup("UNKNOWN"); + break; + } + return val; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbInt(int initValue){ + hdbValue result; + + result.dataType = HIPINT; + result.v.intValue = initValue; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbFloat(double initValue){ + hdbValue result; + + result.dataType = HIPFLOAT; + result.v.doubleValue = initValue; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbText(char *initText){ + hdbValue result; + + result.dataType = HIPTEXT; + result.v.text = initText; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbIntArray(int length, long *data){ + hdbValue result; + + result.dataType = HIPINTAR; + result.arrayLength = length; + result.v.intArray = data; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbFloatArrray(int length, double *data){ + hdbValue result; + + result.dataType = HIPFLOATAR; + result.arrayLength = length; + result.v.floatArray = data; + return result; +} +/*-------------------------------------------------------------------------*/ +void ReleaseHdbValue(hdbValue *v){ + switch(v->dataType){ + case HIPTEXT: + if(v->v.text != NULL){ + free(v->v.text); + } + break; + case HIPINTAR: + case HIPINTVARAR: + if(v->v.intArray != NULL){ + free(v->v.intArray); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(v->v.floatArray != NULL){ + free(v->v.floatArray); + } + break; + } +} +/*------------------------------------------------------------------------*/ +int compareHdbValue(hdbValue v1, hdbValue v2){ + int i; + + if(v1.dataType != v2.dataType){ + return 0; + } + switch(v1.dataType){ + case HIPNONE: + return 0; + break; + case HIPINT: + if(v1.v.intValue == v2.v.intValue){ + return 1; + } else { + return 0; + } + break; + case HIPFLOAT: + if(ABS(v1.v.doubleValue - v2.v.doubleValue) < .01){ + return 1; + } else { + return 0; + } + break; + case HIPTEXT: + if(strcmp(v1.v.text,v2.v.text) == 0){ + return 1; + } else { + return 0; + } + break; + case HIPINTAR: + case HIPINTVARAR: + if(v1.arrayLength != v2.arrayLength){ + return 0; + } + for(i = 0; i < v1.arrayLength; i++){ + if(v1.v.intArray[i] != v2.v.intArray[i]){ + return 0; + } + } + return 1; + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(v1.arrayLength != v2.arrayLength){ + return 0; + } + for(i = 0; i < v1.arrayLength; i++){ + if(ABS(v1.v.floatArray[i] - v2.v.floatArray[i]) > .01){ + return 0; + } + } + return 1; + break; + default: + assert(0); + break; + } + return 0; +} +/*-------------------------------------------------------------------------*/ +int cloneHdbValue(hdbValue *source, hdbValue *clone){ + + memset(clone,0,sizeof(hdbValue)); + clone->dataType = source->dataType; + return copyHdbValue(source, clone); +} +/*================= node functions ========================================*/ +pHdb MakeHipadabaNode(char *name, int datatype, int length){ + pHdb pNew = NULL; + + pNew = malloc(sizeof(Hdb)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(Hdb)); + pNew->magic = HDBMAGICK; + pNew->name = strdup(name); + pNew->value.dataType = datatype; + switch(datatype){ + case HIPINTAR: + case HIPINTVARAR: + pNew->value.arrayLength = length; + pNew->value.v.intArray = malloc(length*sizeof(long)); + if(pNew->value.v.intArray == NULL){ + return NULL; + } + memset(pNew->value.v.intArray,0,length*sizeof(long)); + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + pNew->value.arrayLength = length; + pNew->value.v.floatArray = malloc(length*sizeof(double)); + if(pNew->value.v.floatArray == NULL){ + return NULL; + } + memset(pNew->value.v.floatArray,0,length*sizeof(double)); + break; + case HIPTEXT: + pNew->value.v.text = strdup("UNKNOWN"); + break; + } + return pNew; +} +/*-------------------------------------------------------------------------*/ +void AddHipadabaChild(pHdb parent, pHdb child){ + pHdb current = NULL, prev = NULL; + + assert(parent != NULL && child != NULL); + + current = parent->child; + child->mama = parent; + if(current == NULL){ + parent->child = child; + child->next = NULL; + } else { + /* + * step to end of child chain + */ + while(current != NULL){ + prev = current; + current = current->next; + } + child->next = NULL; + prev->next = child; + } +} +/*--------------------------------------------------------------------------*/ +void DeleteHipadabaNode(pHdb node){ + pHdb current = NULL, tmp = NULL; + + if(node == NULL){ + return; + } + + RemoveHdbNodeFromParent(node); + + DeleteNodeData(node); +} +/*--------------------------------------------------------------------------*/ +int isHdbNodeValid(pHdb node){ + if(node == NULL){ + return 0; + } + if(node->magic == HDBMAGICK){ + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------------*/ +pHdb GetHipadabaNode(pHdb root, char *puth){ + pHdb resultNode = NULL; + char *separator = NULL; + char *path = NULL, *pathData; + + /* + * I need to make a copy in order to get the path in writable memory. + * Otherwise we SEGFAULT in hdbTrim when this function is called with + * a string constant in puth + */ + pathData = strdup(puth); + path = pathData; + if(path == NULL){ + return NULL; + } + path = hdbTrim(path); + + if(strcmp(path,"/") == 0 || strlen(path) == 0){ + free(pathData); + return root; + } + + if(path[0] == '/'){ + path++; + } + + separator = strchr(path,'/'); + if(separator == NULL){ + resultNode = locateChild(root,path); + free(pathData); + return resultNode; + } else { + *separator = '\0'; + resultNode = locateChild(root, path); + if(resultNode == NULL){ + free(pathData); + return NULL; + } else { + separator++; + resultNode = GetHipadabaNode(resultNode,separator); + free(pathData); + return resultNode; + } + } +} +/*--------------------------------------------------------------------------*/ +char *GetHipadabaPath(pHdb node){ + pHdb nodeStack[64]; + int depth = 0, length = 0, i; + pHdb current = NULL; + char *pPtr = NULL; + + /** + * build a nodestack and find out required string length for path + */ + current = node; + while(current != NULL){ + length += strlen(current->name) + 1; + nodeStack[depth] = current; + depth++; + assert(depth < 64); + current = current->mama; + } + + pPtr = malloc(length*sizeof(char)); + if(pPtr == NULL){ + return NULL; + } + memset(pPtr,0,length*sizeof(char)); + + /* + * we wish to decremement by one because above loop + * increments one to many and we wish to ignore the + * root node + */ + for(i = depth - 2; i >= 0; i--){ + strcat(pPtr,"/"); + strcat(pPtr,nodeStack[i]->name); + } + return pPtr; +} +/*==================== Callback Functions ==================================*/ +pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, + void *userData, killUserData killFunc, + int id, int internalID){ + pHdbCallback pNew = NULL; + + assert(func != NULL); + + pNew = malloc(sizeof(hdbCallback)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(hdbCallback)); + + pNew->userCallback = func; + pNew->userData = userData; + pNew->killFunc = killFunc; + pNew->id = id; + pNew->internalID = internalID; + return pNew; +} +/*-------------------------------------------------------------------*/ +void AppendHipadabaCallback(pHdb node, int type, pHdbCallback newCB){ + pHdbCallback current = NULL; + + switch(type){ + case HCBSET: + if(node->writeCallbacks == NULL){ + node->writeCallbacks = newCB; + return; + } else { + current = node->writeCallbacks; + } + break; + case HCBUPDATE: + if(node->updateCallbacks == NULL){ + node->updateCallbacks = newCB; + return; + } else { + current = node->updateCallbacks; + } + break; + case HCBREAD: + if(node->readCallbacks == NULL){ + node->readCallbacks = newCB; + return; + } else { + current = node->readCallbacks; + } + break; + default: + assert(0); + break; + } + if(current != NULL){ + while(current->next != NULL){ + current = (pHdbCallback)current->next; + } + current->next= newCB; + newCB->previous = current; + } +} +/*-------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void PrependHipadabaCallback(pHdb node, int type, pHdbCallback newCB){ + switch(type){ + case HCBSET: + if(node->writeCallbacks == NULL){ + node->writeCallbacks = newCB; + return; + } else { + newCB->next = node->writeCallbacks; + node->writeCallbacks->previous = newCB; + node->writeCallbacks = newCB; + } + break; + case HCBUPDATE: + if(node->updateCallbacks == NULL){ + node->updateCallbacks = newCB; + return; + } else { + newCB->next = node->updateCallbacks; + node->updateCallbacks->previous = newCB; + node->updateCallbacks = newCB; + } + break; + case HCBREAD: + if(node->readCallbacks == NULL){ + node->readCallbacks = newCB; + return; + } else { + newCB->next = node->readCallbacks; + node->readCallbacks->previous = newCB; + node->readCallbacks = newCB; + } + break; + default: + assert(0); + break; + } +} +/*----------------------------------------------------------------------------*/ +void RemoveHipadabaCallback(pHdb root, int id){ + pHdb current = NULL; + + root->writeCallbacks = DeleteForID(root->writeCallbacks,id); + root->updateCallbacks = DeleteForID(root->updateCallbacks,id); + root->readCallbacks = DeleteForID(root->readCallbacks,id); + + current = root->child; + while(current != NULL){ + RemoveHipadabaCallback(current,id); + current = current->next; + } +} +/*----------------------------------------------------------------------------*/ +void InternalRemoveHipadabaCallback(pHdb root, int internalID){ + pHdb current = NULL; + + root->writeCallbacks = DeleteForInternalID(root->writeCallbacks,internalID); + root->updateCallbacks = DeleteForInternalID(root->updateCallbacks,internalID); + root->readCallbacks = DeleteForInternalID(root->readCallbacks,internalID); + + current = root->child; + while(current != NULL){ + InternalRemoveHipadabaCallback(current,internalID); + current = current->next; + } +} +/*=================== parameter interface ====================================*/ +int copyHdbValue(hdbValue *source, hdbValue *target){ + int i; + + if(source->dataType != target->dataType){ + return 0; + } + + switch(source->dataType){ + case HIPNONE: + break; + case HIPINT: + target->v.intValue = source->v.intValue; + break; + case HIPFLOAT: + target->v.doubleValue = source->v.doubleValue; + break; + case HIPTEXT: + if(target->v.text != NULL){ + free(target->v.text); + } + target->v.text = strdup(source->v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + if(target->arrayLength != source->arrayLength){ + if(target->v.intArray != NULL){ + free(target->v.intArray); + } + target->v.intArray = malloc(source->arrayLength * sizeof(long)); + if(target->v.intArray == NULL){ + return 0; + } + memset(target->v.intArray,0,source->arrayLength * sizeof(long)); + target->arrayLength = source->arrayLength; + } + for(i = 0; i < source->arrayLength; i++){ + target->v.intArray[i] = source->v.intArray[i]; + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(target->arrayLength != source->arrayLength){ + if(target->v.floatArray != NULL){ + free(target->v.floatArray); + } + target->v.floatArray = malloc(source->arrayLength * sizeof(double)); + if(target->v.floatArray == NULL){ + return 0; + } + memset(target->v.floatArray,0,source->arrayLength * sizeof(double)); + target->arrayLength = source->arrayLength; + } + for(i = 0; i < source->arrayLength; i++){ + target->v.floatArray[i] = source->v.floatArray[i]; + } + break; + default: + /* + * unknown data type + */ + assert(0); + break; + } + return 1; +} +/*----------------------------------------------------------------------------*/ +int SetHipadabaPar(pHdb node, hdbValue v, void *callData){ + int status; + + status = InvokeCallbackChain(node->writeCallbacks, node, callData, v); + return status; +} +/*-----------------------------------------------------------------------------*/ +int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData){ + int status; + + status = InvokeCallbackChain(node->updateCallbacks, node, callData, v); + if(status != 1 ){ + return status; + } + copyHdbValue(&v,&node->value); + return 1; +} +/*-----------------------------------------------------------------------------*/ +int GetHipadabaPar(pHdb node, hdbValue *v, void *callData){ + int status; + + status = InvokeCallbackChain(node->readCallbacks, node, callData, *v); + if(status != 1 ){ + return status; + } + v->dataType = node->value.dataType; + copyHdbValue(&node->value,v); + return 1; +} + diff --git a/hipadaba.h b/hipadaba.h new file mode 100644 index 00000000..bc0f4e99 --- /dev/null +++ b/hipadaba.h @@ -0,0 +1,270 @@ +/** + * Hipadaba is a hierarchical database of parameters. Parameters can be of + * various types. What happens when a parameter is being set, updated or read + * is largely determined through callbacks which can be registered on + * parameters. This can implement permission checking, range checking, + * automatic notifications and whatever comes up. + * + * There is some subtlety here between updating and setting a parameter. The + * issue is that in instrument control there are two types of parameters: + * Instant program parameters and external parameters like motors which are + * dependent on some possibly slow and inaccurate hardware. Let us consider + * the latter: Setting the parameter should do all necessary checks on the + * parameter and tell the hardware where to go. Some internal code may be + * watching the hardware; that code should use Update which justs sets a new value + * and invokes callbacks which notify interested parties about the new parameter + * value. For program parameters, a callback shall be installed which calls update + * directly after setting the parameter. Thus notification callbacks shall always be + * connected to the update chain. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + */ +#ifndef HIPADABA +#define HIPADABA + +/*------- datatypes */ +#define HIPNONE -1 +#define HIPINT 0 +#define HIPFLOAT 1 +#define HIPTEXT 2 +#define HIPINTAR 3 +#define HIPFLOATAR 4 +#define HIPINTVARAR 5 +#define HIPFLOATVARAR 6 +/* -------- callback types */ +#define HCBSET 0 +#define HCBUPDATE 1 +#define HCBREAD 2 +/*===================== structure definitions ===================================*/ +typedef struct __hdbValue { + int dataType; + int arrayLength; + union __value { + long intValue; + double doubleValue; + char *text; + long *intArray; + double *floatArray; + }v; +}hdbValue; +/*------------------------------------------------------------------------------*/ +typedef struct __hipadaba { + int magic; + struct __hipadaba *mama; + struct __hipadaba *child; + struct __hipadaba *next; + struct __hdbcallback *writeCallbacks; + struct __hdbcallback *updateCallbacks; + struct __hdbcallback *readCallbacks; + char *name; + hdbValue value; + }Hdb, *pHdb; +/*-------------------------------------------------------------------------------*/ +typedef int (*hdbCallbackFunction)(void *userData, void *callData, + pHdb currentNode, hdbValue v ); +typedef void (*killUserData)(void *data); +/*-------------------------------------------------------------------------------*/ +typedef struct __hdbcallback { + void *userData; + killUserData killFunc; + hdbCallbackFunction userCallback; + int id; + int internalID; + struct __hdbcallback *next; + struct __hdbcallback *previous; + }hdbCallback, *pHdbCallback; +/*======================== Function protoypes: hdbData ========================*/ +hdbValue makeHdbValue(int datatype, int length); +/** + * wrap an integer as an hdbValue + * @param initValue the initial value of the int + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbInt(int initValue); +/** + * wrap a float as an hdbValue + * @param initValue the initial value of the float + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbFloat(double initValue); +/** + * wrap a text string as an hdbValue + * @param initText the initial value of the text. WARNING: MakeHdbText does + * not copy the data. The Hdb code only copies data on updates. Normally this + * no problem; however in complicated cenarios it is better if initText points + * to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbText(char *initText); +/** + * wrap a int array as an hdbValue + * @param length The length of the int array + * @param data the initial content of the int array. WARNING: MakeHdbIntArray + * does not copy the data. The Hdb code only copies data on updates. Normally + * this no problem; however in complicated scenarios it is better if + * data points to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbIntArray(int length, long *data); +/** + * wrap a float array as an hdbValue + * @param length The length of the int array + * @param data the initial content of the float array. WARNING: MakeHdbFloatArray + * does not copy the data. The Hdb code only copies data on updates. Normally + * this no problem; however in complicated scenarios it is better if + * data points to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbFloatArray(int length, double *data); +/** + * release any dynamic memory associated with v + * @param v The hdbValue to check for dynamic memory allocation to be + * released. + */ +void ReleaseHdbValue(hdbValue *v); +/** + * copy a hipadaba value field. Takes care of memory allocation + * @param source The hdbValue to copy from + * @param target The hdbValue to copy to. + * @return 1 on success, 0 when out of memory or when type mismatch + */ +int copyHdbValue(hdbValue *source, hdbValue *target); +/** + * compares two hdbValues for identity + * @param v1 The first hdbValue + * @param v2 The second hdbValue + * @return 1 when identical, 0 else + */ +int compareHdbValue(hdbValue v1, hdbValue v2); +/** + * create a hdbValue structure with the identical properties + * as the one given as parameter. Datatypes are copied, memory is + * allocated etc. Data is copied, too + * @param source The hdbValue type to clone + * @param clone the target hdbValue structure + * @return 1 on success, 0 on when out of memory + */ +int cloneHdbValue(hdbValue *source, hdbValue *clone); +/*========================== function protoypes: Nodes =======================*/ +/** + * make a new hipadaba node + * @param name The name of the new node + * @param datatype The datatype of the new node + * @return a new node or NULL when out of memory + */ +pHdb MakeHipadabaNode(char *name, int datatype, int length); +/** + * add a child to a node + * @param parent The node to which to add the child + * @param child The node to add + */ +void AddHipadabaChild(pHdb parent, pHdb child); +/** + * delete a hipadaba node and all its children + * @parma node The node to delete + */ +void DeleteHipadabaNode(pHdb node); +/* + * checks if a Hdb node is valid + * @param node The node to check + * @return 1 when valid, 0 else + */ +int isHdbNodeValid(pHdb node); +/** + * retrieve a node + * @param root The node where to start the search for the node + * @param path The unix path string for the node relative to parent + * @return The desired node or NULL when no such node exists + */ +pHdb GetHipadabaNode(pHdb root, char *path); +/** + * given a node, return the full path name to the node + * @param node The node to get the path for + * @return The full path to the node. This is dynamically allocated memory; + * the caller is reponsible for deleting it. Can be NULL when out of memory. + */ +char *GetHipadabaPath(pHdb node); +/** + * removes a node from the parents child list. + * @node the node to remove + */ +void RemoveHdbNodeFromParent(pHdb node); +/** + * delete a callback chain + * @param root The callback chain to delete + */ +void DeleteCallbackChain(pHdbCallback root); +/*===================== function protoypes: Callbacks ========================*/ +/** + * make a new hipdaba callback + * @param func The function to invoke for this callback + * @param userData userData to be associated with this callback. Can be NULL. + * @param killFunc A function for freeing the userData. Can be NULL, then it will + * not be invoked + * @param id An ID associated with this callback + * @param internalID Another ID to be associated with this callback. ID's come in + * useful when callbacks have to be deleted in a later stage. + * @return A new suitabvly initialised callback structure or NULL when required elements + * are missing or there is nor memory. + */ +pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, + void *userData, killUserData killFunc, + int id, int internalID); +/** + * add a callback at the end of the callback chain + * @param node The node to which to append the callback + * @param type the type of the callback to append + * @param newCB The callback to append + */ +void AppendHipadabaCallback(pHdb node,int type, pHdbCallback newCB); +/** + * add a callback at the head of the callback chain + * @param node The node to which to append the callback + * @param type the type of the callback to append + * @param newCB The callback prepend + */ +void PrependHipadabaCallback(pHdb node, int type, pHdbCallback newCB); +/** + * remove recursively all callbacks witch match the id + * @param root The starting node from where to start removing callbacks + * @param id The ID callbacks have to match in order to be removed. + */ +void RemoveHipadabaCallback(pHdb root, int id); +/** + * remove recursively all callbacks witch match the internal id + * @param root The starting node from where to start removing callbacks + * @param internalID The internal ID callbacks have to match in order to be removed. + */ +void InternalRemoveHipadabaCallback(pHdb root, int internalID); + +/*============== Parameter Handling ===============================*/ +/** + * Set a hipadaba parameter. This is an external set for a parameter. It may cause + * motors to start driving etc. + * @param node The node for which to set the parameter + * @param v The new value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int SetHipadabaPar(pHdb node, hdbValue v, void *callData); +/** + * Update a hipadaba parameter. This is an internal update of a parameter, during + * driving etc. + * @param node The node for which to update the parameter + * @param v The new value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData); +/** + * Read a hipadaba parameter + * @param node The node for which to read the parameter + * @param v The read value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int GetHipadabaPar(pHdb node, hdbValue *v, void *callData); + +#endif diff --git a/sicshipadaba.c b/sicshipadaba.c new file mode 100644 index 00000000..564c037e --- /dev/null +++ b/sicshipadaba.c @@ -0,0 +1,1540 @@ +/** + * This is a set of helper functions for SICS to work with the hierarchical parameter + * database hipadaba. In SICS, the calldata associated with any callback will always + * be the connection object. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + */ +#include +#include +#include +#include +#include +#include +#include + +/*== there can be only hipadaba in SICS, some globals to care for that == */ +static pHdb root = NULL; +static int scriptUpdate = -1; +static hdbUpdateTask taskData; +/*=============== common callback functions used for SICS ===========================*/ +static int SICSCheckPermissionCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + int *testPriv = NULL; + SConnection *pCon = NULL; + + pCon = (SConnection *)callData; + testPriv = (int *)userData; + + /* + * If pCon is NULL, then this is an internal call from some driver + * code where no permission check is necessary. However, when called + * through the hipadaba tree commands and other upper level code, the + * check will be honoured. + */ + if(pCon == NULL){ + return 1; + } + + assert(testPriv != NULL); + + if(SCMatchRights(pCon,*testPriv) == 1){ + return 1; + } else { + return SICSCBPERM; + } +} +/*--------------------------------------------------------------------------------------*/ +pHdbCallback MakeCheckPermissionCallback(int priv){ + int *testPriv = NULL; + + testPriv = malloc(sizeof(int)); + if(testPriv == NULL){ + return NULL; + } + return MakeHipadabaCallback(SICSCheckPermissionCallback, testPriv,free,-1,-1); +} +/*-------------------------------------------------------------------------------------*/ +static int SICSSetUpdateCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + return UpdateHipadabaPar(node,v,callData); +} +/*-------------------------------------------------------------------------------------*/ +pHdbCallback MakeSetUpdateCallback(){ + return MakeHipadabaCallback(SICSSetUpdateCallback, NULL,NULL,-1,-1); +} +/*---------------------------------------------------------------------------------------*/ +static int SICSReadOnlyCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + SConnection *pCon = NULL; + + pCon = (SConnection *)callData; + + if(pCon != NULL){ + SCWrite(pCon,"ERROR: parameter is READ-ONLY", eError); + } + return SICSCBRO; +} +/*-------------------------------------------------------------------------------------*/ +static pHdbCallback MakeReadOnlyCallback(){ + return MakeHipadabaCallback(SICSReadOnlyCallback, NULL,NULL,-1,-1); +} +/*-------------------------------------------------------------------------------------*/ +static int SICSDriveCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + SConnection *pCon = NULL; + pDummy dum = NULL; + + pCon = (SConnection *)callData; + dum = (pDummy)userData; + assert(pCon != NULL && dum != NULL); + return StartDevice(pServ->pExecutor,node->name,dum->pDescriptor, + userData, pCon, (float)v.v.doubleValue); +} +/*---------------------------------------------------------------------------------------*/ +pHdbCallback MakeSICSDriveCallback(void *sicsObject){ + return MakeHipadabaCallback(SICSDriveCallback, sicsObject,NULL,-1,-1); +} +/*---------------------------------------------------------------------------------------*/ +static int SICSReadDriveCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + SConnection *pCon = NULL; + pDummy dum = NULL; + pIDrivable pDriv = NULL; + float value; + + pCon = (SConnection *)callData; + dum = (pDummy)userData; + assert(pCon != NULL && dum != NULL); + + pDriv = dum->pDescriptor->GetInterface(dum,DRIVEID); + assert(pDriv != NULL); + if(pCon != NULL){ + value = pDriv->GetValue(dum,pCon); + node->value.v.doubleValue = (double)value; + v.v.doubleValue = (double)value; + } + return 1; +} +/*--------------------------------------------------------------------------------------*/ +pHdbCallback MakeSICSReadDriveCallback(void *sicsObject){ + return MakeHipadabaCallback(SICSReadDriveCallback, sicsObject,NULL,-1,-1); +} +/*---------------------------------------------------------------------------------------*/ +typedef struct { + SConnection *pCon; + commandContext context; +}HdbCBInfo; +/*----------------------------------------------------------------------------------------*/ +static int SICSNotifyCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + HdbCBInfo *cbInfo = NULL; + pDynString printedData = NULL; + pDynString result = NULL; + char *pPath = NULL; + + cbInfo = (HdbCBInfo *)userData; + pPath = GetHipadabaPath(node); + printedData = formatValue(v); + result = CreateDynString(128,128); + if(pPath == NULL || printedData == NULL || result == NULL){ + SCWriteInContext(cbInfo->pCon,"ERROR: out of memory formatting data" , + eEvent,cbInfo->context); + /* + * no need to interrupt something because writing data to a client does + * not work + */ + return 1; + } + DynStringCopy(result,pPath); + DynStringConcat(result," = "); + DynStringConcat(result,GetCharArray(printedData)); + SCWriteInContext(cbInfo->pCon,GetCharArray(result), + eEvent,cbInfo->context); + free(pPath); + DeleteDynString(result); + DeleteDynString(printedData); + + return 1; +} +/*-----------------------------------------------------------------------------------------*/ +pHdbCallback MakeNotifyCallback(SConnection *pCon, int id){ + HdbCBInfo *cbInfo = NULL; + + cbInfo = malloc(sizeof(HdbCBInfo)); + if(cbInfo == NULL){ + return NULL; + } + cbInfo->pCon = pCon; + cbInfo->context = SCGetContext(pCon); + return MakeHipadabaCallback(SICSNotifyCallback, cbInfo,free,id,pCon->ident); +} +/*----------------------------------------------------------------------------------------*/ +static int SICSScriptWriteCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + char *command = NULL; + SConnection *pCon = NULL; + pDynString newVal = NULL; + char error[1024]; + int status; + + command = (char *)userData; + pCon = (SConnection *)callData; + + assert(command != NULL && pCon != NULL); + + newVal = formatValue(v); + if(newVal == NULL){ + SCWrite(pCon,"ERROR: out of memory setting parameter",eError); + return 0; + } + + /** + * prepend command + */ + DynStringInsert(newVal," ", 0); + DynStringInsert(newVal,command,0); + + /* + * evaluate + */ + status = Tcl_Eval(InterpGetTcl(pServ->pSics),GetCharArray(newVal)); + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + SCWrite(pCon,error,eError); + status = 0; + } else { + status = 1; + } + DeleteDynString(newVal); + return status; +} +/*---------------------------------------------------------------------------------------*/ +static pHdbCallback MakeSICSWriteScriptCallback(char *script){ + return MakeHipadabaCallback(SICSScriptWriteCallback, strdup(script),free,-1,-1); +} +/*----------------------------------------------------------------------------------------*/ +static int SICSScriptReadCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + char *command = NULL, *data = NULL, *equal = NULL; + SConnection *pCon = NULL; + pDynString newVal = NULL; + char error[1024]; + int status; + + command = (char *)userData; + pCon = (SConnection *)callData; + + assert(command != NULL && pCon != NULL); + + /* + * evaluate + */ + status = Tcl_Eval(InterpGetTcl(pServ->pSics),command); + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: Tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + SCWrite(pCon,error,eError); + status = 0; + } else { + status = 1; + } + + /* + * decode result. This handles both the case of the standard SICS answer + * something = anything + * as well as a plain value alone + */ + data = Tcl_GetStringResult(InterpGetTcl(pServ->pSics)); + if(data == NULL){ + SCWrite(pCon,"ERROR: no result returned from script",eError); + return 0; + } + equal = strchr(data,'='); + if(equal != NULL){ + data = equal + 1; + } + strcpy(error,"ERROR: "); + status = readHdbValue(&node->value,data, error+7, 1024-7); + if(status != 1){ + SCWrite(pCon,error,eError); + return 0; + } + return status; +} +/*----------------------------------------------------------------------------*/ +static pHdbCallback MakeSICSReadScriptCallback(char *script){ + return MakeHipadabaCallback(SICSScriptReadCallback, strdup(script), + free,-1,-1); +} +/*---------------------------------------------------------------------------*/ +typedef struct { + int min; + int max; +}hdbIntRange, *pHdbIntRange; +/*---------------------------------------------------------------------------*/ +static int SICSIntRangeCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + char buffer[256]; + pHdbIntRange range = NULL; + SConnection *pCon = NULL; + int status = 1; + + range = (pHdbIntRange)userData; + pCon = (SConnection *)callData; + + assert(range != NULL); + + if(v.v.intValue > range->max || v.v.intValue < range->min) { + status = SICSCBRANGE; + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: %d is not within permitted range: %d to %d", + (int)v.v.intValue, range->min, range->max); + SCWrite(pCon,buffer,eError); + } + } + return status; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeIntRangeCallback(int min, int max){ + pHdbIntRange range = NULL; + + range = malloc(sizeof(hdbIntRange)); + if(range == NULL){ + return NULL; + } + range->min = min; + range->max = max; + return MakeHipadabaCallback(SICSIntRangeCallback, range, + free,-1,-1); +} +/*---------------------------------------------------------------------------*/ +typedef struct { + double min; + double max; +}hdbFloatRange, *pHdbFloatRange; +/*---------------------------------------------------------------------------*/ +static int SICSFloatRangeCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + char buffer[256]; + pHdbFloatRange range = NULL; + SConnection *pCon = NULL; + int status = 1; + + range = (pHdbFloatRange)userData; + pCon = (SConnection *)callData; + + assert(range != NULL); + + if(v.v.doubleValue > range->max || v.v.doubleValue < range->min) { + status = SICSCBRANGE; + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: %lf is not within permitted range: %lf to %lf", + v.v.doubleValue, range->min, range->max); + SCWrite(pCon,buffer,eError); + } + } + return status; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeFloatRangeCallback(double min, double max){ + pHdbFloatRange range = NULL; + + range = malloc(sizeof(hdbFloatRange)); + if(range == NULL){ + return NULL; + } + range->min = min; + range->max = max; + return MakeHipadabaCallback(SICSFloatRangeCallback, range, + free,-1,-1); +} +/*--------------------------------------------------------------------------*/ +static void killHdbValue(void *pData){ + hdbValue *v = NULL; + + v = (hdbValue *)pData; + if(v == NULL){ + return; + } + ReleaseHdbValue(v); + free(v); +} +/*--------------------------------------------------------------------------*/ +static int SICSIntFixedCallback(void *userData, void *callData, pHdb node, + hdbValue v){ + hdbValue *allowed = NULL; + SConnection *pCon = NULL; + int i; + + allowed = (hdbValue *)userData; + pCon = (SConnection *)callData; + assert(allowed != NULL && allowed->dataType == HIPINTAR); + for(i = 0; i < allowed->arrayLength; i++){ + if(v.v.intValue == allowed->v.intArray[i]){ + return 1; + } + } + if(pCon != NULL){ + SCWrite(pCon,"ERROR: value is not in the list of allowed values",eError); + } + return SICSCBBADFIXED; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeIntFixedCallback(long *data, int length){ + pHdbCallback result = NULL; + hdbValue *v = NULL; + + v = malloc(sizeof(hdbValue)); + if(v == NULL){ + return NULL; + } + v->dataType = HIPINTAR; + v->arrayLength = length; + v->v.intArray = malloc(length*sizeof(long)); + if(v->v.intArray == NULL){ + return NULL; + } + memcpy(v->v.intArray,data,length*sizeof(long)); + return MakeHipadabaCallback(SICSIntFixedCallback, v, + killHdbValue,-1,-1); +} +/*============= Parameter Creation ===========================================*/ +pHdb MakeSICSHdbPar(char *name, int priv, hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + pHcb = MakeSetUpdateCallback(); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + return result; +} +/*----------------------------------------------------------------------------*/ +pHdb MakeSICSHdbDriv(char *name, int priv, void *sicsObject, int dataType){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,dataType,0); + if(result == NULL){ + return NULL; + } + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + pHcb = MakeSICSDriveCallback(sicsObject); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + pHcb = MakeSICSReadDriveCallback(sicsObject); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBREAD,pHcb); + + return result; +} +/*----------------------------------------------------------------------------*/ +pHdb MakeSICSROPar(char *name, hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeReadOnlyCallback(); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + return result; +} +/*---------------------------------------------------------------------------*/ +pHdb MakeSICSScriptPar(char *name, char *setScript, char *readScript, + hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeSICSWriteScriptCallback(setScript); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBSET,pHcb); + + pHcb = MakeSICSReadScriptCallback(readScript); + if(pHcb == NULL){ + DeleteHipadabaNode(result); + return NULL; + } + AppendHipadabaCallback(result,HCBREAD,pHcb); + /** + * put into the list of nodes to check with the update task + */ + LLDnodeAppend(scriptUpdate,&result); + + return result; +} +/*--------------------------------------------------------------------------*/ +static void removeNodeFromUpdateList(pHdb node){ + pHdb current = NULL; + int status; + + status = LLDnodePtr2First(scriptUpdate); + while(status != 0){ + current = LLDnodePtr(scriptUpdate); + if(current == node){ + LLDnodeDelete(scriptUpdate); + return; + } + status = LLDnodePtr2Next(scriptUpdate); + } +} +/*-----------------------------------------------------------------------*/ +static void SICSDeleteNodeData(pHdb node){ + pHdb tmp = NULL; + + if(node == NULL){ + return; + } + DeleteCallbackChain(node->writeCallbacks); + DeleteCallbackChain(node->updateCallbacks); + DeleteCallbackChain(node->readCallbacks); + + if(node->name != NULL){ + free(node->name); + } + ReleaseHdbValue(&node->value); + node->magic = 000000; + + while(node->child != NULL){ + tmp = node->child; + node->child = node->child->next; + SICSDeleteNodeData(tmp); + } + removeNodeFromUpdateList(node); + free(node); +} +/*--------------------------------------------------------------------------*/ +void RemoveSICSPar(pHdb node){ + pHdb current = NULL, tmp = NULL; + + if(node == NULL){ + return; + } + + RemoveHdbNodeFromParent(node); + + SICSDeleteNodeData(node); +} +/*==================== access suport functions ==============================*/ +int SICSHdbGetFloat(pHdb parent, SConnection *pCon, + char *path, float *value){ + hdbValue v; + pHdb par = NULL; + int status; + char buffer[256]; + + par = GetHipadabaNode(parent,path); + if(par == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + status = GetHipadabaPar(par,&v,pCon); + if(status < 0){ + return status; + } + if(v.dataType == HIPFLOAT){ + *value = (float)v.v.doubleValue; + } else if(v.dataType == HIPINT){ + *value = (float)v.v.intValue; + } else { + /* + * it is an error to call this for array dada types + */ + assert(0); + } + return 1; +} +/*--------------------------------------------------------------------------*/ +int SICSHdbSetFloat(pHdb parent, SConnection *pCon, + char *path, float value){ + hdbValue v; + pHdb par = NULL; + int status; + char buffer[256]; + + par = GetHipadabaNode(parent,path); + if(par == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + v.dataType = par->value.dataType; + if(v.dataType == HIPFLOAT){ + v.v.doubleValue = (double)value; + } else if(v.dataType == HIPINT){ + v.v.intValue = (long)value; + } else { + /* + * it is an error to call this for array dada types + */ + assert(0); + } + + status = SetHipadabaPar(par,v,pCon); + if(status < 0){ + return status; + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +int InstallSICSNotify(pHdb node, SConnection *pCon, int id, int recurse){ + pHdb currentChild = NULL; + pHdbCallback noty = NULL; + + noty = MakeNotifyCallback(pCon,id); + if(noty == NULL){ + SCWrite(pCon,"ERROR: out of memory installing callback", eError); + return 0; + } + AppendHipadabaCallback(node, HCBUPDATE, noty); + + if(recurse == 1){ + currentChild = node->child; + while(currentChild != NULL){ + InstallSICSNotify(currentChild,pCon,id,recurse); + currentChild = currentChild->next; + } + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int ProcessSICSHdbPar(pHdb root, SConnection *pCon, + char *printPrefix, int argc, char *argv[]){ + hdbValue input; + pHdb parNode = NULL; + pDynString parData = NULL; + char error[512]; + int i, status; + + assert(root != NULL && pCon != NULL); + + if(argc < 1){ + SCWrite(pCon,"ERROR: nor parameter to treat specified",eError); + return -1; + } + + parNode = GetHipadabaNode(root,argv[0]); + if(parNode == NULL){ + /* no error reporting here, upper level code might wish to continue + * processing commands after having tested for parameters. + */ + return -1; + } + + if(argc > 1) { + /* + * setting the value is attempted. + */ + memset(&input,0,sizeof(hdbValue)); + input.dataType = parNode->value.dataType; + copyHdbValue(&parNode->value,&input); + parData = CreateDynString(64,64); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory processing parameter",eError); + return 0; + } + for(i = 1; i < argc; i++){ + DynStringConcat(parData," "); + DynStringConcat(parData, argv[i]); + } + strcpy(error,"ERROR: "); + if(!readHdbValue(&input, GetCharArray(parData), + error+7,512-7)){ + SCWrite(pCon,error, eError); + return 0; + } + DeleteDynString(parData); + status = SetHipadabaPar(parNode,input,pCon); + ReleaseHdbValue(&input); + return status; + } else { + /* + * reading is in demand + */ + status = GetHipadabaPar(parNode,&input, pCon); + if(status != 1){ + return 0; + } + parData = formatValue(input); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory reading parameter data",eError); + return 0; + } + DynStringInsert(parData," =", 0); + DynStringInsert(parData,argv[0],0); + if(printPrefix != NULL){ + DynStringInsert(parData,printPrefix,0); + } + SCWrite(pCon,GetCharArray(parData),eValue); + DeleteDynString(parData); + ReleaseHdbValue(&input); + return 1; + } +} +/*---------------------------------------------------------------------------*/ +int SICSHipadabaTask(void *pData){ + pHdbUpdateTask self = NULL; + hdbValue old, newValue; + pHdb currentNode = NULL; + int status; + + self = (pHdbUpdateTask)pData; + assert(self != NULL); + + if(self->iEnd == 1){ + return 0; + } + if(LLDcheck(self->updateList) == LIST_EMPTY){ + return 1; + } + memset(&old,0,sizeof(hdbValue)); + memset(&newValue,0,sizeof(hdbValue)); + + currentNode = (pHdb)LLDnodePtr(self->updateList); + + if(currentNode != NULL){ + old.dataType = currentNode->value.dataType; + copyHdbValue(¤tNode->value,&old); + if(GetHipadabaPar(currentNode,&newValue, self->pCon) == 1){ + if(!compareHdbValue(old,newValue)){ + UpdateHipadabaPar(currentNode,newValue,self->pCon); + } + } + ReleaseHdbValue(&old); + ReleaseHdbValue(&newValue); + + } + status = LLDnodePtr2Next(self->updateList); + if(status == 0){ + LLDnodePtr2First(self->updateList); + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +void SICSHipadabaSignal(void *pData, int iSignal, void *pSigData){ + pHdbUpdateTask self = NULL; + int *iInt; + + self = (pHdbUpdateTask)pData; + + if(iSignal == SICSINT){ + iInt = (int *)pSigData; + if(*iInt == eEndServer){ + self->iEnd = 1; + } + } +} +/*================ value helpers ============================================*/ +pDynString formatValue(hdbValue v){ + pDynString result = NULL; + int i; + char number[30]; + + result = CreateDynString(64,64); + if(result == NULL){ + return NULL; + } + switch(v.dataType){ + case HIPNONE: + break; + case HIPINT: + snprintf(number,30,"%ld", v.v.intValue); + DynStringCopy(result,number); + break; + case HIPFLOAT: + snprintf(number,30,"%12.4f", v.v.doubleValue); + DynStringCopy(result,number); + break; + case HIPTEXT: + DynStringCopy(result,v.v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + for(i = 0; i < v.arrayLength; i++){ + snprintf(number,30," %ld", v.v.intArray[i]); + DynStringConcat(result,number); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + for(i = 0; i < v.arrayLength; i++){ + snprintf(number,30," %12.4f", v.v.floatArray[i]); + DynStringConcat(result,number); + } + break; + } + return result; +} +/*-------------------------------------------------------------------*/ +static char *getNextHdbNumber(char *pStart, char pNumber[80]){ + int charCount = 0; + pNumber[0] = '\0'; + + /* advance to first digit */ + while(isspace(*pStart) && *pStart != '\0'){ + pStart++; + } + if(*pStart == '\0'){ + return NULL; + } + + /* copy */ + while(!isspace(*pStart) && *pStart != '\0' && charCount < 78){ + pNumber[charCount] = *pStart; + pStart++; + charCount++; + } + pNumber[charCount] = '\0'; + return pStart; +} +/*---------------------------------------------------------------------------------*/ +static int adjustDataLength(hdbValue *v, char *data){ + char number[80]; + int count = 0; + + while(getNextHdbNumber(data,number) != NULL){ + count++; + } + if(count != v->arrayLength){ + v->arrayLength = count; + if(v->dataType == HIPINTVARAR){ + if(v->v.intArray != NULL){ + free(v->v.intArray); + } + v->v.intArray = malloc(count*sizeof(long)); + if(v->v.intArray == NULL){ + return 0; + } + memset(v->v.intArray,0,count*sizeof(long)); + } + if(v->dataType == HIPFLOATVARAR){ + if(v->v.floatArray != NULL){ + free(v->v.floatArray); + } + v->v.floatArray = malloc(count*sizeof(double)); + if(v->v.floatArray == NULL){ + return 0; + } + memset(v->v.floatArray,0,count*sizeof(double)); + } + } + return 1; +} +/*---------------------------------------------------------------------------------*/ +int readHdbValue(hdbValue *v, char *data, char *error, int errlen){ + int i, status; + long lValue; + double dValue; + char number[80]; + char *pPtr = NULL; + + switch(v->dataType){ + case HIPNONE: + break; + case HIPINT: + getNextHdbNumber(data,number); + status = sscanf(number,"%ld", &v->v.intValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to integer", + data); + return 0; + } + break; + case HIPFLOAT: + getNextHdbNumber(data,number); + status = sscanf(number,"%lf", &v->v.doubleValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to double", + data); + return 0; + } + break; + case HIPTEXT: + if(v->v.text != NULL){ + free(v->v.text); + } + v->v.text = strdup(data); + break; + case HIPINTVARAR: + if(!adjustDataLength(v,data)){ + snprintf(error,errlen,"Out of memory allocating variable length data"); + return 0; + } + case HIPINTAR: + for(i = 0; i < v->arrayLength; i++){ + data = getNextHdbNumber(data,number); + if(data == NULL){ + snprintf(error,errlen,"Not enough values to parse, current index %d", + i); + return 0; + } + status = sscanf(number,"%ld", &lValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to integer", + data); + return 0; + } + v->v.intArray[i] = lValue; + } + break; + case HIPFLOATVARAR: + if(!adjustDataLength(v,data)){ + snprintf(error,errlen,"Out of memory allocating variable length data"); + return 0; + } + case HIPFLOATAR: + for(i = 0; i < v->arrayLength; i++){ + data = getNextHdbNumber(data,number); + if(data == NULL){ + snprintf(error,errlen,"Not enough values to parse, current index %d", + i); + return 0; + } + status = sscanf(number,"%lf", &dValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to double", + data); + return 0; + } + v->v.floatArray[i] = dValue; + } + break; + default: + assert(0); + break; + } + return 1; +} +/*================ interpreter interface ==================================*/ +pHdb GetHipadabaRoot(){ + return root; +} +/*-------------------------------------------------------------------------*/ +static char *hdbTypes[] = {"none", + "int", + "float", + "text", + "intar", + "floatar", + "intvarar", + "floatvarar", + NULL}; +/*-------------------------------------------------------------------------*/ +static int convertHdbType(char *text){ + int type; + + type = 0; + while(hdbTypes[type] != NULL){ + if(strcmp(hdbTypes[type], text) == 0){ + break; + } + type++; + } + type--; /* we start counting at -1 */ + return type; +} +/*-------------------------------------------------------------------------*/ +static char *hdbTypeToText(int type){ + return hdbTypes[type+1]; +} +/*--------------------------------------------------------------------------*/ +static int MakeHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int type = 0, i, length = 0, priv = -1; + char *pPtr = NULL; + pHdb parent = NULL; + pHdb child = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 4) { + SCWrite(pCon,"ERROR: not enough arguments to MakeHdbNode",eError); + return 0; + } + + /* + * convert privilege + */ + priv = decodeSICSPriv(argv[2]); + /* + * convert datatype + */ + strtolower(argv[3]); + type = convertHdbType(argv[3]); + if(type >= 7){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type > 2){ + if( argc < 5){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[3]); + } + } + + /* split off last path element */ + pPtr = strrchr(argv[1],'/'); + if(pPtr == NULL){ + SCWrite(pCon,"ERROR: invalid path specification", + eError); + return 0; + } + *pPtr = '\0'; + pPtr++; + if(strlen(pPtr) < 1) { + parent = root; + } else { + parent = GetHipadabaNode(root,argv[1]); + } + if(parent == NULL){ + SCWrite(pCon,"ERROR: parent for new node does not exist",eError); + return 0; + } + if(type != HIPNONE){ + child = MakeSICSHdbPar(pPtr, priv, makeHdbValue(type,length)); + } else { + child = MakeHipadabaNode(pPtr,type,length); + } + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child); + SCSendOK(pCon); + return 1; +} +/*--------------------------------------------------------------------------*/ +static int MakeHdbScriptNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int type = 0, i, length = 0; + char *pPtr = NULL; + pHdb parent = NULL; + pHdb child = NULL; + pHdb current = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 5) { + SCWrite(pCon,"ERROR: not enough arguments to MakeHdbNode",eError); + return 0; + } + + /* + * convert datatype + */ + strtolower(argv[4]); + type = convertHdbType(argv[4]); + if(type >= 7){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type > 2){ + if( argc < 6){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[5]); + } + } + + /* split off last path element */ + pPtr = strrchr(argv[1],'/'); + if(pPtr == NULL){ + SCWrite(pCon,"ERROR: invalid path specification", + eError); + return 0; + } + *pPtr = '\0'; + pPtr++; + if(strlen(pPtr) < 1) { + parent = root; + } else { + parent = GetHipadabaNode(root,argv[1]); + } + if(parent == NULL){ + SCWrite(pCon,"ERROR: parent for new node does not exist",eError); + return 0; + } + child = MakeSICSScriptPar(pPtr, argv[3], argv[2], + makeHdbValue(type,length)); + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child); + SCSendOK(pCon); + return 1; +} + +/*-----------------------------------------------------------------------------------------*/ +static int DeleteHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb killNode = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 2){ + SCWrite(pCon,"ERROR: need path to node to delete",eError); + return 0; + } + killNode = GetHipadabaNode(root,argv[1]); + if(killNode == NULL){ + SCWrite(pCon,"ERROR: node to delete not found",eError); + return 0; + } + RemoveSICSPar(killNode); + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +static pHdb locateSICSNode(SicsInterp *pSics, SConnection *pCon, char *path){ + pHdb result = NULL; + char *pPtr = NULL, sicsObj[128], error[256]; + pDummy pDum = NULL; + CommandList *pCom = NULL; + + if(strstr(path,"/sics/") != NULL){ + pPtr = stptok(path,sicsObj,128,"/"); + pPtr = stptok(pPtr,sicsObj,128,"/"); + pPtr = stptok(pPtr,sicsObj,128,"/"); + strtolower(sicsObj); + pCom = FindCommand(pSics,sicsObj); + if(pCom == NULL) { + snprintf(error,255,"ERROR: object %s not found",sicsObj); + SCWrite(pCon,error,eError); + return NULL; + } + pDum = (pDummy)pCom->pData; + if(pDum == NULL){ + snprintf(error,255,"ERROR: object %s has no data",sicsObj); + SCWrite(pCon,error,eError); + return NULL; + } + if(pDum->pDescriptor->parNode == NULL){ + snprintf(error,255,"ERROR: object %s does not use Hipadaba",sicsObj); + SCWrite(pCon,error,eError); + return NULL; + } + result = GetHipadabaNode(pDum->pDescriptor->parNode,pPtr); + } else { + result = GetHipadabaNode(root,path); + } + if(result == NULL){ + snprintf(error,255,"ERROR: node %s NOT found",path); + SCWrite(pCon,error,eError); + } + return result; +} +/*---------------------------------------------------------------------------*/ +static int SetHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512]; + int i, status; + + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + + if(argc < 3) { + SCWrite(pCon,"ERROR: insufficient number of arguments to SetHdbNode", + eError); + return 0; + } + + targetNode = locateSICSNode(pSics,pCon,argv[1]); + if(targetNode == NULL){ + return 0; + } + if(!cloneHdbValue(&targetNode->value,&newValue)){ + SCWrite(pCon,"ERROR: out of mmeory cloning node", + eError); + return 0; + } + parData = CreateDynString(64,64); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory reading parameter",eError); + return 0; + } + for(i = 2; i < argc; i++){ + DynStringConcat(parData," "); + DynStringConcat(parData, argv[i]); + } + strcpy(error,"ERROR: "); + if(!readHdbValue(&newValue, GetCharArray(parData), + error+7,512-7)){ + SCWrite(pCon,error, eError); + return 0; + } + DeleteDynString(parData); + status = SetHipadabaPar(targetNode,newValue,pCon); + ReleaseHdbValue(&newValue); + if(status == 1){ + SCSendOK(pCon); + } + return status; +} +/*-----------------------------------------------------------------------------*/ +static int GetHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512], oriPath[512];; + int i, status; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + strncpy(oriPath,argv[1], 511); + targetNode = locateSICSNode(pSics,pCon,argv[1]); + if(targetNode == NULL){ + return 0; + } + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode, &newValue, pCon); + parData = formatValue(newValue); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory formatting data",eError); + return 0; + } + DynStringInsert(parData," =",0); + DynStringInsert(parData,oriPath,0); + SCWrite(pCon,GetCharArray(parData),eValue); + DeleteDynString(parData); + ReleaseHdbValue(&newValue); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatPlainList(pHdb node){ + pHdb current; + pDynString result = NULL; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + DynStringConcat(result,current->name); + DynStringConcat(result,"\n"); + current = current->next; + } + return result; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatListWithVal(pHdb node){ + pHdb current; + pDynString result = NULL; + pDynString data = NULL; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + DynStringConcat(result,current->name); + data = formatValue(current->value); + if(data != NULL){ + DynStringConcat(result," = "); + DynStringConcat(result,GetCharArray(data)); + DeleteDynString(data); + } + DynStringConcat(result,"\n"); + current = current->next; + } + return result; +} +/*--------------------------------------------------------------------------*/ +static int countChildren(pHdb node){ + pHdb current = NULL; + int count = 0; + + current = node->child; + while(current != NULL){ + count++; + current = current->next; + } + return count; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatClientList(pHdb node){ + pHdb current; + pDynString result = NULL; + int length; + int i; + char number[50]; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + DynStringConcat(result,current->name); + DynStringConcat(result,","); + DynStringConcat(result,hdbTypeToText(current->value.dataType)); + DynStringConcat(result,","); + snprintf(number,50,"%d",countChildren(current)); + DynStringConcat(result,number); + DynStringConcat(result,","); + if(current->value.dataType >= 3){ + length = current->value.arrayLength; + } else { + length = 1; + } + snprintf(number,50,"%d",length); + DynStringConcat(result,number); + DynStringConcat(result,","); + switch(current->value.dataType){ + case HIPNONE: + break; + case HIPINT: + snprintf(number,50,"%ld",current->value.v.intValue); + DynStringConcat(result,number); + break; + case HIPFLOAT: + snprintf(number,50,"%lf",current->value.v.doubleValue); + DynStringConcat(result,number); + break; + case HIPTEXT: + DynStringConcat(result,current->value.v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + for(i = 0; i < length; i++){ + snprintf(number,50,"%ld",current->value.v.intArray[i]); + DynStringConcat(result,number); + if(i > length -1){ + DynStringConcat(result,","); + } + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + for(i = 0; i < length; i++){ + snprintf(number,50,"%lf",current->value.v.floatArray[i]); + DynStringConcat(result,number); + if(i > length -1){ + DynStringConcat(result,","); + } + } + break; + } + DynStringConcat(result,"\n"); + current = current->next; + } + return result; +} +/*---------------------------------------------------------------------------*/ +static int ListHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb node = NULL; + int pathArg = 1; + pDynString listData = NULL; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + if(strchr(argv[1],'-') != NULL){ + pathArg = 2; + if(argc < 3){ + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + } + + node = locateSICSNode(pSics,pCon,argv[pathArg]); + if(node == NULL){ + return 0; + } + + strtolower(argv[1]); + if(strcmp(argv[1],"-val") == 0){ + listData = formatListWithVal(node); + } else if(strcmp(argv[1],"-cli") == 0){ + listData = formatClientList(node); + } else { + listData = formatPlainList(node); + } + if(listData == NULL){ + SCWrite(pCon,"ERROR: failed to format list", + eError); + return 0; + } + SCWrite(pCon,GetCharArray(listData),eValue); + DeleteDynString(listData); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int AutoNotifyHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb node = NULL; + int id, status; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path and id in order to add notify", + eError); + return 0; + } + + node = locateSICSNode(pSics,pCon,argv[1]); + if(node == NULL){ + return 0; + } + + id = atoi(argv[2]); + + status = InstallSICSNotify(node, pCon, id, 1); + if(status == 1){ + SCSendOK(pCon); + } + + return status; +} +/*---------------------------------------------------------------------------*/ +static int RemoveHdbCallback(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int id; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need callback id to remove", + eError); + return 0; + } + id = atoi(argv[1]); + RemoveHipadabaCallback(root,id); + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +void killSICSHipadaba(){ + if(root != NULL){ + DeleteHipadabaNode(root); + } + root = NULL; + /** + * children have already been removed when killing the + * main tree + */ + if(scriptUpdate > 0 && LLDcheck(scriptUpdate) != LIST_EMPTY){ + LLDdelete(scriptUpdate); + } +} +/*---------------------------------------------------------------------------*/ +int InstallSICSHipadaba(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + + root = MakeHipadabaNode("/",HIPNONE,0); + scriptUpdate = LLDcreate(sizeof(void *)); + taskData.updateList = scriptUpdate; + taskData.iEnd = 0; + taskData.pCon = SCCreateDummyConnection(pSics); + if(taskData.pCon == NULL){ + SCWrite(pCon,"ERROR: out of memory creating Hipadaba",eError); + return 0; + } + TaskRegister(pServ->pTasker, + SICSHipadabaTask, + SICSHipadabaSignal, + NULL, + &taskData,1); + + AddCommand(pSics,"hmake", MakeHdbNode, NULL, NULL); + AddCommand(pSics,"hmakescript", MakeHdbScriptNode, NULL, NULL); + AddCommand(pSics,"hdel", DeleteHdbNode, NULL, NULL); + AddCommand(pSics,"hset", SetHdbNode, NULL, NULL); + AddCommand(pSics,"hget", GetHdbNode, NULL, NULL); + AddCommand(pSics,"hlist", ListHdbNode, NULL, NULL); + AddCommand(pSics,"hnotify", AutoNotifyHdbNode, NULL, NULL); + AddCommand(pSics,"hdelcb", RemoveHdbCallback, NULL, NULL); + + return 1; +} diff --git a/sicshipadaba.h b/sicshipadaba.h new file mode 100644 index 00000000..83f0bf5f --- /dev/null +++ b/sicshipadaba.h @@ -0,0 +1,242 @@ +/** + * This is a set of helper functions for SICS to work with the hierarchical parameter + * database hipadaba. In SICS, the calldata associated with any callback will always + * be the connection object. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + */ +#ifndef SICSHIPADABA_H_ +#define SICSHIPADABA_H_ +#include +#include +#include +/*======================== callback error codes ===============================*/ +#define SICSCBRO -607 +#define SICSCBPERM -608 +#define SICSCBRANGE -609 +#define SICSCBBADFIXED -610 +#define SICSNOPAR -611 +/*======================== data structure for automatic parameter update =======*/ +typedef struct { + SConnection *pCon; + int updateList; + int iEnd; +}hdbUpdateTask, *pHdbUpdateTask; +/*======================== common callbacks =====================================*/ +/** + * make a callback which checks permissions. To be used on write + * @param priv The privilege to check against + * @return a suitably initialized callback structure for + * checking permissions. + */ +pHdbCallback MakeCheckPermissionCallback(int priv); +/** + * make a callback which directly updates a + * paramter after setting. Usefule for program parameters. + * @return a suitably initialized callback structure setting + * program parameters + */ +pHdbCallback MakeSetUpdateCallback(); +/** + * make a callback which starts a parameter driving. + * @param sicsObject The SICS object to drive. + * @return a suitably initialized callback structure for + * starting a parameter driving. + */ +pHdbCallback MakeSICSDriveCallback(void *sicsObject); +/** + * make a callback which reads a SICS drivable object + * @param sicsObject The SICS drivable object to read. + * @return a suitably initialized callback structure for + * reading a drivable parameter + */ +pHdbCallback MakeSICSReadDriveCallback(void *sicsObject); +/** + * make a callback which enables automatically + * notification of pCon on parameter updates. + * @param pCon The connection to notify. The notification + * is issued in the context of this connection. + * @param id An integer id which can later on be used to remove the + * callback. + * @return a suitably initialized callback structure for + * automatic notification. + */ +pHdbCallback MakeNotifyCallback(SConnection *pCon, int id); +/** + * make a callback for checking if a parameter is within a given + * range of integers + * @param min The minimum value of the range + * @param max The maximum value of the range + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeIntRangeCallback(int min, int max); +/** + * make a callback for checking if a parameter is one out + * of a series of permitted integers + * @param data An array of permitted integers + * @param length The length of the data array. + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeIntFixedCallback(long *data, int length); +/** + * make a callback for checking if a parameter is within a given + * range of floats + * @param min The minimum value of the range + * @param max The maximum value of the range + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeFloatRangeCallback(double min, double max); +/*======================== parameter creation ===================================*/ +/** + * make a simple SICS hdb parameter. Setting it will call update immediately. Use + * this for program parameters. + * @param name The name of the parameter + * @param priv The privilege required to change that parameter + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSHdbPar(char *name, int priv, hdbValue v); +/** + * make a SICS hdb drivable parameter. Setting it will start the motor, + * virtual motor or environment parameter. This will call StartDevice + * eventually + * @param name The name of the parameter + * @param priv The privilege required to change that parameter + * @param sicsObject The object corresponding to this parameter. + * @param dataType The datatype of this variable + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ + +pHdb MakeSICSHdbDriv(char *name, int priv,void *sicsObject, int datatype); +/** + * makes a SICS Hdb read only parameter. Setting such a parameter causes an error. + * @param name The name of the parameter + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSROPar(char *name, hdbValue v); +/** + * make a SICS scriptable parameter. I.e. when this parameter is set or read, + * appropriate scripts are invoked. + * @param name The name of the parameter + * @param setScript The script to call when this parameter is being set + * @param readScript The script to call when this parameter is being read. + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSScriptPar(char *name, char *setScript, char *readScript, hdbValue v); +/** + * remove a SICS paramameter node and its children. In contrast to the + * normal DeletHipadabaNode, this function also takes care of + * clearing scipted nodes out of the update tasks watch list. + * @param node The node to delete + */ +void RemoveSICSPar(pHdb node); +/*============== access support functions =================================*/ +/** + * SICSHdbGetFloat returns the float value of a parameter. Integers are + * automatically converted. + * @param parent The parent node where to start searching for the parameter + * @param pCon The optional connection object to use for reporting errors. + * @param path The path to the parameter. + * @param value The value of the parameter + * @return 1 on success, a negative error code else. + */ +int SICSHdbGetFloat(pHdb parent, SConnection *pCon, + char *path, float *value); +/** + * SICSHdbSetFloat sets the value of a parameter. Integers are + * automatically converted. + * @param parent The parent node where to start searching for the parameter + * @param pCon The optional connection object to use for reporting errors. + * @param path The path to the parameter. + * @param value The new value of the parameter + * @return 1 on success, a negative error code else. + */ +int SICSHdbSetFloat(pHdb parent, SConnection *pCon, + char *path, float value); +/*============= common SICS Interactions ===================================*/ +/** + * Install a SICS automatic notification callback on the node. This is + * a default callback using the current connection with its current + * context for notification. + * @param node The parameter on which to install the callback + * @param pCon The connection to which this callback is linked. + * @param id An int associated with this notification callback. A + * precaution for later removal. + * @param recurse a flag 0 or 1 which determines if callbacks are + * installed to all nodes recursively. + * @return 1 on success, 0 when out of memory. + */ +int InstallSICSNotify(pHdb node, SConnection *pCon, int id, int recurse); +/** + * handles the common task of checking for, and processing a SICS parameter. + * @param root The node at which to search for parameters + * @param pCon The connection in whichs context the parameter is processed. + * @param printPrefix A prefix to prepend before printing this parameter. + * Will be ignored if NULL. + * @param argc number of arguments to process. + * @param argv The arguments to process. argv[0] should be the parameter + * name. + * @return -1 when argv[0] is no parameter, 0 on failure, 1 on success. + */ +int ProcessSICSHdbPar(pHdb root, SConnection *pCon, char *printPrefix, + int argc, char *argv[]); +/** + * A SICS task which scans a Hipadaba and reads and updates all parameters, + * one per invocation. TODO: how to distinguish between automatic pars which + * do not need this and pars which need this? Idea 1: make a root point at an + * artificial tree of parameters which need to be checked like this. + * @param pData The root to start scanning at. + * @return 0 when ends, 1 else + */ +int SICSHipadabaTask(void *pData); +void SICSHipadabaSignal(void *pData, int iSignal, void *pSigData); +/*================== value helpers ========================================*/ +/** + * format a Hdb Value into a string using SICS defaults. + * @param v The Hdb value to format + * @return a dynamic string holding the formatted data. NULL when + * out of memory + */ +pDynString formatValue(hdbValue v); +/** + * read values for a Hdb value from a string. + * @param v The hdbValue to read data into. Datatype and arraylength must + * already have been initialised before this call in order to allow for + * checks. Arrays should also have been allocated in the right size. + * @param data The string to parse and convert. + * @param error A string to copy failure reasons too + * @param errlen The length of the error string + * @return 0 on failure, 1 on success + */ +int readHdbValue(hdbValue *v, char *data, char *error, int errlen); +/*================= SICS Interpreter Interface ===========================*/ +/** + * InstallHipadaba installs the Hipadaba commands into the SICS interpreter. + * The actual command implementation is in sicshipadaba.c. + * @param pCon The connection object + * @param pSics The SICS interpreter + * @param pData The object data structure + * @param argc The number of arguments + * @param argv[] The text arguments + * @return 0 on filaure, 1 on success + */ +int InstallSICSHipadaba(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +/** + * get the root of the SICS Hipadaba tree + * @return The root node of the hipdaba + */ +pHdb GetHipadabaRoot(); +/** + * kill the SICS hierarchical database + * Only to be called when shutting down the SICServer + */ +void killSICSHipadaba(void); +#endif /*SICSHIPADABA_H_*/ diff --git a/statistics.c b/statistics.c new file mode 100644 index 00000000..504a531d --- /dev/null +++ b/statistics.c @@ -0,0 +1,149 @@ +#include +#include +#include "statistics.h" + +typedef struct timeval tv_t; + +struct Statistics { + tv_t tim; + long cnt; + char *name; + Statistics *next; +}; + +static Statistics *current; +static tv_t last, lastStat; +static Statistics *idle = NULL, *list; +static int init = 1; +/*-----------------------------------------------------------------------*/ +tv_t timeDif(tv_t t1, tv_t t2) { + tv_t result; + + result.tv_usec = t2.tv_usec - t1.tv_usec; + result.tv_sec = t2.tv_sec - t1.tv_sec; + if (result.tv_usec < 0) { + result.tv_usec += 1000000; + result.tv_sec --; + } + if (result.tv_sec < 0) { + result.tv_sec += 24*3600; + } + return result; +} +/*-----------------------------------------------------------------------*/ +void timeAdd(tv_t *t1, tv_t t2) { + t1->tv_usec += t2.tv_usec; + t1->tv_sec += t2.tv_sec + (t1->tv_usec / 1000000); + t1->tv_usec %= 1000000; +} +/*-----------------------------------------------------------------------*/ +double timeFloat(tv_t t) { + return t.tv_sec + 1e-6 * t.tv_usec; +} +/*-----------------------------------------------------------------------*/ +int StatisticsCommand(SConnection *con, SicsInterp *pSics, void *pData, + int argc, char *argv[]) { + Statistics *p; + tv_t now; + double dif, percent, dt; + + gettimeofday(&now, 0); + dif = timeFloat(timeDif(lastStat, now)); + SCPrintf(con, eStatus, " calls time[%] mean[ms] command"); + SCPrintf(con, eStatus, "--------------------------------------"); + for (p = list; p != NULL; p = p->next) { + if (dif > 0) { + percent = timeFloat(p->tim) * 100 / dif; + if (percent > 0) { + if (p->cnt > 0) { + dt = timeFloat(p->tim) * 1000.0 / p->cnt; + } else { + dt = 0; + } + SCPrintf(con, eStatus, "%7ld %7.1f %8.2f %s", p->cnt, + percent, dt, p->name); + } + } + p->cnt = 0; + p->tim.tv_sec = 0; + p->tim.tv_usec = 0; + } + lastStat = now; + return 1; +} +/*-----------------------------------------------------------------------*/ +Statistics *StatisticsNew(char *name) { + Statistics *new; + + if (init) { + gettimeofday(&lastStat, 0); + last = lastStat; + init = 0; + } + new = calloc(1,sizeof(*new)); + if (new) { + new->cnt = 0; + new->tim.tv_sec = 0; + new->tim.tv_usec = 0; + new->next = list; + new->name = strdup(name); + list = new; + } + return new; +} +/*-----------------------------------------------------------------------*/ +void StatisticsKill(Statistics *stat) { + Statistics *p, **last; + + /* find in list */ + last = &list; + for (p = list; p != NULL && p != stat; p = p->next) { + last = &p->next; + } + + /* remove from list */ + if (p == stat) { + *last = p->next; + } + + /* kill it */ + if (stat->name) { + free(stat->name); + stat->name = NULL; + } + free(stat); +} +/*-----------------------------------------------------------------------*/ +static void StatisticsSet(Statistics *stat) { + tv_t now; + + if (stat != NULL) { + gettimeofday(&now, 0); + timeAdd(¤t->tim, timeDif(last, now)); + last = now; + } + current = stat; +} +/*-----------------------------------------------------------------------*/ +Statistics *StatisticsBegin(Statistics *stat) { + Statistics *res; + + res = current; + StatisticsSet(stat); + current->cnt ++; + return res; +} +/*-----------------------------------------------------------------------*/ +void StatisticsEnd(Statistics *stat) { + StatisticsSet(stat); +} +/*-----------------------------------------------------------------------*/ +void StatisticsInit(void) { + if (idle == NULL) { + AddCmd("statistics", StatisticsCommand); + last = lastStat; + idle = StatisticsNew(""); + current = idle; + } +} + diff --git a/statistics.h b/statistics.h new file mode 100644 index 00000000..b3d5b599 --- /dev/null +++ b/statistics.h @@ -0,0 +1,36 @@ +/* statistics.h + + statistics on the time spend for commands/tasks + +*/ + +#ifndef STATISTICS_H +#define STATISTICS_H + +typedef struct Statistics Statistics; + +Statistics *StatisticsNew(char *name); +/* create a new Statistics item */ + +void StatisticsKill(Statistics *s); +/* kill item */ + +Statistics *StatisticsBegin(Statistics *s); +void StatisticsEnd(Statistics *s); +/* + Switch statistics to item s. + + Typical usage: + + old = StatisticsBegin(thisItem); + + ...do work related with thisItem... + + StatisticsEnd(old); +*/ + +void StatisticsInit(void); + +/* initialize module and activate statistics command */ + +#endif