
This is our new RELEASE-4_0 branch which was taken from ansto/93d9a7c Conflicts: .gitignore SICSmain.c asynnet.c confvirtualmot.c counter.c devexec.c drive.c event.h exebuf.c exeman.c histmem.c interface.h motor.c motorlist.c motorsec.c multicounter.c napi.c napi.h napi4.c network.c nwatch.c nxscript.c nxxml.c nxxml.h ofac.c reflist.c scan.c sicshipadaba.c sicsobj.c site_ansto/docs/Copyright.txt site_ansto/instrument/lyrebird/config/tasmad/sicscommon/nxsupport.tcl site_ansto/instrument/lyrebird/config/tasmad/taspub_sics/tasscript.tcl statusfile.c tasdrive.c tasub.c tasub.h tasublib.c tasublib.h
482 lines
13 KiB
C
482 lines
13 KiB
C
/*------------------------------------------------------------------------
|
|
H M C O N T R O L
|
|
|
|
A module for coordinating several counters and histogram
|
|
memories. One of the counters is the master counter and the rest are
|
|
slaves which have to be kept in sync with the master in their
|
|
operations.
|
|
|
|
copyright: see copyright.h
|
|
|
|
Mark Koennecke, June 2001
|
|
|
|
Added a flag which does not call halt after the counter
|
|
finished. This feauture is necessary to allow normal
|
|
counting on CCD camera. When they get interrupted, the
|
|
image is invalid. This is not what is wanted.
|
|
|
|
Mark Koennecke, July 2011
|
|
|
|
Made a special status function for BOA which aborts the CCD when the
|
|
camera overruns the counter for more then 120 seconds and repeats the
|
|
measurement.
|
|
|
|
Mark Koennecke, July 2012
|
|
-------------------------------------------------------------------------*/
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <tcl.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include "fortify.h"
|
|
#include "hmcontrol.h"
|
|
#include "HistMem.h"
|
|
#include "devexec.h"
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void *HMCGetInterface(void *pData, int ID)
|
|
{
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
if (ID == COUNTID)
|
|
return self->pCount;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static int HMCHalt(void *pData)
|
|
{
|
|
int i, retVal = OKOK, status;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
status = self->slaves[i]->Halt(self->slaveData[i]);
|
|
ReleaseCountLock(self->slaves[i]);
|
|
if (status != OKOK)
|
|
retVal = status;
|
|
}
|
|
ReleaseCountLock(self->pCount);
|
|
return retVal;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int HMCStart(void *pData, SConnection * pCon)
|
|
{
|
|
int i, status;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
if (!GetCountLock(self->pCount, pCon)) {
|
|
return HWFault;
|
|
}
|
|
|
|
for (i = 1; i < self->nSlaves; i++) {
|
|
ReleaseCountLock(self->slaves[i]);
|
|
status = self->slaves[i]->StartCount(self->slaveData[i], pCon);
|
|
if (status != OKOK) {
|
|
HMCHalt(self);
|
|
return status;
|
|
}
|
|
}
|
|
/*
|
|
* This starts the counter: always 0, last.This should be the
|
|
* right thing as the HM do not start counting in earnest before
|
|
* the busy line from the counter box is set.
|
|
*/
|
|
ReleaseCountLock(self->slaves[0]);
|
|
status = self->slaves[0]->StartCount(self->slaveData[0], pCon);
|
|
if (status != OKOK) {
|
|
HMCHalt(self);
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
Warning: this assumes that slaves 1 - MAXSLAVE are histogram memories.
|
|
If this assumption does not hold, change this code to check if this
|
|
is really a histogram memory.
|
|
*/
|
|
for (i = 1; i < MAXSLAVE; i++) {
|
|
if (self->slaves[i] != NULL) {
|
|
HistDirty((pHistMem) self->slaveData[i]);
|
|
}
|
|
}
|
|
self->checkSlaves = 0;
|
|
return OKOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int HMCStatus(void *pData, SConnection * pCon)
|
|
{
|
|
int status, i;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
if(self->checkSlaves == 0) {
|
|
/*
|
|
check master
|
|
*/
|
|
status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon);
|
|
/*
|
|
Warning: this assumes that slaves 1 - MAXSLAVE are histogram memories.
|
|
If this assumption does not hold, change this code to check if this
|
|
is really a histogram memory.
|
|
*/
|
|
for (i = 1; i < MAXSLAVE; i++) {
|
|
if (self->slaves[i] != NULL) {
|
|
HistDirty((pHistMem) self->slaveData[i]);
|
|
}
|
|
}
|
|
if (status == HWIdle || status == HWFault) {
|
|
/*
|
|
stop counting on slaves when finished or when an error
|
|
occurred.
|
|
*/
|
|
if(self->stopSlaves){
|
|
HMCHalt(self);
|
|
}
|
|
ReleaseCountLock(self->pCount);
|
|
self->checkSlaves = 1;
|
|
status = HWBusy;
|
|
}
|
|
} else {
|
|
/*
|
|
* wait for the detectors to report finish too. Otherwise, with the second
|
|
* generation HM data may not be fully transferred.
|
|
*/
|
|
for(i = 1; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->CheckCountStatus(self->slaveData[i], pCon);
|
|
if(status != HWIdle || status != HWFault){
|
|
return status;
|
|
}
|
|
}
|
|
status = HWIdle;
|
|
InvokeCallBack(self->pCall, COUNTEND, pCon);
|
|
self->checkSlaves = 0;
|
|
}
|
|
return status;
|
|
}
|
|
/*----------------------------------------------------------------------*/
|
|
static int HMCBoaStatus(void *pData, SConnection * pCon)
|
|
{
|
|
int status, i, j;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
/* check counter */
|
|
if(self->checkSlaves == 0) {
|
|
status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon);
|
|
if (status == HWIdle || status == HWFault) {
|
|
ReleaseCountLock(self->pCount);
|
|
self->checkSlaves = 1;
|
|
self->counterStop = time(NULL);
|
|
status = HWBusy;
|
|
}
|
|
} else {
|
|
/* check HM */
|
|
for(i = 1; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->CheckCountStatus(self->slaveData[i], pCon);
|
|
if(status != HWIdle || status != HWFault){
|
|
if(time(NULL) > self->counterStop + 100) {
|
|
SCWrite(pCon,"WARNING: CCD overrun, restarting counting...", eLogError);
|
|
HMCHalt(self);
|
|
ReleaseCountLock(self->pCount);
|
|
self->checkSlaves = 0;
|
|
for(j = 0; j < 200; j++){
|
|
SicsWait(1);
|
|
status = self->slaves[i]->CheckCountStatus(self->slaveData[i], pCon);
|
|
if(status == HWIdle || status == HWFault) {
|
|
HMCStart(self, pCon);
|
|
return HWBusy;
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
}
|
|
status = HWIdle;
|
|
InvokeCallBack(self->pCall, COUNTEND, pCon);
|
|
self->checkSlaves = 0;
|
|
}
|
|
return status;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static int HMCPause(void *pData, SConnection * pCon)
|
|
{
|
|
int i, status;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
status = self->slaves[i]->Pause(self->slaveData[i], pCon);
|
|
if (status != OKOK) {
|
|
HMCHalt(self);
|
|
return status;
|
|
}
|
|
}
|
|
return OKOK;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static int HMCContinue(void *pData, SConnection * pCon)
|
|
{
|
|
int i, status;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
status = self->slaves[i]->Continue(self->slaveData[i], pCon);
|
|
if (status != OKOK) {
|
|
HMCHalt(self);
|
|
return status;
|
|
}
|
|
}
|
|
return OKOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int HMCTransfer(void *pData, SConnection * pCon)
|
|
{
|
|
int i, retVal = OKOK, status;
|
|
pHMcontrol self = NULL;
|
|
char pBueffel[132];
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
status = self->slaves[i]->TransferData(self->slaveData[i], pCon);
|
|
if (status != OKOK) {
|
|
retVal = status;
|
|
snprintf(pBueffel,sizeof(pBueffel)-1,
|
|
"WARNING: slave histogram %d failed to transfer data", i);
|
|
SCWrite(pCon, pBueffel, eWarning);
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static void HMCParameter(void *pData, float fPreset, CounterMode eMode)
|
|
{
|
|
int i;
|
|
pHMcontrol self = NULL;
|
|
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
|
|
if(isRunning(self->pCount)){
|
|
return;
|
|
}
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
self->slaves[i]->SetCountParameters(self->slaveData[i], fPreset,
|
|
eMode);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void KillHMcontrol(void *pData)
|
|
{
|
|
pHMcontrol self;
|
|
|
|
self = (pHMcontrol) pData;
|
|
if (!self)
|
|
return;
|
|
|
|
if (self->pDes)
|
|
DeleteDescriptor(self->pDes);
|
|
if (self->pCount)
|
|
free(self->pCount);
|
|
free(self);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int MakeHMControl(SConnection * pCon, SicsInterp * pSics,
|
|
void *pData, int argc, char *argv[])
|
|
{
|
|
int i, status;
|
|
pHMcontrol pNew = NULL;
|
|
char pBueffel[132];
|
|
CommandList *pCom;
|
|
pICountable pCount;
|
|
|
|
/*
|
|
need at least two parameters
|
|
*/
|
|
if (argc < 3) {
|
|
SCWrite(pCon,
|
|
"ERROR: insufficient number of arguments to MakeHMControl",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
allocate our data structure
|
|
*/
|
|
pNew = (pHMcontrol) malloc(sizeof(HMcontrol));
|
|
if (!pNew) {
|
|
SCWrite(pCon, "ERROR: out of memory in MakeHMControl", eError);
|
|
return 0;
|
|
}
|
|
memset(pNew, 0, sizeof(HMcontrol));
|
|
pNew->pDes = CreateDescriptor("HMcontrol");
|
|
pNew->pCount = CreateCountableInterface();
|
|
pNew->pCall = CreateCallBackInterface();
|
|
if (!pNew->pDes || !pNew->pCount) {
|
|
SCWrite(pCon, "ERROR: out of memory in MakeHMControl", eError);
|
|
KillHMcontrol(pNew);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
assign interface functions
|
|
*/
|
|
pNew->pDes->GetInterface = HMCGetInterface;
|
|
pNew->pCount->Halt = HMCHalt;
|
|
pNew->pCount->StartCount = HMCStart;
|
|
pNew->pCount->CheckCountStatus = HMCStatus;
|
|
pNew->pCount->Pause = HMCPause;
|
|
pNew->pCount->Continue = HMCContinue;
|
|
pNew->pCount->TransferData = HMCTransfer;
|
|
pNew->pCount->SetCountParameters = HMCParameter;
|
|
|
|
/*
|
|
now loop through the remaining arguments, thereby entering them into
|
|
the slave list.
|
|
*/
|
|
for (i = 2; i < argc; i++) {
|
|
pCom = FindCommand(pSics, argv[i]);
|
|
if (!pCom) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: object %s not found in MakeHMcontrol",
|
|
argv[i]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
continue;
|
|
}
|
|
pCount = GetCountableInterface(pCom->pData);
|
|
if (!pCount) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: object %s is NOT countable", argv[i]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
continue;
|
|
}
|
|
pNew->slaves[pNew->nSlaves] = pCount;
|
|
pNew->slaveData[pNew->nSlaves] = pCom->pData;
|
|
pNew->nSlaves++;
|
|
pNew->stopSlaves = 1;
|
|
}
|
|
|
|
/*
|
|
now install our action command and we are done
|
|
*/
|
|
status = AddCommand(pSics, argv[1], HMControlAction, KillHMcontrol,
|
|
pNew);
|
|
if (!status) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: duplicate command %s not created", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
KillHMcontrol(pNew);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Syntax: whatever start preset mode
|
|
------------------------------------------------------------------------*/
|
|
int HMControlAction(SConnection * pCon, SicsInterp * pSics,
|
|
void *pData, int argc, char *argv[])
|
|
{
|
|
pHMcontrol self;
|
|
char pBueffel[132];
|
|
double dPreset;
|
|
CounterMode eMode;
|
|
int status;
|
|
|
|
/*
|
|
checks
|
|
*/
|
|
self = (pHMcontrol) pData;
|
|
assert(self);
|
|
if (argc < 2) {
|
|
snprintf(pBueffel, 131, "ERROR: Usage %s start preset mode", argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
strtolower(argv[1]);
|
|
if (strcmp(argv[1], "start") == 0) {
|
|
if (argc < 4) {
|
|
snprintf(pBueffel, 131, "ERROR: Usage %s start preset mode", argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
/*
|
|
interpret count parameters
|
|
*/
|
|
status = Tcl_GetDouble(pSics->pTcl, argv[2], &dPreset);
|
|
if (status != TCL_OK) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: failed to convert %s to number", argv[2]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
strtolower(argv[3]);
|
|
if (strcmp(argv[3], "timer") == 0)
|
|
eMode = eTimer;
|
|
else if (strcmp(argv[3], "monitor") == 0)
|
|
eMode = ePreset;
|
|
else {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: %s is no recognized count mode", argv[3]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
set count parameters and go
|
|
*/
|
|
self->pCount->SetCountParameters(self, (float) dPreset, eMode);
|
|
status = StartDevice(pServ->pExecutor, "hmcontrol", self->pDes,
|
|
self, pCon, RUNRUN, 99);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: failed to start counting", eError);
|
|
return 0;
|
|
}
|
|
InvokeCallBack(self->pCall, COUNTSTART, pCon);
|
|
SCSendOK(pCon);
|
|
}else if(strcmp(argv[1],"stopslaves") == 0){
|
|
if(argc < 3){
|
|
SCPrintf(pCon,eValue,"hm.stopslaves = %d", self->stopSlaves);
|
|
return 1;
|
|
} else {
|
|
self->stopSlaves = atoi(argv[2]);
|
|
SCPrintf(pCon,eValue,"hm.stopslaves = %d", self->stopSlaves);
|
|
return 1;
|
|
}
|
|
}else if(strcmp(argv[1],"boamode") == 0){
|
|
self->pCount->CheckCountStatus = HMCBoaStatus;
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else {
|
|
SCWrite(pCon, "ERROR: subcommand not recognized", eError);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|