643 lines
17 KiB
C
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;
|
|
}
|