diff --git a/devser.c b/devser.c index 008acc8e..a2045fca 100644 --- a/devser.c +++ b/devser.c @@ -8,24 +8,16 @@ typedef struct DevAction { DevActionHandler *hdl; DevPrio prio; DevKillActionData *kill; + double interval; /* -1 for a queued action */ + double timeDue; /* 0 for a queued action */ } DevAction; -typedef struct SchedHeader { - struct SchedHeader *next; - DevAction *actions; /* list of actions for given interval and prio */ - DevAction *followingAction; - double interval; - double timeDue; - DevPrio prio; -} SchedHeader; - struct DevSer { - Ascon *asyncConn; /* connection */ + Ascon *ascon; /* connection */ DevAction *current; int killCurrent; - DevAction *actions; /* the action queue */ - DevAction *toKill; /* list of actions to be killed */ - SchedHeader *headers; + DevAction *actions; /* the action queue */ + DevAction *toKill; /* list of actions to be killed */ int steps; }; @@ -65,52 +57,81 @@ static void DevFreeActionList(DevAction * actions) } } -DevAction *DevNextAction(DevSer * devser) -{ - DevPrio prio; - double now; - SchedHeader *header; - - - devser->current = NULL; - if (devser->actions) { - prio = devser->actions->prio; +static double nextTime(double last, + double lastInterval, + double now, + double interval) { + double base; + /* nextTime gives the next possible time according to the + * following rules: + * (1) result > now + * (2) result is a multiple of interval + * (3) result > last + min(lastInterval,interval) * 0.99 + */ + if (interval > lastInterval) { + base = last + lastInterval * 0.99; } else { - prio = NullPRIO; + base = last + interval * 0.99; } - now = DoubleTime(); - for (header = devser->headers; - header != NULL && header->prio > prio; header = header->next) { - if (header->followingAction == NULL) { - if (now >= header->timeDue) { - header->followingAction = header->actions; - if (header->interval <= 0) { - header->timeDue = now; - } else { - header->timeDue = (floor(now / header->interval) + 1) - * header->interval; - } - } else if (header->prio == StartPRIO && header->actions != NULL) { - /* special case: poll with StartPRIO pending: block all other actions */ - return devser->current; - } - } - if (header->followingAction != NULL) { - devser->current = header->followingAction; - devser->killCurrent = 0; - header->followingAction = header->followingAction->next; - return devser->current; - } + if (now > base) { + base = now; } - if (devser->actions) { - devser->current = devser->actions; - devser->killCurrent = 1; - devser->actions = devser->actions->next; - } - return devser->current; + return (floor(base / interval) + 1) * interval; } -int DevQueueTask(void *ds) +static DevAction *DevNextAction(DevSer * devser) +{ +/* the action queue is primarily ordered by priority (high prioirty first), + * within one priority by usage (last used action at end) + */ + DevPrio prio; + double now, next; + DevAction *action, **ptr2prev; + + devser->current = NULL; + now = DoubleTime(); + for (ptr2prev = &devser->actions, action = devser->actions; + action != NULL; + ptr2prev = &action->next, action = action->next) { + if (now >= action->timeDue) { + /* remove action from queue */ + *ptr2prev = action->next; + devser->current = action; + if (action->interval >= 0) { + /* this is a scheduled action, to be preserved after use */ + devser->killCurrent = 0; + if (action->interval == 0) { + action->timeDue = now; + } else { + /* increase timeDue according to interval */ + action->timeDue = nextTime(action->timeDue, action->interval, + now, action->interval); + } + prio = action->prio; + + /* insert devser->current before the next lower priority */ + for (action = action->next; /* start after this */ + action != NULL && action->prio >= prio; + ptr2prev = &action->next, action = action->next); + + /* action is now NULL or the next action with lower priority */ + *ptr2prev = devser->current; + devser->current->next = action; + } else { + /* this is a queued action, to be killed after use */ + devser->killCurrent = 1; + } + return devser->current; + } + if (action->prio == StartPRIO) { + /* if a poll with StartPRIO is scheduled, block all other actions */ + return NULL; + } + } + return NULL; +} + +static int DevQueueTask(void *ds) { DevSer *devser = ds; AsconStatus status; @@ -131,11 +152,11 @@ int DevQueueTask(void *ds) } while (action != NULL) { - status = AsconTask(devser->asyncConn); + status = AsconTask(devser->ascon); if (status == AsconFailure) { - replyData = AsconGetError(devser->asyncConn); + replyData = AsconGetError(devser->ascon); } else if (status == AsconReady) { - replyData = AsconRead(devser->asyncConn); + replyData = AsconRead(devser->ascon); } else { return 1; } @@ -144,7 +165,7 @@ int DevQueueTask(void *ds) } sendData = action->hdl(action->data, replyData, (status == AsconFailure)); if (sendData != NULL) { - AsconWrite(devser->asyncConn, sendData, 0); + AsconWrite(devser->ascon, sendData, 0); return 1; } if (devser->killCurrent) { @@ -162,19 +183,18 @@ int DevQueueTask(void *ds) DevSer *DevMake(SConnection * con, int argc, char *argv[]) { DevSer *devser = NULL; - Ascon *asyncConn = NULL; + Ascon *ascon = NULL; - asyncConn = AsconMake(con, argc, argv); - if (!asyncConn) { + ascon = AsconMake(con, argc, argv); + if (!ascon) { return NULL; } devser = calloc(1, sizeof(*devser)); assert(devser); - devser->asyncConn = asyncConn; + devser->ascon = ascon; devser->current = NULL; devser->killCurrent = 0; devser->actions = NULL; - devser->headers = NULL; devser->toKill = NULL; devser->steps = -1; /* no debugging by default */ TaskRegister(pServ->pTasker, DevQueueTask, NULL, NULL, devser, 0); @@ -186,38 +206,17 @@ void DevDebugMode(DevSer * devser, int steps) devser->steps = steps; } -DevAction *DevNewAction(void *data, DevActionHandler hdl, - DevKillActionData * killFunc, DevPrio prio) -{ - DevAction *action; - action = calloc(1, sizeof(*action)); - assert(action); - action->data = data; - action->hdl = hdl; - action->kill = killFunc; - action->prio = prio; - action->next = NULL; - return action; -} - void DevKill(DevSer * devser) { - SchedHeader *h, *victim; - - if (devser->asyncConn) { - AsconKill(devser->asyncConn); + if (devser->ascon) { + AsconKill(devser->ascon); } DevFreeActionList(devser->actions); DevFreeActionList(devser->toKill); - h = devser->headers; - while (h != NULL) { - victim = h; - h = victim->next; - DevFreeActionList(victim->actions); - free(victim); - } if (devser->killCurrent) { - if (devser->current->kill != NULL) devser->current->kill(devser->current); + if (devser->current->kill != NULL) { + devser->current->kill(devser->current); + } devser->killCurrent = 0; free(devser->current); } @@ -227,67 +226,35 @@ void DevKill(DevSer * devser) void DevDisconnect(DevSer * devser) { - if (devser->asyncConn) { - AsconDisconnect(devser->asyncConn); + if (devser->ascon) { + AsconDisconnect(devser->ascon); } } -int DevQueue(DevSer * devser, void *actionData, DevPrio prio, - DevActionHandler hdl, DevActionMatch * matchFunc, - DevKillActionData * killFunc) +int DevUnschedule(DevSer * devser, void *callData, + DevActionHandler * hdl, DevActionMatch * matchFunc) { - DevAction *action, **ptr2Last; - DevAction *new; - - if (prio <= NullPRIO) - prio = NullPRIO + 1; - if (prio >= NumberOfPRIO) - prio = NumberOfPRIO - 1; - - ptr2Last = &devser->actions; - for (action = devser->actions; action != NULL && action->prio >= prio; - action = action->next) { - if (action->hdl == hdl && matchFunc(actionData, action->data)) { - return 0; /* there is already an identical action */ - } - ptr2Last = &action->next; - } - new = DevNewAction(actionData, hdl, killFunc, prio); - new->next = action; - *ptr2Last = new; - return 1; -} - -int DevUnschedule(DevSer * devser, void *actionData, - DevActionHandler hdl, DevActionMatch * matchFunc) -{ - SchedHeader *header = NULL; - DevAction **ptr2Last = NULL; + DevAction **ptr2prev = NULL; DevAction *action = NULL; int cnt = 0; - /* scan through all headers */ - for (header = devser->headers; header != NULL; header = header->next) { - ptr2Last = &header->actions; - for (action = header->actions; action != NULL; action = *ptr2Last) { - if (action->hdl == hdl && matchFunc(actionData, action->data)) { - if (action == header->followingAction) { - /* advance followingAction if equal */ - header->followingAction = action->next; - } - if (action == devser->current) { - devser->current = NULL; - devser->killCurrent = 0; /* should already be 0 */ - } - cnt++; - /* remove from list */ - *ptr2Last = action->next; - /* add to kill list */ - action->next = devser->toKill; - devser->toKill = action; - } else { - ptr2Last = &action->next; + /* scan through the queue */ + for (ptr2prev = &devser->actions, action = devser->actions; + action != NULL; + action = action->next) { + if (action->hdl == hdl && matchFunc(callData, action->data)) { + if (action == devser->current) { + devser->current = NULL; + devser->killCurrent = 0; } + cnt++; + /* remove from list */ + *ptr2prev = action->next; + /* add to kill list */ + action->next = devser->toKill; + devser->toKill = action; + } else { + ptr2prev = &action->next; } } return cnt; @@ -295,67 +262,92 @@ int DevUnschedule(DevSer * devser, void *actionData, int DevSchedule(DevSer * devser, void *actionData, DevPrio prio, double interval, - DevActionHandler hdl, DevActionMatch * matchFunc, + DevActionHandler * hdl, DevActionMatch * matchFunc, DevKillActionData * killFunc) { - SchedHeader *header = NULL; - SchedHeader **ptr2LastHeader = NULL; - SchedHeader *newHeader; - DevAction *action = NULL; - DevAction **ptr2Last = NULL; - DevAction *newAction; + DevAction *action; + DevAction *foundAction = NULL; + DevAction **ptr2prev; + DevAction **ptr2insertPos = NULL; int ret; - - if (prio <= NullPRIO) + + if (prio <= NullPRIO) { prio = NullPRIO + 1; - if (prio >= NumberOfPRIO) + } + if (prio >= NumberOfPRIO) { prio = NumberOfPRIO - 1; - ret = DevUnschedule(devser, actionData, hdl, matchFunc); - - newAction = DevNewAction(actionData, hdl, killFunc, prio); - /* find matching header */ - ptr2LastHeader = &devser->headers; - for (header = devser->headers; header != NULL; header = *ptr2LastHeader) { - if (header->prio == newAction->prio && header->interval == interval) { - /* append new action at the tail */ - ptr2Last = &header->actions; - for (action = header->actions; action != NULL; action = action->next) { - ptr2Last = &action->next; - } - *ptr2Last = newAction; - assert(newAction->next == NULL); - return ret; - } else if (header->prio < newAction->prio || - (header->prio == newAction->prio - && header->interval > interval)) { - break; + } + + for (ptr2prev = &devser->actions, action = devser->actions; + action != NULL; + ptr2prev = &action->next, action = action->next) { + if (action->prio < prio && ptr2insertPos == NULL) { + ptr2insertPos = ptr2prev; } - if (header->actions == NULL) { - /* remove empty header */ - *ptr2LastHeader = header->next; - free(header); - } else { - ptr2LastHeader = &header->next; + /* check if it is the same action (only once) */ + if (action->hdl == hdl + && action->kill == killFunc + && (interval < 0) == (action->interval < 0) + && foundAction == NULL + && matchFunc(actionData, action->data)) { + if (prio == action->prio && interval < 0) { + /* do not move an action with equal prio */ + killFunc(actionData); + return 0; + } + /* remove action from list */ + *ptr2prev = action->next; + foundAction = action; } } - - /* insert new header */ - newHeader = calloc(1, sizeof(*newHeader)); - assert(newHeader); - newHeader->actions = newAction; - newHeader->followingAction = NULL; - newHeader->prio = newAction->prio; - newHeader->interval = interval; - newHeader->next = header; - newHeader->timeDue = DoubleTime() + interval; - *ptr2LastHeader = newHeader; + + /* create if needed */ + if (foundAction != NULL) { + action = foundAction; + ret = 0; + killFunc(actionData); + } else { + action = calloc(1, sizeof(*action)); + assert(action); + action->data = actionData; + action->hdl = hdl; + action->kill = killFunc; + action->timeDue = 0; + ret = 1; + } + + action->prio = prio; + /* insert into queue */ + if (ptr2insertPos == NULL) { + ptr2insertPos = ptr2prev; + } + action->next = *ptr2insertPos; + *ptr2insertPos = action; + + if (interval < 0) { /* case "queued" */ + action->interval = -1.0; + } else { /* case "scheduled" */ + if (action->timeDue == 0) { /* not yet scheduled: do it immediately */ + action->timeDue = DoubleTime(); + } else { /* calculate next time */ + action->timeDue = nextTime(action->timeDue, action->interval, + DoubleTime(), interval); + } + action->interval = interval; + } return ret; } +int DevQueue(DevSer * devser, void *actionData, DevPrio prio, + DevActionHandler * hdl, DevActionMatch * matchFunc, + DevKillActionData * killFunc) +{ + return DevSchedule(devser, actionData, prio, -1.0, hdl, matchFunc, killFunc); +} + int DevRemoveAction(DevSer * devser, void *actionData) { - SchedHeader *header = NULL; - DevAction **ptr2Last = NULL; + DevAction **ptr2prev = NULL; DevAction *action = NULL; int cnt = 0; @@ -374,18 +366,32 @@ int DevRemoveAction(DevSer * devser, void *actionData) devser->current = NULL; } /* remove from queue */ - ptr2Last = &devser->actions; - for (action = devser->actions; action != NULL; action = *ptr2Last) { + ptr2prev = &devser->actions; + for (action = devser->actions; action != NULL; action = *ptr2prev) { if (actionData == action->data) { cnt++; /* remove from list */ - *ptr2Last = action->next; + *ptr2prev = action->next; if (action->kill != NULL) action->kill(action->data); free(action); } else { - ptr2Last = &action->next; + ptr2prev = &action->next; } } return cnt++; } + +int DevIsPending(DevSer * devser, void *callData, + DevActionHandler * hdl, DevActionMatch * matchFunc) +{ + DevAction *action = devser->current; + + if (action) { + if (action->hdl == hdl && matchFunc(callData, action->data)) { + return 1; + } + } + return 0; +} + diff --git a/devser.h b/devser.h index 5675fbe4..39ffa54d 100644 --- a/devser.h +++ b/devser.h @@ -72,15 +72,15 @@ void DevDisconnect(DevSer * devser); * \param actionData the action data * \param prio the priority * \param hdl the action handler - * \param matchFunc the match function + * \param matchFunc a match function with two arguments of the same type * \param killFunc the action data kill function (called from DevKill and * after the action has finished, i.e. when hdl returned NULL) * or NULL if no kill function is needed. - * \return 0 when not queued because a similar action is already on the queue, - * 1 on success. + * \return 1 when this was a new action, 0 when an action was overwritten + * in the second case the actionData's kill Function is called */ int DevQueue(DevSer * devser, void *actionData, DevPrio prio, - DevActionHandler hdl, DevActionMatch * matchFunc, + DevActionHandler * hdl, DevActionMatch * matchFunc, DevKillActionData * killFunc); /** \brief Schedule a periodic action @@ -92,25 +92,25 @@ int DevQueue(DevSer * devser, void *actionData, DevPrio prio, * \param prio the priority * \param interval the interval in seconds (0 is allowed) * \param hdl the action handler - * \param matchFunc the match function (callData must be of the same type as actionData) - * \param killFunc the action data kill function (called from DevKill and - * from DevUnschedule) or NULL if no kill function is needed. - * \return 0 when this was a new action, > 0 when an action was overwritten + * \param matchFunc a match function with two arguments of the same type + * \param killFunc the action data kill function or NULL if no kill function is needed. + * \return 1 when this was a new action, 0 when an action was overwritten + * in the second case the actionData's kill Function is called */ int DevSchedule(DevSer * devser, void *actionData, DevPrio prio, double interval, - DevActionHandler hdl, DevActionMatch * matchFunc, + DevActionHandler * hdl, DevActionMatch * matchFunc, DevKillActionData * killFunc); /** \brief Unschedule matching actions * \param devser the device serializer - * \param actionData the callers data to be used as first argument of the match function + * \param callData the callers data to be used as first argument of the match function * \param hdl the action handler - * \param matchFunc the match function (callData does not need to have the same type as actionData) + * \param matchFunc a match function (the first argument might have an other type than the second) * \return the number of unscheduled actions */ -int DevUnschedule(DevSer * devser, void *actionData, - DevActionHandler hdl, DevActionMatch * matchFunc); +int DevUnschedule(DevSer * devser, void *callData, + DevActionHandler * hdl, DevActionMatch * matchFunc); /** \brief remove action from the serializer * \param devser the device serializer @@ -118,6 +118,15 @@ int DevUnschedule(DevSer * devser, void *actionData, */ int DevRemoveAction(DevSer * devser, void *actionData); +/** \brief check if an action is pending + * \param devser the device serializer + * \param callData the callers data to be used as first argument of the match function + * \param hdl the action handler + * \param matchFunc a match function (the first argument might have an other type than the second) + * \return the number of unscheduled actions + */ +int DevIsPending(DevSer * devser, void *callData, + DevActionHandler * hdl, DevActionMatch * matchFunc); /** \brief Convert integer priority to text * \param prio diff --git a/scriptcontext.c b/scriptcontext.c index de5eeb3d..05a6635e 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -54,8 +54,6 @@ static struct { char *name; } actionCallback; -static char *mainCallback = "main callback"; - void PushContext(Hdb * node, Hdb * controllerNode) { ContextItem *new; @@ -98,45 +96,58 @@ void CleanStack(Hdb * node) } } -static void SetProp(Hdb * node, Hdb * cNode, char *key, char *value) +static char *GetPropAndNode(Hdb * node, Hdb * cNode, char *key, Hdb **foundNode) { char *val; + Hdb *mama; - if (node == NULL) { - if (cNode == NULL) - return; - node = cNode; - } else { - val = GetHdbProp(node, key); - if (val == NULL && cNode != NULL) { - val = GetHdbProp(cNode, key); + if (key[0] == '@') { + /* for keys starting with @ look also at the ancestors */ + for (mama = node; mama != NULL; mama = mama->mama) { + val = GetHdbProp(mama, key); if (val != NULL) { - node = cNode; + *foundNode = mama; + return val; } } } + if (node != NULL) { + val = GetHdbProp(node, key); + if (val != NULL) { + *foundNode = node; + return val; + } + } + if (cNode != NULL) { + val = GetHdbProp(cNode, key); + if (val != NULL) { + *foundNode = cNode; + return val; + } + } + *foundNode = node; + return NULL; +} + +static void SetProp(Hdb * node, Hdb * cNode, char *key, char *value) +{ + Hdb *propNode; + char *val; + + val = GetPropAndNode(node, cNode, key, &propNode); if (value == NULL) { - if (GetHdbProperty(node, key, NULL, 0) > 0) { - SetHdbProperty(node, key, ""); + if (val != NULL) { + SetHdbProperty(propNode, key, ""); } } else { - SetHdbProperty(node, key, value); + SetHdbProperty(propNode, 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; + Hdb *propNode; + return GetPropAndNode(node, cNode, key, &propNode); } /* @@ -302,6 +313,7 @@ static int SctMatch(void *data1, void *data2) return a->node == b->node && strcasecmp(a->name, b->name) == 0; } + /* * This routine is running the script chain. It is called repeatedly * with response data from the device serializer (devser). This function @@ -350,7 +362,7 @@ static char *SctActionHandler(void *actionData, char *lastReply, script = NULL; if (!commError && controller->verbose && lastReply != NULL && *lastReply != '\0') { - SCPrintf(con, eLog, "reply : %s", lastReply); + SCPrintf(con, eLog, "reply : %s\n", lastReply); } /* @@ -534,7 +546,7 @@ static int SctMatchNode(void *vNode, void *vData) } /* - * This is the read callback for nodes participating in the + * This is the callback for all nodes participating in the * scriptcontext system */ static hdbCallbackReturn SctMainCallback(Hdb * node, void *userData, @@ -584,11 +596,11 @@ static hdbCallbackReturn SctMainCallback(Hdb * node, void *userData, 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); + if (mm->v->dataType == HIPTEXT) { + if (mm->v->v.text != NULL) { + free(mm->v->v.text); + } + mm->v->v.text = strdup(error); } return hdbAbort; } @@ -982,11 +994,7 @@ void SctQueueNode(SctController * controller, Hdb * node, 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) { + if (!FindHdbCallbackData(node, controller)) { cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); assert(cb); AppendHipadabaCallback(node, cb); @@ -1207,9 +1215,12 @@ static void SctKillController(void *c) { SctController *controller = c; SConnection *con; + hdbPtrMessage m; CleanStack(controller->node); RemoveSICSInternalCallback(controller); + RemoveSICSInternalCallbackFrom(controller->node, controller); + if (controller->conn) { SCDeleteConnection(controller->conn); } @@ -1361,3 +1372,20 @@ int SctVerbose(SctController * c) { return c->verbose; } + +int SctIsPending(SctController *controller, Hdb * node, char *name, int kind) +{ + void *currentAction; + + SctData data; + data.node = node; + data.name = name; + switch (kind) { + case 0: + return DevIsPending(controller->devser, &data, SctWriteHandler, SctMatch); + case 1: + return DevIsPending(controller->devser, &data, SctActionHandler, SctMatch); + default: + return 0; + } +} diff --git a/scriptcontext.h b/scriptcontext.h index 660f4b68..16da7595 100644 --- a/scriptcontext.h +++ b/scriptcontext.h @@ -35,4 +35,15 @@ int SctCallInContext(SConnection * con, char *script, Hdb * node, * \return 1 for verbose, 0 for silent */ int SctVerbose(SctController * c); + + +/** + * check if the specified action is pending + * \param controller the SctController + * \param node the node + * \param name the action + * \param kind 0 for queued action, 1 for polled action + * \return 1 for pending, 0 for not pending + */ +int SctIsPending(SctController *controller, Hdb * node, char *name, int kind); #endif diff --git a/sctdriveobj.c b/sctdriveobj.c index ee777843..f1d4be57 100644 --- a/sctdriveobj.c +++ b/sctdriveobj.c @@ -107,7 +107,8 @@ static int SCTDRIVCheckLimits(void *data, float val, self = (pSICSOBJ) data; pPriv = (pDrivObjPriv) self->pPrivate; - snprintf(script, 1024, "%f", val); + snprintf(script, 1024, "%g", val); + /* set target. This not yet done, as SetValue is called later */ SetHdbProperty(self->objectNode, "target", script); if (GetHdbProperty(self->objectNode, "checklimits", script, 1024)) { status = SctCallInContext(pServ->dummyCon, script, @@ -298,6 +299,7 @@ pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c, memset(pPriv, 0, sizeof(DrivObjPriv)); pNew->pDes = CreateDescriptor(class); + pNew->pDes->SaveStatus = SaveSICSOBJ; pPriv->pDriv = CreateDrivableInterface(); if (pNew->pDes == NULL || pPriv->pDriv == NULL) { free(pNew); @@ -357,6 +359,7 @@ int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics, SCWrite(pCon, "ERROR: node creation failed", eError); return 0; } + SetHdbProperty(node, "sicscommand", argv[1]); sctName = argv[5]; class = argv[4]; doNotKillNode = 0; /* kill the node with the command */