1944 lines
50 KiB
C
1944 lines
50 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"
|
|
#include "ascon.h"
|
|
#include "macro.h"
|
|
#include "syncedprot.h"
|
|
#include "scriptcontext.h"
|
|
|
|
typedef struct ContextItem {
|
|
struct ContextItem *next;
|
|
Hdb *node;
|
|
SctController *controller;
|
|
} ContextItem;
|
|
|
|
typedef struct ScriptContext {
|
|
ObjectDescriptor *desc;
|
|
ContextItem *nodes;
|
|
ContextItem *trash;
|
|
Hdb *sendNode;
|
|
int sendCalled;
|
|
} ScriptContext;
|
|
|
|
struct SctController {
|
|
DevSer *devser;
|
|
Hdb *node; /* the controller node */
|
|
SConnection *conn;
|
|
int verbose;
|
|
FILE *fd;
|
|
};
|
|
|
|
/* action data and write callback data */
|
|
typedef struct SctData {
|
|
char *name;
|
|
SctController *controller;
|
|
SConnection *conCtx;
|
|
int answered;
|
|
int inMacro;
|
|
Hdb *node;
|
|
long syncid;
|
|
} SctData;
|
|
|
|
/* data for updatescript */
|
|
typedef struct SctUpdatescript {
|
|
char *name;
|
|
SctController *controller;
|
|
} SctUpdatescript;
|
|
|
|
|
|
static ScriptContext *sct = NULL;
|
|
static SctData *queueData = NULL;
|
|
|
|
static struct {
|
|
char *name;
|
|
} actionCallback;
|
|
|
|
static struct SctUpdatescript updatescriptCallback;
|
|
|
|
void PushContext(Hdb * node, SctController * controller)
|
|
{
|
|
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->controller = controller;
|
|
}
|
|
|
|
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->controller->node == node) {
|
|
s->controller = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *GetPropAndNode(Hdb * node, Hdb * cNode, char *key, Hdb **foundNode)
|
|
{
|
|
char *val;
|
|
Hdb *mama;
|
|
|
|
if (key[0] == '@') {
|
|
/* for keys starting with @ look also at the ancestors */
|
|
for (mama = node; mama != NULL; mama = mama->mama) {
|
|
val = GetHdbProp(mama, key);
|
|
if (val != NULL) {
|
|
*foundNode = mama;
|
|
return val;
|
|
}
|
|
}
|
|
}
|
|
if (node != NULL) {
|
|
val = GetHdbProp(node, key);
|
|
if (val != NULL) {
|
|
*foundNode = node;
|
|
return val;
|
|
}
|
|
}
|
|
if (cNode != NULL) {
|
|
val = GetHdbProp(cNode, key);
|
|
if (val != NULL) {
|
|
*foundNode = cNode;
|
|
return val;
|
|
}
|
|
}
|
|
*foundNode = node;
|
|
return NULL;
|
|
}
|
|
|
|
static void SetProp(Hdb * node, Hdb * cNode, char *key, char *value)
|
|
{
|
|
Hdb *propNode;
|
|
char *val;
|
|
|
|
val = GetPropAndNode(node, cNode, key, &propNode);
|
|
if (value == NULL) {
|
|
if (val != NULL) {
|
|
SetHdbProperty(propNode, key, "");
|
|
}
|
|
} else {
|
|
SetHdbProperty(propNode, key, value);
|
|
}
|
|
}
|
|
|
|
static char *GetProp(Hdb * node, Hdb * cNode, char *key)
|
|
{
|
|
Hdb *propNode;
|
|
return GetPropAndNode(node, cNode, key, &propNode);
|
|
}
|
|
|
|
/*
|
|
* This is the actual sct command available in scripts.
|
|
*/
|
|
int SctCommand(SConnection * con, SicsInterp * sics, void *object,
|
|
int argc, char *argv[])
|
|
{
|
|
static char value[1024];
|
|
char *val;
|
|
char error[512];
|
|
Hdb *node = NULL;
|
|
Hdb *cNode = NULL;
|
|
hdbValue v;
|
|
SctController *controller;
|
|
|
|
assert(sct == object);
|
|
if (sct->nodes != NULL) {
|
|
node = sct->nodes->node;
|
|
controller = sct->nodes->controller;
|
|
}
|
|
if (node == NULL || controller == NULL || controller->node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s may be called only in proper context",
|
|
argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* get path (pure sct command)
|
|
*/
|
|
if (argc <= 1) {
|
|
GetHdbPath(node, value, sizeof value);
|
|
SCWrite(con, value, eValue);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* update command
|
|
*/
|
|
if (strcmp(argv[1], "update") == 0) {
|
|
cloneHdbValue(&node->value, &v);
|
|
Arg2Text(argc - 2, argv + 2, value, sizeof value);
|
|
if (!readHdbValue(&v, value, error, 512)) {
|
|
SCWrite(con, error, eError);
|
|
return 0;
|
|
}
|
|
UpdateHipadabaPar(node, v, con);
|
|
ReleaseHdbValue(&v);
|
|
SetHdbProperty(node, "geterror", NULL);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* print
|
|
*/
|
|
if (strcmp(argv[1], "print") == 0) {
|
|
if (queueData != NULL) {
|
|
queueData->answered = 1;
|
|
SCsetMacro(con, queueData->inMacro); /* take macro flag stored at set callback */
|
|
}
|
|
Arg2Text(argc - 2, argv + 2, value, sizeof value);
|
|
SCWrite(con, value, eLog);
|
|
SCsetMacro(con, 1); /* we are always in Macro */
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* controller
|
|
*/
|
|
if (strcmp(argv[1], "controller") == 0) {
|
|
SCWrite(con, controller->node->name, eValue);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* time stamping
|
|
*/
|
|
if (strcmp(argv[1], "utime") == 0) {
|
|
snprintf(value, 1024, "%.3f", DoubleTime());
|
|
if (argc > 2) { /* if no property is given, use only return value */
|
|
SetHdbProperty(node, argv[2], value);
|
|
}
|
|
/* write time as return value */
|
|
SCWrite(con, value, eValue);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* with command (execute a script in the context of an other node)
|
|
*/
|
|
if (strcmp(argv[1], "with") == 0) {
|
|
if (argc < 3) {
|
|
SCPrintf(con, eError, "ERROR: syntax must be: %s %s node script",
|
|
argv[0], argv[1]);
|
|
return 0;
|
|
}
|
|
GetHdbPath(node, value, sizeof value);
|
|
node = FindHdbNode(value, argv[2], con); /* get a relative path */
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s %s: node %s not found",
|
|
argv[0], argv[1], argv[2]);
|
|
return 0;
|
|
}
|
|
if (SctCallInContext(con, argv[3], node, controller, &val) == 0) {
|
|
SCPrintf(con, eError, "ERROR: in %s %s %s {%s}: %s",
|
|
argv[0], argv[1], argv[2], argv[3], val);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* parent command (return path of parent node)
|
|
*/
|
|
if (strcmp(argv[1], "parent") == 0) {
|
|
if (argc > 3) {
|
|
SCPrintf(con, eError, "ERROR: syntax must be: %s %s [path]",
|
|
argv[0], argv[1]);
|
|
return 0;
|
|
}
|
|
if (argc == 3) {
|
|
node = FindHdbNode(NULL, argv[2], con);
|
|
}
|
|
GetHdbPath(node->mama, value, sizeof value);
|
|
/* returns an empty string on error */
|
|
SCWrite(con, value, eValue);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* send command
|
|
*/
|
|
if (strcmp(argv[1], "send") == 0 && argc > 2) {
|
|
if (sct->sendNode != node) {
|
|
SCPrintf(con, eError, "ERROR: %s may be called only in action scripts",
|
|
argv[0]);
|
|
return 0;
|
|
}
|
|
sct->sendCalled = 1;
|
|
/* continue with property handling */
|
|
}
|
|
|
|
/*
|
|
* property handling
|
|
*/
|
|
if (argc == 2) { /* get case */
|
|
val = GetProp(node, controller->node, 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 */
|
|
if (argc == 3) {
|
|
SetProp(node, controller->node, argv[1], argv[2]);
|
|
} else {
|
|
val = Arg2Tcl(argc - 2, argv + 2, value, sizeof value);
|
|
SetProp(node, controller->node, argv[1], val);
|
|
if (val != NULL && val != value)
|
|
free(val);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
double secondsOfMinute() {
|
|
double now=DoubleTime();
|
|
return (now/60-floor(now/60))*60;
|
|
}
|
|
|
|
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);
|
|
if (verbose) {
|
|
SCPrintf(con, eLog, "%6.3f script: %s", secondsOfMinute(), script);
|
|
}
|
|
if (controller->fd != NULL) {
|
|
fprintf(controller->fd,"%6.3f script: %s\n", secondsOfMinute(), script);
|
|
}
|
|
|
|
MacroPush(con);
|
|
l = strlen(script);
|
|
ret = Tcl_EvalEx(pTcl, script, l, 0);
|
|
result = (char *) Tcl_GetStringResult(pTcl);
|
|
if (ret != TCL_OK && result[0] != '\0') {
|
|
if (strncmp(result, "ERROR:", 6) == 0) {
|
|
/* remove "ERROR:", as it will be added later */
|
|
result += 6;
|
|
if (result[0] == ' ') {
|
|
result++;
|
|
}
|
|
}
|
|
if (verbose) {
|
|
SCPrintf(con, eLog, "%6.3f error: %s", secondsOfMinute(), result);
|
|
}
|
|
if(controller->fd != NULL){
|
|
fprintf(controller->fd, "%6.3f error: %s\n", secondsOfMinute(), result);
|
|
}
|
|
iRet = 0;
|
|
}
|
|
*resPtr = result;
|
|
|
|
MacroPop();
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* This routine is running the script chain. It is called repeatedly
|
|
* with response data from the device serializer (devser). This function
|
|
* basically:
|
|
* - Figures out which script to run
|
|
* - Runs the script
|
|
* - Based on the result either returns the next string to send to the
|
|
* device or NULL when the script chain is done with
|
|
*/
|
|
static char *SctActionHandler(void *actionData, char *lastReply,
|
|
int commError)
|
|
{
|
|
SctData *data = actionData;
|
|
Hdb *node = data->node;
|
|
SctController *controller = data->controller;
|
|
char *state;
|
|
char *result;
|
|
char *script = NULL;
|
|
char *errorScript = NULL;
|
|
char *send = NULL;
|
|
int i;
|
|
int j;
|
|
SConnection *con;
|
|
char eprop[80];
|
|
char msg[1024];
|
|
char path[MAX_HDB_PATH];
|
|
char origScript[80];
|
|
char *blank;
|
|
char *emsg;
|
|
size_t l;
|
|
int cnt;
|
|
int ret;
|
|
char timeKey[50], timeVal[50];
|
|
int iMacro;
|
|
|
|
assert(data->name);
|
|
if (queueData != NULL && queueData->conCtx != NULL) {
|
|
con = queueData->conCtx;
|
|
} else {
|
|
con = controller->conn;
|
|
}
|
|
/*
|
|
* If this is a followup call, the I/O system will have set the
|
|
* property result to the data from the device. Read this now and
|
|
* print it if diagnostics is required.
|
|
*/
|
|
script = NULL;
|
|
if (lastReply != NULL) {
|
|
SetProp(node, controller->node, "result", lastReply);
|
|
if (*lastReply != '\0') {
|
|
if (!commError && controller->verbose) {
|
|
SCPrintf(con, eLog, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
|
|
}
|
|
if(!commError && controller->fd != NULL) {
|
|
fprintf(controller->fd, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
|
|
}
|
|
if(data != NULL && data->controller != NULL) {
|
|
traceIO(data->controller->node->name, "reply:%s", lastReply);
|
|
} else {
|
|
traceIO("sctunknown", "reply:%s", lastReply);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When this is a followup, we use the content of the
|
|
* state field as the property storing the next script to
|
|
* run. If this is the start of a chain this is set to the
|
|
* data->name which is mostly either read or write
|
|
*/
|
|
state = GetProp(node, controller->node, "state");
|
|
if (state == NULL || strcasecmp(state, "idle") == 0) {
|
|
state = data->name;
|
|
SetProp(node, controller->node, "state", state);
|
|
}
|
|
|
|
/*
|
|
* Sometimes one wishes to call multiple scripts in succession
|
|
* before returning into I/O. Such scripts then do not set the
|
|
* send property. The loop is taking care of this. Not more
|
|
* then 10 scripts can be chained in this way.
|
|
*/
|
|
for (i = 0; i < 10; i++) {
|
|
/*
|
|
* read the script to invoke from the property living
|
|
* in state
|
|
*/
|
|
script = GetProp(node, controller->node, state);
|
|
if (script == NULL)
|
|
script = state;
|
|
snprintf(origScript, sizeof origScript, "%s", script);
|
|
if (commError) {
|
|
errorScript = GetProp(node, controller->node, "commerror");
|
|
if (errorScript != NULL)
|
|
script = errorScript;
|
|
commError = 0;
|
|
}
|
|
/*
|
|
* Set the context and invoke the script
|
|
*/
|
|
script = strdup(script);
|
|
sct->sendNode = node;
|
|
sct->sendCalled = 0;
|
|
SyncedBegin(data->syncid);
|
|
ret = SctCallInContext(con, script, node, controller, &result);
|
|
SyncedEnd(data->syncid);
|
|
sct->sendNode = NULL;
|
|
if (ret == 0) {
|
|
/*
|
|
* an error occurred in the script: store error message in
|
|
* a property, and write the error message the first time it
|
|
* occurs.
|
|
*
|
|
* Replaced <> by - because it messed up XML for Gumtree
|
|
* Mark Koennecke
|
|
* replaced by {} MZ
|
|
*/
|
|
snprintf(eprop, sizeof eprop, "error_during_%s", data->name);
|
|
emsg = GetHdbProp(node, eprop);
|
|
if (emsg == NULL || con != controller->conn) {
|
|
GetHdbPath(node, path, sizeof path);
|
|
SCPrintf(con, eLogError,
|
|
"ERROR: action {%s} in {%s} node %s:\nERROR: %s",
|
|
data->name, origScript, path, result);
|
|
if(data != NULL && data->controller != NULL){
|
|
traceIO(data->controller->node->name, "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
|
|
data->name, origScript, path, result);
|
|
} else {
|
|
traceIO("sctunknown", "reply:%s", "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
|
|
data->name, origScript, path, result);
|
|
}
|
|
}
|
|
snprintf(msg, sizeof msg, "{%s} %s", origScript, result);
|
|
if (strcasecmp(data->name, "read") == 0) {
|
|
SetHdbProperty(node, "geterror", result);
|
|
}
|
|
/* Sanitize the text to reduce TCL problems with unbalanced and substituted items */
|
|
for (j = 0; msg[j]; ++j) {
|
|
switch (msg[j]) {
|
|
case '{':
|
|
case '}':
|
|
case '[':
|
|
case ']':
|
|
case '<':
|
|
case '>':
|
|
case '\'':
|
|
case '"':
|
|
case '$':
|
|
msg[j] = '_';
|
|
}
|
|
}
|
|
SetHdbProperty(node, eprop, msg);
|
|
blank = strchr(origScript, ' ');
|
|
if (blank != NULL) {
|
|
l = blank - origScript;
|
|
} else {
|
|
l = strlen(origScript);
|
|
}
|
|
snprintf(eprop, sizeof eprop, "error_in_%.*s", l, origScript);
|
|
emsg = GetHdbProp(node, eprop);
|
|
cnt = 0;
|
|
if (emsg != NULL) {
|
|
sscanf(emsg, "%d", &cnt);
|
|
}
|
|
cnt++;
|
|
snprintf(msg, sizeof msg, "%dx {%s}: %s", cnt, origScript, result);
|
|
/* Sanitize the text to reduce TCL problems with unbalanced and substituted items */
|
|
for (j = 0; msg[j]; ++j) {
|
|
switch (msg[j]) {
|
|
case '{':
|
|
case '}':
|
|
case '[':
|
|
case ']':
|
|
case '<':
|
|
case '>':
|
|
case '\'':
|
|
case '"':
|
|
case '$':
|
|
msg[j] = '_';
|
|
}
|
|
}
|
|
SetHdbProperty(node, eprop, msg);
|
|
send = NULL;
|
|
free(script);
|
|
goto finish;
|
|
}
|
|
/*
|
|
* The script executed OK.
|
|
* The next state is the result
|
|
*/
|
|
state = result;
|
|
/*
|
|
* if the new state is idle, clean everything up
|
|
* and terminate the script chain
|
|
*/
|
|
if (strcasecmp(state, "idle") == 0 || strcasecmp(state, "unpoll") == 0) {
|
|
if (queueData == data) { /* it was a write action */
|
|
SetHdbProperty(node, "requested", NULL);
|
|
if (!data->answered) {
|
|
if (queueData->inMacro == 0) {
|
|
/*
|
|
* send an O.k. if there was no other reply on write's
|
|
*/
|
|
iMacro = SCinMacro(con);
|
|
con->iMacro = 0;
|
|
SCWrite(con, "o.k.", eWarning);
|
|
SCsetMacro(con, iMacro);
|
|
}
|
|
}
|
|
}
|
|
snprintf(eprop, sizeof eprop, "error_during_%s", data->name);
|
|
emsg = GetHdbProp(node, eprop);
|
|
if (emsg != NULL) {
|
|
GetHdbPath(node, path, sizeof path);
|
|
SCPrintf(con, eError,
|
|
"action {%s}: success after error message (%s)\n %s",
|
|
data->name, path, emsg);
|
|
SetHdbProperty(node, eprop, NULL);
|
|
}
|
|
snprintf(timeKey, sizeof timeKey, "%s_time", data->name);
|
|
snprintf(timeVal, sizeof timeVal, "%.3f", DoubleTime());
|
|
SetHdbProperty(node, timeKey, timeVal);
|
|
send = NULL;
|
|
free(script);
|
|
goto finish;
|
|
}
|
|
/*
|
|
* set the state property to the next state for
|
|
* the next invocation of this routine
|
|
*/
|
|
SetProp(node, controller->node, "state", state);
|
|
free(script);
|
|
script = NULL;
|
|
/*
|
|
* If there is data to send, check it and do so
|
|
*/
|
|
if (sct->sendCalled) {
|
|
send = GetProp(node, controller->node, "send");
|
|
if (send == NULL)
|
|
send = "";
|
|
if (controller->verbose) {
|
|
SCPrintf(con, eLog, "%6.3f send : %s", secondsOfMinute(), send);
|
|
}
|
|
if (controller->fd != NULL) {
|
|
fprintf(controller->fd, "%6.3f send : %s\n", secondsOfMinute(), send);
|
|
}
|
|
if(data != NULL && data->controller != NULL){
|
|
traceIO(data->controller->node->name, "send:%s", send);
|
|
} else {
|
|
traceIO("sctunknown", "send:%s", send);
|
|
}
|
|
return send;
|
|
}
|
|
}
|
|
SCPrintf(con, eLogError, "ERROR: too many quick scripts chained");
|
|
finish:
|
|
if (strcmp(data->name, "write") == 0) {
|
|
if (GetHdbProp(node, "writestatus") != NULL) {
|
|
SetHdbProperty(node, "writestatus", "commandsent");
|
|
}
|
|
}
|
|
if (strcasecmp(state, "unpoll") == 0) {
|
|
DevUnschedule(controller->devser, data, SctActionHandler, SctMatch);
|
|
send = NULL;
|
|
}
|
|
SetProp(node, controller->node, "state", "idle");
|
|
if (data->conCtx != NULL) {
|
|
SCDeleteConnection(data->conCtx);
|
|
data->conCtx = NULL;
|
|
}
|
|
return send;
|
|
}
|
|
|
|
static char *SctWriteHandler(void *actionData, char *lastReply,
|
|
int commError)
|
|
{
|
|
SctData *data = actionData;
|
|
SctData *old;
|
|
char *result;
|
|
|
|
old = queueData;
|
|
queueData = data;
|
|
result = SctActionHandler(data, lastReply, commError);
|
|
queueData = old;
|
|
return result;
|
|
}
|
|
|
|
static int SctMatchNode(void *vNode, void *vData)
|
|
{
|
|
Hdb *node = vNode;
|
|
SctData *d = vData;
|
|
|
|
return node == d->node;
|
|
}
|
|
|
|
static char * SctDataInfo(void *d)
|
|
{
|
|
SctData *data = d;
|
|
char text[256];
|
|
int l;
|
|
|
|
snprintf(text, sizeof text, "%s ", data->name);
|
|
l = strlen(text);
|
|
GetHdbPath(data->node, text + l, sizeof text - l);
|
|
return strdup(text);
|
|
}
|
|
|
|
static void SctEndData(void *d)
|
|
{
|
|
/* no kill, only decrement sync counter */
|
|
SctData *data = d;
|
|
|
|
if (data->syncid > 0) {
|
|
SyncedDecr(data->syncid);
|
|
data->syncid = SYNCED_NO_ID;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is the callback for all nodes participating in the
|
|
* scriptcontext system
|
|
*/
|
|
static hdbCallbackReturn SctMainCallback(Hdb * node, void *userData,
|
|
hdbMessage * msg)
|
|
{
|
|
SctController *controller = userData;
|
|
hdbDataSearch *dsm;
|
|
hdbDataMessage *mm;
|
|
hdbPtrMessage *pm;
|
|
hdbMessage *km;
|
|
SConnection *con;
|
|
char *geterror;
|
|
char error[256];
|
|
|
|
if (controller->devser == NULL) {
|
|
/* defunct controller */
|
|
return hdbContinue;
|
|
}
|
|
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) {
|
|
/* unschedule all actions related to this node */
|
|
DevUnschedule(controller->devser, node, SctActionHandler,
|
|
SctMatchNode);
|
|
CleanStack(node);
|
|
return hdbContinue;
|
|
}
|
|
mm = GetHdbGetMessage(msg);
|
|
if (mm != NULL) {
|
|
con = mm->callData;
|
|
geterror = GetHdbProp(node, "geterror");
|
|
if (geterror != NULL) {
|
|
snprintf(error,255,"ERROR: %s", geterror);
|
|
SCWrite(con, error, eError);
|
|
if (mm->v->dataType == HIPTEXT) {
|
|
if (mm->v->v.text != NULL) {
|
|
free(mm->v->v.text);
|
|
}
|
|
mm->v->v.text = strdup(error);
|
|
}
|
|
return hdbAbort;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
|
|
return hdbContinue;
|
|
}
|
|
/*
|
|
* This is the callback registered for nodes which
|
|
* are written too.
|
|
*/
|
|
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;
|
|
char path[MAX_HDB_PATH];
|
|
char *sicsCommand;
|
|
int iMacro;
|
|
|
|
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) {
|
|
con = mm->callData;
|
|
|
|
/* set target value */
|
|
text = formatValue(*(mm->v), node);
|
|
SetHdbProperty(node, "target", GetCharArray(text));
|
|
SetHdbProperty(node, "requested", GetCharArray(text));
|
|
|
|
/* 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: in {%s}: %s", script, error); */
|
|
SCPrintf(con, eError, "ERROR: %s", error); /* nicer for the user */
|
|
SetHdbProperty(node, "target", NULL);
|
|
SetHdbProperty(node, "requested", NULL);
|
|
return hdbAbort;
|
|
}
|
|
}
|
|
|
|
|
|
/* enqueue write action */
|
|
writeprio = GetProp(node, data->controller->node, "writeprio");
|
|
if (writeprio != NULL) {
|
|
prio = DevText2Prio(writeprio);
|
|
if (prio == NullPRIO) {
|
|
SCPrintf(con, eError, "ERROR: unknown priority: %s", writeprio);
|
|
prio = WritePRIO;
|
|
}
|
|
} else {
|
|
prio = WritePRIO;
|
|
}
|
|
|
|
/*
|
|
* check for duplicate sets on a node.
|
|
*/
|
|
if (data->conCtx != NULL) {
|
|
if (data->inMacro == 0) {
|
|
GetHdbPath(node, path, sizeof path);
|
|
SCsetMacro(data->conCtx, 0);
|
|
SCPrintf(data->conCtx, eWarning,
|
|
"%s target changed to %s before completion", path,
|
|
GetCharArray(text));
|
|
}
|
|
/*
|
|
* the node has already been queued for execution. But as we never
|
|
* know if the script chain is already running, the only clean
|
|
* solution is to requeue the node.
|
|
*/
|
|
SCDeleteConnection(data->conCtx);
|
|
}
|
|
data->conCtx = SCCopyConnection(con);
|
|
data->answered = 0;
|
|
data->inMacro = SCinMacro(con);
|
|
tracePar(node->name,"Queued %s to %s",node->name, GetCharArray(text));
|
|
data->syncid = SyncedIncr(0);
|
|
DevQueue(data->controller->devser, data, prio,
|
|
SctWriteHandler, SctMatch, SctEndData, SctDataInfo);
|
|
/* kill function SctEndData does not kill, data is owned by the node (callback list) */
|
|
DeleteDynString(text);
|
|
return hdbContinue;
|
|
}
|
|
|
|
mm = GetHdbUpdateMessage(msg);
|
|
if (mm != NULL) {
|
|
if (queueData != NULL && queueData->conCtx != NULL && queueData->answered != 1) {
|
|
/* update called from a write action */
|
|
text = formatValue(*(mm->v), node);
|
|
if (queueData->inMacro == 0 && queueData->node == node) {
|
|
queueData->answered = 1;
|
|
iMacro = SCinMacro(queueData->conCtx);
|
|
SCsetMacro(queueData->conCtx, 0);
|
|
sicsCommand = GetHdbProp(node, "sicscommand");
|
|
if (sicsCommand) {
|
|
SCPrintf(queueData->conCtx, eWarning, "OK: %s %s", sicsCommand, GetCharArray(text));
|
|
} else {
|
|
GetHdbPath(node, path, sizeof path);
|
|
SCPrintf(queueData->conCtx, eWarning, "OK: hset %s %s", path, GetCharArray(text));
|
|
}
|
|
SCsetMacro(queueData->conCtx, iMacro);
|
|
}
|
|
DeleteDynString(text);
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
|
|
static char *ParText(Hdb * cmdNode, char *name,
|
|
int nPar, char *defaultValue)
|
|
{
|
|
Hdb *par;
|
|
|
|
for (par = cmdNode->child; nPar > 0 && par != NULL;
|
|
par = par->next, nPar--) {
|
|
if (strcasecmp(par->name, name) == 0) {
|
|
if (par->value.dataType == HIPTEXT) {
|
|
return par->value.v.text;
|
|
}
|
|
}
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
static double ParValue(Hdb * cmdNode, char *name,
|
|
int nPar, double defaultValue)
|
|
{
|
|
Hdb *par;
|
|
|
|
for (par = cmdNode->child; nPar > 0 && par != NULL;
|
|
par = par->next, nPar--) {
|
|
if (strcasecmp(par->name, name) == 0) {
|
|
switch (par->value.dataType) {
|
|
case HIPINT:
|
|
return par->value.v.intValue;
|
|
case HIPFLOAT:
|
|
return par->value.v.doubleValue;
|
|
}
|
|
}
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
static void SctKillData(void *d)
|
|
{
|
|
SctData *data = d;
|
|
|
|
if (data->syncid > 0) {
|
|
SyncedDecr(data->syncid);
|
|
data->syncid = SYNCED_NO_ID;
|
|
}
|
|
|
|
if (data->name) {
|
|
free(data->name);
|
|
data->name = NULL;
|
|
}
|
|
if (data->conCtx)
|
|
SCDeleteConnection(data->conCtx);
|
|
free(data);
|
|
}
|
|
|
|
static void SctKillCBData(void *d)
|
|
{
|
|
SctData *data = d;
|
|
|
|
if (data->controller->devser != NULL) {
|
|
DevRemoveAction(data->controller->devser, data);
|
|
}
|
|
SctKillData(d);
|
|
}
|
|
|
|
int SctAddPollNode(SctController * controller, Hdb * node, double interval,
|
|
DevPrio prio, char *action)
|
|
{
|
|
SctData *data;
|
|
hdbCallback *cb;
|
|
char nodePath[512], info[1024];
|
|
|
|
if (!FindHdbCallbackData(node, controller)) {
|
|
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, cb);
|
|
GetHdbPath(node, nodePath, sizeof nodePath);
|
|
snprintf(info, 1023, "%s: Not read yet", nodePath);
|
|
SetHdbProperty(node, "geterror", info);
|
|
}
|
|
|
|
data = calloc(1, sizeof(*data));
|
|
assert(data);
|
|
data->controller = controller;
|
|
data->node = node;
|
|
data->conCtx = NULL; /* we might need a dummy connection here */
|
|
data->name = strdup(action);
|
|
|
|
data->syncid = SyncedIncr(0);
|
|
if (DevSchedule(controller->devser, data, prio, interval,
|
|
SctActionHandler, SctMatch, SctKillData, SctDataInfo)) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
char *prioText;
|
|
|
|
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", nPar, "");
|
|
node = FindHdbNode(NULL, path, con);
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
interval = ParValue(cmdNode, "interval", nPar, 5);
|
|
prioText = ParText(cmdNode, "prio", nPar, "read");
|
|
prio = DevText2Prio(prioText);
|
|
if (prio == NullPRIO) {
|
|
SCPrintf(con, eError, "ERROR: unknown priority: %s", prioText);
|
|
return 0;
|
|
}
|
|
action = ParText(cmdNode, "action", nPar, "read");
|
|
|
|
if (SctAddPollNode(controller, node, interval, prio, action)) {
|
|
SCPrintf(con, eValue,
|
|
"%s poll registered on %s (%g sec, %s prio)",
|
|
action, path, interval, prioText);
|
|
} else {
|
|
SCPrintf(con, eValue,
|
|
"%s poll on %s changed to %g sec, %s prio",
|
|
action, path, interval, prioText);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctUnpollCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *controller;
|
|
double interval;
|
|
char *path;
|
|
DevPrio prio;
|
|
char *prioText;
|
|
SctData data;
|
|
|
|
if (nPar < 1) {
|
|
SCPrintf(con, eError,
|
|
"ERROR: should be: %s unpoll <node> (<action>)",
|
|
ccmd->objectNode->name);
|
|
return 0;
|
|
}
|
|
controller = ccmd->pPrivate;
|
|
path = ParText(cmdNode, "node", nPar, "");
|
|
data.node = FindHdbNode(NULL, path, con);
|
|
if (data.node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
data.name = ParText(cmdNode, "action", nPar, "read");
|
|
if (DevUnschedule(controller->devser, &data, SctActionHandler, SctMatch)
|
|
== 0) {
|
|
SCPrintf(con, eValue, "%s poll not found on %s", data.name, path);
|
|
} else {
|
|
SCSendOK(con);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctConnectCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
Hdb *node;
|
|
SctController *controller;
|
|
char *path;
|
|
hdbCallback *cb;
|
|
|
|
if (nPar < 1) {
|
|
SCPrintf(con, eError,
|
|
"ERROR: should be: %s connect <node> ",
|
|
ccmd->objectNode->name);
|
|
return 0;
|
|
}
|
|
controller = ccmd->pPrivate;
|
|
path = ParText(cmdNode, "node", nPar, "");
|
|
node = FindHdbNode(NULL, path, con);
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
|
|
if (!FindHdbCallbackData(node, controller)) {
|
|
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, cb);
|
|
}
|
|
SCSendOK(con);
|
|
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 = "write"; /* local, so no strdup here */
|
|
data = FindHdbCallbackData(node, &actionCallback);
|
|
if (data != NULL) {
|
|
return 0;
|
|
}
|
|
data = calloc(1, sizeof(*data));
|
|
assert(data);
|
|
data->controller = controller;
|
|
data->node = node;
|
|
data->conCtx = NULL;
|
|
data->name = strdup("write");
|
|
|
|
cb = MakeHipadabaCallback(SctActionCallback, data, SctKillCBData);
|
|
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", nPar, "");
|
|
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;
|
|
}
|
|
|
|
SCSendOK(con);
|
|
return 1;
|
|
}
|
|
|
|
void SctQueueNode(SctController * controller, Hdb * node,
|
|
DevPrio prio, char *action, SConnection * con)
|
|
{
|
|
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->name = strdup(action);
|
|
data->conCtx = NULL;
|
|
data->answered = 1;
|
|
|
|
data->syncid = SyncedIncr(0);
|
|
|
|
if (DevQueue(data->controller->devser, data, prio,
|
|
SctWriteHandler, SctMatch, SctKillData, SctDataInfo)) {
|
|
if (con != NULL) {
|
|
data->conCtx = SCCopyConnection(con);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
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;
|
|
char *prioText;
|
|
|
|
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", nPar, "");
|
|
node = FindHdbNode(NULL, path, con);
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
prioText = ParText(cmdNode, "prio", nPar, "write");
|
|
prio = DevText2Prio(prioText);
|
|
if (prio == NullPRIO) {
|
|
SCPrintf(con, eError, "ERROR: unknown priority: %s", prioText);
|
|
return 0;
|
|
}
|
|
action = ParText(cmdNode, "action", nPar, "write");
|
|
|
|
SctQueueNode(controller, node, prio, action, con);
|
|
return 1;
|
|
}
|
|
|
|
static void SctKillUpdatescript(void *d)
|
|
{
|
|
SctUpdatescript *us = d;
|
|
|
|
if (us->name) {
|
|
free(us->name);
|
|
}
|
|
free(us);
|
|
}
|
|
|
|
/*
|
|
* This is the callback for update scripts
|
|
*/
|
|
static hdbCallbackReturn SctUpdatescriptCallback(Hdb * node,
|
|
void *userData,
|
|
hdbMessage * msg)
|
|
{
|
|
SctUpdatescript *us = userData;
|
|
hdbDataSearch *dsm;
|
|
hdbDataMessage *mm;
|
|
hdbPtrMessage *pm;
|
|
SConnection *con;
|
|
char *result;
|
|
char *script;
|
|
char path[MAX_HDB_PATH];
|
|
pDynString text;
|
|
char *arg;
|
|
|
|
pm = GetKillPtrMessage(msg);
|
|
if (pm != NULL) {
|
|
if (us->controller == pm->pPtr) {
|
|
return hdbKill;
|
|
}
|
|
if (pm->pPtr == &updatescriptCallback
|
|
&& strcmp(us->name, updatescriptCallback.name) == 0
|
|
&& us->controller == updatescriptCallback.controller) {
|
|
return hdbKill;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
dsm = GetHdbDataSearchMessage(msg);
|
|
if (dsm != NULL) {
|
|
if (dsm->testPtr == &updatescriptCallback) {
|
|
if (strcasecmp(us->name, updatescriptCallback.name) == 0 &&
|
|
us->controller == updatescriptCallback.controller) {
|
|
dsm->result = us;
|
|
}
|
|
return hdbAbort;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
mm = GetHdbUpdateMessage(msg);
|
|
if (mm != NULL) {
|
|
con = mm->callData;
|
|
|
|
script = GetProp(node, us->controller->node, us->name);
|
|
if (script == NULL) script = us->name;
|
|
|
|
text = formatValue(*(mm->v), node);
|
|
arg = GetCharArray(text);
|
|
arg = Arg2Tcl(1, &arg, NULL, 0);
|
|
DynStringCopy(text, script);
|
|
DynStringConcat(text, " ");
|
|
DynStringConcat(text, arg);
|
|
free(arg);
|
|
script = GetCharArray(text);
|
|
|
|
if (!SctCallInContext(con, script, node, us->controller, &result)) {
|
|
|
|
GetHdbPath(node, path, sizeof path);
|
|
SCPrintf(con, eError, "ERROR: in updatescript {%s} node %s: %s",
|
|
script, path, result);
|
|
}
|
|
DeleteDynString(text);
|
|
return hdbContinue;
|
|
}
|
|
|
|
return hdbContinue;
|
|
}
|
|
|
|
int SctUpdatescriptNode(SctController * controller, Hdb * node, char *action)
|
|
{
|
|
SctUpdatescript *us;
|
|
hdbCallback *cb;
|
|
|
|
updatescriptCallback.name = action;
|
|
updatescriptCallback.controller = controller;
|
|
us = FindHdbCallbackData(node, &updatescriptCallback);
|
|
if (us != NULL) {
|
|
return 0;
|
|
}
|
|
us = calloc(1, sizeof(*us));
|
|
us->name = strdup(action);
|
|
us->controller = controller;
|
|
cb = MakeHipadabaCallback(SctUpdatescriptCallback, us, SctKillUpdatescript);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, cb);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int SctUpdatescriptKill(SctController * controller, Hdb * node, char *action)
|
|
{
|
|
updatescriptCallback.name = action;
|
|
updatescriptCallback.controller = controller;
|
|
RemoveSICSInternalCallbackFrom(node, &updatescriptCallback);
|
|
return 1;
|
|
}
|
|
|
|
static int SctUpdatescriptCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
Hdb *node;
|
|
SctController *controller;
|
|
double interval;
|
|
char *path;
|
|
char *action;
|
|
SctData *data;
|
|
char *prioText;
|
|
|
|
if (nPar < 2) {
|
|
SCPrintf(con, eError,
|
|
"ERROR: should be: %s updatescript <node> <action>",
|
|
ccmd->objectNode->name);
|
|
return 0;
|
|
}
|
|
controller = ccmd->pPrivate;
|
|
path = ParText(cmdNode, "node", nPar, "");
|
|
node = FindHdbNode(NULL, path, con);
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
action = ParText(cmdNode, "action", nPar, "updatescript");
|
|
|
|
if (SctUpdatescriptNode(controller, node, action)) {
|
|
SCPrintf(con, eValue, "%s registered for update on %s", action, path);
|
|
} else {
|
|
SCPrintf(con, eValue, "%s already registered for update on %s", action, path);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctKillUpdatescriptCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
Hdb *node;
|
|
SctController *controller;
|
|
double interval;
|
|
char *path;
|
|
char *action;
|
|
SctData *data;
|
|
char *prioText;
|
|
|
|
if (nPar < 2) {
|
|
SCPrintf(con, eError,
|
|
"ERROR: should be: %s killupdatescript <node> <action>",
|
|
ccmd->objectNode->name);
|
|
return 0;
|
|
}
|
|
controller = ccmd->pPrivate;
|
|
path = ParText(cmdNode, "node", nPar, "");
|
|
node = FindHdbNode(NULL, path, con);
|
|
if (node == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s not found", path);
|
|
return 0;
|
|
}
|
|
action = ParText(cmdNode, "action", nPar, "updatescript");
|
|
|
|
SctUpdatescriptKill(controller, node, action);
|
|
SCPrintf(con, eValue, "kill %s updatescript on %s", action, path);
|
|
return 1;
|
|
}
|
|
|
|
typedef struct SctTransact {
|
|
char *command;
|
|
char *reply;
|
|
int sent;
|
|
SConnection *con;
|
|
SctController *controller;
|
|
} SctTransact, *pSctTransact;
|
|
|
|
static void KillSctTransact(void *data)
|
|
{
|
|
pSctTransact self = (pSctTransact) data;
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
if (self->command) {
|
|
free(self->command);
|
|
}
|
|
if (self->reply) {
|
|
free(self->reply);
|
|
}
|
|
if (self->con) {
|
|
SCDeleteConnection(self->con);
|
|
}
|
|
free(self);
|
|
}
|
|
|
|
static char *TransactionHandler(void *actionData, char *lastReply,
|
|
int commError)
|
|
{
|
|
pSctTransact st = (pSctTransact) actionData;
|
|
|
|
if (st->sent == 0) {
|
|
st->sent = 1;
|
|
if (st->controller->verbose) {
|
|
SCPrintf(st->con, eLog, "%6.3f send : %s", secondsOfMinute(), st->command);
|
|
}
|
|
if (st->controller->fd != NULL) {
|
|
fprintf(st->controller->fd, "%6.3f send : %s\n", secondsOfMinute(), st->command);
|
|
}
|
|
if(st->controller != NULL){
|
|
traceIO(st->controller->node->name, "transsend:%s", st->command);
|
|
} else {
|
|
traceIO("sctunknown", "transsend:%s", st->command);
|
|
}
|
|
return st->command;
|
|
} else {
|
|
st->sent = 2;
|
|
/*
|
|
if (st->controller->verbose) {
|
|
SCPrintf(st->con, eLog, "%6.3f reply : %s", secondsOfMinute(), lastReply);
|
|
}
|
|
if (st->controller->fd != NULL) {
|
|
fprintf(st->controller->fd,"%6.3f reply : %s\n", secondsOfMinute(), lastReply);
|
|
}
|
|
*/
|
|
/* printf("Transact: %s got %s\n", st->command, lastReply); */
|
|
if (lastReply == NULL) {
|
|
lastReply = "";
|
|
}
|
|
if(st->controller != NULL){
|
|
traceIO(st->controller->node->name, "transreply:%s", lastReply);
|
|
} else {
|
|
traceIO("sctunknown", "transreply:%s", lastReply);
|
|
}
|
|
if(lastReply != NULL){
|
|
st->reply = strdup(lastReply);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static char *SendHandler(void *actionData, char *lastReply,
|
|
int commError)
|
|
{
|
|
pSctTransact st = (pSctTransact) actionData;
|
|
char *result = TransactionHandler(actionData, lastReply, commError);
|
|
|
|
if (st->sent == 2) {
|
|
SetHdbProperty(st->controller->node, "reply", lastReply);
|
|
if (st->controller->verbose) {
|
|
SCPrintf(st->con, eLog, "%6.3f reply : %s", secondsOfMinute(), lastReply);
|
|
}
|
|
if (st->controller->fd != NULL) {
|
|
fprintf(st->controller->fd, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int SctTransactMatch(void *d1, void *d2)
|
|
{
|
|
return d1 == d2;
|
|
}
|
|
|
|
static int SctTransactCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
pSctTransact st = NULL;
|
|
SctController *c;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
|
|
if(nPar < 1 || par[0] == NULL){
|
|
SCWrite(con,"ERROR: no data to transact found", eError);
|
|
return 0;
|
|
}
|
|
|
|
st = calloc(sizeof(SctTransact), 1);
|
|
if (st == NULL) {
|
|
SCWrite(con, "ERROR: out of memory in SctTransactCommand", eError);
|
|
return 0;
|
|
}
|
|
st->con = SCCopyConnection(con);
|
|
st->command = strdup(par[0]->value.v.text);
|
|
st->controller = c;
|
|
st->sent = 0;
|
|
st->reply = NULL;
|
|
|
|
DevQueue(c->devser, st, WritePRIO,
|
|
TransactionHandler, SctTransactMatch, NULL, NULL);
|
|
while (st->sent != 2) {
|
|
TaskYield(pServ->pTasker);
|
|
/* not yet tested:
|
|
if (SCGetInterrupt(con) != eContinue) {
|
|
DevUnschedule(c->devser, st, TransactionHandler, SctTransactMatch);
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
if (st->reply != NULL) {
|
|
SCWrite(con,st->reply,eValue);
|
|
} else {
|
|
SCWrite(con,"ERROR: no reply!",eError);
|
|
}
|
|
KillSctTransact(st);
|
|
return 1;
|
|
}
|
|
|
|
static int SctSendCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
pSctTransact st = NULL;
|
|
SctController *c;
|
|
char *prioText = NULL;
|
|
int prio;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
|
|
st = calloc(sizeof(SctTransact), 1);
|
|
if (st == NULL) {
|
|
SCWrite(con, "ERROR: out of memory in SctSendCmd", eError);
|
|
return 0;
|
|
}
|
|
st->con = SCCopyConnection(con);
|
|
st->command = strdup(par[0]->value.v.text);
|
|
st->controller = c;
|
|
|
|
prioText = ParText(cmdNode, "prio", nPar, "write");
|
|
prio = DevText2Prio(prioText);
|
|
if (prio == NullPRIO) {
|
|
prio = WritePRIO;
|
|
}
|
|
|
|
SetHdbProperty(c->node, "reply", "");
|
|
DevQueue(c->devser, st, prio,
|
|
SendHandler, SctTransactMatch, KillSctTransact, NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int SctDisconnect(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
DevDisconnect(c->devser);
|
|
return 1;
|
|
}
|
|
|
|
static int SctReconnect(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *reconnectScript, *result;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
DevReconnect(c->devser, ParText(cmdNode, "hostport", nPar, ""));
|
|
reconnectScript = GetProp(c->node, c->node, "reconnect_script");
|
|
if (reconnectScript && reconnectScript[0] != '\0') {
|
|
if (SctCallInContext(con, reconnectScript, c->node, c, &result) == 0) {
|
|
SCPrintf(con, eError, "ERROR: %s", result);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctListActions(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *result;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
result = DevList(c->devser);
|
|
SCWrite(con, result, eValue);
|
|
return 1;
|
|
}
|
|
|
|
static int SctStatistics(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
double avg, max;
|
|
long errCount;
|
|
int errState, maxState, maxCount;
|
|
char state[16];
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
DevStatistics(c->devser,&avg, &max, &maxCount, &errCount, &errState);
|
|
if(errState == 1){
|
|
strcpy(state,"in error");
|
|
} else {
|
|
strcpy(state,"good");
|
|
}
|
|
SCPrintf(con,eValue,"Average roundtrip time: %lf, maximum roundtrip time %lf, count max roundtrip/2 %d, com error count: %ld, com state: %s",
|
|
avg, max, maxCount, errCount, state);
|
|
DevAsconStatistics(c->devser,&avg, &max, &maxState, &maxCount);
|
|
SCPrintf(con,eValue,"Average time in AsconTask: %lf, maximum time spent in AsconTask %lf, state of Ascon on max %d, count > max/2 %d",
|
|
avg, max, maxState, maxCount);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int SctHostport(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *result;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
result = DevHostport(c->devser);
|
|
SCWrite(con, result, eValue);
|
|
return 1;
|
|
}
|
|
|
|
static int SctTimeout(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *result;
|
|
hdbValue *v = &cmdNode->child->value;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
v->v.doubleValue = DevGetSetTimeout(c->devser, v->v.doubleValue, nPar);
|
|
SCPrintf(con, eValue, "%.6g", v->v.doubleValue);
|
|
return 1;
|
|
}
|
|
|
|
static int SctStatus(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *result;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
result = DevStatus(c->devser);
|
|
SCWrite(con, result, eValue);
|
|
return 1;
|
|
}
|
|
|
|
static int SctLog(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
SctController *c;
|
|
char *filename = NULL;
|
|
|
|
c = (SctController *) ccmd->pPrivate;
|
|
filename = par[0]->value.v.text;
|
|
if(strcmp(filename,"close") == 0 ){
|
|
if(c->fd != NULL){
|
|
fclose(c->fd);
|
|
c->fd = NULL;
|
|
}
|
|
} else {
|
|
if(c->fd != NULL){
|
|
fclose(c->fd);
|
|
}
|
|
c->fd = fopen(filename,"w");
|
|
if(c->fd == NULL){
|
|
SCPrintf(con,eError,"ERROR: failed to open %s for logging", filename);
|
|
return 0;
|
|
} else {
|
|
SCPrintf(con,eValue,"Logging to %s", filename);
|
|
}
|
|
}
|
|
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 SctDeferredFree(void *data)
|
|
{
|
|
free(data);
|
|
return;
|
|
}
|
|
|
|
static int SctDeferredTask(void *data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void SctKillController(void *c)
|
|
{
|
|
SctController *controller = c;
|
|
SConnection *con;
|
|
hdbPtrMessage m;
|
|
|
|
CleanStack(controller->node);
|
|
RemoveSICSInternalCallback(controller);
|
|
RemoveSICSInternalCallbackFrom(controller->node, controller);
|
|
|
|
if (controller->conn) {
|
|
SCDeleteConnection(controller->conn);
|
|
}
|
|
DevKill(controller->devser);
|
|
controller->devser = NULL;
|
|
|
|
if(controller->fd != NULL){
|
|
fclose(controller->fd);
|
|
controller->fd = NULL;
|
|
}
|
|
|
|
if (pServ->pTasker) {
|
|
TaskRegister(pServ->pTasker, SctDeferredTask, NULL, SctDeferredFree,
|
|
controller, 0);
|
|
} else {
|
|
free(controller);
|
|
}
|
|
}
|
|
|
|
static int SctMakeController(SConnection * con, SicsInterp * sics,
|
|
void *object, int argc, char *argv[])
|
|
{
|
|
SICSOBJ *ccmd;
|
|
Hdb *parent, *par, *cmd;
|
|
char *objName;
|
|
hdbCallback *cb;
|
|
SctController *controller;
|
|
|
|
if (argc < 2) {
|
|
SCPrintf(con, eError,
|
|
"ERROR: should be %s <path or objectname> <protocol args> ...",
|
|
argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (strchr(argv[1], '/') == NULL) {
|
|
/* object name only -> do not anchor in tree */
|
|
parent = NULL;
|
|
objName = argv[1];
|
|
} else {
|
|
/* full path given -> install into the hipadaba */
|
|
parent = FindHdbParent(NULL, argv[1], &objName, con);
|
|
if (parent == NULL)
|
|
return 0; /* error message already written */
|
|
}
|
|
|
|
controller = calloc(1, sizeof(*controller));
|
|
assert(controller);
|
|
controller->verbose = 0;
|
|
|
|
ccmd = MakeSICSOBJv(objName, "SctController", HIPNONE, usSpy);
|
|
controller->node = ccmd->objectNode;
|
|
controller->conn = SCCreateDummyConnection(pServ->pSics);
|
|
|
|
assert(ccmd);
|
|
assert(controller->node->mama == NULL);
|
|
ccmd->pPrivate = controller;
|
|
ccmd->KillPrivate = SctKillController;
|
|
|
|
if (parent != NULL) {
|
|
AddHipadabaChild(parent, controller->node, con);
|
|
}
|
|
|
|
controller->devser = DevMake(con, argc - 2, argv + 2);
|
|
if (!controller->devser)
|
|
return 0;
|
|
|
|
|
|
SetHdbProperty(controller->node, "controllerName", objName);
|
|
SetHdbProperty(controller->node, "sicsdev", objName);
|
|
|
|
AddCommand(pServ->pSics, objName, InterInvokeSICSOBJ, KillSICSOBJ, ccmd);
|
|
RegisterSICSOBJKillCmd(ccmd, objName);
|
|
SetDescriptorKey(ccmd->pDes, "creationCommand", "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,
|
|
"unpoll", usMugger, MakeSICSFunc(SctUnpollCmd));
|
|
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
|
|
AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("read"));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"connect", usMugger, MakeSICSFunc(SctConnectCmd));
|
|
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
|
|
|
|
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"));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"updatescript", usMugger, MakeSICSFunc(SctUpdatescriptCmd));
|
|
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
|
|
AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("updatescript"));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"killupdatescript", usMugger, MakeSICSFunc(SctKillUpdatescriptCmd));
|
|
AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText(""));
|
|
AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("updatescript"));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"log", usMugger, MakeSICSFunc(SctLog));
|
|
AddSICSHdbPar(cmd, "filename", usMugger, MakeHdbText(""));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"transact", usMugger, MakeSICSFunc(SctTransactCmd));
|
|
AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText(""));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"send", usMugger, MakeSICSFunc(SctSendCmd));
|
|
AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText(""));
|
|
AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText(""));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"disconnect", usMugger, MakeSICSFunc(SctDisconnect));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"reconnect", usMugger, MakeSICSFunc(SctReconnect));
|
|
AddSICSHdbPar(cmd, "hostport", usMugger, MakeHdbText(""));
|
|
|
|
par = AddSICSHdbPar(controller->node, "debug", usUser, MakeHdbInt(-1));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"actions", usUser, MakeSICSFunc(SctListActions));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"statistics", usSpy, MakeSICSFunc(SctStatistics));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"hostport", usSpy, MakeSICSFunc(SctHostport));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"status", usSpy, MakeSICSFunc(SctStatus));
|
|
|
|
cmd = AddSICSHdbPar(controller->node,
|
|
"timeout", usSpy, MakeSICSFunc(SctTimeout));
|
|
AddSICSHdbPar(cmd, "value", usUser, MakeHdbFloat(-1.0));
|
|
|
|
/* get the actual timeout value */
|
|
SctTimeout(ccmd, con, cmd, &par, 0);
|
|
|
|
cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL);
|
|
if (cb)
|
|
AppendHipadabaCallback(par, cb);
|
|
|
|
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");
|
|
AddCommand(pServ->pSics, "sct", SctCommand, SctKill, sct);
|
|
AddCmd("makesctcontroller", SctMakeController);
|
|
}
|
|
|
|
int SctVerbose(SctController * c)
|
|
{
|
|
return c->verbose;
|
|
}
|
|
|