- use dig for resolving host names - ascon.c: fix terminator parsing - property callback: change property before callback - logger.c:default for logger period must be the old value instead of 1 - add frappy type history writing - increase max. logreader line length - HIPNONE returns "null" with json protocol - encode strings properly in formatNameValue - fix memory leak in json2tcl - scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty - scriptcontext: remove args for action timestamp - scriptcontext: "que" function will replace an already queued action, e.g. for 'halt - introduced updatestatus script
345 lines
9.9 KiB
C
345 lines
9.9 KiB
C
/**
|
|
* This is an adapter to a node under the control of the new
|
|
* scriptcontext generic device model. This is a wrapper around
|
|
* such a node which implements the drivable interface.
|
|
*
|
|
* Some cooperation from the node is required: It has to provide
|
|
* certain properties the value of which define scripts which
|
|
* have to be called at various stages. These are:
|
|
*
|
|
* checklimits, for limits checking
|
|
* checkstatus, for evaluating progress
|
|
* halt , for halting things
|
|
*
|
|
* copyright: see file COPYRIGHT
|
|
*
|
|
* Mark Koennecke, June 2008
|
|
* --------------------------------------------------------------*/
|
|
#include <sics.h>
|
|
#include <sicshipadaba.h>
|
|
#include <devser.h>
|
|
#include <tcl.h>
|
|
#include <macro.h>
|
|
#include <sicsobj.h>
|
|
#include "scriptcontext.h"
|
|
/*---------------------------------------------------------------*/
|
|
typedef struct {
|
|
pObjectDescriptor pDes;
|
|
pIDrivable pDriv;
|
|
pHdb node;
|
|
SctController *c;
|
|
} SctDrive, *pSctDrive;
|
|
/*---------------------------------------------------------------*/
|
|
static void *SCTDRIVGetInterface(void *data, int iD)
|
|
{
|
|
pSctDrive self = NULL;
|
|
|
|
self = (pSctDrive) data;
|
|
if (self != NULL && iD == DRIVEID) {
|
|
if (self->node == NULL)
|
|
return NULL;
|
|
return self->pDriv;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
This routine can return either OKOK or HWFault when thing
|
|
go wrong. However, the return value of Halt is usually ignored!
|
|
------------------------------------------------------------------*/
|
|
static int SCTDRIVHalt(void *data)
|
|
{
|
|
pSctDrive self = NULL;
|
|
char dummy[16];
|
|
|
|
self = (pSctDrive) data;
|
|
if (GetHdbProperty(self->node, "halt", dummy, sizeof dummy)) {
|
|
SctQueueNode(self->c, self->node, HaltPRIO, "halt", 1, NULL);
|
|
} else if (GetHdbProperty(self->node, "status", dummy, sizeof dummy)) {
|
|
SetHdbProperty(self->node, "status", "idle");
|
|
}
|
|
return OKOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
This routine can return either 1 or 0. 1 means the position can
|
|
be reached, 0 NOT
|
|
If 0, error shall contain up to errlen characters of information
|
|
about which limit was violated
|
|
------------------------------------------------------------------*/
|
|
static int SCTDRIVCheckLimits(void *data, float val,
|
|
char *error, int errlen)
|
|
{
|
|
pSctDrive self = NULL;
|
|
char script[1024];
|
|
int status;
|
|
Tcl_Interp *pTcl = NULL;
|
|
char *result;
|
|
|
|
self = (pSctDrive) data;
|
|
snprintf(script, 1024, "%f", val);
|
|
SetHdbProperty(self->node, "target", script);
|
|
if (GetHdbProperty(self->node, "checklimits", script, 1024)) {
|
|
status = SctCallInContext(pServ->dummyCon, script,
|
|
self->node, self->c, &result);
|
|
if (SctDebugConn(self->c)) {
|
|
SCPf(SCPureSockWrite, SctDebugConn(self->c), eWarning,
|
|
"script %s called with result %s\n ", script, result);
|
|
}
|
|
if (status == 0) {
|
|
strlcpy(error, result, errlen);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
This routine can return 0 when a limit problem occurred
|
|
OKOK when the motor was successfully started
|
|
HWFault when a problem occured starting the device
|
|
Possible errors shall be printed to pCon
|
|
For real motors, this is supposed to try at least three times
|
|
to start the motor in question
|
|
val is the value to drive the motor too
|
|
------------------------------------------------------------------*/
|
|
static long SCTDRIVSetValue(void *data, SConnection * pCon, float val)
|
|
{
|
|
pSctDrive self = NULL;
|
|
int status;
|
|
hdbValue v;
|
|
|
|
self = (pSctDrive) data;
|
|
v.dataType = HIPFLOAT;
|
|
v.v.doubleValue = (double) val;
|
|
SetHdbProperty(self->node, "writestatus", "start");
|
|
status = SetHipadabaPar(self->node, v, pCon);
|
|
if (status == 1) {
|
|
return OKOK;
|
|
} else {
|
|
return HWFault;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
Checks the status of a running motor. Possible return values
|
|
HWBusy The motor is still running
|
|
OKOK or HWIdle when the motor finished driving
|
|
HWFault when a hardware problem ocurred
|
|
HWPosFault when the hardware cannot reach a position
|
|
Errors are duly to be printed to pCon
|
|
For real motors CheckStatus again shall try hard to fix any
|
|
issues with the motor
|
|
------------------------------------------------------------------*/
|
|
static int SCTDRIVCheckStatus(void *data, SConnection * pCon)
|
|
{
|
|
pSctDrive self = NULL;
|
|
char script[1024];
|
|
int status;
|
|
Tcl_Interp *pTcl = NULL;
|
|
char *result;
|
|
SConnection *con;
|
|
|
|
self = (pSctDrive) data;
|
|
|
|
/*
|
|
* check if the write command has gone through
|
|
*/
|
|
if (GetHdbProperty(self->node, "writestatus", script, 1024)) {
|
|
if (strcmp(script, "start") == 0) {
|
|
return HWBusy;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* run the checkstatus script
|
|
*/
|
|
if (!GetHdbProperty(self->node, "checkstatus", script, 1024)) {
|
|
if (!GetHdbProperty(self->node, "status", script, 1024)) {
|
|
SCWrite(pCon,
|
|
"ERROR: configuration problem: no checkstatus script!",
|
|
eError);
|
|
return HWFault;
|
|
}
|
|
result = script;
|
|
} else {
|
|
status = SctCallInContext(pCon, script, self->node, self->c, &result);
|
|
if (status == 0) {
|
|
SCPrintf(pCon, eError, " script %s returned %s", script, result);
|
|
return HWFault;
|
|
}
|
|
if (SctDebugConn(self->c)) {
|
|
SCPf(SCPureSockWrite, SctDebugConn(self->c), eError,
|
|
" script %s returned %s", script, result);
|
|
}
|
|
}
|
|
if (strstr(result, "run") != NULL) {
|
|
return HWBusy;
|
|
} else if (strstr(result, "posfault") != NULL) {
|
|
return HWPosFault;
|
|
} else if (strstr(result, "error") != NULL) {
|
|
return HWFault;
|
|
} else if (strstr(result, "idle") != NULL) {
|
|
return HWIdle;
|
|
} else {
|
|
SCPrintf(pCon, eError,
|
|
"ERROR: invalid status code %s returned from checkstatus script",
|
|
result);
|
|
return HWFault;
|
|
}
|
|
return HWFault;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
GetValue is supposed to read a motor position
|
|
On errors, -99999999.99 is returned and messages printed to pCon
|
|
------------------------------------------------------------------*/
|
|
static float SCTDRIVGetValue(void *data, SConnection * pCon)
|
|
{
|
|
pSctDrive self = NULL;
|
|
float val = -99999999.99;
|
|
int status;
|
|
char error[256];
|
|
hdbValue v;
|
|
|
|
self = (pSctDrive) data;
|
|
if (GetHdbProperty(self->node, "geterror", error, 256)) {
|
|
SCWrite(pCon, error, eError);
|
|
return val;
|
|
}
|
|
return (float) self->node->value.v.doubleValue;
|
|
}
|
|
|
|
/*----------------------------------------------------------------
|
|
returns NULL on failure, a new datastructure else
|
|
------------------------------------------------------------------*/
|
|
static pSctDrive SCTDRIVMakeObject()
|
|
{
|
|
pSctDrive self = NULL;
|
|
|
|
self = calloc(sizeof(SctDrive), 1);
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
self->pDes = CreateDescriptor("SctDriveAdapter");
|
|
self->pDriv = CreateDrivableInterface();
|
|
if (self->pDes == NULL || self->pDriv == NULL) {
|
|
free(self);
|
|
return NULL;
|
|
}
|
|
|
|
self->pDes->GetInterface = SCTDRIVGetInterface;
|
|
self->pDriv->Halt = SCTDRIVHalt;
|
|
self->pDriv->CheckLimits = SCTDRIVCheckLimits;
|
|
self->pDriv->SetValue = SCTDRIVSetValue;
|
|
self->pDriv->CheckStatus = SCTDRIVCheckStatus;
|
|
self->pDriv->GetValue = SCTDRIVGetValue;
|
|
|
|
return self;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
static int SctDriveCommand(SConnection * pCon, SicsInterp * sics,
|
|
void *object, int argc, char *argv[])
|
|
{
|
|
pSctDrive self = (pSctDrive) object;
|
|
float val;
|
|
|
|
assert(self != NULL);
|
|
|
|
if (self->node == NULL) {
|
|
SCWrite(pCon, "ERROR: defunct object", eError);
|
|
return 0;
|
|
}
|
|
/*
|
|
* only action: print value
|
|
*/
|
|
val = self->pDriv->GetValue(self, pCon);
|
|
SCPrintf(pCon, eValue, "%s = %f", argv[0], val);
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
static void SctDriveKill(void *data)
|
|
{
|
|
pSctDrive self = (pSctDrive) data;
|
|
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
if (self->pDriv != NULL) {
|
|
free(self->pDriv);
|
|
}
|
|
if (self->pDes != NULL) {
|
|
DeleteDescriptor(self->pDes);
|
|
}
|
|
free(self);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
static hdbCallbackReturn SctDummyCallback(Hdb * node, void *userData,
|
|
hdbMessage * msg)
|
|
{
|
|
return hdbContinue;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
static void SctDriveDeleteNode(void *data)
|
|
{
|
|
pSctDrive self = (pSctDrive) data;
|
|
self->node = NULL;
|
|
}
|
|
|
|
/*---------------------------------------------------------------*/
|
|
int SctMakeDriveAdapter(SConnection * pCon, SicsInterp * pSics,
|
|
void *object, int argc, char *argv[])
|
|
{
|
|
pSctDrive pNew = NULL;
|
|
pSICSOBJ obj = NULL;
|
|
hdbCallback *cb;
|
|
|
|
pNew = SCTDRIVMakeObject();
|
|
if (pNew == NULL) {
|
|
SCWrite(pCon, "ERROR: out of memory in SctMakeDriveAdapter", eError);
|
|
return 0;
|
|
}
|
|
|
|
if (argc < 4) {
|
|
SCPrintf(pCon, eError,
|
|
"ERROR: should be %s <object name> <existing-node> <controller>",
|
|
argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
pNew->node = FindHdbNode(NULL, argv[2], pCon);
|
|
obj = FindCommandData(pSics, argv[3], "SctController");
|
|
if (pNew->node == NULL || obj == NULL) {
|
|
SCWrite(pCon, "ERROR: node or controller not found", eError);
|
|
SctDriveKill(pNew);
|
|
return 0;
|
|
}
|
|
pNew->c = (SctController *) obj->pPrivate;
|
|
|
|
if (strcasecmp(argv[0], "dynsctdrive") == 0) {
|
|
/* make object dynamic by defining a descriptor command */
|
|
SetDescriptorKey(pNew->pDes, "creationCommand", "0");
|
|
}
|
|
AddCommand(pSics, argv[1], SctDriveCommand, SctDriveKill, pNew);
|
|
SetHdbProperty(pNew->node, "sicsdev", argv[1]);
|
|
|
|
cb = MakeHipadabaCallback(SctDummyCallback, pNew, SctDriveDeleteNode);
|
|
assert(cb);
|
|
AppendHipadabaCallback(pNew->node, cb);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------*/
|
|
void SctDriveAdapterInit(void)
|
|
{
|
|
AddCmd("makesctdrive", SctMakeDriveAdapter);
|
|
AddCmd("dynsctdrive", SctMakeDriveAdapter);
|
|
}
|