/** * 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; }