- Adding first working version of new AMOR settings module
- Improved sls magnet driver
This commit is contained in:
600
amorset.c
Normal file
600
amorset.c
Normal file
@ -0,0 +1,600 @@
|
||||
/*-------------------------------------------------------------------
|
||||
AMORSET together with amorcomp and amordrive implement the position
|
||||
control facility for the reflectometer AMOR. This uses the algorithm
|
||||
with the beam height as the baseline.
|
||||
|
||||
copyright: see file COPYRIGHT
|
||||
|
||||
Mark Koennecke, October 2005
|
||||
--------------------------------------------------------------------*/
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "amorset.h"
|
||||
#include <motorlist.h>
|
||||
#include "amorcomp.h"
|
||||
#include "amordrive.h"
|
||||
#include <trigd.h>
|
||||
#include <lld.h>
|
||||
#include "motor.h"
|
||||
/*--------------------- type defines ------------------------------*/
|
||||
#define TYM2T 1
|
||||
#define TYS2T 2
|
||||
#define TYATH 3
|
||||
|
||||
#define ABS(x) (x < 0 ? -(x) : (x))
|
||||
/*------------ The meat of it all: The settings calculation --------*/
|
||||
static int readMotors(pamorSet self, SConnection *pCon){
|
||||
int result, status;
|
||||
float val;
|
||||
|
||||
result = LLDcreate(sizeof(MotControl));
|
||||
status = addMotorToList(result,"d2t",.0);
|
||||
if(status != 1){
|
||||
SCWrite(pCon,"ERROR: configuration error: d2t not found", eError);
|
||||
return -1;
|
||||
}
|
||||
status = addMotorToList(result,"d3t",.0);
|
||||
if(status != 1){
|
||||
SCWrite(pCon,"ERROR: configuration error: d3t not found", eError);
|
||||
return -1;
|
||||
}
|
||||
status = addMotorToList(result,"d4t",.0);
|
||||
if(status != 1){
|
||||
SCWrite(pCon,"ERROR: configuration error: d4t not found", eError);
|
||||
return -1;
|
||||
}
|
||||
status = addMotorToList(result,"d5t",.0);
|
||||
if(status != 1){
|
||||
SCWrite(pCon,"ERROR: configuration error: d5t not found", eError);
|
||||
return -1;
|
||||
}
|
||||
val = self->listDrive->GetValue(&result,pCon);
|
||||
if(val < -99999){
|
||||
LLDdelete(result);
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/*------------------------------------------------------------------*/
|
||||
static int calcAmorSettings(pamorSet self,SConnection *pCon){
|
||||
int readList;
|
||||
double val, dist, com = .0, soz, mot;
|
||||
|
||||
/*
|
||||
* read motors
|
||||
*/
|
||||
readList = readMotors(self,pCon);
|
||||
if(readList < 0){
|
||||
SCWrite(pCon,
|
||||
"ERROR: failed to read motors for amor settings",eError);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* initialize drive list
|
||||
*/
|
||||
LLDdelete(self->driveList);
|
||||
self->driveList = LLDcreate(sizeof(MotControl));
|
||||
|
||||
/*
|
||||
* soz
|
||||
*/
|
||||
dist = calcCompPosition(&self->S) - calcCompPosition(&self->M);
|
||||
soz = dist*Tand(self->targetm2t);
|
||||
addMotorToList(self->driveList,"soz",soz);
|
||||
|
||||
/*
|
||||
* monochromator slit
|
||||
*/
|
||||
if(self->DS.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->DS) - calcCompPosition(&self->M);
|
||||
val = dist*Tand(self->targetm2t) - self->dspar;
|
||||
addMotorToList(self->driveList,"dbs",val);
|
||||
}
|
||||
|
||||
/*
|
||||
* slit 2
|
||||
*/
|
||||
if(self->D2.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->D2) - calcCompPosition(&self->M);
|
||||
mot = getListMotorPosition(readList,"d2t");
|
||||
if(mot < -99999){
|
||||
SCWrite(pCon,"WARNING: skipping d2 because of bad read on d2t",
|
||||
eWarning);
|
||||
} else {
|
||||
val = dist*Tand(self->targetm2t) - .5 * mot;
|
||||
addMotorToList(self->driveList,"d2b",val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* slit 3
|
||||
*/
|
||||
if(self->D3.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->D3) - calcCompPosition(&self->M);
|
||||
mot = getListMotorPosition(readList,"d3t");
|
||||
if(mot < -99999){
|
||||
SCWrite(pCon,"WARNING: skipping d3 because of bad read on d3t",
|
||||
eWarning);
|
||||
} else {
|
||||
val = dist*Tand(self->targetm2t) - .5 * mot;
|
||||
addMotorToList(self->driveList,"d3b",val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* detector
|
||||
*/
|
||||
com = self->targetm2t + self->targets2t;
|
||||
if(self->D.activeFlag == 1){
|
||||
addMotorToList(self->driveList,"com",com);
|
||||
dist = calcCompPosition(&self->D) - calcCompPosition(&self->S);
|
||||
val = dist*(Cosd(com) - 1.);
|
||||
addMotorToList(self->driveList,"cox",val);
|
||||
val = dist*Sind(com) + soz;
|
||||
addMotorToList(self->driveList,"coz",val);
|
||||
}
|
||||
|
||||
/*
|
||||
* slit 4
|
||||
*/
|
||||
if(self->D4.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->D4) - calcCompPosition(&self->S);
|
||||
mot = getListMotorPosition(readList,"d4t");
|
||||
if(mot < -99999){
|
||||
SCWrite(pCon,"WARNING: skipping d4 because of bad read on d4t",
|
||||
eWarning);
|
||||
} else {
|
||||
val = soz + dist*Tand(com) - .5 * mot;
|
||||
addMotorToList(self->driveList,"d4b",val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* slit 5
|
||||
*/
|
||||
if(self->D5.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->D5) - calcCompPosition(&self->S);
|
||||
mot = getListMotorPosition(readList,"d5t");
|
||||
if(mot < -99999){
|
||||
SCWrite(pCon,"WARNING: skipping d5 because of bad read on d5t",
|
||||
eWarning);
|
||||
} else {
|
||||
val = soz + dist*Tand(com) - .5 * mot;
|
||||
addMotorToList(self->driveList,"d5b",val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Analyzer
|
||||
*/
|
||||
if(self->A.activeFlag == 1){
|
||||
dist = calcCompPosition(&self->A) - calcCompPosition(&self->S);
|
||||
val = soz + dist*Tand(com);
|
||||
addMotorToList(self->driveList,"aoz",val);
|
||||
addMotorToList(self->driveList,"aom",com + self->targetath);
|
||||
}
|
||||
|
||||
LLDdelete(readList);
|
||||
self->mustDrive = 0;
|
||||
return 1;
|
||||
}
|
||||
/*----------------------------------------------------------------*/
|
||||
static int updateActualPositions(pamorSet self, SConnection *pCon){
|
||||
int readList, status;
|
||||
float val, dist, tmp, com;
|
||||
|
||||
/**
|
||||
* read some motors
|
||||
*/
|
||||
readList = LLDcreate(sizeof(MotControl));
|
||||
addMotorToList(readList,"soz",125);
|
||||
addMotorToList(readList,"com",125);
|
||||
addMotorToList(readList,"aom",125);
|
||||
val = self->listDrive->GetValue(&readList,pCon);
|
||||
if(val < -99999.){
|
||||
SCWrite(pCon,
|
||||
"ERROR: failed to read motors, values for m2t,s2t,ath invalid",
|
||||
eError);
|
||||
LLDdelete(readList);
|
||||
return 0;
|
||||
}
|
||||
val = getListMotorPosition(readList,"soz");
|
||||
dist = calcCompPosition(&self->S) - calcCompPosition(&self->M);
|
||||
tmp = val/dist;
|
||||
if(ABS(tmp) > .0001){
|
||||
self->actualm2t = Atand(tmp);
|
||||
} else {
|
||||
self->actualm2t = .0;
|
||||
}
|
||||
com = getListMotorPosition(readList,"com");
|
||||
self->actuals2t = com - self->actualm2t;
|
||||
|
||||
val = getListMotorPosition(readList,"aom");
|
||||
self->actualath = val - com;
|
||||
|
||||
LLDdelete(readList);
|
||||
self->mustRecalculate = 1;
|
||||
return 1;
|
||||
}
|
||||
/*=================== SICS internal interface functions============*/
|
||||
static void *AMOSETGetInterface(void *data, int iD){
|
||||
pamorSet self = NULL;
|
||||
|
||||
/*
|
||||
* this object shall never be driven directly
|
||||
*/
|
||||
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 AMOSETHalt(void *data) {
|
||||
pamorSet self = NULL;
|
||||
|
||||
self = (pamorSet)data;
|
||||
|
||||
self->listDrive->Halt(&self->driveList);
|
||||
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 AMOSETCheckLimits(void *data, float val,
|
||||
char *error, int errlen){
|
||||
pamorSet self = NULL;
|
||||
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 AMOSETSetValue(void *data, SConnection *pCon, float val){
|
||||
pamorSet self = NULL;
|
||||
return OKOK;
|
||||
}
|
||||
/*----------------------------------------------------------------
|
||||
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 AMOSETCheckStatus(void *data, SConnection *pCon){
|
||||
pamorSet self = NULL;
|
||||
int status;
|
||||
|
||||
self = (pamorSet)data;
|
||||
|
||||
if(self->mustDrive == 1){
|
||||
status = calcAmorSettings(self,pCon);
|
||||
if(status <= 0){
|
||||
return HWFault;
|
||||
}
|
||||
if(self->verbose == 1){
|
||||
printMotorList(self->driveList,pCon);
|
||||
}
|
||||
return self->listDrive->SetValue(&self->driveList,pCon,.37);
|
||||
} else {
|
||||
self->mustRecalculate = 1;
|
||||
return self->listDrive->CheckStatus(&self->driveList,pCon);
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------------
|
||||
GetValue is supposed to read a motor position
|
||||
On errors, -99999999.99 is returned and messages printed to pCon
|
||||
------------------------------------------------------------------*/
|
||||
static float AMOSETGetValue(void *data, SConnection *pCon){
|
||||
pamorSet self = NULL;
|
||||
float val = -99999999.99;
|
||||
|
||||
self = (pamorSet)data;
|
||||
|
||||
return val;
|
||||
}
|
||||
/*================ external functions for amordrive ============*/
|
||||
void amorSetMotor(pamorSet amor, int type, double value){
|
||||
switch(type){
|
||||
case TYM2T:
|
||||
amor->targetm2t = value;
|
||||
break;
|
||||
case TYS2T:
|
||||
amor->targets2t = value;
|
||||
break;
|
||||
case TYATH:
|
||||
amor->targetath = value;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
amor->mustDrive = 1;
|
||||
}
|
||||
/*----------------------------------------------------------------*/
|
||||
double amorGetMotor(pamorSet amor, SConnection *pCon, int type){
|
||||
if(amor->mustRecalculate == 1){
|
||||
updateActualPositions(amor,pCon);
|
||||
}
|
||||
switch(type){
|
||||
case TYM2T:
|
||||
return amor->actualm2t;
|
||||
break;
|
||||
case TYS2T:
|
||||
return amor->actuals2t;
|
||||
break;
|
||||
case TYATH:
|
||||
return amor->actualath;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
return -99999.999;
|
||||
}
|
||||
/*----------------------------------------------------------------
|
||||
Live and Deatch of objects.........
|
||||
returns NULL on failure, a new datastructure else
|
||||
------------------------------------------------------------------*/
|
||||
static int amorSetSave(void *data, char *name,FILE *fd){
|
||||
pamorSet self = NULL;
|
||||
|
||||
self = (pamorSet)data;
|
||||
if(self == NULL){
|
||||
return 0;
|
||||
}
|
||||
fprintf(fd,"%s dspar %f\n", name, self->dspar);
|
||||
fprintf(fd,"%s verbose %d\n", name, self->verbose);
|
||||
saveAmorComp(fd,name,"mono",&self->M);
|
||||
saveAmorComp(fd,name,"ds",&self->DS);
|
||||
saveAmorComp(fd,name,"slit2",&self->D2);
|
||||
saveAmorComp(fd,name,"slit3",&self->D3);
|
||||
saveAmorComp(fd,name,"sample",&self->S);
|
||||
saveAmorComp(fd,name,"slit4",&self->D4);
|
||||
saveAmorComp(fd,name,"slit5",&self->D5);
|
||||
saveAmorComp(fd,name,"ana",&self->A);
|
||||
saveAmorComp(fd,name,"detector",&self->D);
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------*/
|
||||
static pamorSet AMOSETMakeObject(){
|
||||
pamorSet self = NULL;
|
||||
|
||||
self = (pamorSet)malloc(sizeof(amorSet));
|
||||
if(self == NULL){
|
||||
return NULL;
|
||||
}
|
||||
memset(self,0,sizeof(amorSet));
|
||||
self->pDes = CreateDescriptor("AmorSet");
|
||||
self->pDriv = CreateDrivableInterface();
|
||||
self->listDrive = makeMotListInterface();
|
||||
self->driveList = LLDcreate(sizeof(MotControl));
|
||||
if(self->pDes == NULL || self->pDriv == NULL ||
|
||||
self->listDrive == NULL || self->driveList < 0){
|
||||
free(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->pDes->GetInterface = AMOSETGetInterface;
|
||||
self->pDes->SaveStatus = amorSetSave;
|
||||
self->pDriv->Halt = AMOSETHalt;
|
||||
self->pDriv->CheckLimits = AMOSETCheckLimits;
|
||||
self->pDriv->SetValue = AMOSETSetValue;
|
||||
self->pDriv->CheckStatus = AMOSETCheckStatus;
|
||||
self->pDriv->GetValue = AMOSETGetValue;
|
||||
|
||||
return self;
|
||||
}
|
||||
/*-----------------------------------------------------------------*/
|
||||
static void killAmorSet(void *data){
|
||||
pamorSet self = (pamorSet)data;
|
||||
|
||||
if(self == NULL){
|
||||
return;
|
||||
}
|
||||
|
||||
if(self->pDes != NULL) {
|
||||
DeleteDescriptor(self->pDes);
|
||||
}
|
||||
if(self->pDriv != NULL){
|
||||
free(self->pDriv);
|
||||
}
|
||||
if(self->listDrive != NULL){
|
||||
free(self->listDrive);
|
||||
}
|
||||
LLDdelete(self->driveList);
|
||||
free(self);
|
||||
}
|
||||
/*-------------------------------------------------------------------*/
|
||||
static int testRequiredMotors(SConnection *pCon){
|
||||
char motList[][20] = {"soz", "com",
|
||||
"cox","coz","dbs","d2b","d2t",
|
||||
"d3b", "d3t", "d4b","d4t",
|
||||
"d5t", "d5b","aoz", "aom"};
|
||||
int i = 0, status = 1;
|
||||
pMotor pMot = NULL;
|
||||
char pBueffel[132];
|
||||
|
||||
|
||||
for(i = 0; i < 15; i++){
|
||||
pMot = NULL;
|
||||
pMot = FindMotor(pServ->pSics,motList[i]);
|
||||
if(pMot == NULL){
|
||||
snprintf(pBueffel,131,"ERROR: motor %s for amorset not found",
|
||||
motList[i]);
|
||||
SCWrite(pCon,pBueffel,eError);
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
/*======================= interpreter interface section ============*/
|
||||
int AmorSetFactory(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||
int argc, char *argv[]){
|
||||
int status;
|
||||
pamorSet pNew = NULL;
|
||||
pamorDrive pTuk = NULL;
|
||||
|
||||
if(testRequiredMotors(pCon) == 0){
|
||||
SCWrite(pCon,
|
||||
"ERROR: aborting initialization of amorset due to missing motors",
|
||||
eError);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pNew = AMOSETMakeObject();
|
||||
if(pNew == NULL){
|
||||
SCWrite(pCon,"ERROR: out of memory creating amorset",
|
||||
eError);
|
||||
return 0;
|
||||
}
|
||||
status = AddCommand(pSics,"amorset",AmorSetAction,killAmorSet,pNew);
|
||||
if(!status){
|
||||
SCWrite(pCon,"ERROR: duplicate command amorset NOT created",eError);
|
||||
return 0;
|
||||
}
|
||||
pTuk = makeAmorDrive(pNew,TYM2T);
|
||||
if(pTuk == NULL){
|
||||
SCWrite(pCon,"ERROR: failed to allocate data fro m2t",eError);
|
||||
return 0;
|
||||
}
|
||||
status = AddCommand(pSics,"m2t",AmorDriveAction,killAmorDrive,pTuk);
|
||||
if(!status){
|
||||
SCWrite(pCon,"ERROR: duplicate command amorset m2t reated",eError);
|
||||
return 0;
|
||||
}
|
||||
pTuk = makeAmorDrive(pNew,TYATH);
|
||||
if(pTuk == NULL){
|
||||
SCWrite(pCon,"ERROR: failed to allocate data for ath",eError);
|
||||
return 0;
|
||||
}
|
||||
status = AddCommand(pSics,"ath",AmorDriveAction,killAmorDrive,pTuk);
|
||||
if(!status){
|
||||
SCWrite(pCon,"ERROR: duplicate command amorset ath reated",eError);
|
||||
return 0;
|
||||
}
|
||||
pTuk = makeAmorDrive(pNew,TYS2T);
|
||||
if(pTuk == NULL){
|
||||
SCWrite(pCon,"ERROR: failed to allocate data for s2t",eError);
|
||||
return 0;
|
||||
}
|
||||
status = AddCommand(pSics,"s2t",AmorDriveAction,killAmorDrive,pTuk);
|
||||
if(!status){
|
||||
SCWrite(pCon,"ERROR: duplicate command amorset s2t reated",eError);
|
||||
return 0;
|
||||
}
|
||||
SCSendOK(pCon);
|
||||
return 1;
|
||||
}
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static pamorComp locateComponent(pamorSet self, char *name){
|
||||
if(strcmp(name,"mono") == 0){
|
||||
return &self->M;
|
||||
} else if(strcmp(name,"ds") == 0){
|
||||
return &self->DS;
|
||||
}else if(strcmp(name,"slit2") == 0){
|
||||
return &self->D2;
|
||||
}else if(strcmp(name,"slit3") == 0){
|
||||
return &self->D3;
|
||||
}else if(strcmp(name,"sample") == 0){
|
||||
return &self->S;
|
||||
} else if(strcmp(name,"slit4") == 0){
|
||||
return &self->D4;
|
||||
}else if(strcmp(name,"slit5") == 0){
|
||||
return &self->D5;
|
||||
}else if(strcmp(name,"detector") == 0){
|
||||
return &self->D;
|
||||
}else if(strcmp(name,"ana") == 0){
|
||||
return &self->A;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------------------*/
|
||||
static double calcCD(pamorSet self){
|
||||
double soz, cmh, smh, sdh, cd, dist;
|
||||
|
||||
soz = dist*Cotd(self->targetm2t);
|
||||
cmh = calcCompPosition(&self->M);
|
||||
smh = calcCompPosition(&self->S) - calcCompPosition(&self->M);
|
||||
sdh = calcCompPosition(&self->D) - calcCompPosition(&self->M);
|
||||
cd = cmh + sqrt(smh*smh + soz*soz) + sdh;
|
||||
return cd;
|
||||
}
|
||||
/*-----------------------------------------------------------------------*/
|
||||
int AmorSetAction(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||
int argc, char *argv[]){
|
||||
pamorSet self = NULL;
|
||||
pamorComp comp = NULL;
|
||||
char pBueffel[132];
|
||||
|
||||
self = (pamorSet)pData;
|
||||
assert(self);
|
||||
|
||||
if(argc < 2){
|
||||
SCWrite(pCon,"ERROR: not enough arguments to amorset",eError);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* catch component commands
|
||||
*/
|
||||
strtolower(argv[1]);
|
||||
comp = locateComponent(self,argv[1]);
|
||||
if(comp != NULL){
|
||||
return handleCompCommand(comp,pCon,argc,argv);
|
||||
}
|
||||
|
||||
/*
|
||||
* now it is for us ....
|
||||
*/
|
||||
if(strcmp(argv[1],"dspar") == 0){
|
||||
if(argc > 2){
|
||||
if(!SCMatchRights(pCon,usMugger)){
|
||||
return 0;
|
||||
}
|
||||
self->dspar = atof(argv[2]);
|
||||
SCSendOK(pCon);
|
||||
return 1;
|
||||
} else {
|
||||
snprintf(pBueffel,131,"%s dspar = %f", argv[0], self->dspar);
|
||||
SCWrite(pCon,pBueffel,eValue);
|
||||
return 1;
|
||||
}
|
||||
}else if(strcmp(argv[1],"verbose") == 0){
|
||||
if(argc > 2){
|
||||
if(!SCMatchRights(pCon,usMugger)){
|
||||
return 0;
|
||||
}
|
||||
self->verbose = atoi(argv[2]);
|
||||
SCSendOK(pCon);
|
||||
return 1;
|
||||
} else {
|
||||
snprintf(pBueffel,131,"%s verbose = %d", argv[0], self->verbose);
|
||||
SCWrite(pCon,pBueffel,eValue);
|
||||
return 1;
|
||||
}
|
||||
} else if(strcmp(argv[1],"cd") == 0){
|
||||
snprintf(pBueffel,131,"%s cd = %f", argv[0], calcCD(self));
|
||||
SCWrite(pCon,pBueffel,eValue);
|
||||
return 1;
|
||||
} else {
|
||||
SCWrite(pCon,"ERROR: unknown subcommand to amorset",eError);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user