#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" /* TODO: retry feature, error handling */ #define MAX_HDB_PATH 1024 typedef struct ScriptContext { ObjectDescriptor *desc; Hdb *node; } ScriptContext; typedef struct ContextStack { struct ContextStack *next; Hdb *node; } ContextStack; 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 *nctx = NULL; /* the node context */ static ScriptContext *cctx = NULL; /* the controller context */ static ContextStack *stack, *stackTrash; static SCStore *currentCon = NULL; static struct { char *name; } actionCallback; static char *mainCallback = "main callback"; void PushContext(Hdb *node) { ContextStack *new; if (stackTrash == NULL) { new = calloc(1, sizeof(*new)); } else { new = stackTrash; stackTrash = stackTrash->next; } new->next = stack; new->node = node; stack = new; } Hdb *PopContext(void) { ContextStack *c; c = stack; stack = stack->next; c->next = stackTrash; stackTrash = c; return c->node; } static void SetProp(Hdb *node, char *key, char *value) { if (value == NULL) { if (GetHdbProperty(node, key, NULL, 0) > 0) { SetHdbProperty(node, key, ""); } } else { SetHdbProperty(node, key, value); } } int SctCommand(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { ScriptContext *ctx = object; static char value[1024]; char *val; Hdb *node = ctx->node; if (node == 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 = GetHdbProp(node, 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); if (val != NULL) { SetHdbProperty(node, argv[1], val); if (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(nctx->node); PushContext(cctx->node); nctx->node = node; cctx->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; cctx->node = PopContext(); nctx->node = PopContext(); return iRet; } void SctKill(void *object) { ScriptContext *ctx = object; if (ctx == nctx) nctx = NULL; if (ctx == cctx) cctx = NULL; if (ctx->desc != NULL) { DeleteDescriptor(ctx->desc); } free(object); } ScriptContext *SctMake(char *name) { ScriptContext *ctx; ctx = calloc(1, sizeof(*ctx)); assert(ctx); ctx->desc = CreateDescriptor("ScriptContext"); ctx->node = NULL; AddCommand(pServ->pSics, name, SctCommand, SctKill, ctx); return ctx; } 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(controller->node, "result", lastReply); if (controller->verbose && lastReply != NULL && *lastReply != '\0') { SCPrintf(con, eWarning, "reply : %s", lastReply); } state = GetHdbProp(controller->node, "state"); if (state == NULL || state[0] == '\0') { state = data->name; SetProp(controller->node, "state", state); } for (i = 0; i < 10; i++) { SetProp(controller->node, "send", NULL); script = GetHdbProp(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(controller->node, "state", state); send = GetHdbProp(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(controller->node, "state", ""); 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; ContextStack *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) { /* clean context from killed nodes */ for (s = stack; s != NULL; s = s->next) { if (s->node == node) { s->node = NULL; } } if (cctx->node == node) cctx->node = NULL; if (nctx->node == node) nctx->node = NULL; 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 = GetHdbProp(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 = GetHdbProp(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); if (stack == NULL) { nctx->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; } if (stack == NULL) { nctx->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; 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"); SetProp(controller->node, "state", ""); AddHipadabaChild(parent, controller->node, con); controller->devser = DevMake(con, argc - 2, argv + 2); if (!controller->devser) return 0; cmd = MakeSICSHdbPar("poll", usMugger, MakeHdbFunc((voidFunc*)SctPollCmd)); assert(cmd); AddHipadabaChild(controller->node, cmd, con); par = MakeSICSHdbPar("node", usMugger, MakeHdbText("")); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("interval", usMugger, MakeHdbFloat(5.0)); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("prio", usMugger, MakeHdbText("read")); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("action", usMugger, MakeHdbText("read")); assert(par); AddHipadabaChild(cmd, par, con); cmd = MakeSICSHdbPar("write", usMugger, MakeHdbFunc((voidFunc*)SctWriteCmd)); assert(cmd); AddHipadabaChild(controller->node, cmd, con); par = MakeSICSHdbPar("node", usMugger, MakeHdbText("")); assert(par); AddHipadabaChild(cmd, par, con); cmd = MakeSICSHdbPar("queue", usMugger, MakeHdbFunc((voidFunc*)SctQueueCmd)); assert(cmd); AddHipadabaChild(controller->node, cmd, con); par = MakeSICSHdbPar("node", usMugger, MakeHdbText("")); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("prio", usMugger, MakeHdbText("write")); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("action", usMugger, MakeHdbText("write")); assert(par); AddHipadabaChild(cmd, par, con); par = MakeSICSHdbPar("debug", usUser, MakeHdbInt(0)); assert(par); AddHipadabaChild(controller->node, par, con); cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL); assert(cb); AppendHipadabaCallback(par, cb); if (stack == NULL) { cctx->node = controller->node; nctx->node = NULL; } return 1; } void SctInit(void) { nctx = SctMake("nctx"); cctx = SctMake("cctx"); AddCmd("makesctcontroller", SctMakeController); }