620 lines
16 KiB
C
620 lines
16 KiB
C
/*----------------------------------------------------------------------
|
|
Implementation file for the Sics variables module.
|
|
|
|
|
|
|
|
Mark Koennecke, November 1996
|
|
revised: Mark Koennecke, June 1997
|
|
|
|
Copyright:
|
|
|
|
Labor fuer Neutronenstreuung
|
|
Paul Scherrer Institut
|
|
CH-5423 Villigen-PSI
|
|
|
|
|
|
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 "fortify.h"
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <tcl.h>
|
|
#include "sics.h"
|
|
#include <string.h>
|
|
#include "splitter.h"
|
|
#include "status.h"
|
|
#include "sicsvar.h"
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int VarSave(void *pData, char *name, FILE * fd)
|
|
{
|
|
char pBueffel[512];
|
|
pSicsVariable pVar = NULL;
|
|
|
|
assert(pData);
|
|
assert(fd);
|
|
pVar = (pSicsVariable) pData;
|
|
if (pVar->iAccessCode == usInternal) {
|
|
return 1;
|
|
}
|
|
|
|
if(pVar->iLock == 1) {
|
|
return 1;
|
|
}
|
|
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "# Variable %s\n", name);
|
|
switch (pVar->eType) {
|
|
case veText:
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s %s\n", name, pVar->text);
|
|
break;
|
|
case veInt:
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s %d\n", name, pVar->iVal);
|
|
break;
|
|
case veFloat:
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s %f\n", name, pVar->fVal);
|
|
break;
|
|
}
|
|
fputs(pBueffel, fd);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s setAccess %d\n", name, pVar->iAccessCode);
|
|
fputs(pBueffel, fd);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static void *VarInterface(void *pData, int iInter)
|
|
{
|
|
pSicsVariable self = NULL;
|
|
|
|
self = (pSicsVariable) pData;
|
|
assert(self);
|
|
|
|
if (iInter == CALLBACKINTERFACE) {
|
|
return self->pCall;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
pSicsVariable VarCreate(int iAccessCode, VarType eTyp, char *name)
|
|
{
|
|
pSicsVariable pRes = NULL;
|
|
|
|
pRes = (SicsVariable *) malloc(sizeof(SicsVariable));
|
|
if (!pRes) {
|
|
return NULL;
|
|
}
|
|
|
|
pRes->pDescriptor = CreateDescriptor("SicsVariable");
|
|
if (!pRes->pDescriptor) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
pRes->pDescriptor->GetInterface = VarInterface;
|
|
pRes->pDescriptor->SaveStatus = VarSave;
|
|
pRes->eType = eTyp;
|
|
pRes->iAccessCode = iAccessCode;
|
|
pRes->fVal = .0;
|
|
pRes->iVal = 0;
|
|
pRes->iLock = 0;
|
|
pRes->text = strdup("UNKNOWN");
|
|
pRes->name = strdup(name);
|
|
|
|
pRes->pCall = CreateCallBackInterface();
|
|
return pRes;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
VarFactory will be used in the initialisation phase to configure a new
|
|
variable.
|
|
|
|
Syntax: VarMake name type access
|
|
|
|
type can be one of: Text, Int, Float
|
|
access can be one of: Internal, Mugger, User, Spy
|
|
-------------------------------------------------------------------------*/
|
|
static char *cType[] = {
|
|
"text",
|
|
"int",
|
|
"float",
|
|
NULL
|
|
};
|
|
|
|
int VarFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pSicsVariable pRes = NULL;
|
|
char pBueffel[512];
|
|
VarType eType;
|
|
int i;
|
|
int iCode, iRet;
|
|
|
|
assert(pCon);
|
|
assert(pSics);
|
|
|
|
/* check if enough commands */
|
|
argtolower(argc, argv);
|
|
if (argc < 4) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1,
|
|
"Insufficient no of args to %s, Usage: %s name type accescode",
|
|
argv[0], argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* argv[1] is expected to be the name of the var, argv[2] the type */
|
|
/* interpret the type */
|
|
i = 0;
|
|
while (cType[i] != NULL) {
|
|
if (strcmp(cType[i], argv[2]) == 0) {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
switch (i) {
|
|
case 0:
|
|
eType = veText;
|
|
break;
|
|
case 1:
|
|
eType = veInt;
|
|
break;
|
|
case 2:
|
|
eType = veFloat;
|
|
break;
|
|
default:
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "Var %s Type --> %s <-- not recognized",
|
|
argv[1], argv[2]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* argv[3] must be the access code, check that now */
|
|
i = decodeSICSPriv(argv[3]);
|
|
if (i < 0) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, " %s access code %s not recognized",
|
|
argv[1], argv[3]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* now we can actually install the variable */
|
|
pRes = VarCreate(i, eType, argv[1]);
|
|
if (!pRes) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "Memory Error creating variable %s", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
iRet = AddCommand(pSics, argv[1], VarWrapper, (KillFunc) VarKill, pRes);
|
|
if (!iRet) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: duplicate command %s not created", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
VarKill(pRes);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int VarKill(pSicsVariable self)
|
|
{
|
|
|
|
assert(self);
|
|
|
|
if (self->text) {
|
|
free(self->text);
|
|
}
|
|
if (self->pDescriptor) {
|
|
DeleteDescriptor(self->pDescriptor);
|
|
}
|
|
if (self->pCall) {
|
|
DeleteCallBackInterface(self->pCall);
|
|
}
|
|
if (self->name) {
|
|
free(self->name);
|
|
}
|
|
|
|
free(self);
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int VarSetFloat(pSicsVariable self, float fNew, int iUserRights)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veFloat);
|
|
|
|
if (self->iAccessCode < iUserRights) {
|
|
return 0;
|
|
} else if (self->fVal != fNew) {
|
|
self->fVal = fNew;
|
|
InvokeCallBack(self->pCall, VALUECHANGE, self);
|
|
tracePar(self->name,"%f",fNew);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarSetInt(pSicsVariable self, int iNew, int iUserRights)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veInt);
|
|
|
|
if (self->iAccessCode < iUserRights) {
|
|
return 0;
|
|
} else if (self->iVal != iNew) {
|
|
self->iVal = iNew;
|
|
InvokeCallBack(self->pCall, VALUECHANGE, self);
|
|
tracePar(self->name,"%d",iNew);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarSetText(pSicsVariable self, char *pNew, int iUserRights)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veText);
|
|
|
|
if (self->iAccessCode < iUserRights) {
|
|
return 0;
|
|
} else if (self->text && strcmp(self->text, pNew)) {
|
|
if (self->text) {
|
|
free(self->text);
|
|
}
|
|
self->text = strdup(pNew);
|
|
InvokeCallBack(self->pCall, VALUECHANGE, self);
|
|
tracePar(self->name,"%s",pNew);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarGetFloat(pSicsVariable self, float *fNew)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veFloat);
|
|
|
|
*fNew = self->fVal;
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarGetInt(pSicsVariable self, int *iNew)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veInt);
|
|
|
|
*iNew = self->iVal;
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarGetText(pSicsVariable self, char **pNew)
|
|
{
|
|
assert(self);
|
|
assert(self->eType == veText);
|
|
|
|
*pNew = strdup(self->text);
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
VarType GetVarType(pSicsVariable self)
|
|
{
|
|
assert(self);
|
|
return self->eType;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarSetRights(pSicsVariable self, int iNewRights, int iYourRights)
|
|
{
|
|
assert(self);
|
|
|
|
if (iYourRights > 1) { /* only muggers allowed here */
|
|
return 0;
|
|
} else {
|
|
self->iAccessCode = iNewRights;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
static int VarInterestCallback(int iEvent, void *pEvent, void *pUser)
|
|
{
|
|
SConnection *pCon;
|
|
char pBueffel[512];
|
|
pSicsVariable pVar = NULL;
|
|
int iVal, status;
|
|
float fVal;
|
|
char *pText;
|
|
|
|
assert(pEvent);
|
|
assert(pUser);
|
|
|
|
pVar = (pSicsVariable) pEvent;
|
|
pCon = (SConnection *) pUser;
|
|
|
|
/* check kill conditions */
|
|
if (pCon == NULL || !SCisConnected(pCon)) {
|
|
return -1;
|
|
}
|
|
|
|
switch (pVar->eType) {
|
|
case veInt:
|
|
VarGetInt(pVar, &iVal);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %d", pVar->name, iVal);
|
|
SCWrite(pCon, pBueffel, eEvent);
|
|
status = 1;
|
|
break;
|
|
case veFloat:
|
|
VarGetFloat(pVar, &fVal);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %f", pVar->name, fVal);
|
|
SCWrite(pCon, pBueffel, eEvent);
|
|
status = 1;
|
|
break;
|
|
case veText:
|
|
VarGetText(pVar, &pText);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %s", pVar->name, pText);
|
|
SCWrite(pCon, pBueffel, eEvent);
|
|
if (pText) {
|
|
free(pText);
|
|
}
|
|
status = 1;
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int VarSetFromText(pSicsVariable self, SConnection * pCon,
|
|
char *text)
|
|
{
|
|
int status;
|
|
double dVal;
|
|
char pBueffel[132];
|
|
|
|
if (!SCMatchRights(pCon, self->iAccessCode)) {
|
|
return 0;
|
|
}
|
|
if (self->eType == veText) {
|
|
return VarSetText(self, text, SCGetRights(pCon));
|
|
}
|
|
status = Tcl_GetDouble(InterpGetTcl(pServ->pSics), text, &dVal);
|
|
if (status != TCL_OK) {
|
|
snprintf(pBueffel, 131, "ERROR: failed to convert %s to number", text);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
if (self->eType == veInt) {
|
|
return VarSetInt(self, (int) dVal, SCGetRights(pCon));
|
|
} else if (self->eType == veFloat) {
|
|
return VarSetFloat(self, (float) dVal, SCGetRights(pCon));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
Variables understands some commands:
|
|
setrights : for setting user rights
|
|
lock : for locking the variable
|
|
interest : for notifictaion on value change
|
|
uninterest : delete notification
|
|
*/
|
|
int VarWrapper(SConnection * pCon, SicsInterp * pInterp, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
float fVal;
|
|
int iVal;
|
|
char *pText = NULL;
|
|
pSicsVariable pVar = NULL;
|
|
VarType eTyp;
|
|
TokenList *pList = NULL;
|
|
TokenList *pCurrent;
|
|
char pBueffel[256];
|
|
int iRet;
|
|
Status eStat;
|
|
long lID;
|
|
|
|
assert(pCon);
|
|
assert(pInterp);
|
|
assert(pData);
|
|
|
|
/* get Variable pointer */
|
|
pVar = (pSicsVariable) pData;
|
|
eTyp = GetVarType(pVar);
|
|
|
|
/* tokenize arguments */
|
|
pList = SplitArguments(argc, argv);
|
|
if (!pList) {
|
|
SCWrite(pCon, "ERROR: cannot parse arguments", eError);
|
|
return 0;
|
|
}
|
|
|
|
pCurrent = pList->pNext; /* if only one arg: print the value */
|
|
if (!pCurrent) {
|
|
switch (eTyp) {
|
|
case veInt:
|
|
VarGetInt(pVar, &iVal);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %d", argv[0], iVal);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
DeleteTokenList(pList);
|
|
return 1;
|
|
case veFloat:
|
|
VarGetFloat(pVar, &fVal);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %#g", argv[0], fVal);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
DeleteTokenList(pList);
|
|
return 1;
|
|
case veText:
|
|
VarGetText(pVar, &pText);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %s", argv[0], pText);
|
|
SCWrite(pCon, pBueffel, eValue);
|
|
if (pText) {
|
|
free(pText);
|
|
}
|
|
DeleteTokenList(pList);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* now either new value, lock or setAccess */
|
|
strtolower(pCurrent->text);
|
|
if (strcmp(pCurrent->text, "setaccess") == 0) {
|
|
pCurrent = pCurrent->pNext;
|
|
if (pCurrent) {
|
|
if (pCurrent->Type != eInt) {
|
|
SCWrite(pCon, "Wrong argument for setAccess, expect Integer",
|
|
eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
} else {
|
|
/* is control grabbed ? */
|
|
if (SCGetGrab(pCon) != 0) {
|
|
SCWrite(pCon,
|
|
"ERROR: somebody else has grabbed control, Request REJECTED",
|
|
eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
}
|
|
/* finaly do it */
|
|
iRet = VarSetRights(pVar, pCurrent->iVal, SCGetRights(pCon));
|
|
if (!iRet) {
|
|
SCWrite(pCon,
|
|
"You have no privilege to change AccessCodes", eError);
|
|
}
|
|
DeleteTokenList(pList);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
} else {
|
|
SCWrite(pCon, "Missing argument to setAccess", eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
}
|
|
} else if (strcmp(pCurrent->text, "interest") == 0) { /* interest */
|
|
lID = RegisterCallback(pVar->pCall,
|
|
VALUECHANGE, VarInterestCallback,
|
|
SCCopyConnection(pCon), SCDeleteConnection);
|
|
DeleteTokenList(pList);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else if (strcmp(pCurrent->text, "uninterest") == 0) {
|
|
RemoveCallbackCon(pVar->pCall, pCon);
|
|
DeleteTokenList(pList);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else if (strcmp(pCurrent->text, "lock") == 0) {
|
|
pVar->iLock = 1;
|
|
DeleteTokenList(pList);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else if (strcmp(pCurrent->text, "unlock") == 0) {
|
|
pVar->iLock = 0;
|
|
DeleteTokenList(pList);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else if (strcmp(pCurrent->text, "force") == 0) {
|
|
/*
|
|
Undocumented feauture: force a set even while driving etc.
|
|
Internal privilege required to do this.
|
|
*/
|
|
if (!SCMatchRights(pCon, usUser)) {
|
|
return 0;
|
|
}
|
|
pCurrent = pCurrent->pNext;
|
|
if (!pCurrent) {
|
|
SCWrite(pCon, "ERROR: new value missing for force", eError);
|
|
return 0;
|
|
}
|
|
Arg2Text(argc - 2, &argv[2], pBueffel, 255);
|
|
iRet = VarSetFromText(pVar, pCon, pBueffel);
|
|
if (iRet == 1) {
|
|
SCSendOK(pCon);
|
|
}
|
|
SCparChange(pCon);
|
|
DeleteTokenList(pList);
|
|
return iRet;
|
|
} else {
|
|
/* now, only a new value is still possible */
|
|
if (DevExecLevelRunning(pServ->pExecutor, RUNDRIVE)) {
|
|
SCWrite(pCon,
|
|
"You cannot set variables while a scan is running", eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
|
|
}
|
|
iRet = 0;
|
|
if (pCurrent) {
|
|
/* is it locked ? */
|
|
if (pVar->iLock) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: variable %s is configured locked!",
|
|
argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
}
|
|
/* is control grabbed ? */
|
|
if (SCGetGrab(pCon) != 0) {
|
|
SCWrite(pCon,
|
|
"ERROR: somebody else has grabbed control, Request REJECTED",
|
|
eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
}
|
|
Arg2Text(argc - 1, &argv[1], pBueffel, 255);
|
|
iRet = VarSetFromText(pVar, pCon, pBueffel);
|
|
if (iRet == 1) {
|
|
SCSendOK(pCon);
|
|
}
|
|
SCparChange(pCon);
|
|
DeleteTokenList(pList);
|
|
return iRet;
|
|
}
|
|
}
|
|
|
|
/* if we are here, no valid command was found */
|
|
SCWrite(pCon, "No valid syntax found in Variable", eError);
|
|
DeleteTokenList(pList);
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
pSicsVariable FindVariable(SicsInterp * pSics, char *name)
|
|
{
|
|
CommandList *pC;
|
|
pSicsVariable pVar;
|
|
|
|
pC = FindCommand(pSics, name);
|
|
if (!pC) {
|
|
return NULL;
|
|
}
|
|
pVar = (pSicsVariable) pC->pData;
|
|
if (!pVar) {
|
|
return NULL;
|
|
}
|
|
if (strcmp(pVar->pDescriptor->name, "SicsVariable") != 0) {
|
|
return NULL;
|
|
}
|
|
return pVar;
|
|
}
|