Files
sics/sctdriveadapter.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

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);
}