Files
sics/genericcontroller.c
koennecke 91d4af0541 - Adapted indenation to new agreed upon system
- Added support for second generation scriptcontext based counter
2009-02-13 09:00:03 +00:00

643 lines
17 KiB
C

/**
* This is a generic controller for devices in SICS. It is configurable via Tcl
* scripts.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, November 2007
*/
#include <stdlib.h>
#include <assert.h>
#include <tcl.h>
#include <sics.h>
#include <macro.h>
#include <sicshipadaba.h>
#include <sicsobj.h>
#include <dynstring.h>
#include <genericcontroller.h>
/*--------------------------------------------------------------------------*/
static hdbCallbackReturn GenConSetCallback(pHdb node, void *userData,
pHdbMessage message)
{
pSICSOBJ self = (pSICSOBJ) userData;
SConnection *pCon = NULL;
pGenController priv = NULL;
char command[1024];
char value[80];
int status, privilege;
pDynString data;
pHdbDataMessage mm = NULL;
assert(self != NULL);
if ((mm = GetHdbSetMessage(message)) == NULL) {
return hdbContinue;
}
priv = (pGenController) self->pPrivate;
pCon = mm->callData;
/*
* check rights
*/
memset(value, 0, 80);
if (GetHdbProperty(node, "priv", value, 80) && pCon != NULL) {
privilege = usInternal;
if (strcmp(value, "manager") == 0) {
privilege = usMugger;
}
if (strcmp(value, "user") == 0) {
privilege = usUser;
}
if (!SCMatchRights(pCon, privilege)) {
return hdbAbort;
}
}
/*
* check writeCommand
*/
memset(value, 0, 80);
GetHdbProperty(node, "writeCommand", value, 80);
if (strlen(value) < 2) {
if (pCon != NULL) {
SCWrite(pCon, "ERROR: parameter is read-only", eError);
return hdbAbort;
}
return hdbAbort;
}
/*
* check status
*/
memset(value, 0, 80);
GetHdbProperty(node, "status", value, 80);
if (strstr(value, "idle") == NULL) {
return hdbAbort;
}
SetHdbProperty(node, "status", "setting");
data = formatValue(*(mm->v), node);
if (data != NULL) {
SetHdbProperty(node, "target", GetCharArray(data));
DeleteDynString(data);
}
/*
* issue command
*/
if (priv->enqueueNodeHead != NULL) {
priv->enqueueNodeHead(self, pCon, node);
} else {
if (pCon != NULL) {
SCWrite(pCon, "ERROR: generic controller NOT configured", eError);
}
return hdbAbort;
}
return hdbContinue;
}
/*--------------------------------------------------------------------------*/
static hdbCallbackReturn GenConGetCallback(pHdb node, void *userData,
pHdbMessage message)
{
pSICSOBJ self = (pSICSOBJ) userData;
SConnection *pCon = NULL;
pGenController priv = NULL;
char command[1024];
char value[256];
int status, privilege;
pHdbDataMessage mm = NULL;
assert(self != NULL);
if ((mm = GetHdbGetMessage(message)) == NULL) {
return hdbContinue;
}
pCon = mm->callData;
priv = (pGenController) self->pPrivate;
/*
* check status
*/
memset(value, 0, 80);
GetHdbProperty(node, "status", value, 80);
if (strstr(value, "idle") == NULL) {
return hdbContinue;
}
SetHdbProperty(node, "status", "getting");
/*
* check readCommand
*/
memset(value, 0, 256);
GetHdbProperty(node, "readCommand", value, 255);
if (strlen(value) < 2) {
return hdbAbort;
} else {
if (priv->enqueueNode != NULL) {
priv->enqueueNode(self, pCon, node);
} else {
if (pCon != NULL) {
SCWrite(pCon,
"ERROR: generic controller connection NOT configured",
eError);
}
return hdbAbort;
}
}
/*
* Upper Level GetHipadabaPar will automatically return the
* node value. Which should have been updated through an update
* during the execution of enqueueNode
*/
return hdbContinue;
}
/*---------------------------------------------------------------------------*/
static pHdb MakeGenConPar(pSICSOBJ self, char *name, int type, int length)
{
pHdb result = NULL;
pHdbCallback kalle = NULL;
result = MakeHipadabaNode(name, type, length);
if (result == NULL) {
return NULL;
}
kalle = MakeHipadabaCallback(GenConSetCallback, self, NULL);
if (kalle == NULL) {
return NULL;
}
AppendHipadabaCallback(result, kalle);
kalle = MakeHipadabaCallback(GenConGetCallback, self, NULL);
if (kalle == NULL) {
return NULL;
}
AppendHipadabaCallback(result, kalle);
SetHdbProperty(result, "priv", "manager");
SetHdbProperty(result, "readCommand", "");
SetHdbProperty(result, "writeCommand", "");
SetHdbProperty(result, "replyCommand", "");
SetHdbProperty(result, "status", "idle");
return result;
}
/*---------------------------------------------------------------------------*/
static int MakeGenPar(pSICSOBJ self, SConnection * pCon,
char *argv[], int argc)
{
char buffer[2048];
int type, length = 1;
pHdb node = NULL, parent;
char *pPtr = NULL;
if (argc < 5) {
snprintf(buffer, 2048, "ERROR: insufficient arguments to %s makepar",
argv[0]);
SCWrite(pCon, buffer, eError);
return 0;
}
type = convertHdbType(argv[4]);
if (argc > 5) {
length = atoi(argv[5]);
}
strncpy(buffer, argv[3], 2047);
pPtr = strrchr(buffer, '/');
if (pPtr == NULL) {
node = MakeGenConPar(self, argv[3], type, length);
parent = self->objectNode;
} else {
*pPtr = '\0';
pPtr++;
node = MakeGenConPar(self, pPtr, type, length);
parent = GetHipadabaNode(self->objectNode, buffer);
}
if (node == NULL || parent == NULL) {
SCWrite(pCon, "ERROR: failed to create node or parent not found",
eError);
return 0;
}
AddHipadabaChild(parent, node, pCon);
SCSendOK(pCon);
return 1;
}
/*=============================== ==========================================
* This stuff is for the Tcl - AsynQueue implementation of GenericController
* ==========================================================================*/
typedef struct {
pHdb node;
pSICSOBJ obj;
SConnection *pCon;
pGenController priv;
pAsyncUnit assi;
pAsyncTxn trans;
char replyCommand[2048];
} GenContext, *pGenContext;
/*--------------------------------------------------------------------------
* This is called by AsyncQueue when a reply has been received.
* -------------------------------------------------------------------------*/
static int GenConTxnHandler(pAsyncTxn pTxn)
{
pGenContext genCon = NULL;
char reply[10240];
genCon = (pGenContext) pTxn->cntx;
assert(genCon != NULL);
memset(reply, 0, 10240 * sizeof(char));
switch (pTxn->txn_state) {
case ATX_NULL:
case ATX_ACTIVE:
return 1;
break;
case ATX_TIMEOUT:
strcpy(reply, "TIMEOUT");
break;
case ATX_DISCO:
strcpy(reply, "DISCONNECTED");
break;
case ATX_COMPLETE:
strncpy(reply, pTxn->inp_buf, 10240);
break;
}
genCon->priv->replyCallback(genCon->obj, genCon->pCon,
genCon->node, genCon->replyCommand, reply,
strlen(reply));
if (genCon->pCon != NULL) {
SCDeleteConnection(genCon->pCon);
}
free(genCon);
return 1;
}
/*--------------------------------------------------------------------------*/
static char *formatCommand(pHdb node, SConnection * pCon)
{
pDynString com = NULL;
char value[512];
Tcl_Interp *pTcl = NULL;
int status;
com = CreateDynString(256, 128);
if (com == NULL) {
return NULL;
}
memset(value, 0, 512);
GetHdbProperty(node, "status", value, 512);
if (strstr(value, "set") != NULL) {
memset(value, 0, 512);
GetHdbProperty(node, "writeCommand", value, 512);
DynStringConcat(com, value);
DynStringConcatChar(com, ' ');
memset(value, 0, 512);
GetHdbProperty(node, "target", value, 512);
DynStringConcat(com, value);
} else {
memset(value, 0, 512);
GetHdbProperty(node, "readCommand", value, 512);
DynStringConcat(com, value);
}
pTcl = InterpGetTcl(pServ->pSics);
if (pCon != NULL) {
MacroPush(pCon);
}
status = Tcl_Eval(pTcl, GetCharArray(com));
if (pCon != NULL) {
MacroPop();
}
DeleteDynString(com);
if (status != TCL_OK) {
SetHdbProperty(node, "result", (char *) Tcl_GetStringResult(pTcl));
return NULL;
}
return strdup(Tcl_GetStringResult(pTcl));
}
/*--------------------------------------------------------------------------*/
static pGenContext PrepareToEnque(pSICSOBJ self, SConnection * pCon,
pHdb node)
{
pGenContext result = NULL;
char *command = NULL;
pGenController priv = NULL;
priv = (pGenController) self->pPrivate;
assert(priv != NULL);
command = formatCommand(node, pCon);
if (command == NULL) {
return NULL;
}
result = malloc(sizeof(GenContext));
if (result == NULL) {
return NULL;
}
memset(result, 0, sizeof(GenContext));
if (!GetHdbProperty(node, "replyCommand", result->replyCommand, 2048)) {
if (pCon != NULL) {
SCWrite(pCon, "ERROR: no replyCommand found", eError);
}
free(result);
return NULL;
}
result->assi = AsyncUnitFromQueue((pAsyncQueue) priv->comContext);
if (result->assi == NULL) {
return NULL;
}
result->trans = AsyncUnitPrepareTxn(result->assi,
command, strlen(command),
GenConTxnHandler, result, 2048);
if (result->trans == NULL) {
return NULL;
}
result->node = node;
result->priv = priv;
result->obj = self;
result->pCon = SCCopyConnection(pCon);
priv->comError = GCOK;
free(command);
return result;
}
/*---------------------------------------------------------------------------*/
static int AsyncEnqueueNode(pSICSOBJ self, SConnection * pCon, pHdb node)
{
pGenContext genCon = NULL;
genCon = PrepareToEnque(self, pCon, node);
if (genCon == NULL) {
return 0;
}
return AsyncUnitEnqueueTxn(genCon->assi, genCon->trans);
}
/*---------------------------------------------------------------------------*/
static int AsyncEnqueueNodeHead(pSICSOBJ self, SConnection * pCon,
pHdb node)
{
pGenContext genCon = NULL;
genCon = PrepareToEnque(self, pCon, node);
if (genCon == NULL) {
return 0;
}
return AsyncUnitEnqueueHead(genCon->assi, genCon->trans);
}
/*---------------------------------------------------------------------------*/
static int AsyncReply(pSICSOBJ self, SConnection * pCon, pHdb node,
char *replyCommand, char *reply, int replylen)
{
pDynString com = NULL;
Tcl_Interp *pTcl;
int status;
SetHdbProperty(node, "result", reply);
com = CreateDynString(256, 128);
if (com == NULL) {
return 0;
}
DynStringConcat(com, replyCommand);
DynStringConcat(com, " \"");
DynStringConcat(com, reply);
DynStringConcat(com, "\"\0");
if (pCon != NULL) {
MacroPush(pCon);
}
pTcl = InterpGetTcl(pServ->pSics);
status = Tcl_Eval(pTcl, GetCharArray(com));
if (pCon != NULL) {
MacroPop();
}
DeleteDynString(com);
if (status != TCL_OK) {
SetHdbProperty(node, "lastError", (char *) Tcl_GetStringResult(pTcl));
}
return status;
}
/*============= GenController Object Functions ==============================*/
static int ConnectAsync(pSICSOBJ self, SConnection * pCon,
char *argv[], int argc)
{
pGenController priv = NULL;
pAsyncQueue assi = NULL;
char buffer[2048];
pAsyncUnit uni = NULL;
priv = (pGenController) self->pPrivate;
assert(priv != NULL);
if (argc < 4) {
snprintf(buffer, 2048,
"ERROR: insufficient arguments to %s asynconnect", argv[0]);
SCWrite(pCon, buffer, eError);
return 0;
}
assi =
(pAsyncQueue) FindCommandData(pServ->pSics, argv[3], "AsyncQueue");
if (assi == NULL) {
snprintf(buffer, 2048, "ERROR: %s not found or no AsyncQueue",
argv[3]);
SCWrite(pCon, buffer, eError);
return 0;
}
priv->comContext = assi;
priv->killComContext = NULL; /* not ours, cleaned up by AsyncQueue module */
priv->enqueueNode = AsyncEnqueueNode;
priv->enqueueNodeHead = AsyncEnqueueNodeHead;
priv->replyCallback = AsyncReply;
priv->comError = GCOK;
/*
* This unit is solely for the purpose of receiving event notifications
*/
uni = AsyncUnitFromQueue(assi);
SCSendOK(pCon);
return 1;
}
/*---------------------------------------------------------------------------*/
int GenControllerConfigure(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
{
pSICSOBJ controller = NULL;
pGenController self = NULL;
char buffer[2048];
if (argc < 3) {
snprintf(buffer, 2048, "ERROR: insufficient arguments to %s", argv[0]);
SCWrite(pCon, buffer, eError);
return 0;
}
controller =
(pSICSOBJ) FindCommandData(pSics, argv[2], "GenericController");
if (controller == NULL) {
snprintf(buffer, 2048, "ERROR: controller %s not found", argv[2]);
SCWrite(pCon, buffer, eError);
return 0;
}
strtolower(argv[1]);
if (strcmp(argv[1], "makepar") == 0) {
return MakeGenPar(controller, pCon, argv, argc);
} else if (strcmp(argv[1], "asynconnect") == 0) {
return ConnectAsync(controller, pCon, argv, argc);
} else {
SCWrite(pCon, "ERROR: argument to GenControllerConfigure not found",
eError);
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
static void killGeneric(void *data)
{
pGenController self = (pGenController) data;
if (self == NULL) {
return;
}
if (self->comContext != NULL && self->killComContext != NULL) {
self->killComContext(self->comContext);
}
free(self);
}
/*---------------------------------------------------------------------------*/
static int EnqueFunc(pSICSOBJ self, SConnection * pCon, pHdb commandNode,
pHdb par[], int nPar)
{
pGenController priv = NULL;
pHdb node = NULL;
char buffer[512];
priv = (pGenController) self->pPrivate;
assert(priv != NULL);
assert(nPar >= 1);
node = GetHipadabaNode(self->objectNode, par[0]->value.v.text);
if (node == NULL) {
snprintf(buffer, 511, "ERROR: node %s to enqueue not found",
par[0]->value.v.text);
SCWrite(pCon, buffer, eError);
return 0;
}
if (priv->enqueueNode != NULL) {
priv->enqueueNode(self, pCon, node);
} else {
SCWrite(pCon, "ERROR: GenController NOT configured", eError);
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
static int EnqueHeadFunc(pSICSOBJ self, SConnection * pCon,
Hdb commandNode, pHdb par[], int nPar)
{
pGenController priv = NULL;
pHdb node = NULL;
char buffer[512];
priv = (pGenController) self->pPrivate;
assert(priv != NULL);
assert(nPar >= 1);
node = GetHipadabaNode(self->objectNode, par[0]->value.v.text);
if (node == NULL) {
snprintf(buffer, 511, "ERROR: node %s to enqueue not found",
par[0]->value.v.text);
SCWrite(pCon, buffer, eError);
return 0;
}
if (priv->enqueueNodeHead != NULL) {
priv->enqueueNodeHead(self, pCon, node);
} else {
SCWrite(pCon, "ERROR: GenController NOT configured", eError);
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
int GenControllerFactory(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
{
pSICSOBJ pNew = NULL;
pGenController priv = NULL;
hdbValue funcValue, textValue;
pHdb node = NULL;
char line[132];
int status;
if (argc < 2) {
SCWrite(pCon,
"ERROR: insufficient number of arguments to GenControllerFactrory",
eError);
return 0;
}
priv = malloc(sizeof(GenController));
if (priv == NULL) {
SCWrite(pCon, "ERROR: out of memory in GenControllerFactory", eError);
return 0;
}
memset(priv, 0, sizeof(GenController));
pNew = MakeSICSOBJ(argv[1], "GenericController");
if (pNew == NULL) {
SCWrite(pCon, "ERROR: out of memory in GenControllerFactory", eError);
return 0;
}
pNew->pPrivate = priv;
pNew->KillPrivate = killGeneric;
textValue = MakeHdbText("Undefined");
funcValue = MakeHdbFunc((voidFunc *) EnqueFunc);
node = MakeSICSHdbPar("enqueue", usUser, funcValue);
AddSICSHdbPar(node, "node", usUser, textValue);
AppendHipadabaCallback(node, MakeSICSFuncCallback(pNew));
AddHipadabaChild(pNew->objectNode, node, NULL);
funcValue = MakeHdbFunc((voidFunc *) EnqueHeadFunc);
node = MakeSICSHdbPar("enqueuehead", usUser, funcValue);
AddSICSHdbPar(node, "node", usUser, textValue);
AppendHipadabaCallback(node, MakeSICSFuncCallback(pNew));
AddHipadabaChild(pNew->objectNode, node, NULL);
status = AddCommand(pSics, argv[1], InvokeSICSOBJ, KillSICSOBJ, pNew);
if (status != 1) {
KillSICSOBJ(pNew);
SCPrintf(pCon, eError, "ERROR: failed create duplicate command %s",
argv[1]);
return 0;
}
SCSendOK(pCon);
return 1;
}