957 lines
26 KiB
C
957 lines
26 KiB
C
/*---------------------------------------------------------------------------
|
|
SICS object for driving a couple of motors along a tabulated given path.
|
|
|
|
copyright: see file COPYRIGHT
|
|
|
|
Mark Koennecke, July 2005
|
|
---------------------------------------------------------------------------*/
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <lld.h>
|
|
#include "tabledrive.h"
|
|
/*--------------------------------------------------------------------------*/
|
|
#define OUTOFSYNC 100
|
|
#define STARTING 101
|
|
#define STEPPING 102
|
|
#define WAITING 103
|
|
#define WAITFINISH 104
|
|
#define IDLE 105
|
|
|
|
#define ABS(x) (x < 0 ? -(x) : (x))
|
|
#define SIGN(x) (x < .0 ? (-1) : (1))
|
|
extern char *trim(char *txt);
|
|
/*
|
|
* copied from motor.c!!
|
|
*/
|
|
#define SLOW 0
|
|
#define SUPP 1
|
|
|
|
/*=================== the drivable interface functions =====================*/
|
|
static int TableDriveHalt(void *pData)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
int status;
|
|
tdMotor moti;
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
return 0;
|
|
}
|
|
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
if (moti.pMot != NULL) {
|
|
moti.pMot->pDrivInt->Halt(moti.pMot);
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
self->state = OUTOFSYNC;
|
|
return 1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int TableDriveCheckLimits(void *pData, float fVal, char *error,
|
|
int iErrLen)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
tdMotor moti;
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
strlcpy(error, "Path Table Not Defined!", 25);
|
|
return 0;
|
|
}
|
|
if (fVal < 1. || fVal > self->tableLength) {
|
|
strlcpy(error, "Out of Range", 25);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static long TableDriveSetValue(void *pData, SConnection * pCon, float fVal)
|
|
{
|
|
|
|
pTableDrive self = (pTableDrive) pData;
|
|
char pError[132];
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
SCWrite(pCon, "ERROR: no path defined", eError);
|
|
return HWFault;
|
|
}
|
|
strcpy(pError, "ERROR:");
|
|
if (!TableDriveCheckLimits(self, fVal, &pError[6], 120)) {
|
|
SCWrite(pCon, pError, eError);
|
|
return HWFault;
|
|
}
|
|
self->state = STARTING;
|
|
self->targetPosition = fVal;
|
|
return OKOK;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int findOrientingMotor(pTableDrive self, ptdMotor moti)
|
|
{
|
|
int status;
|
|
|
|
if (!self->oriInvalid) {
|
|
*moti = self->oriMotor;
|
|
return 1;
|
|
}
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, moti);
|
|
if (strcmp(moti->motorName, self->orientMotor) == 0) {
|
|
self->oriMotor = *moti;
|
|
self->oriInvalid = 0;
|
|
return 1;
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static float locatePosition(tdMotor moti, SConnection * pCon)
|
|
{
|
|
int status, begin = 1;
|
|
float value;
|
|
double diff;
|
|
tdEntry lower, upper;
|
|
|
|
status = MotorGetSoftPosition(moti.pMot, pCon, &value);
|
|
if (!status) {
|
|
return -9999.99;
|
|
}
|
|
status = LLDnodePtr2First(moti.table);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(moti.table, &lower);
|
|
status = LLDnodePtr2Next(moti.table);
|
|
if (status != 0) {
|
|
LLDnodeDataTo(moti.table, &upper);
|
|
} else {
|
|
/*
|
|
* end of table
|
|
*/
|
|
return lower.tablePos;
|
|
}
|
|
/**
|
|
* we have to deal with ascending and descending tables. This is
|
|
* why we have to have some funny logic here
|
|
*/
|
|
if (upper.position > lower.position) {
|
|
/*
|
|
* ascending table
|
|
*/
|
|
if (value >= lower.position && value < upper.position) {
|
|
diff = upper.position - lower.position;
|
|
return lower.tablePos + (value - lower.position) / diff;
|
|
}
|
|
if (begin == 1 && value < lower.position) {
|
|
return lower.tablePos;
|
|
}
|
|
} else {
|
|
/*
|
|
* descending table
|
|
*/
|
|
if (value <= lower.position && value > upper.position) {
|
|
diff = lower.position - upper.position;
|
|
return lower.tablePos + ABS(value - lower.position) / diff;
|
|
}
|
|
if (begin == 1 && value > lower.position) {
|
|
return lower.tablePos;
|
|
}
|
|
}
|
|
begin = 0;
|
|
}
|
|
return -999.99;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void findBracket(tdMotor moti, float value, ptdEntry upper,
|
|
ptdEntry lower)
|
|
{
|
|
int status, targetCount;
|
|
float diff;
|
|
|
|
targetCount = (int) floor(value);
|
|
status = LLDnodePtr2First(moti.table);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(moti.table, lower);
|
|
if (lower->tablePos == targetCount) {
|
|
LLDnodeDataTo(moti.table, upper);
|
|
if (LLDnodePtr2Next(moti.table)) {
|
|
LLDnodeDataTo(moti.table, upper);
|
|
}
|
|
return;
|
|
}
|
|
status = LLDnodePtr2Next(moti.table);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static float findTarget(tdMotor moti, float value)
|
|
{
|
|
int targetCount;
|
|
tdEntry lower, upper;
|
|
float diff;
|
|
|
|
targetCount = (int) floor(value);
|
|
findBracket(moti, value, &upper, &lower);
|
|
if (ABS(targetCount - value) < .00001) {
|
|
return lower.position;
|
|
} else {
|
|
diff = upper.position - lower.position;
|
|
return (float) lower.position + (value - targetCount) * diff;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void checkSync(pTableDrive self, SConnection * pCon, float value)
|
|
{
|
|
int status, test;
|
|
float motorPosition, targetPosition, tolerance, diff;
|
|
tdMotor moti;
|
|
char pBueffel[256];
|
|
|
|
if (self->state != IDLE) {
|
|
return;
|
|
}
|
|
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
test = MotorGetSoftPosition(moti.pMot, pCon, &motorPosition);
|
|
if (!test) {
|
|
snprintf(pBueffel, 255, "ERROR: failed to read motor %s",
|
|
moti.motorName);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
} else {
|
|
MotorGetPar(moti.pMot, "precision", &tolerance);
|
|
targetPosition = findTarget(moti, value);
|
|
if (ABS(targetPosition - motorPosition) > tolerance) {
|
|
snprintf(pBueffel, 256,
|
|
"WARNING: motor %s out of sync by %f",
|
|
moti.motorName, ABS(targetPosition - motorPosition));
|
|
SCWrite(pCon, pBueffel, eWarning);
|
|
}
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static float TableDriveGetValue(void *pData, SConnection * pCon)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
tdMotor orient;
|
|
float value;
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
SCWrite(pCon, "ERROR: no path defined", eError);
|
|
return -9999.99;
|
|
}
|
|
if (!findOrientingMotor(self, &orient)) {
|
|
SCWrite(pCon, "ERROR: table corrupted or orienting motor not found",
|
|
eError);
|
|
return -9999.99;
|
|
}
|
|
value = locatePosition(orient, pCon);
|
|
checkSync(self, pCon, value);
|
|
|
|
self->currentPosition = value;
|
|
return value;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void tableInfo(pTableDrive self, SConnection * pCon)
|
|
{
|
|
int status, test;
|
|
float motorPosition, targetPosition, tolerance, diff, value;
|
|
tdMotor moti;
|
|
char pBueffel[256];
|
|
|
|
value = TableDriveGetValue(self, pCon);
|
|
snprintf(pBueffel, 255, " Triffid Position: %8.2f", value);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
test = MotorGetSoftPosition(moti.pMot, pCon, &motorPosition);
|
|
if (!test) {
|
|
snprintf(pBueffel, 255, "ERROR: failed to read motor %s",
|
|
moti.motorName);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
} else {
|
|
MotorGetPar(moti.pMot, "precision", &tolerance);
|
|
targetPosition = findTarget(moti, value);
|
|
snprintf(pBueffel, 256,
|
|
"Motor %10s, should: %8.2f, is %8.2f, diff = %8.2f",
|
|
moti.motorName, targetPosition, motorPosition,
|
|
ABS(targetPosition - motorPosition));
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static void tableSetPar(pMotor pMot, char *name, float value)
|
|
{
|
|
|
|
MotorSetPar(pMot,pServ->dummyCon,name,value);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------
|
|
* Before I can drive the motors, I have to release the software limits..
|
|
-------------------------------------------------------------------------*/
|
|
static void liberateMotors(pTableDrive self, SConnection * pCon)
|
|
{
|
|
int status;
|
|
tdMotor moti;
|
|
float upper, lower;
|
|
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
MotorGetPar(moti.pMot, "hardupperlim", &upper);
|
|
MotorGetPar(moti.pMot, "hardlowerlim", &lower);
|
|
|
|
tableSetPar(moti.pMot, "softupperlim", upper);
|
|
tableSetPar(moti.pMot, "softlowerlim", lower);
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------
|
|
* after driving the motors, we have to set the software limits in order
|
|
* to prevent the user from manually crashing components
|
|
------------------------------------------------------------------------ */
|
|
static void closeMotors(pTableDrive self, SConnection * pCon)
|
|
{
|
|
int status;
|
|
tdMotor moti;
|
|
tdEntry tdLower, tdUpper;
|
|
float upper, lower, diff, targetCount;
|
|
|
|
targetCount = floor(self->currentPosition);
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
findBracket(moti, self->currentPosition, &tdUpper, &tdLower);
|
|
diff = tdUpper.upper - tdLower.upper;
|
|
upper = tdLower.upper + (self->currentPosition - targetCount) * diff;
|
|
diff = tdUpper.lower - tdLower.lower;
|
|
lower = tdLower.lower + (self->currentPosition - targetCount) * diff;
|
|
tableSetPar(moti.pMot, "softupperlim", upper);
|
|
tableSetPar(moti.pMot, "softlowerlim", lower);
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static float calculateNextStep(pTableDrive self)
|
|
{
|
|
float step;
|
|
|
|
step = self->targetPosition - self->currentPosition;
|
|
if (ABS(step) > 1.) {
|
|
step = SIGN(step) * 1.0;
|
|
}
|
|
return step;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static int runToNextStep(pTableDrive self, SConnection * pCon, float value)
|
|
{
|
|
int status;
|
|
tdMotor moti;
|
|
float target;
|
|
long motID;
|
|
|
|
self->groupID = GetTaskGroupID(pServ->pTasker);
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
target = findTarget(moti, value);
|
|
motID = StartDriveTask(moti.pMot,pCon,moti.motorName,target);
|
|
if(motID < 0){
|
|
return HWFault;
|
|
}
|
|
AddTaskToGroup(pServ->pTasker,motID,self->groupID);
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static int checkRunning(pTableDrive self, SConnection * pCon)
|
|
{
|
|
int status;
|
|
int test;
|
|
tdMotor moti;
|
|
|
|
if(isTaskGroupRunning(pServ->pTasker,self->groupID)){
|
|
return HWBusy;
|
|
}
|
|
return HWIdle;
|
|
}
|
|
/*------------------------------------------------------------------------*/
|
|
static void showPositions(pTableDrive self, SConnection * pCon,
|
|
char *pBueffel, int buffLen)
|
|
{
|
|
float value;
|
|
int status, test, len;
|
|
tdMotor moti;
|
|
|
|
value = TableDriveGetValue(self, pCon);
|
|
snprintf(pBueffel, buffLen, " %8.2f : ", value);
|
|
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
test = MotorGetSoftPosition(moti.pMot, pCon, &value);
|
|
len = strlen(pBueffel);
|
|
snprintf(&pBueffel[len], buffLen - len,
|
|
"%s = %8.2f ", moti.motorName, value);
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* This is the old scheme: drive table positions tablestep by tablestep.
|
|
* I leave this code here: may be it is useful in another scenario then
|
|
* MARS. Then some means must be found to select the table drive
|
|
* algorithm.
|
|
* -----------------------------------------------------------------------*/
|
|
static int TableDriveCheckStatusOld(void *pData, SConnection * pCon)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
int status;
|
|
float step;
|
|
char pBueffel[1024];
|
|
|
|
switch (self->state) {
|
|
case STARTING:
|
|
/*
|
|
* make sure that our current position is up-to-date
|
|
*/
|
|
TableDriveGetValue(self, pCon);
|
|
liberateMotors(self, pCon);
|
|
self->state = STEPPING;
|
|
return HWBusy;
|
|
break;
|
|
case STEPPING:
|
|
step = calculateNextStep(self);
|
|
status = runToNextStep(self, pCon, self->currentPosition + step);
|
|
if (status == OKOK) {
|
|
if (ABS(step) < 1.) {
|
|
self->state = WAITFINISH;
|
|
} else {
|
|
self->state = WAITING;
|
|
}
|
|
return HWBusy;
|
|
} else {
|
|
TableDriveHalt(self);
|
|
return HWFault;
|
|
}
|
|
break;
|
|
case WAITING:
|
|
status = checkRunning(self, pCon);
|
|
switch (status) {
|
|
case HWIdle:
|
|
case OKOK:
|
|
self->state = STEPPING;
|
|
if (self->debug > 0) {
|
|
showPositions(self, pCon, pBueffel, 1023);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
} else {
|
|
self->currentPosition += calculateNextStep(self);
|
|
}
|
|
return HWBusy;
|
|
break;
|
|
case HWBusy:
|
|
return HWBusy;
|
|
break;
|
|
default:
|
|
TableDriveHalt(self);
|
|
self->state = WAITFINISH;
|
|
return HWBusy;
|
|
break;
|
|
}
|
|
case WAITFINISH:
|
|
status = checkRunning(self, pCon);
|
|
if (status != HWBusy) {
|
|
TableDriveGetValue(self, pCon);
|
|
closeMotors(self, pCon);
|
|
self->state = IDLE;
|
|
return status;
|
|
} else {
|
|
return HWBusy;
|
|
}
|
|
break;
|
|
case OUTOFSYNC:
|
|
SCWrite(pCon, "WARNING: tabledrive out of sync", eWarning);
|
|
self->state = IDLE;
|
|
return HWFault;
|
|
break;
|
|
default:
|
|
SCWrite(pCon,
|
|
"ERROR: programming error in tabledrive, invalid state",
|
|
eError);
|
|
return HWFault;
|
|
break;
|
|
}
|
|
return HWFault;
|
|
}
|
|
|
|
/*===========================================================================
|
|
* This is code for the new scheme of driving MARS tables:
|
|
* 1) drive ds and as to 0
|
|
* 2) drive all other motors to targets directly
|
|
* 3) drive ds and as to desired positions.
|
|
* =========================================================================*/
|
|
#define WAITDSAS 110
|
|
#define WAITOTHER 111
|
|
#define ASDSTO0 200
|
|
#define ASDSTOTARGET 201
|
|
/*-------------------------------------------------------------------------*/
|
|
static int startASDS(pTableDrive self, SConnection * pCon, int target)
|
|
{
|
|
tdMotor moti;
|
|
int status;
|
|
char name[132];
|
|
float targetValue;
|
|
long motID;
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
return 0;
|
|
}
|
|
|
|
self->groupID = GetTaskGroupID(pServ->pTasker);
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
strlcpy(name, moti.motorName, 131);
|
|
strtolower(name);
|
|
if (strstr(name, "ds") != NULL || strstr(name, "as") != NULL) {
|
|
if (target == ASDSTO0) {
|
|
targetValue = 0.0;
|
|
} else {
|
|
targetValue = findTarget(moti, self->targetPosition);
|
|
}
|
|
motID = StartDriveTask(moti.pMot,pCon,moti.motorName,targetValue);
|
|
if(motID < 0){
|
|
return HWFault;
|
|
}
|
|
AddTaskToGroup(pServ->pTasker,motID,self->groupID);
|
|
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
return OKOK;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int startOthers(pTableDrive self, SConnection * pCon)
|
|
{
|
|
tdMotor moti;
|
|
int status;
|
|
char name[132];
|
|
float targetValue;
|
|
long motID;
|
|
|
|
if (self == NULL || self->motorTable < 0) {
|
|
return 0;
|
|
}
|
|
|
|
self->groupID = GetTaskGroupID(pServ->pTasker);
|
|
status = LLDnodePtr2First(self->motorTable);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(self->motorTable, &moti);
|
|
strlcpy(name, moti.motorName, 131);
|
|
strtolower(name);
|
|
if (strstr(name, "ds") == NULL && strstr(name, "as") == NULL) {
|
|
targetValue = findTarget(moti, self->targetPosition);
|
|
motID = StartDriveTask(moti.pMot,pCon,moti.motorName,targetValue);
|
|
if(motID < 0){
|
|
return HWFault;
|
|
}
|
|
AddTaskToGroup(pServ->pTasker,motID,self->groupID);
|
|
}
|
|
status = LLDnodePtr2Next(self->motorTable);
|
|
}
|
|
return OKOK;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int TableDriveCheckStatus(void *pData, SConnection * pCon)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
int status;
|
|
float step;
|
|
char pBueffel[1024];
|
|
|
|
switch (self->state) {
|
|
case STARTING:
|
|
/*
|
|
* make sure that our current position is up-to-date
|
|
*/
|
|
TableDriveGetValue(self, pCon);
|
|
liberateMotors(self, pCon);
|
|
status = startASDS(self, pCon, ASDSTO0);
|
|
if (status != OKOK) {
|
|
TableDriveHalt(self);
|
|
return HWFault;
|
|
}
|
|
self->state = WAITDSAS;
|
|
return HWBusy;
|
|
break;
|
|
case WAITDSAS:
|
|
status = checkRunning(self, pCon);
|
|
switch (status) {
|
|
case HWIdle:
|
|
case OKOK:
|
|
if (self->debug > 0) {
|
|
showPositions(self, pCon, pBueffel, 1023);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
}
|
|
status = startOthers(self, pCon);
|
|
if (status != OKOK) {
|
|
TableDriveHalt(self);
|
|
return HWFault;
|
|
}
|
|
self->state = WAITOTHER;
|
|
return HWBusy;
|
|
break;
|
|
case HWBusy:
|
|
return HWBusy;
|
|
break;
|
|
default:
|
|
TableDriveHalt(self);
|
|
self->state = WAITFINISH;
|
|
return HWBusy;
|
|
break;
|
|
}
|
|
break;
|
|
case WAITOTHER:
|
|
status = checkRunning(self, pCon);
|
|
switch (status) {
|
|
case HWIdle:
|
|
case OKOK:
|
|
if (self->debug > 0) {
|
|
showPositions(self, pCon, pBueffel, 1023);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
}
|
|
status = startASDS(self, pCon, ASDSTOTARGET);
|
|
if (status != OKOK) {
|
|
TableDriveHalt(self);
|
|
return HWFault;
|
|
}
|
|
self->state = WAITFINISH;
|
|
return HWBusy;
|
|
break;
|
|
case HWBusy:
|
|
return HWBusy;
|
|
break;
|
|
default:
|
|
TableDriveHalt(self);
|
|
self->state = WAITFINISH;
|
|
return HWBusy;
|
|
break;
|
|
}
|
|
break;
|
|
case WAITFINISH:
|
|
status = checkRunning(self, pCon);
|
|
if (status != HWBusy) {
|
|
if (self->debug > 0) {
|
|
showPositions(self, pCon, pBueffel, 1023);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
}
|
|
TableDriveGetValue(self, pCon);
|
|
closeMotors(self, pCon);
|
|
self->state = IDLE;
|
|
return status;
|
|
} else {
|
|
return HWBusy;
|
|
}
|
|
break;
|
|
case OUTOFSYNC:
|
|
SCWrite(pCon, "WARNING: tabledrive out of sync", eWarning);
|
|
self->state = IDLE;
|
|
return HWFault;
|
|
break;
|
|
default:
|
|
SCWrite(pCon,
|
|
"ERROR: programming error in tabledrive, invalid state",
|
|
eError);
|
|
return HWFault;
|
|
break;
|
|
}
|
|
return HWFault;
|
|
}
|
|
|
|
/*================== live and death ========================================*/
|
|
static void *TableDriveInterface(void *pData, int ID)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ID == DRIVEID) {
|
|
return self->pDriv;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static void clearTable(int table)
|
|
{
|
|
int status;
|
|
tdMotor moti;
|
|
|
|
status = LLDnodePtr2First(table);
|
|
while (status != 0) {
|
|
LLDnodeDataTo(table, &moti);
|
|
LLDdelete(moti.table);
|
|
status = LLDnodePtr2Next(table);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static int loadTable(pTableDrive self, char *filename, SConnection * pCon)
|
|
{
|
|
FILE *fd = NULL;
|
|
tdMotor moti;
|
|
tdEntry entry;
|
|
char pBueffel[512];
|
|
int lineCount, tableCount = -1, read;
|
|
|
|
if (self->motorTable >= 0) {
|
|
clearTable(self->motorTable);
|
|
}
|
|
fd = fopen(filename, "r");
|
|
if (fd == NULL) {
|
|
snprintf(pBueffel, 511, "ERROR: failed to open %s for reading",
|
|
filename);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
self->motorTable = LLDcreate(sizeof(tdMotor));
|
|
if (self->motorTable < 0) {
|
|
SCWrite(pCon, "ERROR: failed to allocate motor table", eError);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
|
|
while (fgets(pBueffel, 511, fd) != NULL) {
|
|
if (pBueffel[0] == '#') {
|
|
strcpy(moti.motorName, trim(&pBueffel[1]));
|
|
moti.pMot = FindCommandData(pServ->pSics, moti.motorName, "Motor");
|
|
if (moti.pMot == NULL) {
|
|
snprintf(pBueffel, 511, "ERROR: motor %s NOT found!",
|
|
moti.motorName);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
moti.table = LLDcreate(sizeof(tdEntry));
|
|
if (moti.table < 0) {
|
|
SCWrite(pCon, "ERROR: failed to allocate table data", eError);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
lineCount = 1;
|
|
} else {
|
|
read = sscanf(pBueffel, "%lf %lf %lf", &entry.lower, &entry.position,
|
|
&entry.upper);
|
|
if (read >= 3) {
|
|
entry.tablePos = lineCount;
|
|
LLDnodeAppendFrom(moti.table, &entry);
|
|
lineCount++;
|
|
} else {
|
|
/*
|
|
* we are on a break line
|
|
*/
|
|
if (tableCount < 0) {
|
|
tableCount = lineCount;
|
|
} else {
|
|
if (lineCount != tableCount) {
|
|
SCWrite(pCon,
|
|
"ERROR: bad table file, table length mismatch",
|
|
eError);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
}
|
|
LLDnodeAppendFrom(self->motorTable, &moti);
|
|
}
|
|
}
|
|
}
|
|
self->tableLength = tableCount - 1;
|
|
fclose(fd);
|
|
return 1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void killTableDrive(void *pData)
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
if (self->pDes != NULL) {
|
|
DeleteDescriptor(self->pDes);
|
|
}
|
|
if (self->motorTable >= 0) {
|
|
clearTable(self->motorTable);
|
|
LLDdelete(self->motorTable);
|
|
self->motorTable = -1;
|
|
}
|
|
if (self->pDriv != NULL) {
|
|
free(self->pDriv);
|
|
}
|
|
free(self);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int TableDriveFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pTableDrive pNew = NULL;
|
|
char pBueffel[256];
|
|
int status;
|
|
|
|
if (argc < 3) {
|
|
SCWrite(pCon, "ERROR: insufficient parameters to TableDriveFactory",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
pNew = (pTableDrive) malloc(sizeof(TableDrive));
|
|
if (pNew == NULL) {
|
|
SCWrite(pCon, "ERROT: out of memory creating table drive", eError);
|
|
return 0;
|
|
}
|
|
memset(pNew, 0, sizeof(TableDrive));
|
|
|
|
pNew->pDes = CreateDescriptor("TableDrive");
|
|
pNew->pDriv = CreateDrivableInterface();
|
|
if (pNew->pDes == NULL || pNew->pDriv == NULL) {
|
|
SCWrite(pCon, "ERROT: out of memory creating table drive", eError);
|
|
return 0;
|
|
}
|
|
pNew->pDes->GetInterface = TableDriveInterface;
|
|
pNew->pDriv->CheckLimits = TableDriveCheckLimits;
|
|
pNew->pDriv->CheckStatus = TableDriveCheckStatus;
|
|
pNew->pDriv->GetValue = TableDriveGetValue;
|
|
pNew->pDriv->Halt = TableDriveHalt;
|
|
pNew->pDriv->SetValue = TableDriveSetValue;
|
|
pNew->motorTable = -1;
|
|
pNew->state = OUTOFSYNC;
|
|
pNew->tableLength = 0;
|
|
pNew->targetPosition = 0;
|
|
pNew->oriInvalid = 1;
|
|
|
|
if (!loadTable(pNew, argv[2], pCon)) {
|
|
killTableDrive(pNew);
|
|
return 0;
|
|
}
|
|
pNew->state = IDLE;
|
|
|
|
status = AddCommand(pSics, argv[1],
|
|
TableDriveAction, killTableDrive, pNew);
|
|
if (!status) {
|
|
snprintf(pBueffel, 256, "ERROR: duplicate command %s not created",
|
|
argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
killTableDrive(pNew);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*====================== interpreter interface ============================*/
|
|
int TableDriveAction(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pTableDrive self = (pTableDrive) pData;
|
|
int status;
|
|
char pBueffel[1024];
|
|
float value;
|
|
pDynString print = NULL;
|
|
|
|
if (argc < 2) {
|
|
value = TableDriveGetValue(self, pCon);
|
|
snprintf(pBueffel, 255, "%s = %f", argv[0], value);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
if (value > -999) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
strtolower(argv[1]);
|
|
if (strcmp(argv[1], "load") == 0) {
|
|
if (!SCMatchRights(pCon, usMugger)) {
|
|
return 0;
|
|
}
|
|
if (argc < 3) {
|
|
SCWrite(pCon, "ERROR: require filename to load", eError);
|
|
return 0;
|
|
}
|
|
status = loadTable(self, argv[2], pCon);
|
|
if (status == 1) {
|
|
SCSendOK(pCon);
|
|
}
|
|
return status;
|
|
} else if (strcmp(argv[1], "show") == 0) {
|
|
SCStartBuffering(pCon);
|
|
showPositions(self, pCon, pBueffel, 1023);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
print = SCEndBuffering(pCon);
|
|
if(print != NULL){
|
|
SCWrite(pCon,GetCharArray(print), eValue);
|
|
}
|
|
return 1;
|
|
} else if (strcmp(argv[1], "info") == 0) {
|
|
SCStartBuffering(pCon);
|
|
tableInfo(self, pCon);
|
|
print = SCEndBuffering(pCon);
|
|
if (print != NULL) {
|
|
SCWrite(pCon, GetCharArray(print), eValue);
|
|
}
|
|
return 1;
|
|
} else if (strcmp(argv[1], "orient") == 0) {
|
|
if (argc > 2) {
|
|
if (!SCMatchRights(pCon, usMugger)) {
|
|
return 0;
|
|
}
|
|
strlcpy(self->orientMotor, argv[2], 80);
|
|
self->oriInvalid = 1;
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else {
|
|
snprintf(pBueffel, 255, "%s.orient = %s",
|
|
argv[0], self->orientMotor);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
return 1;
|
|
}
|
|
} else if (strcmp(argv[1], "debug") == 0) {
|
|
if (self->debug == 0) {
|
|
self->debug = 1;
|
|
} else {
|
|
self->debug = 0;
|
|
}
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else {
|
|
SCWrite(pCon, "ERROR: sub command not understood", eError);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|