#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" #include "scriptcontext.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; Hdb *sendNode; int sendCalled; } ScriptContext; struct SctController { DevSer *devser; Hdb *node; /* the controller node */ SConnection *conn; int verbose; }; /* action data and write callback data */ typedef struct SctData { char *name; SctController *controller; SConnection *conCtx; int answered; int inMacro; Hdb *node; } SctData; static ScriptContext *sct = NULL; static SctData *queueData = 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(cNode, 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 = NULL; Hdb *cNode = NULL; hdbValue v; double dtime; assert(sct == object); if (sct->nodes != NULL) { node = sct->nodes->node; cNode = sct->nodes->controllerNode; } 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); ReleaseHdbValue(&v); SetHdbProperty(node, "geterror", NULL); return 1; } /* * print */ if (strcmp(argv[1], "print") == 0) { if (queueData != NULL) { queueData->answered = 1; SCsetMacro(con, queueData->inMacro); /* take macro flag stored at set callback */ } Arg2Text(argc - 2, argv + 2, value, sizeof value); SCWrite(con, value, eWarning); SCsetMacro(con, 1); /* where are always in Macro */ return 1; } /** * controller */ if (strcmp(argv[1], "controller") == 0) { SCWrite(con, cNode->name, eValue); 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; } /* * send command */ if (strcmp(argv[1], "send") == 0 && argc > 2) { if (sct->sendNode != node) { SCPrintf(con, eError, "ERROR: %s may be called only in action scripts", argv[0]); return 0; } sct->sendCalled = 1; /* continue with property handling */ } /* * 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 */ if (argc == 3) { SetProp(node, cNode, argv[1], argv[2]); } else { 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, eLog, "script: %s", 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 (strncmp(result, "ERROR:", 6) == 0) { /* remove "ERROR:", as it will be added later */ result += 6; if (result[0] == ' ') { result++; } } if (verbose) { SCPrintf(con, eLog, "error: %s", 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; } /* * This routine is running the script chain */ static char *SctActionHandler(void *actionData, char *lastReply, int commError) { SctData *data = actionData; Hdb *node = data->node; SctController *controller = data->controller; char *state; char *result; char *script = NULL; char *errorScript = NULL; char *send = NULL; int i; SConnection *con; char eprop[80]; char msg[1024]; char path[1024]; char origScript[80]; char *blank; char *emsg; size_t l; int cnt; int ret; char timeKey[50], timeVal[50]; int iMacro; if (queueData != NULL && queueData->conCtx != NULL) { con = queueData->conCtx; } else { con = controller->conn; } /* * If this is a followup call, the I/O system will have set the * property result to the data from the device. Read this now and * print it if diagnostics is required. */ SetProp(node, controller->node, "result", lastReply); script = NULL; if (!commError && controller->verbose && lastReply != NULL && *lastReply != '\0') { SCPrintf(con, eLog, "reply : %s", lastReply); } /* * When this is a followup, we use the content of the * state field as the property storing the next script to * run. If this is the start of a chain this is set to the * data->name which is either read or write */ state = GetProp(node, controller->node, "state"); if (state == NULL || strcasecmp(state, "idle") == 0) { state = data->name; SetProp(node, controller->node, "state", state); } /* * Sometimes one wishes to call multiple scripts in succession * before returning into I/O. Such scripts then do not set the * send property. The loop is taking care of this. Not more * then 10 scripts can be changed in this way. */ for (i = 0; i < 10; i++) { /* * read the script to invoke from the property living * in state */ script = GetProp(node, controller->node, state); if (script == NULL) script = state; snprintf(origScript, sizeof origScript, "%s", script); if (commError) { errorScript = GetProp(node, controller->node, "commerror"); if (errorScript != NULL) script = errorScript; commError = 0; } /* * Set the context and invoke the script */ script = strdup(script); sct->sendNode = node; sct->sendCalled = 0; ret = SctCallInContext(con, script, node, controller, &result); sct->sendNode = NULL; if (ret == 0) { /* * an error occurred in the script: complain bitterly */ snprintf(eprop, sizeof eprop, "error_during_%s", data->name); emsg = GetHdbProp(node, eprop); if (emsg == NULL || con != controller->conn) { GetHdbPath(node, path, sizeof path); SCPrintf(con, eError, "ERROR: action <%s> in {%s} node %s:\nERROR: %s", data->name, origScript, path, result); } snprintf(msg, sizeof msg, "<%s> %s", origScript, result); SetHdbProperty(node, eprop, msg); blank = strchr(origScript, ' '); if (blank != NULL) { l = blank - origScript; } else { l = strlen(origScript); } snprintf(eprop, sizeof eprop, "error_in_%.*s", l, origScript); emsg = GetHdbProp(node, eprop); cnt = 0; if (emsg != NULL) { sscanf(emsg, "%d", &cnt); } cnt++; snprintf(msg, sizeof msg, "%dx {%s}: %s", cnt, origScript, result); SetHdbProperty(node, eprop, msg); send = NULL; free(script); goto finish; } /* * The script executed OK. * The next state is the result */ state = result; /* * if the new state is idle, clean everything up * and terminate the script chain */ if (strcasecmp(state, "idle") == 0 || strcasecmp(state, "unpoll") == 0) { /* * send an O.k. if there was no othe rreply on write's */ if (queueData == data && !data->answered) { if (queueData->inMacro == 0) { iMacro = SCinMacro(con); con->iMacro = 0; SCWrite(con, "o.k.", eValue); SCsetMacro(con, iMacro); } } snprintf(eprop, sizeof eprop, "error_during_%s", data->name); emsg = GetHdbProp(node, eprop); if (emsg != NULL) { GetHdbPath(node, path, sizeof path); SCPrintf(con, eError, "action <%s>: success after error message (%s)\n %s", data->name, path, emsg); SetHdbProperty(node, eprop, NULL); } snprintf(timeKey, sizeof timeKey, "%s_time", data->name); snprintf(timeVal, sizeof timeVal, "%.3f", DoubleTime()); SetHdbProperty(node, timeKey, timeVal); send = NULL; free(script); goto finish; } /* * set the state property to the next state for * the next invocation of this routine */ SetProp(node, controller->node, "state", state); free(script); script = NULL; /* * If there is data to send, check it and do so */ if (sct->sendCalled) { send = GetProp(node, controller->node, "send"); if (send == NULL) send = ""; if (controller->verbose) { SCPrintf(con, eLog, "send : %s", send); } return send; } } SCPrintf(con, eLogError, "ERROR: too many quick scripts chained"); finish: if (strcmp(data->name, "write") == 0) { SetProp(node, controller->node, "writestatus", "commandsent"); } if (strcasecmp(state, "unpoll") == 0) { DevUnschedule(controller->devser, data, SctActionHandler, SctMatch); send = NULL; } SetProp(node, controller->node, "state", "idle"); if (data->conCtx != NULL) { SCDeleteConnection(data->conCtx); data->conCtx = NULL; } return send; } static char *SctWriteHandler(void *actionData, char *lastReply, int commError) { SctData *data = actionData; SctData *old; char *result; old = queueData; queueData = data; result = SctActionHandler(data, lastReply, commError); queueData = old; return result; } static int SctMatchNode(void *vNode, void *vData) { Hdb *node = vNode; SctData *d = vData; return node == d->node; } /* * This is the read callback for nodes participating in the * scriptcontext system */ 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; char error[256]; if (controller->devser == NULL) { /* defunct controller */ return hdbContinue; } 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) { /* unschedule all actions related to this node */ DevUnschedule(controller->devser, node, SctActionHandler, SctMatchNode); CleanStack(node); return hdbContinue; } mm = GetHdbGetMessage(msg); if (mm != NULL) { con = mm->callData; geterror = GetHdbProp(node, "geterror"); if (geterror != NULL) { snprintf(error,255,"ERROR: %s", geterror); SCWrite(con, error, eError); if(mm->v->dataType == HIPTEXT){ if(mm->v->v.text != NULL){ free(mm->v->v.text); } mm->v->v.text = strdup(error); } return hdbAbort; } return hdbContinue; } return hdbContinue; } /* * This is the callback registered for nodes which * are written too. */ static hdbCallbackReturn SctActionCallback(Hdb * node, void *userData, hdbMessage * msg) { hdbDataSearch *dsm; hdbDataMessage *mm; hdbPtrMessage *pm; hdbMessage *km; SctData *data = userData; Hdb *target; char *script; int l; int iRet; pDynString text; DevPrio prio; char *writeprio; char *error; SConnection *con; char path[MAX_HDB_PATH]; char *sicsCommand; int iMacro; 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) { 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: in {%s}: %s", script, 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 == NullPRIO) { SCPrintf(con, eError, "ERROR: unknown priority: %s", writeprio); prio = WritePRIO; } } else { prio = WritePRIO; } /* * check for duplicate sets on a node. */ if (data->conCtx != NULL) { if (data->inMacro == 0) { GetHdbPath(node, path, sizeof path); SCsetMacro(data->conCtx, 0); SCPrintf(data->conCtx, eWarning, "%s target changed to %s before completion", path, GetCharArray(text)); } /* * the node has already been queued for execution. But as we never * know if the script chain is already running, the only clean * solution is to requeue the node. */ SCDeleteConnection(data->conCtx); } DeleteDynString(text); data->conCtx = SCCopyConnection(con); data->answered = 0; data->inMacro = SCinMacro(con); DevQueue(data->controller->devser, data, prio, SctWriteHandler, SctMatch, NULL); /* no kill function in DevQueue: data is owned by the node (callback list) */ return hdbContinue; } mm = GetHdbUpdateMessage(msg); if (mm != NULL) { if (queueData != NULL && queueData->conCtx != NULL && queueData->answered != 1) { /* update called from a write action */ text = formatValue(*(mm->v), node); if (queueData->inMacro == 0 && queueData->node == node) { queueData->answered = 1; iMacro = SCinMacro(queueData->conCtx); SCsetMacro(queueData->conCtx, 0); sicsCommand = GetHdbProp(node, "sicscommand"); if (sicsCommand) { SCPrintf(queueData->conCtx, eWarning, "OK: %s %s", sicsCommand, GetCharArray(text)); } else { GetHdbPath(node, path, sizeof path); SCPrintf(queueData->conCtx, eWarning, "OK: hset %s %s", path, GetCharArray(text)); } SCsetMacro(queueData->conCtx, iMacro); } DeleteDynString(text); } return hdbContinue; } return hdbContinue; } static char *ParText(Hdb * cmdNode, char *name, int nPar, char *defaultValue) { Hdb *par; for (par = cmdNode->child; nPar > 0 && par != NULL; par = par->next, nPar--) { if (strcasecmp(par->name, name) == 0) { if (par->value.dataType == HIPTEXT) { return par->value.v.text; } } } return defaultValue; } static double ParValue(Hdb * cmdNode, char *name, int nPar, double defaultValue) { Hdb *par; for (par = cmdNode->child; nPar > 0 && par != NULL; par = par->next, nPar--) { if (strcasecmp(par->name, name) == 0) { switch (par->value.dataType) { case HIPINT: return par->value.v.intValue; case HIPFLOAT: return par->value.v.doubleValue; } } } return defaultValue; } static void SctKillData(void *d) { SctData *data = d; if (data->name) free(data->name); if (data->conCtx) SCDeleteConnection(data->conCtx); free(data); } static void SctKillCBData(void *d) { SctData *data = d; if (data->controller->devser != NULL) { DevRemoveAction(data->controller->devser, data); } SctKillData(d); } int 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); SetHdbProperty(node, "geterror", "Not read yet"); } data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->conCtx = NULL; /* we might need a dummy connection here */ data->name = strdup(action); return DevSchedule(controller->devser, data, prio, interval, SctActionHandler, SctMatch, SctKillData); } 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; char *prioText; if (nPar < 1) { SCPrintf(con, eError, "ERROR: should be: %s poll ( )", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node", nPar, ""); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } interval = ParValue(cmdNode, "interval", nPar, 5); prioText = ParText(cmdNode, "prio", nPar, "read"); prio = DevText2Prio(prioText); if (prio == NullPRIO) { SCPrintf(con, eError, "ERROR: unknown priority: %s", prioText); return 0; } action = ParText(cmdNode, "action", nPar, "read"); if (SctAddPollNode(controller, node, interval, prio, action) > 0) { SCPrintf(con, eValue, "%s poll on %s changed to %g sec, %s prio", action, path, interval, prioText); } else { SCPrintf(con, eValue, "%s poll registered on %s (%g sec, %s prio)", action, path, interval, prioText); } return 1; } static int SctUnpollCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { SctController *controller; double interval; char *path; DevPrio prio; char *prioText; SctData data; if (nPar < 1) { SCPrintf(con, eError, "ERROR: should be: %s unpoll ()", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node", nPar, ""); data.node = FindHdbNode(NULL, path, con); if (data.node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } data.name = ParText(cmdNode, "action", nPar, "read"); if (DevUnschedule(controller->devser, &data, SctActionHandler, SctMatch) == 0) { SCPrintf(con, eValue, "%s poll not found on %s", data.name, path); } else { SCSendOK(con); } 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", nPar, ""); 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 = "write"; /* local, so no strdup here */ data = FindHdbCallbackData(node, &actionCallback); if (data != NULL) { return 0; } data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->conCtx = NULL; data->name = strdup("write"); cb = MakeHipadabaCallback(SctActionCallback, data, SctKillCBData); 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", nPar, ""); 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; } SCSendOK(con); return 1; } void SctQueueNode(SctController * controller, Hdb * node, DevPrio prio, char *action, SConnection * con) { SctData *data; hdbCallback *cb; /* * The test for read below is questionable. If this becomes a problem * take it out and tell Mark and Markus about the fact. */ if (!FindHdbCallbackData(node, controller) && strcmp(action,"read") == 0) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); } data = calloc(1, sizeof(*data)); assert(data); data->controller = controller; data->node = node; data->name = strdup(action); data->conCtx = NULL; data->answered = 1; if (!DevQueue(data->controller->devser, data, prio, SctWriteHandler, SctMatch, SctKillData)) { if (data->name != NULL) { free(data->name); } free(data); } else { if (con != NULL) { data->conCtx = SCCopyConnection(con); } } return; } 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; char *prioText; if (nPar < 2) { SCPrintf(con, eError, "ERROR: should be: %s queue ", ccmd->objectNode->name); return 0; } controller = ccmd->pPrivate; path = ParText(cmdNode, "node", nPar, ""); node = FindHdbNode(NULL, path, con); if (node == NULL) { SCPrintf(con, eError, "ERROR: %s not found", path); return 0; } prioText = ParText(cmdNode, "prio", nPar, "write"); prio = DevText2Prio(prioText); if (prio == NullPRIO) { SCPrintf(con, eError, "ERROR: unknown priority: %s", prioText); return 0; } action = ParText(cmdNode, "action", nPar, "write"); SctQueueNode(controller, node, prio, action, con); 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); } if (self->con) { SCDeleteConnection(self->con); } free(self); } static char *TransactionHandler(void *actionData, char *lastReply, int commError) { 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 = SCCopyConnection(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 SctSendCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { pSctTransact st = NULL; SctController *c; char *prioText = NULL; int prio; c = (SctController *) ccmd->pPrivate; st = calloc(sizeof(SctTransact), 1); if (st == NULL) { SCWrite(con, "ERROR: out of memory in SctSendCmd", eError); return 0; } st->con = SCCopyConnection(con); st->command = strdup(par[0]->value.v.text); prioText = ParText(cmdNode, "prio", nPar, "write"); prio = DevText2Prio(prioText); if (prio == NullPRIO) { prio = WritePRIO; } DevQueue(c->devser, st, prio, TransactionHandler, SctTransactMatch, KillSctTransact); return 1; } static int SctDisconnect(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { SctController *c; c = (SctController *) ccmd->pPrivate; DevDisconnect(c->devser); 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 SctDeferredFree(void *data) { free(data); return; } static int SctDeferredTask(void *data) { return 0; } static void SctKillController(void *c) { SctController *controller = c; SConnection *con; CleanStack(controller->node); RemoveSICSInternalCallback(controller); if (controller->conn) { SCDeleteConnection(controller->conn); } DevKill(controller->devser); controller->devser = NULL; if (pServ->pTasker) { TaskRegister(pServ->pTasker, SctDeferredTask, NULL, SctDeferredFree, controller, 0); } else { free(controller); } } static int SctMakeController(SConnection * con, SicsInterp * sics, void *object, int argc, char *argv[]) { SICSOBJ *ccmd; Hdb *parent, *par, *cmd; char *objName; hdbCallback *cb; SctController *controller; if (argc < 2) { SCPrintf(con, eError, "ERROR: should be %s ...", argv[0]); return 0; } if (strchr(argv[1], '/') == NULL) { /* object name only -> do not anchor in tree */ parent = NULL; objName = argv[1]; } else { /* full path given -> install into the hipadaba */ parent = FindHdbParent(NULL, argv[1], &objName, con); if (parent == NULL) return 0; /* error message already written */ } controller = calloc(1, sizeof(*controller)); assert(controller); controller->verbose = 0; ccmd = MakeSICSOBJv(objName, "SctController", HIPNONE, usSpy); controller->node = ccmd->objectNode; controller->conn = SCCreateDummyConnection(pServ->pSics); assert(ccmd); assert(controller->node->mama == NULL); ccmd->pPrivate = controller; ccmd->KillPrivate = SctKillController; if (parent != NULL) { AddHipadabaChild(parent, controller->node, con); } controller->devser = DevMake(con, argc - 2, argv + 2); if (!controller->devser) return 0; SetHdbProperty(controller->node, "controllerName", objName); SetHdbProperty(controller->node, "sicsdev", objName); AddCommand(pServ->pSics, objName, InterInvokeSICSOBJ, KillSICSOBJ, ccmd); RegisterSICSOBJKillCmd(ccmd, objName); SetDescriptorKey(ccmd->pDes, "creationCommand", "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, "unpoll", usMugger, MakeSICSFunc(SctUnpollCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); 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, "send", usMugger, MakeSICSFunc(SctSendCmd)); AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText("")); AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("")); cmd = AddSICSHdbPar(controller->node, "disconnect", usMugger, MakeSICSFunc(SctDisconnect)); par = AddSICSHdbPar(controller->node, "debug", usUser, MakeHdbInt(-1)); cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL); if (cb) AppendHipadabaCallback(par, cb); 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"); AddCommand(pServ->pSics, "sct", SctCommand, SctKill, sct); AddCmd("makesctcontroller", SctMakeController); } int SctVerbose(SctController * c) { return c->verbose; }