525 lines
15 KiB
C
525 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;
|
|
}
|