Update from PSI
r1040 | ffr | 2006-08-03 10:05:34 +1000 (Thu, 03 Aug 2006) | 2 lines
This commit is contained in:

committed by
Douglas Clowes

parent
074f1cb3cd
commit
dbc07ee639
815
hipadaba.c
Normal file
815
hipadaba.c
Normal file
@ -0,0 +1,815 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
The hierarchical parameter database code. For more information, see
|
||||||
|
hipadaba.h
|
||||||
|
|
||||||
|
copyright: GPL
|
||||||
|
|
||||||
|
Mark Koennecke, June 2006
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
270
hipadaba.h
Normal file
270
hipadaba.h
Normal file
@ -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
|
1540
sicshipadaba.c
Normal file
1540
sicshipadaba.c
Normal file
File diff suppressed because it is too large
Load Diff
242
sicshipadaba.h
Normal file
242
sicshipadaba.h
Normal file
@ -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 <hipadaba.h>
|
||||||
|
#include <sics.h>
|
||||||
|
#include <dynstring.h>
|
||||||
|
/*======================== 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_*/
|
149
statistics.c
Normal file
149
statistics.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#include <sics.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#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("<idle>");
|
||||||
|
current = idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
36
statistics.h
Normal file
36
statistics.h
Normal file
@ -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
|
Reference in New Issue
Block a user