#include #include #include #include #include "sics.h" #include "sicsobj.h" #include "splitter.h" #include "initializer.h" #include "commandlog.h" #include "hipadaba.h" #include "sicshipadaba.h" #include "dynstring.h" #include "devser.h" #include "ascon.h" #include "macro.h" #define MAX_HDB_PATH 1024 typedef struct ContextItem { struct ContextItem *next; Hdb *node; Hdb *controllerNode; } ContextItem; typedef struct ScriptContext { ObjectDescriptor *desc; ContextItem *nodes; ContextItem *trash; ContextItem *base; } ScriptContext; typedef struct SctController { DevSer *devser; Hdb *node; /* the controller node */ SCStore *conn; int verbose; } SctController; typedef struct SctData { int writable; char *name; SctController *controller; SCStore *conCtx; int answered; Hdb *node; /* back link used by SctMatchWrite/SctMatchPoll */ } SctData; static ScriptContext *sct = NULL; static SCStore *currentCon = NULL; static struct { char *name; } actionCallback; static char *mainCallback = "main callback"; void PushContext(Hdb *node, Hdb *controllerNode) { ContextItem *new; if (sct->trash == NULL) { new = calloc(1, sizeof(*new)); } else { new = sct->trash; sct->trash = sct->trash->next; } new->next = sct->nodes; sct->nodes = new; new->node = node; new->controllerNode = controllerNode; } void PopContext(void) { ContextItem *c; c = sct->nodes; assert(c); sct->nodes = c->next; c->next = sct->trash; sct->trash = c; } void CleanStack(Hdb *node) { ContextItem *s; /* clean context from killed nodes */ for (s = sct->nodes; s != NULL; s = s->next) { if (s->node == node) { s->node = NULL; } if (s->controllerNode == node) { s->controllerNode = NULL; } } } static void SetProp(Hdb *node, Hdb *cNode, char *key, char *value) { char *val; if (node == NULL) { if (cNode == NULL) return; node = cNode; } else { val = GetHdbProp(node, key); if (val == NULL && cNode != NULL) { val = GetHdbProp(cNode, key); if (val != NULL) { node = cNode; } } } if (value == NULL) { if (GetHdbProperty(node, key, NULL, 0) > 0) { SetHdbProperty(node, key, ""); } } else { SetHdbProperty(node, key, value); } } static char *GetProp(Hdb *node, Hdb *cNode, char *key) { char *val; if (node != NULL) { val = GetHdbProp(node, key); if (val != NULL) return val; } if (cNode != NULL) { val = GetHdbProp(node, key); } return val; } /* * This is the actual sct command available in scripts. */ int SctCommand(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { static char value[1024]; char *val; char error[512]; Hdb *node = sct->nodes->node; Hdb *cNode = sct->nodes->controllerNode; hdbValue v; double dtime; assert(sct == object); if (node == NULL && cNode == NULL) { SCPrintf(con, eError, "ERROR: %s may be called only in proper context", argv[0]); return 0; } if (argc <= 1) { GetHdbPath(node, value, sizeof value); SCWrite(con, value, eValue); return 1; } /* * update command */ if(strcmp(argv[1],"update") == 0){ cloneHdbValue(&node->value, &v); Arg2Text(argc-2, argv+2, value, sizeof value); if(!readHdbValue(&v, value, error, 512)){ SCWrite(con, error, eError); return 0; } UpdateHipadabaPar(node,v,con); SetHdbProperty(node,"geterror", NULL); return 1; } /* * print */ if(strcmp(argv[1],"print") == 0){ Arg2Text(argc-2, argv+2, value, sizeof value); SCWrite(con,value,eWarning); return 1; } /* * time stamping */ if(strcmp(argv[1],"utime") == 0){ if(argc < 3){ SCWrite(con,"ERROR: need property to write time stamp too", eError); return 0; } dtime = DoubleTime(); snprintf(value,1024,"%.3f",dtime); SetHdbProperty(node,argv[2], value); return 1; } /* * property handling */ if (argc == 2) { /* get case */ val = GetProp(node, cNode, argv[1]); if (val == NULL) { SCPrintf(con, eError, "ERROR: %s %s not found", argv[0], argv[1]); return 0; } SCWrite(con, val, eValue); } else { /* set case */ val = Arg2Tcl(argc-2, argv+2, value, sizeof value); SetProp(node, cNode, argv[1], val); if (val != NULL && val != value) free(val); } return 1; } int SctCallInContext(SConnection *con, char *script, Hdb *node, SctController *controller, char **resPtr) { Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics); int ret, l; char *result = NULL; int iRet = 1; int verbose = controller->verbose; PushContext(node, controller->node); if (verbose) { SCPrintf(con, eInError, "\nscript: %s\n", script); } MacroPush(con); l = strlen(script); ret = Tcl_EvalEx(pTcl, script, l, 0); result = (char *)Tcl_GetStringResult(pTcl); if (ret != TCL_OK && result[0]!='\0') { if (verbose) { SCPrintf(con, eInError, "\nerror: %s\n", result); } iRet = 0; } *resPtr = result; MacroPop(); PopContext(); return iRet; } static int SctMatch(void *data1, void *data2) { SctData *a = data1; SctData *b = data2; return a->node == b->node && strcasecmp(a->name, b->name) == 0; } static char *SctActionHandler(void *actionData, char *lastReply) { SctData *data = actionData; Hdb *node = data->node; SctController *controller = data->controller; char *state; char *result; char *script; char *send = NULL; int i; SConnection *con; if (currentCon) { con = SCStorePush(currentCon); } else { con = SCStorePush(controller->conn); } SetProp(node, controller->node, "result", lastReply); if (controller->verbose && lastReply != NULL && *lastReply != '\0') { SCPrintf(con, eWarning, "reply : %s", lastReply); } state = GetProp(node, controller->node, "state"); if (state == NULL || strcasecmp(state, "idle") == 0) { state = data->name; SetProp(node, controller->node, "state", state); } for (i = 0; i < 10; i++) { SetProp(node, controller->node, "send", NULL); script = GetProp(node, controller->node, state); if (script == NULL) script = state; if (! SctCallInContext(con, script, node, data->controller, &result)) { SCPrintf(con, eError, "ERROR: %s", result); goto finish; } state = result; if (strcasecmp(state, "idle") == 0) { if (currentCon && ! data->answered) { SCWrite(con, "o.k.", eValue); } if (data->conCtx != NULL) { SCStoreFree(data->conCtx); data->conCtx = NULL; } goto finish; } SetProp(node, controller->node, "state", state); send = GetProp(node, controller->node, "send"); if (send != NULL && send[0] != '\0') { if (controller->verbose) { SCPrintf(con, eWarning, "send : %s", send); } goto quit; } } SCPrintf(con, eError, "ERROR: too many quick scripts chained"); finish: SetProp(node, controller->node, "state", "idle"); quit: if (currentCon) { SCStorePop(currentCon); } else { SCStorePop(controller->conn); } return send; } static char *SctWriteHandler(void *actionData, char *lastReply) { SctData *data = actionData; SCStore *old; char *result; old = currentCon; currentCon = data->conCtx; result = SctActionHandler(data, lastReply); currentCon = old; return result; } static hdbCallbackReturn SctMainCallback(Hdb *node, void *userData, hdbMessage *msg) { SctController *controller = userData; hdbDataSearch *dsm; hdbDataMessage *mm; hdbPtrMessage *pm; hdbMessage *km; ContextItem *s; SConnection *con; char *geterror; pm = GetKillPtrMessage(msg); if (pm != NULL) { if (controller == pm->pPtr) { return hdbKill; } return hdbContinue; } dsm = GetHdbDataSearchMessage(msg); if (dsm != NULL) { if (dsm->testPtr == controller) { dsm->result = controller; return hdbAbort; } return hdbContinue; } km = GetHdbKillNodeMessage(msg); if (km != NULL) { CleanStack(node); return hdbContinue; } mm = GetHdbGetMessage(msg); if (mm != NULL) { con = mm->callData; geterror = GetHdbProp(node, "geterror"); if (geterror != NULL) { SCPrintf(con, eError, "ERROR: %s", geterror); return hdbAbort; } return hdbContinue; } return hdbContinue; } static hdbCallbackReturn SctActionCallback(Hdb *node, void *userData, hdbMessage *msg) { hdbDataSearch *dsm; hdbDataMessage *mm; hdbPtrMessage *pm; SctData *data = userData; Hdb *target; char *script; int l; int iRet; pDynString text; DevPrio prio; char *writeprio; char *error; SConnection *con; SConnection *con2; char path[MAX_HDB_PATH]; pm = GetKillPtrMessage(msg); if (pm != NULL) { if (data->controller == pm->pPtr) { return hdbKill; } return hdbContinue; } dsm = GetHdbDataSearchMessage(msg); if (dsm != NULL) { if (dsm->testPtr == &actionCallback) { if (strcasecmp(data->name, actionCallback.name) == 0) { dsm->result = data; } return hdbAbort; } return hdbContinue; } mm = GetHdbSetMessage(msg); if (mm != NULL && data->writable) { con = mm->callData; /* set target value */ text = formatValue(*(mm->v), node); SetHdbProperty(node, "target", GetCharArray(text)); /* call check script, if available */ script = GetProp(node, data->controller->node, "check"); if (script != NULL) { if (SctCallInContext(con, script, node, data->controller, &error) == 0) { SCPrintf(con, eError, "ERROR: %s", error); SetHdbProperty(node,"target", NULL); return hdbAbort; } } /* enqueue write action */ writeprio = GetProp(node, data->controller->node, "writeprio"); if (writeprio != NULL) { prio = DevText2Prio(writeprio); if (prio < 0) prio = WritePRIO; } else { prio = WritePRIO; } if (data->conCtx != NULL) { con2 = SCStorePush(data->conCtx); GetHdbPath(node, path, sizeof path); SCPrintf(con2, eValue, "%s target changed to %s before completion", path, GetCharArray(text)); SCStorePop(data->conCtx); } DeleteDynString(text); data->conCtx = SCSave(con, data->conCtx); data->answered = 0; DevQueue(data->controller->devser, data, prio, SctWriteHandler, SctMatch, NULL); return hdbContinue; } mm = GetHdbUpdateMessage(msg); if (mm != NULL) { if (currentCon) { /* update called from a write action */ data->answered = 1; GetHdbPath(node, path, sizeof path); con = SCStorePush(currentCon); text = formatValue(*(mm->v), node); SCPrintf(con, eStatus, "%s = %s", path, GetCharArray(text)); DeleteDynString(text); SCStorePop(currentCon); } return hdbContinue; } return hdbContinue; } static char *ParText(Hdb *cmdNode, char *name) { Hdb *par; par = GetHipadabaNode(cmdNode, name); if (par && par->value.dataType == HIPTEXT) { return par->value.v.text; } return ""; } static double ParValue(Hdb *cmdNode, char *name) { Hdb *par; par = GetHipadabaNode(cmdNode, name); if (par) { switch (par->value.dataType) { case HIPINT: return par->value.v.intValue; case HIPFLOAT: return par->value.v.doubleValue; } } return -999; } void SctKillData(void *d) { SctData *data = d; if (data->name) free(data->name); if (data->conCtx) SCStoreFree(data->conCtx); free(data); } void SctAddPollNode(SctController *controller, Hdb *node, double interval, DevPrio prio, char *action) { SctData *data; hdbCallback *cb; if (! FindHdbCallbackData(node, controller)) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); } actionCallback.name = action; data = FindHdbCallbackData(node, &actionCallback); if (data == NULL) { data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->writable = 0; data->conCtx = NULL; data->name = strdup(action); } DevSchedule(controller->devser, data, prio, interval, SctActionHandler, SctMatch, NULL); } static int SctPollCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { Hdb *node; SctController *controller; double interval; char *path; DevPrio prio; char *action; if(nPar < 1){ SCPrintf(con,eError, "ERROR: should be: %s poll ( )", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node"); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } SetHdbProperty(node,"geterror","Not read yet"); interval = ParValue(cmdNode, "interval"); prio = DevText2Prio(ParText(cmdNode, "prio")); action = ParText(cmdNode, "action"); SctAddPollNode(controller, node, interval, prio, action); sct->base->node = node; return 1; } static int SctConnectCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { Hdb *node; SctController *controller; char *path; hdbCallback *cb; if(nPar < 1){ SCPrintf(con,eError, "ERROR: should be: %s connect ", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node"); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } if (! FindHdbCallbackData(node, controller)) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); } SCSendOK(con); return 1; } int SctAddWriteNode(SctController *controller, Hdb *node) { hdbCallback *cb; SctData *data; if (! FindHdbCallbackData(node, controller)) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); } actionCallback.name = strdup("write"); data = FindHdbCallbackData(node, &actionCallback); if (data != NULL) { return 0; } data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->writable = 1; data->conCtx = NULL; data->name = strdup("write"); cb = MakeHipadabaCallback(SctActionCallback, data, SctKillData); assert(cb); data->node = node; AppendHipadabaCallback(node, cb); RemoveSetUpdateCallback(node); return 1; } static int SctWriteCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { Hdb *node; SctController *controller; double interval; char *path; if(nPar < 1){ SCPrintf(con, eError, "ERROR: should be: %s write ", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node"); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } if (SctAddWriteNode(controller, node) == 0) { SCPrintf(con, eError, "ERROR: %s has already a write action", path); return 0; } sct->base->node = node; return 1; } SctData *SctQueueNode(SctController *controller, Hdb *node, DevPrio prio, char *action) { SctData *data; hdbCallback *cb; if (! FindHdbCallbackData(node, controller)) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); } data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->writable = 0; data->name = strdup(action); data->conCtx = NULL; DevQueue(data->controller->devser, data, prio, SctWriteHandler, SctMatch, SctKillData); return data; } static int SctQueueCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { Hdb *node; SctController *controller; double interval; char *path; char *action; DevPrio prio; SctData *data; if(nPar < 2){ SCPrintf(con,eError, "ERROR: should be: %s queue ", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node"); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } prio = DevText2Prio(ParText(cmdNode, "prio")); action = ParText(cmdNode, "action"); data = SctQueueNode(controller, node, prio, action); data->conCtx = SCSave(con, NULL); return 1; } typedef struct SctTransact { char *command; int sent; SConnection *con; }SctTransact, *pSctTransact; static void KillSctTransact(void *data){ pSctTransact self = (pSctTransact)data; if(self == NULL){ return; } if(self->command){ free(self->command); } free(self); } static char *TransactionHandler(void *actionData, char *lastReply){ pSctTransact st = (pSctTransact)actionData; if(st->sent == 0){ st->sent = 1; return st->command; } else { st->sent = 2; SCWrite(st->con,lastReply, eValue); return NULL; } } static int SctTransactMatch(void *d1, void *d2){ return d1 == d2; } static int SctTransactCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { pSctTransact st = NULL; SctController *c; c = (SctController *)ccmd->pPrivate; st = calloc(sizeof(SctTransact),1); if(st == NULL){ SCWrite(con,"ERROR: out of memory in SctTransactCommand", eError); return 0; } st->con = con; st->command = strdup(par[0]->value.v.text); DevQueue(c->devser, st, WritePRIO, TransactionHandler, SctTransactMatch, NULL); while(st->sent != 2){ TaskYield(pServ->pTasker); if(SCGetInterrupt(con) != eContinue){ break; } } KillSctTransact(st); return 1; } static int SctQueueComCmd(pSICSOBJ ccmd, SConnection *con, Hdb *cmdNode, Hdb *par[], int nPar) { pSctTransact st = NULL; SctController *c; c = (SctController *)ccmd->pPrivate; st = calloc(sizeof(SctTransact),1); if(st == NULL){ SCWrite(con,"ERROR: out of memory in SctTransactCommand", eError); return 0; } st->con = con; st->command = strdup(par[0]->value.v.text); DevQueue(c->devser, st, WritePRIO, TransactionHandler, SctTransactMatch, KillSctTransact); return 1; } static hdbCallbackReturn SctDebugCallback(Hdb *node, void *userData, hdbMessage *msg) { hdbDataMessage *mm; SctController *controller = userData; SConnection *con; int i; mm = GetHdbSetMessage(msg); if (mm != NULL) { i = mm->v->v.intValue; if (i < 0) { controller->verbose = 0; DevDebugMode(controller->devser, -1); } else if (i == 0) { controller->verbose = 1; DevDebugMode(controller->devser, -1); } else { controller->verbose = 1; DevDebugMode(controller->devser, i); } } return hdbContinue; } static void SctKillController(void *c) { SctController *controller = c; CleanStack(controller->node); RemoveSICSInternalCallback(controller); /* devser is killed by task function */ } static int SctMakeController(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { SICSOBJ *ccmd; Hdb *parent, *par, *cmd; char *nodeName; hdbCallback *cb; SctController *controller; if (argc < 2) { SCPrintf(con, eError, "ERROR: should be %s ...", argv[0]); return 0; } parent = FindHdbParent(NULL, argv[1], &nodeName, con); if (parent == NULL) return 0; /* error message already written */ controller = calloc(1, sizeof(*controller)); assert(controller); controller->verbose = 0; ccmd = MakeSICSOBJv(nodeName, "SctController", HIPNONE, usSpy); controller->node = ccmd->objectNode; controller->conn = SCSave(SCCreateDummyConnection(pServ->pSics), NULL); assert(ccmd); assert(controller->node->mama == NULL); ccmd->pPrivate = controller; ccmd->KillPrivate = SctKillController; AddCommand(pServ->pSics, nodeName, InvokeSICSOBJ, KillSICSOBJ, ccmd); SetDescriptorKey(ccmd->pDes, "creationCommand", "0"); AddHipadabaChild(parent, controller->node, con); controller->devser = DevMake(con, argc - 2, argv + 2); if (!controller->devser) return 0; cmd = AddSICSHdbPar(controller->node, "poll", usMugger, MakeSICSFunc(SctPollCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); AddSICSHdbPar(cmd, "interval", usMugger, MakeHdbFloat(5.0)); AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("read")); AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("read")); cmd = AddSICSHdbPar(controller->node, "connect", usMugger, MakeSICSFunc(SctConnectCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); cmd = AddSICSHdbPar(controller->node, "write", usMugger, MakeSICSFunc(SctWriteCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); cmd = AddSICSHdbPar(controller->node, "queue", usMugger, MakeSICSFunc(SctQueueCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("write")); AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("write")); cmd = AddSICSHdbPar(controller->node, "transact", usMugger, MakeSICSFunc(SctTransactCmd)); AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText("")); cmd = AddSICSHdbPar(controller->node, "queuecom", usMugger, MakeSICSFunc(SctQueueComCmd)); AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText("")); par = AddSICSHdbPar(controller->node, "debug", usUser, MakeHdbInt(0)); cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL); if (cb) AppendHipadabaCallback(par, cb); sct->base->controllerNode = controller->node; sct->base->node = NULL; return 1; } void SctKill(void *object) { assert(sct == object); ContextItem *p, *q; for (p = sct->nodes; p != NULL; p = q) { q = p->next; free(p); } for (p = sct->trash; p != NULL; p = q) { q = p->next; free(p); } if (sct->desc != NULL) { DeleteDescriptor(sct->desc); } free(sct); sct = NULL; } /* from sctdriveadapter.c */ int SctMakeDriveAdapter(SConnection *pCon, SicsInterp *pSics, void *object, int argc, char *argv[]); void SctInit(void) { if (sct) return; sct = calloc(1, sizeof(*sct)); assert(sct); sct->desc = CreateDescriptor("ScriptContext"); PushContext(NULL, NULL); sct->base = sct->nodes; AddCommand(pServ->pSics, "sct", SctCommand, SctKill, sct); AddCmd("makesctcontroller", SctMakeController); AddCmd("makesctdrive", SctMakeDriveAdapter); }