498 lines
15 KiB
C
498 lines
15 KiB
C
/**
|
|
* This is the new Hipadaba based queuing system in support of the MountainGum
|
|
* user interface.
|
|
*
|
|
* copyright: see file COPYRIGHT
|
|
*
|
|
* Mark Koennecke, July 2007
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <sics.h>
|
|
#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;
|
|
}
|