Files
sics/sctdriveobj.c
zolliker 61341b52f4 various improvements
- 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
2021-09-16 12:26:18 +02:00

425 lines
13 KiB
C

/**
* This is a base class for any drivable object which uses the new
* scriptcontext to control the actual driving operation. This can also
* serve as a drivable adapter to nodes in other objects.
*
* 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
*
* If no checklimits script is given, nothing is checked.
* If no checkstatus scripts is given, the value of the status property is
* returned. In that case the status value should be updated by some poll script.
* If no halt script is given, the status is set to idle. M.Z. Sept. 08
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, June-July 2008
*/
#include <sics.h>
#include <tcl.h>
#include <sicsobj.h>
#include <scriptcontext.h>
#include <sicshipadaba.h>
typedef struct {
pIDrivable pDriv;
SctController *c;
int doNotKillNode;
SConnection *pCon;
} DrivObjPriv, *pDrivObjPriv;
/*--------------------------------------------------------------*/
static void KillDrivePriv(void *data)
{
pDrivObjPriv priv = (pDrivObjPriv) data;
if (priv == NULL) {
return;
}
if (priv->pDriv != NULL) {
free(priv->pDriv);
}
if (priv->pCon != NULL) {
SCDeleteConnection(priv->pCon);
}
free(priv);
}
/*---------------------------------------------------------------*/
static void *SCTDRIVGetInterface(void *data, int iD)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
if (self != NULL && pPriv != NULL && iD == DRIVEID) {
if (self->objectNode == NULL)
return NULL;
return pPriv->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)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
char dummy[16];
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
if (GetHdbProperty(self->objectNode, "halt", dummy, sizeof dummy)) {
SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", 1,
pPriv->pCon);
} else
if (GetHdbProperty(self->objectNode, "status", dummy, sizeof dummy))
{
SetHdbProperty(self->objectNode, "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)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
char script[1024];
int status;
Tcl_Interp *pTcl = NULL;
char *result;
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
snprintf(script, 1024, "%g", val);
/* set target. This not yet done, as SetValue is called later */
SetHdbProperty(self->objectNode, "target", script);
if (GetHdbProperty(self->objectNode, "checklimits", script, 1024)) {
status = SctCallInContext(pServ->dummyCon, script,
self->objectNode, pPriv->c, &result);
if (SctDebugConn(pPriv->c)) {
SCPf(SCPureSockWrite, SctDebugConn(pPriv->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)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
int status;
hdbValue v;
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
if (pPriv->pCon != NULL) {
SCDeleteConnection(pPriv->pCon);
}
pPriv->pCon = SCCopyConnection(pCon);
v = MakeHdbFloat(val);
SetHdbProperty(self->objectNode, "writestatus", "start");
status = SetHipadabaPar(self->objectNode, 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 duely 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)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
char script[1024];
int status;
Tcl_Interp *pTcl = NULL;
char *result;
SConnection *con;
int ret;
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
/*
* check if the write command has gone through
*/
if (GetHdbProperty(self->objectNode, "writestatus", script, 1024)) {
if (strcmp(script, "start") == 0) {
ret = HWBusy;
goto Return;
}
}
/*
* run the checkstatus script
*/
if (!GetHdbProperty(self->objectNode, "checkstatus", script, 1024)) {
if (!GetHdbProperty(self->objectNode, "status", script, 1024)) {
SCWrite(pCon,
"ERROR: configuration problem: no checkstatus script!",
eError);
ret = HWFault;
result = "error";
goto Return;
}
result = script;
} else {
status = SctCallInContext(pCon, script, self->objectNode,
pPriv->c, &result);
if (status == 0) {
SCPrintf(pCon, eError, " script %s returned %s", script, result);
ret = HWFault;
result = "error";
goto Return;
}
if (SctDebugConn(pPriv->c)) {
SCPf(SCPureSockWrite, SctDebugConn(pPriv->c), eError,
" script %s returned %s", script, result);
}
}
if (strstr(result, "run") != NULL) {
ret = HWBusy;
} else if (strstr(result, "posfault") != NULL) {
ret = HWPosFault;
} else if (strstr(result, "error") != NULL) {
ret = HWFault;
} else if (strstr(result, "idle") != NULL) {
ret = HWIdle;
} else {
SCPrintf(pCon, eError,
"ERROR: invalid status code %s returned from checkstatus script",
result);
ret = HWFault;
}
Return:
if (GetHdbProperty(self->objectNode, "updatestatus", script, 1024)) {
status = SctCallInContext(pCon, script, self->objectNode,
pPriv->c, &result);
if (status == 0) {
SCPrintf(pCon, eError, " %s returned %s", script, result);
}
}
return ret;
}
/*----------------------------------------------------------------
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)
{
pSICSOBJ self = NULL;
pDrivObjPriv pPriv;
float val = -99999999.99;
int status;
char error[256];
hdbValue v;
self = (pSICSOBJ) data;
pPriv = (pDrivObjPriv) self->pPrivate;
if (GetHdbProperty(self->objectNode, "geterror", error, 256)) {
SCWrite(pCon, error, eError);
return val;
}
return (float) self->objectNode->value.v.doubleValue;
}
/*-----------------------------------------------------------------*/
void AssignSctDrive(pIDrivable pDriv)
{
pDriv->Halt = SCTDRIVHalt;
pDriv->CheckLimits = SCTDRIVCheckLimits;
pDriv->SetValue = SCTDRIVSetValue;
pDriv->CheckStatus = SCTDRIVCheckStatus;
pDriv->GetValue = SCTDRIVGetValue;
}
/*---------------------------------------------------------------------------*/
static void KillDriveOBJ(void *data)
{
pSICSOBJ self = (pSICSOBJ) data;
pDrivObjPriv pPriv;
if (self == NULL) {
return;
}
pPriv = (pDrivObjPriv) self->pPrivate;
if (pPriv->doNotKillNode && GetDescriptorKey(self->pDes, "creationCommand") == NULL /* it's not a dynamic object */
&& self->pDes != NULL) {
self->objectNode = NULL; /* do not call RemoveHdbNodeFromParent in KillSICSOBJ */
self->pDes->parNode = NULL; /* do not kill the node in KillSICSOBJ/DeleteDescriptor */
}
KillSICSOBJ(self);
}
/*-----------------------------------------------------------------------------*/
pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c,
int doNotKillNode)
{
pSICSOBJ pNew = NULL;
pDrivObjPriv pPriv = NULL;
hdbValue val;
pNew = (pSICSOBJ) malloc(sizeof(SICSOBJ));
pPriv = (pDrivObjPriv) malloc(sizeof(DrivObjPriv));
if (pNew == NULL || pPriv == NULL) {
return NULL;
}
memset(pNew, 0, sizeof(SICSOBJ));
memset(pPriv, 0, sizeof(DrivObjPriv));
pNew->pDes = CreateDescriptor(class);
pNew->pDes->SaveStatus = SaveSICSOBJ;
pPriv->pDriv = CreateDrivableInterface();
if (pNew->pDes == NULL || pPriv->pDriv == NULL) {
free(pNew);
free(pPriv);
return (NULL);
}
pNew->objectNode = node;
AssignSctDrive(pPriv->pDriv);
pPriv->c = c;
pPriv->doNotKillNode = doNotKillNode;
pNew->pDes->parNode = pNew->objectNode;
pNew->pDes->GetInterface = SCTDRIVGetInterface;
pNew->pPrivate = pPriv;
pNew->KillPrivate = KillDrivePriv;
return pNew;
}
/*--------------------------------------------------------------------------
* This actually has two syntaxes:
* makesctdrive name path-to-existing-node class SctController
* makesctdrive name float priv class SctController
*--------------------------------------------------------------------------*/
int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics,
void *object, int argc, char *argv[])
{
pHdb node = NULL;
pDrivObjPriv pPriv = NULL;
pSICSOBJ pNew = NULL;
pSICSOBJ pSct = NULL;
SctController *c = NULL;
int priv, type, status, doNotKillNode;
hdbValue val;
char *sctName;
char *class;
if (argc < 5)
goto Usage;
if (argc == 5) {
node = FindHdbNode(NULL, argv[2], pCon);
if (node == NULL) {
SCPrintf(pCon, eError, "ERROR: node %s not found", argv[2]);
goto Usage;
}
sctName = argv[4];
class = argv[3];
doNotKillNode = 1; /* the node is owned by someone else */
} else {
/* convert datatype */
type = convertHdbType(argv[2]);
/* convert privilege */
priv = decodeSICSPriv(argv[3]);
if (priv < 0 || type != HIPFLOAT)
goto Usage;
node = MakeSICSHdbPar(argv[1], priv, MakeHdbFloat(0.0));
if (node == NULL) {
SCWrite(pCon, "ERROR: node creation failed", eError);
return 0;
}
SetHdbProperty(node, "sicscommand", argv[1]);
sctName = argv[5];
class = argv[4];
doNotKillNode = 0; /* kill the node with the command */
}
pSct = FindCommandData(pSics, sctName, "SctController");
if (pSct == NULL) {
SCPrintf(pCon, eError, "ERROR: SctController %s not found", sctName);
goto Usage;
}
c = (SctController *) pSct->pPrivate;
pNew = MakeSctDriveObj(node, class, c, doNotKillNode);
SetHdbProperty(node, "objectName", argv[1]);
if (pNew == NULL) {
SCWrite(pCon, "ERROR: failed to create drive object", eError);
return 0;
}
if (strcasecmp(argv[0], "dynsctdriveobj") == 0) {
/* make object dynamic by defining a creation command */
SetDescriptorKey(pNew->pDes, "creationCommand", "0");
RegisterSICSOBJKillCmd(pNew, argv[1]);
}
SetHdbProperty(pNew->objectNode, "sicsdev", argv[1]);
SetHdbProperty(pNew->objectNode, "type", "drivable");
status = AddCommand(pSics,
argv[1], InterInvokeSICSOBJ, KillDriveOBJ, pNew);
if (status != 1) {
KillSICSOBJ(pNew);
SCPrintf(pCon, eError, "ERROR: failed create duplicate command %s",
argv[1]);
return 0;
}
return 1;
Usage:
SCPrintf(pCon, eError,
"ERROR: should be %s <object name> <existing node> <class> <controller>",
argv[0]);
SCPrintf(pCon, eError,
"ERROR: or %s <object name> <type> <priv> <class> <controller>",
argv[0]);
return 0;
}
/*---------------------------------------------------------------*/
void SctDriveObjInit(void)
{
AddCmd("makesctdriveobj", SctMakeDriveObject);
AddCmd("dynsctdriveobj", SctMakeDriveObject);
}