Files
sics/scriptcontext.c
zolliker dc6225ecce -
2008-05-30 10:33:19 +00:00

751 lines
18 KiB
C

#include <strings.h>
#include <tcl.h>
#include <time.h>
#include <math.h>
#include "sics.h"
#include "sicsobj.h"
#include "splitter.h"
#include "initializer.h"
#include "commandlog.h"
#include "hipadaba.h"
#include "sicshipadaba.h"
#include "dynstring.h"
#include "devser.h"
#define MAX_HDB_PATH 1024
typedef struct ContextItem {
struct ContextItem *next;
Hdb *node;
Hdb *controllerNode;
} ContextItem;
typedef struct ScriptContext {
ObjectDescriptor *desc;
ContextItem *nodes;
ContextItem *trash;
ContextItem *base;
} ScriptContext;
typedef struct SctController {
DevSer *devser;
Hdb *node; /* the controller node */
SCStore *conn;
int verbose;
} SctController;
typedef struct SctData {
int writable;
char *name;
SctController *controller;
SCStore *conCtx;
int answered;
Hdb *node; /* back link used by SctMatchWrite/SctMatchPoll */
} SctData;
static ScriptContext *sct = NULL;
static SCStore *currentCon = NULL;
static struct {
char *name;
} actionCallback;
static char *mainCallback = "main callback";
void PushContext(Hdb *node, Hdb *controllerNode) {
ContextItem *new;
if (sct->trash == NULL) {
new = calloc(1, sizeof(*new));
} else {
new = sct->trash;
sct->trash = sct->trash->next;
}
new->next = sct->nodes;
sct->nodes = new;
new->node = node;
new->controllerNode = controllerNode;
}
void PopContext(void) {
ContextItem *c;
c = sct->nodes;
assert(c);
sct->nodes = c->next;
c->next = sct->trash;
sct->trash = c;
}
void CleanStack(Hdb *node) {
ContextItem *s;
/* clean context from killed nodes */
for (s = sct->nodes; s != NULL; s = s->next) {
if (s->node == node) {
s->node = NULL;
}
if (s->controllerNode == node) {
s->controllerNode = NULL;
}
}
}
static void SetProp(Hdb *node, Hdb *cNode, char *key, char *value) {
char *val;
if (node == NULL) {
if (cNode == NULL) return;
node = cNode;
} else {
val = GetHdbProp(node, key);
if (val == NULL && cNode != NULL) {
val = GetHdbProp(cNode, key);
if (val != NULL) {
node = cNode;
}
}
}
if (value == NULL) {
if (GetHdbProperty(node, key, NULL, 0) > 0) {
SetHdbProperty(node, key, "");
}
} else {
SetHdbProperty(node, key, value);
}
}
static char *GetProp(Hdb *node, Hdb *cNode, char *key) {
char *val;
if (node != NULL) {
val = GetHdbProp(node, key);
if (val != NULL) return val;
}
if (cNode != NULL) {
val = GetHdbProp(node, key);
}
return val;
}
int SctCommand(SConnection *con, SicsInterp *sics, void *object,
int argc, char *argv[]) {
static char value[1024];
char *val;
Hdb *node = sct->nodes->node;
Hdb *cNode = sct->nodes->controllerNode;
assert(sct == object);
if (node == NULL && cNode == NULL) {
SCPrintf(con, eError, "ERROR: %s may be called only in proper context",
argv[0]);
return 0;
}
if (argc <= 1) {
GetHdbPath(node, value, sizeof value);
SCWrite(con, value, eValue);
return 1;
}
if (argc == 2) { /* get case */
val = GetProp(node, cNode, argv[1]);
if (val == NULL) {
SCPrintf(con, eError, "ERROR: %s %s not found", argv[0], argv[1]);
return 0;
}
SCWrite(con, val, eValue);
} else { /* set case */
val = Arg2Tcl(argc-2, argv+2, value, sizeof value);
SetProp(node, cNode, argv[1], val);
if (val != NULL && val != value) free(val);
}
return 1;
}
int SctCallInContext(SConnection *con, char *script, Hdb *node,
SctController *controller, char **resPtr) {
Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics);
int ret, l;
char *result = NULL;
int iRet = 1;
int verbose = controller->verbose;
PushContext(node, controller->node);
if (verbose) {
SCPrintf(con, eInError, "\nscript: %s\n", script);
}
l = strlen(script);
ret = Tcl_EvalEx(pTcl, script, l, 0);
result = (char *)Tcl_GetStringResult(pTcl);
if (ret != TCL_OK && result[0]!='\0') {
if (verbose) {
SCPrintf(con, eInError, "\nerror: %s\n", result);
}
iRet = 0;
}
*resPtr = result;
PopContext();
PopContext();
return iRet;
}
static int SctMatch(void *data1, void *data2) {
SctData *a = data1;
SctData *b = data2;
return a->node == b->node && strcasecmp(a->name, b->name) == 0;
}
static char *SctActionHandler(void *actionData, char *lastReply) {
SctData *data = actionData;
Hdb *node = data->node;
SctController *controller = data->controller;
char *state;
char *result;
char *script;
char *send = NULL;
int i;
SConnection *con;
if (currentCon) {
con = SCStorePush(currentCon);
} else {
con = SCStorePush(controller->conn);
}
SetProp(node, controller->node, "result", lastReply);
if (controller->verbose && lastReply != NULL && *lastReply != '\0') {
SCPrintf(con, eWarning, "reply : %s", lastReply);
}
state = GetProp(node, controller->node, "state");
if (state == NULL || strcasecmp(state, "idle") == 0) {
state = data->name;
SetProp(node, controller->node, "state", state);
}
for (i = 0; i < 10; i++) {
SetProp(node, controller->node, "send", NULL);
script = GetProp(node, controller->node, state);
if (script == NULL) script = state;
if (! SctCallInContext(con, script, node, data->controller, &result)) {
SCPrintf(con, eError, "ERROR: %s", result);
goto finish;
}
state = result;
if (strcasecmp(state, "idle") == 0) {
if (currentCon && ! data->answered) {
SCWrite(con, "o.k.", eValue);
}
if (data->conCtx != NULL) {
SCStoreFree(data->conCtx);
data->conCtx = NULL;
}
goto finish;
}
SetProp(node, controller->node, "state", state);
send = GetProp(node, controller->node, "send");
if (send != NULL && send[0] != '\0') {
if (controller->verbose) {
SCPrintf(con, eWarning, "send : %s", send);
}
goto quit;
}
}
SCPrintf(con, eError, "ERROR: too many quick scripts chained");
finish:
SetProp(node, controller->node, "state", "idle");
quit:
if (currentCon) {
SCStorePop(currentCon);
} else {
SCStorePop(controller->conn);
}
return send;
}
static char *SctWriteHandler(void *actionData, char *lastReply) {
SctData *data = actionData;
SCStore *old;
char *result;
old = currentCon;
currentCon = data->conCtx;
result = SctActionHandler(data, lastReply);
currentCon = old;
return result;
}
static hdbCallbackReturn SctMainCallback(Hdb *node, void *userData,
hdbMessage *msg) {
SctController *controller = userData;
hdbDataSearch *dsm;
hdbDataMessage *mm;
hdbPtrMessage *pm;
hdbMessage *km;
ContextItem *s;
SConnection *con;
char *geterror;
pm = GetKillPtrMessage(msg);
if (pm != NULL) {
if (controller == pm->pPtr) {
return hdbKill;
}
return hdbContinue;
}
dsm = GetHdbDataSearchMessage(msg);
if (dsm != NULL) {
if (dsm->testPtr == controller) {
dsm->result = controller;
return hdbAbort;
}
return hdbContinue;
}
km = GetHdbKillNodeMessage(msg);
if (km != NULL) {
CleanStack(node);
return hdbContinue;
}
mm = GetHdbGetMessage(msg);
if (mm != NULL) {
con = mm->callData;
geterror = GetHdbProp(node, "geterror");
if (geterror != NULL) {
SCPrintf(con, eError, "ERROR: %s", geterror);
return hdbAbort;
}
return hdbContinue;
}
return hdbContinue;
}
static hdbCallbackReturn SctActionCallback(Hdb *node, void *userData,
hdbMessage *msg) {
hdbDataSearch *dsm;
hdbDataMessage *mm;
hdbPtrMessage *pm;
SctData *data = userData;
Hdb *target;
char *script;
int l;
int iRet;
pDynString text;
DevPrio prio;
char *writeprio;
char *error;
SConnection *con;
SConnection *con2;
char path[MAX_HDB_PATH];
pm = GetKillPtrMessage(msg);
if (pm != NULL) {
if (data->controller == pm->pPtr) {
return hdbKill;
}
return hdbContinue;
}
dsm = GetHdbDataSearchMessage(msg);
if (dsm != NULL) {
if (dsm->testPtr == &actionCallback) {
if (strcasecmp(data->name, actionCallback.name) == 0) {
dsm->result = data;
}
return hdbAbort;
}
return hdbContinue;
}
mm = GetHdbSetMessage(msg);
if (mm != NULL && data->writable) {
con = mm->callData;
/* call check script, if available */
script = GetProp(node, data->controller->node, "check");
if (script != NULL) {
if (SctCallInContext(con, script, node, data->controller, &error) == 0) {
SCPrintf(con, eError, "ERROR: %s", error);
return hdbAbort;
}
}
/* set target value */
text = formatValue(*(mm->v), node);
SetHdbProperty(node, "target", GetCharArray(text));
/* enqueue write action */
writeprio = GetProp(node, data->controller->node, "writeprio");
if (writeprio != NULL) {
prio = DevText2Prio(writeprio);
if (prio < 0) prio = WritePRIO;
} else {
prio = WritePRIO;
}
if (data->conCtx != NULL) {
con2 = SCStorePush(data->conCtx);
GetHdbPath(node, path, sizeof path);
SCPrintf(con2, eValue, "%s target changed to %s before completion",
path, GetCharArray(text));
SCStorePop(data->conCtx);
}
DeleteDynString(text);
data->conCtx = SCSave(con, data->conCtx);
data->answered = 0;
DevQueue(data->controller->devser, data, prio,
SctWriteHandler, SctMatch, NULL);
return hdbContinue;
}
mm = GetHdbUpdateMessage(msg);
if (mm != NULL) {
if (currentCon) { /* update called from a write action */
data->answered = 1;
GetHdbPath(node, path, sizeof path);
con = SCStorePush(currentCon);
text = formatValue(*(mm->v), node);
SCPrintf(con, eStatus, "%s = %s", path,
GetCharArray(text));
DeleteDynString(text);
SCStorePop(currentCon);
}
return hdbContinue;
}
return hdbContinue;
}
static char *ParText(Hdb *cmdNode, char *name) {
Hdb *par;
par = GetHipadabaNode(cmdNode, name);
if (par && par->value.dataType == HIPTEXT) {
return par->value.v.text;
}
return "<undefined>";
}
static double ParValue(Hdb *cmdNode, char *name) {
Hdb *par;
par = GetHipadabaNode(cmdNode, name);
if (par) {
switch (par->value.dataType) {
case HIPINT: return par->value.v.intValue;
case HIPFLOAT: return par->value.v.doubleValue;
}
}
return -999;
}
void SctKillData(void *d) {
SctData *data = d;
if (data->name) free(data->name);
if (data->conCtx) SCStoreFree(data->conCtx);
free(data);
}
void SctAddPollNode(SctController *controller, Hdb *node, double interval,
DevPrio prio, char *action) {
SctData *data;
hdbCallback *cb;
if (! FindHdbCallbackData(node, controller)) {
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
assert(cb);
AppendHipadabaCallback(node, cb);
}
actionCallback.name = action;
data = FindHdbCallbackData(node, &actionCallback);
if (data == NULL) {
data = calloc(1, sizeof(*data));
assert(data);
data->controller = controller;
data->node = node;
data->writable = 0;
data->conCtx = NULL;
data->name = strdup(action);
}
DevSchedule(controller->devser, data, prio, interval,
SctActionHandler, SctMatch, NULL);
}
static int SctPollCmd(pSICSOBJ ccmd, SConnection *con,
Hdb *cmdNode, Hdb *par[], int nPar) {
Hdb *node;
SctController *controller;
double interval;
char *path;
DevPrio prio;
char *action;
if(nPar < 1){
SCPrintf(con,eError,
"ERROR: should be: %s poll <node> (<interval> <prio> <action>)",
ccmd->objectNode->name);
return 0;
}
controller = ccmd->pPrivate;
path = ParText(cmdNode, "node");
node = FindHdbNode(NULL, path, con);
if (node == NULL) {
SCPrintf(con, eError, "ERROR: %s not found", path);
return 0;
}
interval = ParValue(cmdNode, "interval");
prio = DevText2Prio(ParText(cmdNode, "prio"));
action = ParText(cmdNode, "action");
SctAddPollNode(controller, node, interval, prio, action);
sct->base->node = node;
return 1;
}
int SctAddWriteNode(SctController *controller, Hdb *node) {
hdbCallback *cb;
SctData *data;
if (! FindHdbCallbackData(node, controller)) {
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
assert(cb);
AppendHipadabaCallback(node, cb);
}
actionCallback.name = strdup("write");
data = FindHdbCallbackData(node, &actionCallback);
if (data != NULL) {
return 0;
}
data = calloc(1, sizeof(*data));
assert(data);
data->controller = controller;
data->node = node;
data->writable = 1;
data->conCtx = NULL;
data->name = strdup("write");
cb = MakeHipadabaCallback(SctActionCallback, data, SctKillData);
assert(cb);
data->node = node;
AppendHipadabaCallback(node, cb);
RemoveSetUpdateCallback(node);
return 1;
}
static int SctWriteCmd(pSICSOBJ ccmd, SConnection *con,
Hdb *cmdNode, Hdb *par[], int nPar) {
Hdb *node;
SctController *controller;
double interval;
char *path;
if(nPar < 1){
SCPrintf(con, eError, "ERROR: should be: %s write <node>",
ccmd->objectNode->name);
return 0;
}
controller = ccmd->pPrivate;
path = ParText(cmdNode, "node");
node = FindHdbNode(NULL, path, con);
if (node == NULL) {
SCPrintf(con, eError, "ERROR: %s not found", path);
return 0;
}
if (SctAddWriteNode(controller, node) == 0) {
SCPrintf(con, eError,
"ERROR: %s has already a write action", path);
return 0;
}
sct->base->node = node;
return 1;
}
SctData *SctQueueNode(SctController *controller, Hdb *node,
DevPrio prio, char *action) {
SctData *data;
hdbCallback *cb;
if (! FindHdbCallbackData(node, controller)) {
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
assert(cb);
AppendHipadabaCallback(node, cb);
}
data = calloc(1, sizeof(*data));
assert(data);
data->controller = controller;
data->node = node;
data->writable = 0;
data->name = strdup(action);
data->conCtx = NULL;
DevQueue(data->controller->devser, data, prio,
SctWriteHandler, SctMatch, SctKillData);
return data;
}
static int SctQueueCmd(pSICSOBJ ccmd, SConnection *con,
Hdb *cmdNode, Hdb *par[], int nPar) {
Hdb *node;
SctController *controller;
double interval;
char *path;
char *action;
DevPrio prio;
SctData *data;
if(nPar < 2){
SCPrintf(con,eError, "ERROR: should be: %s queue <node> <prio> <action>",
ccmd->objectNode->name);
return 0;
}
controller = ccmd->pPrivate;
path = ParText(cmdNode, "node");
node = FindHdbNode(NULL, path, con);
if (node == NULL) {
SCPrintf(con, eError, "ERROR: %s not found", path);
return 0;
}
prio = DevText2Prio(ParText(cmdNode, "prio"));
action = ParText(cmdNode, "action");
data = SctQueueNode(controller, node, prio, action);
data->conCtx = SCSave(con, NULL);
return 1;
}
static hdbCallbackReturn SctDebugCallback(Hdb *node, void *userData,
hdbMessage *msg) {
hdbDataMessage *mm;
SctController *controller = userData;
SConnection *con;
int i;
mm = GetHdbSetMessage(msg);
if (mm != NULL) {
i = mm->v->v.intValue;
if (i < 0) {
controller->verbose = 0;
DevDebugMode(controller->devser, -1);
} else if (i == 0) {
controller->verbose = 1;
DevDebugMode(controller->devser, -1);
} else {
controller->verbose = 1;
DevDebugMode(controller->devser, i);
}
}
return hdbContinue;
}
static void SctKillController(void *c) {
SctController *controller = c;
CleanStack(controller->node);
RemoveSICSInternalCallback(controller);
DevKill(controller->devser);
}
static int SctMakeController(SConnection *con, SicsInterp *sics,
void *object, int argc, char *argv[]) {
SICSOBJ *ccmd;
Hdb *parent, *par, *cmd;
char *nodeName;
hdbCallback *cb;
SctController *controller;
if (argc < 2) {
SCPrintf(con, eError,
"ERROR: should be %s <path> <protocol args> ...",
argv[0]);
return 0;
}
parent = FindHdbParent(NULL, argv[1], &nodeName, con);
if (parent == NULL) return 0; /* error message already written */
controller = calloc(1, sizeof(*controller));
assert(controller);
controller->verbose = 0;
ccmd = MakeSICSOBJv(nodeName, "SctController", HIPNONE, usSpy);
controller->node = ccmd->objectNode;
controller->conn = SCSave(SCCreateDummyConnection(pServ->pSics), NULL);
assert(ccmd);
assert(controller->node->mama == NULL);
ccmd->pPrivate = controller;
ccmd->KillPrivate = SctKillController;
AddCommand(pServ->pSics, nodeName, InvokeSICSOBJ, KillSICSOBJ, ccmd);
SetDescriptorKey(ccmd->pDes, "creationCommand", "0");
AddHipadabaChild(parent, controller->node, con);
controller->devser = DevMake(con, argc - 2, argv + 2);
if (!controller->devser) return 0;
cmd = AddSICSHdbPar(controller->node,
"poll", usMugger, MakeSICSFunc(SctPollCmd));
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
AddSICSHdbPar(cmd, "interval", usMugger, MakeHdbFloat(5.0));
AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("read"));
AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("read"));
cmd = AddSICSHdbPar(controller->node,
"write", usMugger, MakeSICSFunc(SctWriteCmd));
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
cmd = AddSICSHdbPar(controller->node,
"queue", usMugger, MakeSICSFunc(SctQueueCmd));
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("write"));
AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("write"));
par = AddSICSHdbPar(controller->node, "debug", usUser, MakeHdbInt(0));
cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL);
if (cb) AppendHipadabaCallback(par, cb);
sct->base->controllerNode = controller->node;
sct->base->node = NULL;
return 1;
}
void SctKill(void *object) {
assert(sct == object);
ContextItem *p, *q;
for (p = sct->nodes; p != NULL; p = q) {
q = p->next;
free(p);
}
for (p = sct->trash; p != NULL; p = q) {
q = p->next;
free(p);
}
if (sct->desc != NULL) {
DeleteDescriptor(sct->desc);
}
free(sct);
sct = NULL;
}
void SctInit(void) {
if (sct) return;
sct = calloc(1, sizeof(*sct));
assert(sct);
sct->desc = CreateDescriptor("ScriptContext");
PushContext(NULL, NULL);
sct->base = sct->nodes;
AddCommand(pServ->pSics, "sct", SctCommand, SctKill, sct);
AddCmd("makesctcontroller", SctMakeController);
}