#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" #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; } int SctCommand(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { static char value[1024]; char *val; Hdb *node = sct->nodes->node; Hdb *cNode = sct->nodes->controllerNode; 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; } 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); } 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; PopContext(); 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; /* 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); return hdbAbort; } } /* set target value */ text = formatValue(*(mm->v), node); SetHdbProperty(node, "target", GetCharArray(text)); /* 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; } 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; } 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; } 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); DevKill(controller->devser); } 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, "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")); 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; } 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); }