/** * This is the new Hipadaba based queuing system in support of the MountainGum * user interface. * * copyright: see file COPYRIGHT * * Mark Koennecke, July 2007 */ #include #include #include #include "sicsobj.h" #include "hdbqueue.h" #include "sicshipadaba.h" #include "dynstring.h" #include "exeman.h" #include "macro.h" /*--------------------------------------------------------------------------*/ typedef struct { int iStop; int isRunning; SConnection *pCon; } HdbQueue, *pHdbQueue; /*--------------------------------------------------------------------------*/ static pHdbCallback CopyCallbackChain(pHdbCallback source) { pHdbCallback current = NULL; pHdbCallback result = NULL; pHdbCallback head = NULL; pHdbCallback tail = NULL; current = source; while (current != NULL) { result = MakeHipadabaCallback(current->userCallback, current->userData, NULL, current->id, current->internalID); if (head == NULL) { head = result; tail = result; } else { tail->next = result; result->previous = tail; tail = result; } current = current->next; } return head; } /*---------------------------------------------------------------------------*/ static pHdb MakeNewEntry(char *name, pHdbCallback update) { pHdb entry = NULL, child = NULL; hdbValue v; v = MakeHdbText("Undefined"); entry = MakeHipadabaNode(name, HIPNONE, 1); entry->updateCallbacks = CopyCallbackChain(update); child = MakeSICSHdbPar("description", usUser, v); child->updateCallbacks = CopyCallbackChain(update); AddHipadabaChild(entry, child, NULL); child = MakeSICSHdbPar("commands", usUser, v); child->updateCallbacks = CopyCallbackChain(update); AddHipadabaChild(entry, child, NULL); child = MakeSICSHdbPar("log", usUser, v); child->updateCallbacks = CopyCallbackChain(update); AddHipadabaChild(entry, child, NULL); return entry; } /*---------------------------------------------------------------------------*/ static int EnqueFunc(pSICSOBJ self, SConnection * pCon, Hdb commandNode, pHdb par[], int nPar) { pHdb entry = NULL; pHdb work = NULL; char name[80]; hdbValue v; if (nPar < 1) { SCWrite(pCon, "ERROR: internal: not enough parameters to EnqueFunc", eError); return 0; } /* * new entry */ memset(&v, 0, sizeof(hdbValue)); work = GetHipadabaNode(self->objectNode, "control/maxEntry"); assert(work != NULL); snprintf(name, 80, "%3.3d", work->value.v.intValue); entry = MakeNewEntry(name, work->updateCallbacks); if (entry == NULL) { SCWrite(pCon, "ERROR: out of memory in EnqueFunc", eError); return 0; } /* * Update maxEntry */ cloneHdbValue(&work->value, &v); v.v.intValue++; UpdateHipadabaPar(work, v, pCon); work = GetHipadabaNode(self->objectNode, "queue"); assert(work != NULL); AddHipadabaChild(work, entry, pCon); /* * save description */ work = GetHipadabaNode(entry, "description"); assert(work != NULL); UpdateHipadabaPar(work, par[0]->value, pCon); return 1; } /*---------------------------------------------------------------------------*/ static int AddCmdData(pSICSOBJ self, SConnection * pCon, Hdb comNode, pHdb par[], int nPar) { pHdb work = NULL; pHdb commandNode = NULL; char name[80]; pDynString txt = NULL; if (nPar < 1) { SCWrite(pCon, "ERROR: internal: not enough parameters to AddCmdData", eError); return 0; } work = GetHipadabaNode(self->objectNode, "control/maxEntry"); assert(work != NULL); snprintf(name, 80, "queue/%3.3d/commands", work->value.v.intValue - 1); commandNode = GetHipadabaNode(self->objectNode, name); if (commandNode == NULL) { SCWrite(pCon, "ERROR: Internal error in AddCommand", eError); return 0; } txt = CreateDynString(80, 80); if (strstr(commandNode->value.v.text, "Undefined") == NULL) { DynStringCopy(txt, commandNode->value.v.text); } DynStringConcat(txt, par[0]->value.v.text); DynStringConcat(txt, "\n"); free(commandNode->value.v.text); commandNode->value.v.text = strdup(GetCharArray(txt)); NotifyHipadabaPar(commandNode, pCon); DeleteDynString(txt); return 1; } /*--------------------------------------------------------------------------*/ static void sequentialNames(pHdb obj, SConnection * pCon) { pHdb work = NULL; pHdb current = NULL; int count = 0; char name[80]; work = GetHipadabaNode(obj, "queue"); assert(work != NULL); current = work->child; while (current != NULL) { snprintf(name, 80, "%3.3d", count); if (current->name != NULL) { free(current->name); } current->name = strdup(name); count++; current = current->next; } InvokeCallbackChain(work->treeChangeCallbacks, work, pCon, work->value); work = GetHipadabaNode(obj, "control/maxEntry"); assert(work != NULL); work->value.v.intValue = count; NotifyHipadabaPar(work, pCon); } /*---------------------------------------------------------------------------*/ static int Dequeue(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { pHdb work = NULL; char name[80]; pHdbQueue priv = (pHdbQueue) self->pPrivate; if (priv->isRunning == 1) { SCWrite(pCon, "ERROR: cannot dequeue while running", eError); return 0; } if (nPar < 1) { SCWrite(pCon, "ERROR: internal: not enough parameters to Dequeue", eError); return 0; } snprintf(name, 80, "queue/%3.3d", par[0]->value.v.intValue); work = GetHipadabaNode(self->objectNode, name); if (work != NULL) { DeleteHipadabaNode(work, pCon); sequentialNames(self->objectNode, pCon); return 1; } return 0; } /*---------------------------------------------------------------------------*/ static int Clean(pSICSOBJ self, SConnection * pCon, Hdb commandNode, pHdb par[], int nPar) { int i; pHdb current = NULL, queue = NULL; pHdb currentEntry = NULL, tmp = NULL; pHdbQueue priv = (pHdbQueue) self->pPrivate; if (priv->isRunning == 1) { SCWrite(pCon, "ERROR: cannot clean while running", eError); return 0; } currentEntry = GetHipadabaNode(self->objectNode, "control/currentEntry"); queue = GetHipadabaNode(self->objectNode, "queue"); current = queue->child; for (i = 0; i < currentEntry->value.v.intValue; i++) { if (current != NULL) { tmp = current->next; DeleteNodeData(current); current = tmp; } } queue->child = tmp; currentEntry->value.v.intValue = 0; sequentialNames(self->objectNode, pCon); NotifyHipadabaPar(currentEntry, pCon); return 1; } /*---------------------------------------------------------------------------*/ static int CleanAll(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { int i; pHdb current = NULL, queue = NULL; pHdb currentEntry = NULL, tmp; pHdbQueue priv = (pHdbQueue) self->pPrivate; if (priv->isRunning == 1) { SCWrite(pCon, "ERROR: cannot clear queue while executing", eError); return 0; } currentEntry = GetHipadabaNode(self->objectNode, "control/currentEntry"); queue = GetHipadabaNode(self->objectNode, "queue"); current = queue->child; while (current != NULL) { tmp = current->next; DeleteNodeData(current); current = tmp; } queue->child = NULL; currentEntry->value.v.intValue = 0; sequentialNames(self->objectNode, pCon); NotifyHipadabaPar(currentEntry, pCon); return 1; } /*----------------------------------------------------------------------------*/ static int QueueTask(void *pData) { pSICSOBJ self = (pSICSOBJ) pData; int status, pos; pHdb work = NULL; pHdb exeNode = NULL; pHdb max = NULL; char name[80]; pHdbQueue priv = (pHdbQueue) self->pPrivate; if (priv->iStop == 1) { priv->isRunning = 0; return 0; } work = GetHipadabaNode(self->objectNode, "control/currentEntry"); max = GetHipadabaNode(self->objectNode, "control/maxEntry"); assert(work != NULL && max != NULL); pos = work->value.v.intValue; snprintf(name, 80, "queue/%3.3d", pos); exeNode = GetHipadabaNode(self->objectNode, name); if (exeNode != NULL) { MacroPush(priv->pCon); exeHdbNode(exeNode, priv->pCon); MacroPop(); } if (priv->iStop == 1 || SCGetInterrupt(priv->pCon) != eContinue) { priv->isRunning = 0; return 0; } pos++; work->value.v.intValue = pos; NotifyHipadabaPar(work, priv->pCon); if (pos >= max->value.v.intValue) { priv->isRunning = 0; return 0; } return 1; } /*---------------------------------------------------------------------------*/ static int Start(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { pHdbQueue priv = (pHdbQueue) self->pPrivate; priv->iStop = 0; priv->pCon = pCon; if (priv->isRunning == 1) { SCWrite(pCon, "ERROR: Hdbqueue is already running", eError); return 0; } priv->isRunning = 1; TaskRegister(pServ->pTasker, QueueTask, NULL, NULL, self, 10); return 1; } /*---------------------------------------------------------------------------*/ static int Restart(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { pHdbQueue priv = (pHdbQueue) self->pPrivate; pHdb maxCurrent = NULL; maxCurrent = GetHipadabaNode(self->objectNode, "control/currentEntry"); if (maxCurrent != NULL) { maxCurrent->value.v.intValue = 0; NotifyHipadabaPar(maxCurrent, pCon); } return Start(self, pCon, commandNode, par, nPar); } /*---------------------------------------------------------------------------*/ static int Stop(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { pHdbQueue priv = (pHdbQueue) self->pPrivate; priv->iStop = 1; return 1; } /*----------------------------------------------------------------------------*/ static int Move(pSICSOBJ self, SConnection * pCon, pHdb commandNode, pHdb par[], int nPar) { pHdb moveNode = NULL; pHdb insertNode = NULL; pHdb prevNode = NULL, queueNode = NULL; pHdb tmp; char name[80]; pHdbQueue priv = (pHdbQueue) self->pPrivate; if (priv->isRunning == 1) { SCWrite(pCon, "ERROR: cannot move while running", eError); return 0; } if (nPar < 2) { SCWrite(pCon, "ERROR: internal: not enough parameters to Move", eError); return 1; } if (par[1]->value.v.intValue == par[0]->value.v.intValue + 1) { /* * already in right sequence, nothing to do */ return 1; } snprintf(name, 80, "queue/%3.3d", par[1]->value.v.intValue); moveNode = GetHipadabaNode(self->objectNode, name); snprintf(name, 80, "queue/%3.3d", par[0]->value.v.intValue); insertNode = GetHipadabaNode(self->objectNode, name); if (moveNode == NULL || insertNode == NULL) { SCWrite(pCon, "ERROR: move not possible, participating nodes not found", eError); return 0; } queueNode = GetHipadabaNode(self->objectNode, "queue"); if (moveNode == queueNode->child) { queueNode->child = queueNode->child->next; moveNode->next = insertNode->next; insertNode->next = moveNode; } else { prevNode = queueNode->child; while (prevNode != NULL && prevNode->next != moveNode) { prevNode = prevNode->next; } if (insertNode == queueNode->child) { /* * insert at top */ tmp = queueNode->child; queueNode->child = moveNode; prevNode->next = moveNode->next; moveNode->next = tmp; } else { tmp = insertNode->next; insertNode->next = moveNode; prevNode->next = moveNode->next; moveNode->next = tmp; } } sequentialNames(self->objectNode, pCon); return 1; } /*---------------------------------------------------------------------------*/ static void Configure(pSICSOBJ self) { pHdb n = NULL, par = NULL; hdbValue intValue, textValue, funcValue; pHdb obj = self->objectNode; intValue = MakeHdbInt(0); textValue = MakeHdbText("Undefined"); n = MakeHipadabaNode("control", HIPNONE, 1); AddHipadabaChild(obj, n, NULL); AddSICSHdbPar(n, "maxEntry", usInternal, intValue); AddSICSHdbPar(n, "currentEntry", usInternal, intValue); n = MakeHipadabaNode("queue", HIPNONE, 1); AddHipadabaChild(obj, n, NULL); funcValue = MakeSICSFunc(EnqueFunc); n = MakeSICSHdbPar("enqueue", usUser, funcValue); AddSICSHdbPar(n, "description", usUser, textValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(AddCmdData); n = MakeSICSHdbPar("addcommand", usUser, funcValue); AddSICSHdbPar(n, "command", usUser, textValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Dequeue); n = MakeSICSHdbPar("dequeue", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AddSICSHdbPar(n, "index", usUser, intValue); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Clean); n = MakeSICSHdbPar("clean", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(CleanAll); n = MakeSICSHdbPar("cleanall", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Start); n = MakeSICSHdbPar("start", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Restart); n = MakeSICSHdbPar("restart", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Stop); n = MakeSICSHdbPar("stop", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); funcValue = MakeSICSFunc(Move); n = MakeSICSHdbPar("move", usUser, funcValue); AddHipadabaChild(obj, n, NULL); AddSICSHdbPar(n, "moveindex", usUser, intValue); AddSICSHdbPar(n, "insertindex", usUser, intValue); AppendHipadabaCallback(n, HCBSET, MakeSICSFuncCallback(self)); } /*---------------------------------------------------------------------------*/ int MakeHDBQueue(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pSICSOBJ self = NULL; pHdbQueue priv = NULL; priv = (pHdbQueue) malloc(sizeof(HdbQueue)); self = SetupSICSOBJ(pCon, pSics, pData, argc, argv); if (self == NULL || priv == NULL) { return 0; } Configure(self); memset(priv, 0, sizeof(HdbQueue)); self->pPrivate = priv; self->KillPrivate = free; return 1; }