486 lines
14 KiB
C
486 lines
14 KiB
C
/*--------------------------------------------------------------------------
|
|
I N T E R F A C E
|
|
|
|
|
|
Just a few utility function for creating interfaces.
|
|
|
|
Mark Koennecke, June 1997
|
|
|
|
Copyright:
|
|
|
|
Labor fuer Neutronenstreuung
|
|
Paul Scherrer Institut
|
|
CH-5423 Villigen-PSI
|
|
|
|
Extended over time with utility functions
|
|
|
|
Added Drivable and Countable task functions
|
|
|
|
Mark Koennecke, February 2013
|
|
|
|
The authors hereby grant permission to use, copy, modify, distribute,
|
|
and license this software and its documentation for any purpose, provided
|
|
that existing copyright notices are retained in all copies and that this
|
|
notice is included verbatim in any distributions. No written agreement,
|
|
license, or royalty fee is required for any of the authorized uses.
|
|
Modifications to this software may be copyrighted by their authors
|
|
and need not follow the licensing terms described here, provided that
|
|
the new terms are clearly indicated on the first page of each file where
|
|
they apply.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
|
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
|
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
MODIFICATIONS.
|
|
----------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include "fortify.h"
|
|
#include "sics.h"
|
|
#include "motor.h"
|
|
#include <status.h>
|
|
/*=========================================================================
|
|
Empty driveable interface functions
|
|
==========================================================================*/
|
|
static int EmptyHalt(void *self)
|
|
{
|
|
return OKOK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int EmptyLimits(void *self, float fVal, char *error, int errLen)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static long EmptyValue(void *self, SConnection * pCon, float fVal)
|
|
{
|
|
SCWrite(pCon, "WARNING: empty SetValue", eWarning);
|
|
return OKOK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int EmptyStatus(void *self, SConnection * pCon)
|
|
{
|
|
return HWIdle;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static float EmptyGet(void *self, SConnection * pCon)
|
|
{
|
|
SCWrite(pCon, "WARNING: empty GetValue", eWarning);
|
|
return 555.55;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
pIDrivable CreateDrivableInterface(void)
|
|
{
|
|
pIDrivable pRes = NULL;
|
|
|
|
pRes = (pIDrivable) malloc(sizeof(IDrivable));
|
|
if (!pRes) {
|
|
return NULL;
|
|
}
|
|
memset(pRes, 0, sizeof(IDrivable));
|
|
pRes->ID = DRIVEID;
|
|
pRes->Halt = EmptyHalt;
|
|
pRes->CheckLimits = EmptyLimits;
|
|
pRes->SetValue = EmptyValue;
|
|
pRes->CheckStatus = EmptyStatus;
|
|
pRes->GetValue = EmptyGet;
|
|
pRes->drivableStatus = HWIdle;
|
|
return pRes;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static int DefaultReadStatus(void *self, SConnection *pCon)
|
|
{
|
|
pICountable pCount = GetCountableInterface(self);
|
|
if(pCount != NULL){
|
|
return pCount->lastStatus;
|
|
} else {
|
|
return HWFault;
|
|
}
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
pICountable CreateCountableInterface(void)
|
|
{
|
|
pICountable pRes = NULL;
|
|
|
|
pRes = (pICountable) malloc(sizeof(ICountable));
|
|
if (!pRes) {
|
|
return NULL;
|
|
}
|
|
memset(pRes, 0, sizeof(ICountable));
|
|
pRes->ID = COUNTID;
|
|
pRes->lastStatus = HWIdle;
|
|
pRes->ReadStatus = DefaultReadStatus;
|
|
return pRes;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
pEVInterface CreateEVInterface(void)
|
|
{
|
|
pEVInterface pRes = NULL;
|
|
|
|
pRes = (pEVInterface) malloc(sizeof(EVInterface));
|
|
if (!pRes) {
|
|
return NULL;
|
|
}
|
|
memset(pRes, 0, sizeof(EVInterface));
|
|
pRes->iID = ENVIRINTERFACE;
|
|
return pRes;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static void *FindInterface(void *pObject, int iID)
|
|
{
|
|
pDummy pDum = NULL;
|
|
|
|
pDum = (pDummy) pObject;
|
|
if (!pDum) {
|
|
return NULL;
|
|
}
|
|
if (!pDum->pDescriptor) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDum->pDescriptor->GetInterface(pDum, iID);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
pIDrivable GetDrivableInterface(void *pObject)
|
|
{
|
|
return (pIDrivable) FindInterface(pObject, DRIVEID);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int GetDrivablePosition(void *pObject, SConnection * pCon, float *fPos)
|
|
{
|
|
pIDrivable pDriv = NULL;
|
|
pMotor pMot = NULL;
|
|
float value;
|
|
|
|
pDriv = GetDrivableInterface(pObject);
|
|
if (pDriv == NULL) {
|
|
return 0;
|
|
}
|
|
if (iHasType(pObject, "Motor")) {
|
|
pMot = (pMotor) pObject;
|
|
return MotorGetSoftPosition(pMot, pCon, fPos);
|
|
}
|
|
value = pDriv->GetValue(pObject, pCon);
|
|
if (value < -9999.99) {
|
|
return 0;
|
|
}
|
|
*fPos = value;
|
|
return 1;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct {
|
|
int id;
|
|
void *obj;
|
|
pIDrivable pDriv;
|
|
SConnection *pCon;
|
|
char *name;
|
|
}DriveTaskData;
|
|
/*-------------------------------------------------------------------------*/
|
|
static void KillDriveTaskData(void *data)
|
|
{
|
|
DriveTaskData *taskData = (DriveTaskData *)data;
|
|
|
|
if(taskData == NULL){
|
|
return;
|
|
}
|
|
if(taskData->name != NULL){
|
|
free(taskData->name);
|
|
}
|
|
if(taskData->pCon != NULL){
|
|
SCDeleteConnection(taskData->pCon);
|
|
}
|
|
free(taskData);
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void DriveTaskSignal(void *data, int iSignal, void *pSigData)
|
|
{
|
|
DriveTaskData *taskData = (DriveTaskData *)data;
|
|
int *interrupt;
|
|
|
|
assert(taskData != NULL);
|
|
|
|
if(iSignal == SICSINT){
|
|
interrupt = (int *)pSigData;
|
|
if(*interrupt != eContinue){
|
|
SCPrintf(taskData->pCon,eLogError,"ERROR: Interrupting %s", taskData->name);
|
|
taskData->pDriv->Halt(taskData->obj);
|
|
SCSetInterrupt(taskData->pCon,*interrupt);
|
|
}
|
|
} else if (iSignal == ENDDRIVETASK) {
|
|
if (taskData->obj == pSigData) {
|
|
taskData->pDriv = NULL; /* tell DriveTaskFunc to quit */
|
|
}
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void EndDriveTask(void *obj) {
|
|
/* End a drive task for obj.
|
|
Originally in SICS, it was not possible to run an object already running
|
|
this was relaxed in SEA, but with the downside, that several drive tasks
|
|
for the same object may be running, removed only when the run succeds.
|
|
This is called in StartDevice, before creating a new drive task.
|
|
*/
|
|
TaskSignal(pServ->pTasker, ENDDRIVETASK, obj);
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static int DriveTaskFunc(void *data)
|
|
{
|
|
DriveTaskData *taskData = (DriveTaskData *)data;
|
|
int status;
|
|
|
|
assert(taskData != NULL);
|
|
|
|
if (!taskData->pDriv) return 0;
|
|
|
|
status = taskData->pDriv->CheckStatus(taskData->obj,taskData->pCon);
|
|
if(status == HWBusy){
|
|
return 1;
|
|
}
|
|
if(status == HWFault){
|
|
taskData->pDriv->iErrorCount++;
|
|
} else {
|
|
taskData->pDriv->iErrorCount = 0;
|
|
}
|
|
if(status == HWFault || status == HWPosFault){
|
|
SetDevexecStatus(pServ->pExecutor,DEVERROR);
|
|
}
|
|
DevexecLog("STOP",taskData->name);
|
|
if(status == HWIdle || status == OKOK){
|
|
ExeInterest(pServ->pExecutor,taskData->name, "finished");
|
|
|
|
} else {
|
|
ExeInterest(pServ->pExecutor,taskData->name, "finished with problem");
|
|
}
|
|
traceSys("drive","DriveTask %s finished with state %d", taskData->name,status);
|
|
if(SCGetTransID(taskData->pCon) > 100000) {
|
|
SCPrintf(taskData->pCon,eLog,"TASKEND %d", SCGetTransID(taskData->pCon));
|
|
}
|
|
return 0;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
long StartDriveTask(void *obj, SConnection *pCon, char *name, float fTarget)
|
|
{
|
|
pIDrivable pDriv = NULL;
|
|
char error[132], buffer[132];
|
|
DriveTaskData *taskData = NULL;
|
|
|
|
pDriv = GetDrivableInterface(obj);
|
|
if(pDriv == NULL){
|
|
SCPrintf(pCon,eError,"ERROR: %s is not drivable", name);
|
|
return -1;
|
|
}
|
|
if(pDriv->CheckLimits(obj,fTarget,error,sizeof(error)) != OKOK){
|
|
SCPrintf(pCon,eLogError,"ERROR: %s cannot reach %f, reason %s", name,
|
|
fTarget, error);
|
|
return -1;
|
|
}
|
|
taskData = calloc(1,sizeof(DriveTaskData));
|
|
if(taskData == NULL){
|
|
SCPrintf(pCon,eError,"ERROR: out of memory starting %s", name);
|
|
return -1;
|
|
}
|
|
if(pDriv->SetValue(obj,pCon,fTarget) != OKOK){
|
|
return -1;
|
|
}
|
|
ExeInterest(pServ->pExecutor,name,"started");
|
|
DevexecLog("START",name);
|
|
InvokeNewTarget(pServ->pExecutor,name,fTarget);
|
|
if(SCGetTransID(pCon) > 100000) {
|
|
SCPrintf(pCon,eLog,"TASKSTART %d", SCGetTransID(pCon));
|
|
}
|
|
|
|
taskData->id = DRIVEID;
|
|
taskData->obj = obj;
|
|
taskData->pDriv = pDriv;
|
|
taskData->pCon = SCCopyConnection(pCon);
|
|
taskData->name = strdup(name);
|
|
|
|
LogIS(INFO,SSYS,"drive:DriveTask started: %s to %f", name, fTarget);
|
|
|
|
return TaskRegisterN(pServ->pTasker,
|
|
name,
|
|
DriveTaskFunc,
|
|
DriveTaskSignal,
|
|
KillDriveTaskData,
|
|
taskData, TASK_PRIO_HIGH);
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
pICountable GetCountableInterface(void *pObject)
|
|
{
|
|
return (pICountable) FindInterface(pObject, COUNTID);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int GetCountLock(pICountable self, SConnection * pCon)
|
|
{
|
|
if (self->running == 1) {
|
|
SCWrite(pCon, "ERROR: someone else is already counting!", eError);
|
|
/* printf("Countlock denied\n"); */
|
|
return 0;
|
|
} else {
|
|
/* printf("Countlock aquired\n");*/
|
|
self->running = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void ReleaseCountLock(pICountable self)
|
|
{
|
|
/* printf("Countlock released\n"); */
|
|
self->running = 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int isRunning(pICountable self)
|
|
{
|
|
return self->running;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct {
|
|
int id;
|
|
void *obj;
|
|
pICountable pCount;
|
|
SConnection *pCon;
|
|
char *name;
|
|
}CountTaskData;
|
|
/*-------------------------------------------------------------------------*/
|
|
static void KillCountTaskData(void *data)
|
|
{
|
|
CountTaskData *taskData = (CountTaskData *)data;
|
|
|
|
if(taskData == NULL){
|
|
return;
|
|
}
|
|
if(taskData->name != NULL){
|
|
free(taskData->name);
|
|
}
|
|
if(taskData->pCon != NULL){
|
|
SCDeleteConnection(taskData->pCon);
|
|
}
|
|
free(taskData);
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void CountTaskSignal(void *data, int iSignal, void *pSigData)
|
|
{
|
|
CountTaskData *taskData = (CountTaskData *)data;
|
|
int *interrupt;
|
|
|
|
assert(taskData != NULL);
|
|
|
|
if(iSignal == SICSINT){
|
|
interrupt = (int *)pSigData;
|
|
if(*interrupt != eContinue){
|
|
SCPrintf(taskData->pCon,eLogError,"ERROR: Interrupting %s", taskData->name);
|
|
taskData->pCount->Halt(taskData->obj);
|
|
SCSetInterrupt(taskData->pCon,*interrupt);
|
|
}
|
|
} else if(iSignal == IPAUSE){
|
|
taskData->pCount->Pause(taskData->obj,taskData->pCon);
|
|
} else if(iSignal == CONTINUE){
|
|
taskData->pCount->Continue(taskData->obj,taskData->pCon);
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static int CountTaskFunc(void *data)
|
|
{
|
|
CountTaskData *taskData = (CountTaskData *)data;
|
|
int status;
|
|
|
|
assert(taskData != NULL);
|
|
|
|
status = taskData->pCount->CheckCountStatus(taskData->obj,taskData->pCon);
|
|
taskData->pCount->lastStatus = status;
|
|
if(status == HWBusy) {
|
|
return 1;
|
|
} else if(status == HWNoBeam){
|
|
return 1;
|
|
} else if(status == HWPause){
|
|
return 1;
|
|
}
|
|
|
|
taskData->pCount->TransferData(taskData->obj, taskData->pCon);
|
|
|
|
if(status == HWFault){
|
|
SetDevexecStatus(pServ->pExecutor,DEVERROR);
|
|
}
|
|
|
|
DevexecLog("STOP",taskData->name);
|
|
if(status == HWIdle || status == OKOK){
|
|
ExeInterest(pServ->pExecutor,taskData->name, "finished");
|
|
} else {
|
|
ExeInterest(pServ->pExecutor,taskData->name, "finished with problem");
|
|
}
|
|
traceSys("count","CountTask %s finished with state %d", taskData->name,status);
|
|
if(SCGetTransID(taskData->pCon) > 100000) {
|
|
SCPrintf(taskData->pCon,eLog,"TASKEND %d", SCGetTransID(taskData->pCon));
|
|
}
|
|
return 0;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
long StartCountTask(void *obj, SConnection *pCon, char *name)
|
|
{
|
|
pICountable pCount = NULL;
|
|
char error[132], buffer[132];
|
|
CountTaskData *taskData = NULL;
|
|
|
|
pCount = FindInterface(obj,COUNTID);
|
|
if(pCount == NULL){
|
|
SCPrintf(pCon,eError,"ERROR: %s is not countable", name);
|
|
return 1;
|
|
}
|
|
taskData = calloc(1,sizeof(CountTaskData));
|
|
if(taskData == NULL){
|
|
SCPrintf(pCon,eError,"ERROR: out of memory starting %s", name);
|
|
return -1;
|
|
}
|
|
if(pCount->StartCount(obj,pCon) != OKOK){
|
|
pCount->running = 0;
|
|
return -1;
|
|
}
|
|
ExeInterest(pServ->pExecutor,name,"started");
|
|
DevexecLog("START",name);
|
|
if(SCGetTransID(pCon) > 100000) {
|
|
SCPrintf(pCon,eLog,"TASKSTART %d", SCGetTransID(pCon));
|
|
}
|
|
|
|
taskData->id = COUNTID;
|
|
taskData->obj = obj;
|
|
taskData->pCount = pCount;
|
|
taskData->pCon = SCCopyConnection(pCon);
|
|
taskData->name = strdup(name);
|
|
|
|
LogIS(INFO,SSYS,"count:CountTask started: %s", name);
|
|
|
|
return TaskRegisterN(pServ->pTasker,
|
|
name,
|
|
CountTaskFunc,
|
|
CountTaskSignal,
|
|
KillCountTaskData,
|
|
taskData, TASK_PRIO_HIGH);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
pICallBack GetCallbackInterface(void *pObject)
|
|
{
|
|
return (pICallBack) FindInterface(pObject, CALLBACKINTERFACE);
|
|
}
|