diff --git a/ADPandABlocksApp/Db/ADPandABlocksCustomParam.template b/ADPandABlocksApp/Db/ADPandABlocksCustomParam.template new file mode 100644 index 0000000..35471f6 --- /dev/null +++ b/ADPandABlocksApp/Db/ADPandABlocksCustomParam.template @@ -0,0 +1,40 @@ +# % macro, PORT, Asyn Port name for ADPandABlocks comms +# % macro, P, Device prefix +# % macro, R, Device suffix +# % macro PARAM_IND, Index of custom param + +record(stringout, "$(P)$(R)PARAM$(PARAM_IND):BLOCK") { + field(DESC, "Custom Param block name") + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM_IND):BLOCK") +} + +record(stringin, "$(P)$(R)PARAM$(PARAM_IND):BLOCK_RBV") { + field(DESC, "Custom Param block name") + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM_IND):BLOCK") +} + +record(stringout, "$(P)$(R)PARAM$(PARAM_IND):FIELD") { + field(DESC, "Custom Param field name") + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM_IND):FIELD") +} + +record(stringin, "$(P)$(R)PARAM$(PARAM_IND):FIELD_RBV") { + field(DESC, "Custom Param field name") + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM_IND):FIELD") +} + +record(stringout, "$(P)$(R)PARAM$(PARAM_IND):DEMAND") { + field(DESC, "Custom Param demand value") + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM_IND):DEMAND") +} + +record(stringin, "$(P)$(R)PARAM$(PARAM_IND):DEMAND_RBV") { + field(DESC, "Custom Param demand value") + field(DTYP, "asynOctetRead") + field(INP), "@asyn($(PORT),0)PARAM$(PARAM_IND):DEMAND") +} \ No newline at end of file diff --git a/ADPandABlocksApp/Db/ADPandABlocksMotorSync.template b/ADPandABlocksApp/Db/ADPandABlocksMotorSync.template index 6d957fe..4397b52 100644 --- a/ADPandABlocksApp/Db/ADPandABlocksMotorSync.template +++ b/ADPandABlocksApp/Db/ADPandABlocksMotorSync.template @@ -102,7 +102,7 @@ record(longout, "$(P)$(R)INENC$(ENC_IND):CALIBRATE") { field(OUT, "@asyn($(PORT),0)INENC$(ENC_IND):CALIBRATE") field(FLNK, "$(P)$(R)INENC$(ENC_IND):SETPOSCALC.PROC PP") info(asyn:READBACK, "1") - } +} # Underlying motor name record (stringout, "$(P)$(R)INENC$(ENC_IND):MOTORNAME") { diff --git a/ADPandABlocksApp/Db/ADPandABlocksPosBus.template b/ADPandABlocksApp/Db/ADPandABlocksPosBus.template index 9eeeaf7..2e75163 100644 --- a/ADPandABlocksApp/Db/ADPandABlocksPosBus.template +++ b/ADPandABlocksApp/Db/ADPandABlocksPosBus.template @@ -40,6 +40,7 @@ record(mbbo, "$(P)$(R)POSBUS$(POSBUS_IND):CAPTURE") { info(asyn:READBACK, "1") } +#% archiver 10 Monitor record(ao, "$(P)$(R)POSBUS$(POSBUS_IND):SCALE") { field(DESC, "INENC scale value") field(DTYP, "asynFloat64") @@ -48,6 +49,7 @@ record(ao, "$(P)$(R)POSBUS$(POSBUS_IND):SCALE") { info(asyn:READBACK, "1") } +#% archiver 10 Monitor record(ao, "$(P)$(R)POSBUS$(POSBUS_IND):OFFSET") { field(DESC, "INENC offset value") field(DTYP, "asynFloat64") @@ -105,9 +107,10 @@ record(longout, "$(P)$(R)POSBUS$(POSBUS_IND):SETPOS") { } # Underlying motor name from MotorSync template (if it exists) -record (stringout, "$(P)$(R)POSBUS$(POSBUS_IND):MOTORNAME") { +record (stringin, "$(P)$(R)POSBUS$(POSBUS_IND):MOTORNAME") { field(DESC, "Name of motor") - field(DTYP, "asynOctetWrite") - field(OUT, "@asyn($(PORT),$(POSBUS_IND))POSBUS$(POSBUS_IND):MOTORNAME") - info(asyn:READBACK, "1") + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),$(POSBUS_IND))POSBUS$(POSBUS_IND):MOTORNAME") + field(SCAN, "I/O Intr") + #info(asyn:READBACK, "1") } diff --git a/ADPandABlocksApp/Db/ADPandABlocksSoftTTLTrigger.template b/ADPandABlocksApp/Db/ADPandABlocksSoftTTLTrigger.template new file mode 100644 index 0000000..4a7f929 --- /dev/null +++ b/ADPandABlocksApp/Db/ADPandABlocksSoftTTLTrigger.template @@ -0,0 +1,168 @@ +# % macro, PORT, Asyn Port name for ADPandABlocks comms +# % macro, P, Device prefix +# % macro, R, Device suffix +# % macro TTL_IND, Index of custom param +# % macro PULSE_IND, Index of custom param + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_TTL:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "TTLOUT$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM01:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_TTL:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "VAL") + field(OUT, "@asyn($(PORT),0)PARAM01:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_TTL:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND).OUT") + field(OUT, "@asyn($(PORT),0)PARAM01:DEMAND") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:WIDTH:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM02:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:WIDTH:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "WIDTH") + field(OUT, "@asyn($(PORT),0)PARAM02:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:WIDTH:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM02:DEMAND") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:UNITS:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND).WIDTH") + field(OUT, "@asyn($(PORT),0)PARAM03:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:UNITS:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "UNITS") + field(OUT, "@asyn($(PORT),0)PARAM03:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:UNITS:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(VAL, "s") + field(OUT, "@asyn($(PORT),0)PARAM03:DEMAND") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:ENA:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM04:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:ENA:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "ENABLE") + field(OUT, "@asyn($(PORT),0)PARAM04:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:ENA:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(VAL, "ONE") + field(OUT, "@asyn($(PORT),0)PARAM04:DEMAND") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:TRIG:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM05:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:TRIG:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "TRIG") + field(OUT, "@asyn($(PORT),0)PARAM05:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:TRIG:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM05:DEMAND") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:EDGE:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "PULSE$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM06:BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:EDGE:_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "TRIG_EDGE") + field(OUT, "@asyn($(PORT),0)PARAM06:FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:EDGE:_DEMAND") { + field(DTYP, "asynOctetWrite") + field(VAL, "Either") + field(OUT, "@asyn($(PORT),0)PARAM06:DEMAND") + field(PINI, "YES") +} + +record(scalcout, "$(P)$(R)SOFTTRIG$(TTL_IND):_CALC0") { + field(CALC, "A==0 ? 'ZERO' : 'ONE'") + field(INPA, "$(P)$(R)SOFTTRIG$(TTL_IND):_CALC1") + field(OUT, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:TRIG:_DEMAND PP") +} + +record(calcout, "$(P)$(R)SOFTTRIG$(TTL_IND):_CALC1") { + field(CALC, "(A+1) % 2") + field(INPA, "$(P)$(R)SOFTTRIG$(TTL_IND):_FLIP") + field(OUT, "$(P)$(R)SOFTTRIG$(TTL_IND):_FLIP") + field(FLNK, "$(P)$(R)SOFTTRIG$(TTL_IND):_CALC0") + field(SCAN, "Passive") +} + +record(ai, "$(P)$(R)SOFTTRIG$(TTL_IND):_FLIP") { + field(VAL, 0) +} + +record(fanout, "$(P)$(R)SOFTTRIG$(TTL_IND):Acquire") { + field(LNK1, "$(P)$(R)SOFTTRIG$(TTL_IND):_CALC1") +} + +record(ao, "$(P)$(R)SOFTTRIG$(TTL_IND):AcquireTime") { + field(OUT, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:WIDTH:_DEMAND PP") +} + +record(stringin, "$(P)$(R)SOFTTRIG$(TTL_IND):AcquireTime_RBV") { + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM02:RBV") + field(SCAN, "I/O Intr") +} + +record(fanout, "$(P)$(R)SOFTTRIG$(TTL_IND):Configure") { + field(LNK1, "$(P)$(R)SOFTTRIG$(TTL_IND):_TTL:_DEMAND") + field(LNK2, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:ENA:_DEMAND") + field(LNK3, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:UNITS:_DEMAND") + field(LNK4, "$(P)$(R)SOFTTRIG$(TTL_IND):_PULSE:EDGE:_DEMAND") +} + diff --git a/ADPandABlocksApp/Db/ADPandABlocksTTLControl.template b/ADPandABlocksApp/Db/ADPandABlocksTTLControl.template new file mode 100644 index 0000000..5e06fd1 --- /dev/null +++ b/ADPandABlocksApp/Db/ADPandABlocksTTLControl.template @@ -0,0 +1,94 @@ +# % macro, PORT, Asyn Port name for ADPandABlocks comms +# % macro, P, Device prefix +# % macro, R, Device suffix +# % macro, TTL_IND, Index of custom param +# % macro, PARAM1_IND +# % macro, PARAM2_IND + +record(stringout, "$(P)$(R)TTL$(TTL_IND):_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "TTLOUT$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM1_IND):BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "VAL") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM1_IND):FIELD") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):_DEMAND") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM1_IND):DEMAND") +} + +record(stringin, "$(P)$(R)TTL$(TTL_IND):_RBV") { + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM1_IND):RBV") + field(SCAN, "I/O Intr") +} + +record(stringin, "$(P)$(R)TTL$(TTL_IND):_CUSTOM_RBV") { + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM2_IND):RBV") + field(SCAN, "I/O Intr") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC3") +} + +record(ai, "$(P)$(R)TTL$(TTL_IND):RBV") { +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC3") { + field(CALC, "A==0 ? 0 : ( A==1 ? 1 : SSCANF(AA, '%d'))") + field(INPA, $(P)$(R)TTL$(TTL_IND):OUTPUT) + field(INAA, $(P)$(R)TTL$(TTL_IND):_CUSTOM_RBV) + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV PP") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):RBV:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM2_IND):BLOCK") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):RBV:_FIELD") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM2_IND):FIELD") +} + +record(stringin, "$(P)$(R)TTL$(TTL_IND):CUSTOM") { + field(VAL, "CUSTOM") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC1") +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC0") { + field(CALC, "A==0 ? 'ZERO' : ( A==1 ? 'ONE' : AA)") + field(INPA, $(P)$(R)TTL$(TTL_IND):OUTPUT) + field(INAA, $(P)$(R)TTL$(TTL_IND):CUSTOM) + field(OUT, "$(P)$(R)TTL$(TTL_IND):_DEMAND PP") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC3") +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC1") { + field(CALC, "SSCANF(AA, '%[^.].%*s')") + field(INAA, $(P)$(R)TTL$(TTL_IND):CUSTOM) + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC2") + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV:_BLOCK PP") +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC2") { + field(CALC, "SSCANF(AA, '%*[^.].%s')") + field(INAA, $(P)$(R)TTL$(TTL_IND):CUSTOM) + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV:_FIELD PP") +} + +record(mbbi, "$(P)$(R)TTL$(TTL_IND):OUTPUT") { + field(ZRST, "ZERO") + field(ONST, "ONE") + field(TWST, "CUSTOM") + field(VAL, 0) + field(PINI, "YES") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC0") +} + diff --git a/ADPandABlocksApp/Db/ADPandABlocksTTLOutRBV.template b/ADPandABlocksApp/Db/ADPandABlocksTTLOutRBV.template new file mode 100644 index 0000000..1a157b7 --- /dev/null +++ b/ADPandABlocksApp/Db/ADPandABlocksTTLOutRBV.template @@ -0,0 +1,67 @@ +# % macro, PORT, Asyn Port name for ADPandABlocks comms +# % macro, P, Device prefix +# % macro, R, Device suffix +# % macro, TTL_IND, Index of custom param +# % macro, PARAM1_IND +# % macro, PARAM2_IND + +record(stringout, "$(P)$(R)TTL$(TTL_IND):_BLOCK") { + field(DTYP, "asynOctetWrite") + field(VAL, "TTLOUT$(TTL_IND)") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM1_IND):BLOCK") + field(PINI, "YES") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):_FIELD") { + field(DTYP, "asynOctetWrite") + field(VAL, "VAL") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM1_IND):FIELD") + field(PINI, "YES") +} + +record(stringin, "$(P)$(R)TTL$(TTL_IND):_LINK") { + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM1_IND):RBV") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC1") + field(SCAN, "I/O Intr") +} + +record(stringin, "$(P)$(R)TTL$(TTL_IND):_LINK_RBV") { + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),0)PARAM$(PARAM2_IND):RBV") + field(SCAN, "I/O Intr") + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC3") +} + +record(ai, "$(P)$(R)TTL$(TTL_IND):RBV") { +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC3") { + field(CALC, "SSCANF(AA, '%d')") + field(INAA, $(P)$(R)TTL$(TTL_IND):_LINK_RBV) + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV PP") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):RBV:_BLOCK") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM2_IND):BLOCK") +} + +record(stringout, "$(P)$(R)TTL$(TTL_IND):RBV:_FIELD") { + field(DTYP, "asynOctetWrite") + field(OUT, "@asyn($(PORT),0)PARAM$(PARAM2_IND):FIELD") +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC1") { + field(CALC, "SSCANF(AA, '%[^.].%*s')") + field(INAA, $(P)$(R)TTL$(TTL_IND):_LINK) + field(FLNK, "$(P)$(R)TTL$(TTL_IND):_CALC2") + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV:_BLOCK PP") +} + +record(scalcout, "$(P)$(R)TTL$(TTL_IND):_CALC2") { + field(CALC, "SSCANF(AA, '%*[^.].%s')") + field(INAA, $(P)$(R)TTL$(TTL_IND):_LINK) + field(OUT, "$(P)$(R)TTL$(TTL_IND):RBV:_FIELD PP") +} + diff --git a/ADPandABlocksApp/Db/Makefile b/ADPandABlocksApp/Db/Makefile index e4ea6c2..2b7e01c 100644 --- a/ADPandABlocksApp/Db/Makefile +++ b/ADPandABlocksApp/Db/Makefile @@ -17,6 +17,10 @@ include $(TOP)/configure/CONFIG DB += ADPandABlocks.template DB += ADPandABlocksPosBus.template DB += ADPandABlocksMotorSync.template +DB += ADPandABlocksCustomParam.template +DB += ADPandABlocksTTLControl.template +DB += ADPandABlocksTTLOutRBV.template +DB += ADPandABlocksSoftTTLTrigger.template #---------------------------------------------------- # In a Diamond IOC Application, build db files from diff --git a/ADPandABlocksApp/src/ADPandABlocks.cpp b/ADPandABlocksApp/src/ADPandABlocks.cpp index 7261288..ebe58ea 100644 --- a/ADPandABlocksApp/src/ADPandABlocks.cpp +++ b/ADPandABlocksApp/src/ADPandABlocks.cpp @@ -24,286 +24,317 @@ #include -static void pollDataPortC(void *userPvt) { - ADPandABlocks *pPvt = (ADPandABlocks *) userPvt; - pPvt->readDataPort(); +static void pollDataPortC(void *userPvt) +{ + ADPandABlocks *pPvt = (ADPandABlocks *) userPvt; + pPvt->readDataPort(); } -static void pollCommandPortC(void *userPvt) { - ADPandABlocks *pPvt = (ADPandABlocks *) userPvt; - pPvt->pollCommandPort(); +static void pollCommandPortC(void *userPvt) +{ + ADPandABlocks *pPvt = (ADPandABlocks *) userPvt; + pPvt->pollCommandPort(); } -static void callbackC(asynUser *pasynUser, asynException exception){ - ADPandABlocks *pPvt = (ADPandABlocks *) pasynUser->drvUser; - pPvt->exceptionCallback(pasynUser, exception); +static void callbackC(asynUser *pasynUser, asynException exception) +{ + ADPandABlocks *pPvt = (ADPandABlocks *) pasynUser->drvUser; + pPvt->exceptionCallback(pasynUser, exception); } -void ADPandABlocks::exceptionCallback(asynUser *pasynUser, asynException exception){ - int port_connected = 0; - pandaResponsive = false; - setIntegerParam(ADPandABlocksIsResponsive, 0); - pasynManager->isConnected(pasynUser, &port_connected); - if(port_connected) { - // It seems that the PandA doesn't successfuly receive/respond to messages - // straight away after the AsynPort reconnects; keep trying until it does - while (!pandaResponsive) { - sendReceivingFormat(); - epicsThreadSleep(5.0); - } - } +void ADPandABlocks::exceptionCallback(asynUser *pasynUser, asynException exception) +{ + int port_connected = 0; + pandaResponsive = false; + setIntegerParam(ADPandABlocksIsResponsive, 0); + pasynManager->isConnected(pasynUser, &port_connected); + if (port_connected) + { + // It seems that the PandA doesn't successfuly receive/respond to messages + // straight away after the AsynPort reconnects; keep trying until it does + while (!pandaResponsive) + { + sendReceivingFormat(); + epicsThreadSleep(5.0); + } + } } typedef int static_assert_endianness[EPICS_BYTE_ORDER != EPICS_ENDIAN_BIG ? 1 : -1]; static const char *driverName = "ADPandABlocks"; -static std::map errorMsg; - -ADPandABlocks::ADPandABlocks(const char* portName, const char* pandaAddress, int maxPts, int maxBuffers, int maxMemory) : - ADDriver(portName, 1 /*maxAddr*/, NUM_PARAMS, maxBuffers, maxMemory, - asynInt8ArrayMask | asynFloat64ArrayMask | asynInt32Mask | asynFloat64Mask | asynOctetMask | asynDrvUserMask, - asynInt8ArrayMask | asynFloat64ArrayMask | asynInt32Mask | asynFloat64Mask | asynOctetMask, - ASYN_CANBLOCK, /*ASYN_CANBLOCK=1, ASYN_MULTIDEVICE=0 */ - 1, /*autoConnect*/ 0, /*default priority */ 0 /*default stack size*/) { +static std::map errorMsg; + +ADPandABlocks::ADPandABlocks(const char *portName, const char *pandaAddress, int maxPts, int maxBuffers, int maxMemory) + : + ADDriver(portName, 1 /*maxAddr*/, NUM_PARAMS, maxBuffers, maxMemory, + asynInt8ArrayMask | asynFloat64ArrayMask | asynInt32Mask | asynFloat64Mask | asynOctetMask | + asynDrvUserMask, + asynInt8ArrayMask | asynFloat64ArrayMask | asynInt32Mask | asynFloat64Mask | asynOctetMask, + ASYN_CANBLOCK, /*ASYN_CANBLOCK=1, ASYN_MULTIDEVICE=0 */ + 1, /*autoConnect*/ 0, /*default priority */ 0 /*default stack size*/) +{ - //private variables - state = waitHeaderStart; //init state for the data read - pandaResponsive = false; + //private variables + state = waitHeaderStart; //init state for the data read + pandaResponsive = false; epicsTimeGetCurrent(&lastHeaderErrorTime); - errorMsg[asynSuccess] = "asynSuccess"; - errorMsg[asynTimeout] = "asynTimeout"; - errorMsg[asynOverflow] = "asynOverflow"; - errorMsg[asynError] = "asynError"; - errorMsg[asynDisconnected] = "asynDisconnected"; - errorMsg[asynDisabled] = "asynDisabled"; - - captureType["No"] = 0; - captureType["Value"] = 1; - captureType["Diff"] = 2; - captureType["Sum"] = 3; - captureType["Mean"] = 4; - captureType["Min"] = 5; - captureType["Max"] = 6; - captureType["Min Max"] = 7; - captureType["Min Max Mean"] = 8; - captureStrings.push_back("No"); - captureStrings.push_back("Value"); - captureStrings.push_back("Diff"); - captureStrings.push_back("Sum"); - captureStrings.push_back("Mean"); - captureStrings.push_back("Min"); - captureStrings.push_back("Max"); - captureStrings.push_back("Min Max"); - captureStrings.push_back("Min Max Mean"); - - const char *functionName = "ADPandABlocks"; - asynStatus status = asynSuccess; - asynInterface *pasynInterface; - - /* For areaDetector image */ - pArray = NULL; - arrayCounter = 0; - numImagesCounter = 0; - numExposures = 1; - imgMode = ADImageContinuous; - imgNo = 0; - - /* Connection status */ - createParam("ISCONNECTED", asynParamInt32, &ADPandABlocksIsConnected); - setIntegerParam(ADPandABlocksIsConnected, 0); - createParam("ISRESPONSIVE", asynParamInt32, &ADPandABlocksIsResponsive); - setIntegerParam(ADPandABlocksIsResponsive, 0); - - /*Create a parameter to store the header value /*/ - createParam("HEADER", asynParamOctet, &ADPandABlocksHeader); - - /*Create a parameter to store the end of data string */ - createParam("DATAEND", asynParamOctet, &ADPandABlocksDataEnd); - setStringParam(ADPandABlocksDataEnd, ""); - - /* initialise areaDetector parameters */ - setStringParam(ADManufacturer, "Diamond Light Source Ltd."); - setStringParam(ADModel, "ADPandABlocks"); - setIntegerParam(ADMaxSizeX, NPOSBUS); - setIntegerParam(ADMaxSizeY, 1); - // setIntegerParam(NDDataType, 7); - setIntegerParam(ADStatus, ADStatusIdle); - setStringParam(ADStatusMessage, "Idle"); - - /* Connect to the device port */ - ctrlPort = std::string(portName).append("_CTRL").c_str(); - //drvAsynIPPortConfigure(ctrlPort, std::string(pandaAddress).append(":").append(CTRL_PORT).c_str(), 100, 0, 0); - /* Copied from asynOctecSyncIO->connect */ - pasynUser_ctrl_tx = pasynManager->createAsynUser(0, 0); - status = pasynManager->connectDevice(pasynUser_ctrl_tx, ctrlPort, 0); - if (status != asynSuccess) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Connect failed, port=%s, error=%d\n", driverName, functionName, ctrlPort, status); - return; - } - pasynInterface = pasynManager->findInterface(pasynUser_ctrl_tx, asynCommonType, 1); - if (!pasynInterface) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: %s interface not supported", driverName, functionName, asynCommonType); - return; - } - pasynInterface = pasynManager->findInterface(pasynUser_ctrl_tx, asynOctetType, 1); - if (!pasynInterface) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: %s interface not supported", driverName, functionName, asynOctetType); - return; - } - - pasynOctet_ctrl = (asynOctet *) pasynInterface->pinterface; - octetPvt_ctrl = pasynInterface->drvPvt; - asynSetOption(ctrlPort, 0, "disconnectOnReadTimeout", "Y"); - pasynUser_ctrl_tx->drvUser = (void *) this; - pasynManager->exceptionCallbackAdd(pasynUser_ctrl_tx, callbackC); - - /* Set EOS and flush */ - pasynOctet_ctrl->flush(octetPvt_ctrl, pasynUser_ctrl_tx); - pasynOctet_ctrl->setInputEos(octetPvt_ctrl, pasynUser_ctrl_tx, "\n", 1); - pasynOctet_ctrl->setOutputEos(octetPvt_ctrl, pasynUser_ctrl_tx, "\n", 1); - - // duplicate port & set timeout - + errorMsg[asynSuccess] = "asynSuccess"; + errorMsg[asynTimeout] = "asynTimeout"; + errorMsg[asynOverflow] = "asynOverflow"; + errorMsg[asynError] = "asynError"; + errorMsg[asynDisconnected] = "asynDisconnected"; + errorMsg[asynDisabled] = "asynDisabled"; + + captureType["No"] = 0; + captureType["Value"] = 1; + captureType["Diff"] = 2; + captureType["Sum"] = 3; + captureType["Mean"] = 4; + captureType["Min"] = 5; + captureType["Max"] = 6; + captureType["Min Max"] = 7; + captureType["Min Max Mean"] = 8; + captureStrings.push_back("No"); + captureStrings.push_back("Value"); + captureStrings.push_back("Diff"); + captureStrings.push_back("Sum"); + captureStrings.push_back("Mean"); + captureStrings.push_back("Min"); + captureStrings.push_back("Max"); + captureStrings.push_back("Min Max"); + captureStrings.push_back("Min Max Mean"); + + const char *functionName = "ADPandABlocks"; + asynStatus status = asynSuccess; + asynInterface *pasynInterface; + + /* For areaDetector image */ + pArray = NULL; + arrayCounter = 0; + numImagesCounter = 0; + numExposures = 1; + imgMode = ADImageContinuous; + imgNo = 0; + + /* Connection status */ + createParam("ISCONNECTED", asynParamInt32, &ADPandABlocksIsConnected); + setIntegerParam(ADPandABlocksIsConnected, 0); + createParam("ISRESPONSIVE", asynParamInt32, &ADPandABlocksIsResponsive); + setIntegerParam(ADPandABlocksIsResponsive, 0); + + /*Create a parameter to store the header value /*/ + createParam("HEADER", asynParamOctet, &ADPandABlocksHeader); + + /*Create a parameter to store the end of data string */ + createParam("DATAEND", asynParamOctet, &ADPandABlocksDataEnd); + setStringParam(ADPandABlocksDataEnd, ""); + + /* initialise areaDetector parameters */ + setStringParam(ADManufacturer, "Diamond Light Source Ltd."); + setStringParam(ADModel, "ADPandABlocks"); + setIntegerParam(ADMaxSizeX, NPOSBUS); + setIntegerParam(ADMaxSizeY, 1); + // setIntegerParam(NDDataType, 7); + setIntegerParam(ADStatus, ADStatusIdle); + setStringParam(ADStatusMessage, "Idle"); + + /* Connect to the device port */ + ctrlPort = std::string(portName).append("_CTRL").c_str(); + //drvAsynIPPortConfigure(ctrlPort, std::string(pandaAddress).append(":").append(CTRL_PORT).c_str(), 100, 0, 0); + /* Copied from asynOctecSyncIO->connect */ + pasynUser_ctrl_tx = pasynManager->createAsynUser(0, 0); + status = pasynManager->connectDevice(pasynUser_ctrl_tx, ctrlPort, 0); + if (status != asynSuccess) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Connect failed, port=%s, error=%d\n", driverName, functionName, ctrlPort, status); + return; + } + pasynInterface = pasynManager->findInterface(pasynUser_ctrl_tx, asynCommonType, 1); + if (!pasynInterface) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s interface not supported", driverName, functionName, asynCommonType); + return; + } + pasynInterface = pasynManager->findInterface(pasynUser_ctrl_tx, asynOctetType, 1); + if (!pasynInterface) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s interface not supported", driverName, functionName, asynOctetType); + return; + } + + pasynOctet_ctrl = (asynOctet *) pasynInterface->pinterface; + octetPvt_ctrl = pasynInterface->drvPvt; + asynSetOption(ctrlPort, 0, "disconnectOnReadTimeout", "Y"); + pasynUser_ctrl_tx->drvUser = (void *) this; + pasynManager->exceptionCallbackAdd(pasynUser_ctrl_tx, callbackC); + + /* Set EOS and flush */ + pasynOctet_ctrl->flush(octetPvt_ctrl, pasynUser_ctrl_tx); + pasynOctet_ctrl->setInputEos(octetPvt_ctrl, pasynUser_ctrl_tx, "\n", 1); + pasynOctet_ctrl->setOutputEos(octetPvt_ctrl, pasynUser_ctrl_tx, "\n", 1); + + // duplicate port & set timeout + pasynUser_ctrl_rx = pasynManager->duplicateAsynUser(pasynUser_ctrl_tx, 0, 0); - pasynUser_ctrl_rx->timeout = 3.0; - - /* Connect to the data port */ - dataPort = std::string(portName).append("_DATA").c_str(); - //drvAsynIPPortConfigure(dataPort, std::string(pandaAddress).append(":").append(DATA_PORT).c_str(), 100, 0, 0); - /* Copied from asynOctecSyncIO->connect */ - pasynUser_data = pasynManager->createAsynUser(0, 0); - status = pasynManager->connectDevice(pasynUser_data, dataPort, 0); - if (status != asynSuccess) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Connect failed, port=%s, error=%d\n", driverName, functionName, dataPort, status); - return; - } - pasynInterface = pasynManager->findInterface(pasynUser_data, asynCommonType, 1); - if (!pasynInterface) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: %s interface not supported", driverName, functionName, asynCommonType); - return; - } - pasynCommon_data = (asynCommon *) pasynInterface->pinterface; - pcommonPvt_data = pasynInterface->drvPvt; - pasynInterface = pasynManager->findInterface(pasynUser_data, asynOctetType, 1); - if (!pasynInterface) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: %s interface not supported", driverName, functionName, asynOctetType); - return; - } - pasynOctet_data = (asynOctet *) pasynInterface->pinterface; - octetPvt_data = pasynInterface->drvPvt; - - /* Set EOS and flush */ - pasynOctet_data->flush(octetPvt_data, pasynUser_data); - pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "\n", 1); - pasynOctet_data->setOutputEos(octetPvt_data, pasynUser_data, "\n", 1); - pasynUser_data->timeout = LONGWAIT; - - /*set the receiving format on the data channel*/ - sendReceivingFormat(); - /*Get the labels for the bitmask*/ - int numBitMasks = 0; - for(int n=0; n<4; n++) - { - std::stringstream bitmaskCmd; - bitmaskCmd << "PCAP.BITS"<< n << ".BITS?"; - sendCtrl(bitmaskCmd.str()); - bitMasks.push_back(readFieldNames(&numBitMasks)); - } - - /*Get the POSITION fields*/ - std::stringstream fieldCmd; - fieldCmd << "*POSITIONS?"; - sendCtrl(fieldCmd.str()); - int numPosFields = 0; - posFields.push_back(readFieldNames(&numPosFields)); - while (numPosFields < 32) - { - std::stringstream fieldNum; - fieldNum << "POSBUS" << numPosFields+1; - posFields[0].push_back(fieldNum.str()); - numPosFields++; - } - - /*Make params for each of the POSITION fields*/ - char str[NBUFF]; - for(int a = 0; a < 32; a++) - { - if(a == 0 || a < numPosFields){ - epicsSnprintf(str, NBUFF, "POSBUS%d", a); - createParam(str, asynParamOctet, &ADPandABlocksPosFields[a]); - setStringParam(ADPandABlocksPosFields[a], posFields[0][a].c_str()); - } - else{ - epicsSnprintf(str, NBUFF, "POSBUS%d", a); - createParam(str, asynParamOctet, &ADPandABlocksPosFields[a]); - setStringParam(ADPandABlocksPosFields[a], "UNUSED"); - } - } - - /*Make the params for the Motor fields*/ - for(int a = 0; a < 4; a++) - { - epicsSnprintf(str, NBUFF, "INENC%d:SCALE", a+1); - createParam(str, asynParamFloat64, &ADPandABlocksMScale[a]); - epicsSnprintf(str, NBUFF, "INENC%d:OFF", a+1); - createParam(str, asynParamFloat64, &ADPandABlocksMOffset[a]); - epicsSnprintf(str, NBUFF, "INENC%d:UNITS", a+1); - createParam(str, asynParamOctet, &ADPandABlocksMUnits[a]); - epicsSnprintf(str, NBUFF, "INENC%d:SETPOS", a+1); - createParam(str, asynParamInt32, &ADPandABlocksMSetpos[a]); - epicsSnprintf(str, NBUFF, "INENC%d:SCREENTYPE", a+1); - createParam(str, asynParamInt32, &ADPandABlocksMScreenType[a]); - epicsSnprintf(str, NBUFF, "INENC%d:CALIBRATE", a+1); - createParam(str, asynParamInt32, &ADPandABlocksMCalibrate[a]); - epicsSnprintf(str, NBUFF, "INENC%d:MOTORNAME", a+1); - createParam(str, asynParamOctet, &ADPandABlocksMMotorName[a]); - } - - // Create the lookup table parameters for the position bus - int posBusInd = 0; - for(std::vector::iterator it = posFields[0].begin(); it != posFields[0].end(); ++it) - { - createLookup(*it, "VAL", &ADPandABlocksPosVals[posBusInd], posBusInd); - createLookup(*it, "SCALE", &ADPandABlocksScale[posBusInd], posBusInd); - createLookup(*it, "OFFSET", &ADPandABlocksOffset[posBusInd], posBusInd); - createLookup(*it, "UNITS", &ADPandABlocksUnits[posBusInd], posBusInd); - createLookup(*it, "CAPTURE", &ADPandABlocksCapture[posBusInd], posBusInd); - createLookup(*it, "UNSCALEDVAL", &ADPandABlocksPosUnscaledVals[posBusInd], posBusInd); - createLookup(*it, "SCREENTYPE", &ADPandABlocksScreenType[posBusInd], posBusInd); - createLookup(*it, "CALIBRATE", &ADPandABlocksCalibrate[posBusInd], posBusInd); - createLookup(*it, "SETPOS", &ADPandABlocksSetpos[posBusInd], posBusInd); - createLookup(*it, "MOTORNAME", &ADPandABlocksMotorName[posBusInd], posBusInd); - posBusInd++; - } - - // Initialise position bus values - processChanges("*CHANGES.ATTR?", false); - processChanges("*CHANGES.POSN?", true); - - /* Create thread to monitor command port for position bus changes */ - if (epicsThreadCreate("ADPandABlocksPollCommandPort", epicsThreadPriorityMedium, - epicsThreadGetStackSize(epicsThreadStackMedium), - (EPICSTHREADFUNC) pollCommandPortC, this) == NULL) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: epicsThreadCreate failure for reading task\n", driverName, functionName); - return; - } - - /* Create thread to monitor data port */ - if (epicsThreadCreate("ADPandABlocksPollDataPort", epicsThreadPriorityMedium, - epicsThreadGetStackSize(epicsThreadStackMedium), - (EPICSTHREADFUNC) pollDataPortC, this) == NULL) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: epicsThreadCreate failure for reading task\n", driverName, functionName); - return; - } + pasynUser_ctrl_rx->timeout = 3.0; + + /* Connect to the data port */ + dataPort = std::string(portName).append("_DATA").c_str(); + //drvAsynIPPortConfigure(dataPort, std::string(pandaAddress).append(":").append(DATA_PORT).c_str(), 100, 0, 0); + /* Copied from asynOctecSyncIO->connect */ + pasynUser_data = pasynManager->createAsynUser(0, 0); + status = pasynManager->connectDevice(pasynUser_data, dataPort, 0); + if (status != asynSuccess) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Connect failed, port=%s, error=%d\n", driverName, functionName, dataPort, status); + return; + } + pasynInterface = pasynManager->findInterface(pasynUser_data, asynCommonType, 1); + if (!pasynInterface) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s interface not supported", driverName, functionName, asynCommonType); + return; + } + pasynCommon_data = (asynCommon *) pasynInterface->pinterface; + pcommonPvt_data = pasynInterface->drvPvt; + pasynInterface = pasynManager->findInterface(pasynUser_data, asynOctetType, 1); + if (!pasynInterface) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: %s interface not supported", driverName, functionName, asynOctetType); + return; + } + pasynOctet_data = (asynOctet *) pasynInterface->pinterface; + octetPvt_data = pasynInterface->drvPvt; + + /* Set EOS and flush */ + pasynOctet_data->flush(octetPvt_data, pasynUser_data); + pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "\n", 1); + pasynOctet_data->setOutputEos(octetPvt_data, pasynUser_data, "\n", 1); + pasynUser_data->timeout = LONGWAIT; + + /*set the receiving format on the data channel*/ + sendReceivingFormat(); + /*Get the labels for the bitmask*/ + int numBitMasks = 0; + for (int n = 0; n < 4; n++) + { + std::stringstream bitmaskCmd; + bitmaskCmd << "PCAP.BITS" << n << ".BITS?"; + sendCtrl(bitmaskCmd.str()); + bitMasks.push_back(readFieldNames(&numBitMasks)); + } + + /*Get the POSITION fields*/ + std::stringstream fieldCmd; + fieldCmd << "*POSITIONS?"; + sendCtrl(fieldCmd.str()); + int numPosFields = 0; + posFields.push_back(readFieldNames(&numPosFields)); + while (numPosFields < 32) + { + std::stringstream fieldNum; + fieldNum << "POSBUS" << numPosFields + 1; + posFields[0].push_back(fieldNum.str()); + numPosFields++; + } + + /*Make params for each of the POSITION fields*/ + char str[NBUFF]; + for (int a = 0; a < 32; a++) + { + if (a == 0 || a < numPosFields) + { + epicsSnprintf(str, NBUFF, "POSBUS%d", a); + createParam(str, asynParamOctet, &ADPandABlocksPosFields[a]); + setStringParam(ADPandABlocksPosFields[a], posFields[0][a].c_str()); + } else + { + epicsSnprintf(str, NBUFF, "POSBUS%d", a); + createParam(str, asynParamOctet, &ADPandABlocksPosFields[a]); + setStringParam(ADPandABlocksPosFields[a], "UNUSED"); + } + } + + /*Make the params for the Motor fields*/ + for (int a = 0; a < 4; a++) + { + epicsSnprintf(str, NBUFF, "INENC%d:SCALE", a + 1); + createParam(str, asynParamFloat64, &ADPandABlocksMScale[a]); + epicsSnprintf(str, NBUFF, "INENC%d:OFF", a + 1); + createParam(str, asynParamFloat64, &ADPandABlocksMOffset[a]); + epicsSnprintf(str, NBUFF, "INENC%d:UNITS", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksMUnits[a]); + epicsSnprintf(str, NBUFF, "INENC%d:SETPOS", a + 1); + createParam(str, asynParamInt32, &ADPandABlocksMSetpos[a]); + epicsSnprintf(str, NBUFF, "INENC%d:SCREENTYPE", a + 1); + createParam(str, asynParamInt32, &ADPandABlocksMScreenType[a]); + epicsSnprintf(str, NBUFF, "INENC%d:CALIBRATE", a + 1); + createParam(str, asynParamInt32, &ADPandABlocksMCalibrate[a]); + epicsSnprintf(str, NBUFF, "INENC%d:MOTORNAME", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksMMotorName[a]); + } + + /*Make the params for the custom block params*/ + for (int a = 0; a < NCUSTOM; a++) + { + epicsSnprintf(str, NBUFF, "PARAM%02d:BLOCK", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksCustomParamBlock[a]); + epicsSnprintf(str, NBUFF, "PARAM%02d:FIELD", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksCustomParamField[a]); + epicsSnprintf(str, NBUFF, "PARAM%02d:DEMAND", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksCustomParamDemand[a]); + + epicsSnprintf(str, NBUFF, "PARAM%02d:RBV", a + 1); + createParam(str, asynParamOctet, &ADPandABlocksCustomParamRBV[a]); + } + + // Create the lookup table parameters for the position bus + int posBusInd = 0; + for (std::vector::iterator it = posFields[0].begin(); it != posFields[0].end(); ++it) + { + createLookup(*it, "VAL", &ADPandABlocksPosVals[posBusInd], posBusInd); + createLookup(*it, "SCALE", &ADPandABlocksScale[posBusInd], posBusInd); + createLookup(*it, "OFFSET", &ADPandABlocksOffset[posBusInd], posBusInd); + createLookup(*it, "UNITS", &ADPandABlocksUnits[posBusInd], posBusInd); + createLookup(*it, "CAPTURE", &ADPandABlocksCapture[posBusInd], posBusInd); + createLookup(*it, "UNSCALEDVAL", &ADPandABlocksPosUnscaledVals[posBusInd], posBusInd); + createLookup(*it, "SCREENTYPE", &ADPandABlocksScreenType[posBusInd], posBusInd); + createLookup(*it, "CALIBRATE", &ADPandABlocksCalibrate[posBusInd], posBusInd); + createLookup(*it, "SETPOS", &ADPandABlocksSetpos[posBusInd], posBusInd); + createLookup(*it, "MOTORNAME", &ADPandABlocksMotorName[posBusInd], posBusInd); + posBusInd++; + } + + // Initialise position bus values + processChanges("*CHANGES?"); + + /* Create thread to monitor command port for position bus changes */ + if (epicsThreadCreate("ADPandABlocksPollCommandPort", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + (EPICSTHREADFUNC) pollCommandPortC, this) == NULL) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: epicsThreadCreate failure for reading task\n", driverName, functionName); + return; + } + + /* Create thread to monitor data port */ + if (epicsThreadCreate("ADPandABlocksPollDataPort", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + (EPICSTHREADFUNC) pollDataPortC, this) == NULL) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: epicsThreadCreate failure for reading task\n", driverName, functionName); + return; + } }; /* @@ -319,61 +350,77 @@ ADPandABlocks::~ADPandABlocks () { * \param[in] value New value */ template -void ADPandABlocks::updatePandAParam(std::string name, std::string field, T value) +bool ADPandABlocks::updatePandAParam(std::string name, std::string field, T value) { } + template<> -void ADPandABlocks::updatePandAParam(std::string name, std::string field, double value) -{ - if(posBusLookup.find(name) != posBusLookup.end()) - { - if(posBusLookup[name].find(field) != posBusLookup[name].end()) - { - setDoubleParam(*posBusLookup[name][field], value); - if (field == "SCALE" || field == "OFFSET") - { - updateScaledPositionValue(name); - } - } - } +bool ADPandABlocks::updatePandAParam(std::string name, std::string field, double value) +{ + if (posBusLookup.find(name) != posBusLookup.end()) + { + if (posBusLookup[name].find(field) != posBusLookup[name].end()) + { + setDoubleParam(*posBusLookup[name][field], value); + if (field == "SCALE" || field == "OFFSET") + { + updateScaledPositionValue(name); + } + return true; + } + } + return false; } + template<> -void ADPandABlocks::updatePandAParam(std::string name, std::string field, std::string value) +bool ADPandABlocks::updatePandAParam(std::string name, std::string field, std::string value) { - if(posBusLookup.find(name) != posBusLookup.end()) - { - if(posBusLookup[name].find(field) != posBusLookup[name].end()) - { - setStringParam(*posBusLookup[name][field], value); - } - } + if (posBusLookup.find(name) != posBusLookup.end()) + { + if (posBusLookup[name].find(field) != posBusLookup[name].end()) + { + setStringParam(*posBusLookup[name][field], value); + return true; + } + } + return false; } + template<> -void ADPandABlocks::updatePandAParam(std::string name, std::string field, int value) -{ - if(posBusLookup.find(name) != posBusLookup.end()) - { - if(posBusLookup[name].find(field) != posBusLookup[name].end()) - { - if (field == "VAL") - { - setIntegerParam(*posBusLookup[name]["UNSCALEDVAL"], value); - updateScaledPositionValue(name); - } - else setIntegerParam(*posBusLookup[name][field], value); - } - } +bool ADPandABlocks::updatePandAParam(std::string name, std::string field, int value) +{ + if (posBusLookup.find(name) != posBusLookup.end()) + { + if (posBusLookup[name].find(field) != posBusLookup[name].end()) + { + if (field == "VAL") + { + setIntegerParam(*posBusLookup[name]["UNSCALEDVAL"], value); + updateScaledPositionValue(name); + return true; + } else + { + setIntegerParam(*posBusLookup[name][field], value); + return true; + } + } + } + return false; } + template<> -void ADPandABlocks::updatePandAParam(std::string name, std::string field, ADPandABlocks::embeddedScreenType value) +bool ADPandABlocks::updatePandAParam(std::string name, std::string field, + ADPandABlocks::embeddedScreenType value) { - if(posBusLookup.find(name) != posBusLookup.end()) - { - if(posBusLookup[name].find(field) != posBusLookup[name].end()) - { - setIntegerParam(*posBusLookup[name]["SCREENTYPE"], static_cast(value)); - } - } + if (posBusLookup.find(name) != posBusLookup.end()) + { + if (posBusLookup[name].find(field) != posBusLookup[name].end()) + { + setIntegerParam(*posBusLookup[name]["SCREENTYPE"], static_cast(value)); + return true; + } + } + return false; } /* @@ -386,760 +433,877 @@ template void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, T value) { } + template<> void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, int value) { - std::stringstream posBusName, posBusField; - posBusName << "INENC" << motorIndex << ".VAL"; - switch(field) - { - case setpos: { - // Send command to PandA to calibrate motor position - posBusField << "SETPOS"; - setPandASetPos(posBusName.str(), value); - break; - } - default: { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); - break; - } - } - updatePandAParam(posBusName.str(), posBusField.str(), value); + std::stringstream posBusName, posBusField; + posBusName << "INENC" << motorIndex << ".VAL"; + switch (field) + { + case setpos: + { + // Send command to PandA to calibrate motor position + posBusField << "SETPOS"; + setPandASetPos(posBusName.str(), value); + break; + } + default: + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); + break; + } + } + updatePandAParam(posBusName.str(), posBusField.str(), value); } + template<> -void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, ADPandABlocks::embeddedScreenType value) -{ - std::stringstream posBusName, posBusField; - posBusName << "INENC" << motorIndex << ".VAL"; - switch(field) - { - case screen: { - // Change screen type to use readOnly - posBusField << "SCREENTYPE"; - break; - } - default: { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); - break; - } - } - updatePandAParam(posBusName.str(), posBusField.str(), value); +void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, + ADPandABlocks::embeddedScreenType value) +{ + std::stringstream posBusName, posBusField; + posBusName << "INENC" << motorIndex << ".VAL"; + switch (field) + { + case screen: + { + // Change screen type to use readOnly + posBusField << "SCREENTYPE"; + break; + } + default: + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); + break; + } + } + updatePandAParam(posBusName.str(), posBusField.str(), value); } + template<> void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, double value) { - std::stringstream posBusName, posBusField; - posBusName << "INENC" << motorIndex << ".VAL"; - switch(field) - { - case offset: { - posBusField << "OFFSET"; - break; - } - case scale: { - posBusField << "SCALE"; - break; - } - default: { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); - break; - } - } - updatePandAParam(posBusName.str(), posBusField.str(), value); + std::stringstream posBusName, posBusField; + posBusName << "INENC" << motorIndex << ".VAL"; + switch (field) + { + case offset: + { + posBusField << "OFFSET"; + break; + } + case scale: + { + posBusField << "SCALE"; + break; + } + default: + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); + break; + } + } + updatePandAParam(posBusName.str(), posBusField.str(), value); } + template<> void ADPandABlocks::updatePandAMotorParam(int motorIndex, motorField field, std::string value) { - std::stringstream posBusName, posBusField; - posBusName << "INENC" << motorIndex << ".VAL"; - switch(field) - { - case units: { - posBusField << "UNITS"; - break; - } - case motorName: { - posBusField << "MOTORNAME"; - break; - } - default: { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); - break; - } - } - updatePandAParam(posBusName.str(), posBusField.str(), value); -} - -std::string ADPandABlocks::getPosBusField(std::string posbus, const char* paramName){ - std::string field; - std::stringstream cmdStr; - cmdStr << posbus << "." << paramName << "?"; - sendCtrl(cmdStr.str()); - readPosBusValues(&field); - return field; -} - -void ADPandABlocks::createPosBusParam(const char* paramName, asynParamType paramType, int* paramIndex, int paramNo){ - char str[NBUFF]; - epicsSnprintf(str, NBUFF, "POSBUS%d:%s", paramNo, paramName); - createParam(str, paramType, paramIndex); -} - -bool ADPandABlocks::posBusInUse(std::string posBusName) { - // Unused position buses will have the name POSBUS - std::size_t found = posBusName.find("POSBUS"); - if (found == std::string::npos) return true; - else return false; + std::stringstream posBusName, posBusField; + posBusName << "INENC" << motorIndex << ".VAL"; + switch (field) + { + case units: + { + posBusField << "UNITS"; + break; + } + case motorName: + { + posBusField << "MOTORNAME"; + break; + } + default: + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "Invalid switch case for: %d\n", field); + break; + } + } + updatePandAParam(posBusName.str(), posBusField.str(), value); +} + +std::string ADPandABlocks::getPosBusField(std::string posbus, const char *paramName) +{ + std::string field; + std::stringstream cmdStr; + cmdStr << posbus << "." << paramName << "?"; + sendCtrl(cmdStr.str()); + readPosBusValues(&field); + return field; +} + +void ADPandABlocks::createPosBusParam(const char *paramName, asynParamType paramType, int *paramIndex, int paramNo) +{ + char str[NBUFF]; + epicsSnprintf(str, NBUFF, "POSBUS%d:%s", paramNo, paramName); + createParam(str, paramType, paramIndex); +} + +bool ADPandABlocks::posBusInUse(std::string posBusName) +{ + // Unused position buses will have the name POSBUS + std::size_t found = posBusName.find("POSBUS"); + if (found == std::string::npos) return true; + else return false; } // TODO: consider renaming paramName to position bus name -void ADPandABlocks::createLookup(std::string paramName, std::string paramNameEnd, int* paramInd, int posBusInd) -{ - std::map lpMap2; - if(paramNameEnd == "CAPTURE" || paramNameEnd == "UNSCALEDVAL" || paramNameEnd == "SCREENTYPE" || paramNameEnd == "CALIBRATE" || paramNameEnd == "SETPOS"){ - createPosBusParam(paramNameEnd.c_str(), asynParamInt32, paramInd, posBusInd); - posBusLookup.insert(std::pair >(paramName, lpMap2)); - posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); - // Set screen type based on whether PosBus is in use - if (paramNameEnd == "SCREENTYPE") - { - if (posBusInUse(paramName)){ - setIntegerParam(*posBusLookup[paramName][paramNameEnd], writeable); - } - else setIntegerParam(*posBusLookup[paramName][paramNameEnd], empty); - } - } - else if(paramNameEnd == "UNITS" || paramNameEnd == "MOTORNAME") - { - createPosBusParam(paramNameEnd.c_str(), asynParamOctet, paramInd, posBusInd); - posBusLookup.insert(std::pair >(paramName, lpMap2)); - posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); - } - // VAL, SCALE and OFFSET - else - { - createPosBusParam(paramNameEnd.c_str(), asynParamFloat64, paramInd, posBusInd); - posBusLookup.insert(std::pair >(paramName, lpMap2)); - posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); - } +void ADPandABlocks::createLookup(std::string paramName, std::string paramNameEnd, int *paramInd, int posBusInd) +{ + std::map lpMap2; + if (paramNameEnd == "CAPTURE" || paramNameEnd == "UNSCALEDVAL" || paramNameEnd == "SCREENTYPE" || + paramNameEnd == "CALIBRATE" || paramNameEnd == "SETPOS") + { + createPosBusParam(paramNameEnd.c_str(), asynParamInt32, paramInd, posBusInd); + posBusLookup.insert(std::pair < std::string, std::map < std::string, int * > > (paramName, lpMap2)); + posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); + // Set screen type based on whether PosBus is in use + if (paramNameEnd == "SCREENTYPE") + { + if (posBusInUse(paramName)) + { + setIntegerParam(*posBusLookup[paramName][paramNameEnd], writeable); + } else setIntegerParam(*posBusLookup[paramName][paramNameEnd], empty); + } + } else if (paramNameEnd == "UNITS" || paramNameEnd == "MOTORNAME") + { + createPosBusParam(paramNameEnd.c_str(), asynParamOctet, paramInd, posBusInd); + posBusLookup.insert(std::pair < std::string, std::map < std::string, int * > > (paramName, lpMap2)); + posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); + } + // VAL, SCALE and OFFSET + else + { + createPosBusParam(paramNameEnd.c_str(), asynParamFloat64, paramInd, posBusInd); + posBusLookup.insert(std::pair < std::string, std::map < std::string, int * > > (paramName, lpMap2)); + posBusLookup[paramName].insert(std::pair(paramNameEnd, paramInd)); + } } /* This is the function that will be run for the read thread */ -std::vector ADPandABlocks::readFieldNames(int* numFields) { - const char *functionName = "readFieldNames"; - char rxBuffer[N_BUFF_CTRL]; - size_t nBytesIn; - int eomReason; - asynStatus status = asynSuccess; - std::vector fieldNameStrings; - status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, - &nBytesIn, &eomReason); - - // We failed to read the field names, return empty object - if (status != asynSuccess) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: readFieldNames read failed, error=%d\n", driverName, functionName, status); - return fieldNameStrings; - } - - int i = 0; - while(rxBuffer[0] != '.') - { - if (strlen(rxBuffer) == 0) break; - i++; - if (eomReason & ASYN_EOM_EOS) { - // Replace the terminator with a null so we can use it as a string - rxBuffer[nBytesIn] = '\0'; - asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, - "%s:%s: Message: '%s'\n", driverName, functionName, rxBuffer); - } else { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Bad message 'bt%.*s'\n", driverName, functionName, (int)nBytesIn, rxBuffer); - } - if(rxBuffer[0] == '!') - { - char* strippedLabel = rxBuffer + 1; - fieldNameStrings.push_back(strippedLabel); - } - // Push the whole bitmask for 'n' to the vector of vectors - status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, - &nBytesIn, &eomReason); - } - *numFields = i; - return fieldNameStrings; +std::vector ADPandABlocks::readFieldNames(int *numFields) +{ + const char *functionName = "readFieldNames"; + char rxBuffer[N_BUFF_CTRL]; + size_t nBytesIn; + int eomReason; + asynStatus status = asynSuccess; + std::vector fieldNameStrings; + status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, + &nBytesIn, &eomReason); + + // We failed to read the field names, return empty object + if (status != asynSuccess) + { + + // Check if we are connected + int connected; + getIntegerParam(ADPandABlocksIsConnected, &connected); + // Only print error if we expect a response + if (connected) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: readFieldNames read failed, error=%d\n", driverName, functionName, status); + } + return fieldNameStrings; + } + + int i = 0; + while (rxBuffer[0] != '.') + { + if (strlen(rxBuffer) == 0) break; + i++; + if (eomReason & ASYN_EOM_EOS) + { + // Replace the terminator with a null so we can use it as a string + rxBuffer[nBytesIn] = '\0'; + asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s: Message: '%s'\n", driverName, functionName, rxBuffer); + } else + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Bad message 'bt%.*s'\n", driverName, functionName, (int) nBytesIn, rxBuffer); + } + if (rxBuffer[0] == '!') + { + char *strippedLabel = rxBuffer + 1; + fieldNameStrings.push_back(strippedLabel); + } + // Push the whole bitmask for 'n' to the vector of vectors + status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, + &nBytesIn, &eomReason); + } + *numFields = i; + return fieldNameStrings; } /* This is the function that will be run for the read thread */ -asynStatus ADPandABlocks::readPosBusValues(std::string* posBusValue) { - const char *functionName = "readPosBusValues"; - char rxBuffer[N_BUFF_CTRL]; - size_t nBytesIn; - int eomReason; - asynStatus status = asynSuccess; - status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, - &nBytesIn, &eomReason); - if (eomReason & ASYN_EOM_EOS) { - // Replace the terminator with a null so we can use it as a string - rxBuffer[nBytesIn] = '\0'; - asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, - "%s:%s: Message: '%s'\n", driverName, functionName, rxBuffer); - } else { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Bad message 'bt%.*s'\n", driverName, functionName, (int)nBytesIn, rxBuffer); - } - - if(rxBuffer[0] == 'O' && rxBuffer[1] == 'K') - { - char* readValue = rxBuffer + 4; - posBusValue->assign(readValue); - } else { - //we didn't recieve OK so something went wrong - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s Bad response: %d, %s\n", driverName, functionName, (int)nBytesIn, rxBuffer); - status = asynError; - } - return status; -} - -void ADPandABlocks::pollCommandPort(){ - /* This will check if anything has changed on the panda and update the +asynStatus ADPandABlocks::readPosBusValues(std::string *posBusValue) +{ + const char *functionName = "readPosBusValues"; + char rxBuffer[N_BUFF_CTRL]; + size_t nBytesIn; + int eomReason; + asynStatus status = asynSuccess; + status = pasynOctet_ctrl->read(octetPvt_ctrl, pasynUser_ctrl_rx, rxBuffer, N_BUFF_CTRL - 1, + &nBytesIn, &eomReason); + if (eomReason & ASYN_EOM_EOS) + { + // Replace the terminator with a null so we can use it as a string + rxBuffer[nBytesIn] = '\0'; + asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s: Message: '%s'\n", driverName, functionName, rxBuffer); + } else + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Bad message 'bt%.*s'\n", driverName, functionName, (int) nBytesIn, rxBuffer); + } + + if (rxBuffer[0] == 'O' && rxBuffer[1] == 'K') + { + char *readValue = rxBuffer + 4; + posBusValue->assign(readValue); + } else + { + //we didn't recieve OK so something went wrong + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s Bad response: %d, %s\n", driverName, functionName, (int) nBytesIn, rxBuffer); + status = asynError; + } + return status; +} + +void ADPandABlocks::pollCommandPort() +{ + /* This will check if anything has changed on the panda and update the Readback values */ - while(true) - { - epicsTimeGetCurrent(&pollStartTime); - this->lock(); - processChanges("*CHANGES.ATTR?", false); - processChanges("*CHANGES.POSN?", true); - this->unlock(); - callParamCallbacks(); - epicsTimeGetCurrent(&pollEndTime); - epicsThreadSleep(0.1-epicsTimeDiffInSeconds(&pollEndTime, &pollStartTime)); - } -} - -void ADPandABlocks::processChanges(std::string cmd, bool posn) -{ - std::vector > changed; - std::vector changedField; - int numChanges = 0; - sendCtrl(cmd); - changed.clear(); - changed.push_back(readFieldNames(&numChanges)); - for(std::vector::iterator it = changed[0].begin(); it != changed[0].end(); ++it) - { - std::vector changedParts = stringSplit(*it, '='); - std::stringstream posBusName; - std::string posBusNameStr; - changedField.clear(); - changedField = stringSplit(changedParts[0], '.'); - std::string fieldName = changedField[1]; - std::string fieldVal = ""; - if(posn){ - posBusName << changedParts[0]; - fieldName = "VAL"; - } - else{ - posBusName << changedField[0] << "." << changedField[1]; - fieldName = changedField[2]; - } - if(changedParts.size() == 2) - { - fieldVal = changedParts[1]; - } - posBusNameStr = posBusName.str(); - - // Update asyn parameter - if (fieldName == "CAPTURE") - { - updatePandAParam(posBusNameStr, fieldName, captureType[fieldVal.c_str()]); - } - else if (fieldName == "VAL") - { - updatePandAParam(posBusNameStr, fieldName, stringToInteger(fieldVal)); - } - else if (fieldName == "SCALE" || fieldName == "OFFSET") - { - updatePandAParam(posBusNameStr, fieldName, stringToDouble(fieldVal)); - } - else - { - updatePandAParam(posBusNameStr, fieldName, fieldVal); - } - - } + while (true) + { + epicsTimeGetCurrent(&pollStartTime); + this->lock(); + processChanges("*CHANGES?"); + this->unlock(); + callParamCallbacks(); + epicsTimeGetCurrent(&pollEndTime); + epicsThreadSleep(0.1 - epicsTimeDiffInSeconds(&pollEndTime, &pollStartTime)); + } +} + +bool ADPandABlocks::checkIfCustomParam(std::string paramName, std::string fieldName, std::string val) +{ + std::map < std::string, std::map < std::string, std::pair < int *, int * > > > ::iterator + it = customParamLookup.find(paramName); + + if (it != customParamLookup.end()) + { + std::map >::iterator it2 = it->second.find(fieldName); + if (it2 != it->second.end()) + { + setStringParam(*(it2->second.second), val.c_str()); + return true; + } + } + return false; +} + +void ADPandABlocks::processChanges(std::string cmd) +{ + std::vector > changed; + std::vector changedField; + int numChanges = 0; + sendCtrl(cmd); + changed.clear(); + changed.push_back(readFieldNames(&numChanges)); + for (std::vector::iterator it = changed[0].begin(); it != changed[0].end(); ++it) + { + std::vector changedParts = stringSplit(*it, '='); + std::stringstream posBusName; + std::string posBusNameStr; + changedField.clear(); + changedField = stringSplit(changedParts[0], '.'); + std::string fieldName = changedField[1]; + std::string fieldVal = ""; + + if (changedField.size() == 2) + { + if (changedField[0].rfind("INENC", 0) == 0) + { + if (changedField[1] == "VAL") + { + posBusName << changedParts[0]; + fieldName = "VAL"; + } + } else + { + posBusName << changedField[0]; + fieldName = changedField[1]; + } + } else + { + posBusName << changedField[0] << "." << changedField[1]; + fieldName = changedField[2]; + } + if (changedParts.size() == 2) + { + fieldVal = changedParts[1]; + } + posBusNameStr = posBusName.str(); + // Update asyn parameter + bool updated = false; + if (fieldName == "CAPTURE") + { + updated = updatePandAParam(posBusNameStr, fieldName, captureType[fieldVal.c_str()]); + } else if (fieldName == "VAL") + { + updated = updatePandAParam(posBusNameStr, fieldName, stringToInteger(fieldVal)); + } else if (fieldName == "SCALE" || fieldName == "OFFSET") + { + updated = updatePandAParam(posBusNameStr, fieldName, stringToDouble(fieldVal)); + } else + { + updated = updatePandAParam(posBusNameStr, fieldName, fieldVal); + } + if (!updated) + { + checkIfCustomParam(posBusNameStr, fieldName, fieldVal); + } + } } void ADPandABlocks::updateScaledPositionValue(std::string posBusName) { - double scale, offset; - int counts; - getDoubleParam(*posBusLookup[posBusName]["SCALE"], &scale); - getDoubleParam(*posBusLookup[posBusName]["OFFSET"], &offset); - getIntegerParam(*posBusLookup[posBusName]["UNSCALEDVAL"], &counts); - setDoubleParam(*posBusLookup[posBusName]["VAL"], counts*scale + offset); + double scale, offset; + int counts; + getDoubleParam(*posBusLookup[posBusName]["SCALE"], &scale); + getDoubleParam(*posBusLookup[posBusName]["OFFSET"], &offset); + getIntegerParam(*posBusLookup[posBusName]["UNSCALEDVAL"], &counts); + setDoubleParam(*posBusLookup[posBusName]["VAL"], counts * scale + offset); } double ADPandABlocks::stringToDouble(std::string str) { - std::stringstream strStream(str); - double value; - strStream >> value; - return value; + std::stringstream strStream(str); + double value; + strStream >> value; + return value; } int ADPandABlocks::stringToInteger(std::string str) { - std::stringstream strStream(str); - int value; - strStream >> value; - return value; + std::stringstream strStream(str); + int value; + strStream >> value; + return value; } std::string ADPandABlocks::doubleToString(double value) { - std::stringstream strStream; - strStream << value; - return strStream.str(); + std::stringstream strStream; + strStream << value; + return strStream.str(); } -std::vector ADPandABlocks::stringSplit(const std::string& s, char delimiter) +std::vector ADPandABlocks::stringSplit(const std::string &s, char delimiter) { - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) - { - tokens.push_back(token); - } - return tokens; + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; } -asynStatus ADPandABlocks::sendReceivingFormat() { +asynStatus ADPandABlocks::sendReceivingFormat() +{ - return sendData("XML FRAMED SCALED\n"); + return sendData("XML FRAMED SCALED\n"); } -asynStatus ADPandABlocks::readHeaderLine(char* rxBuffer, const size_t buffSize, epicsTimeStamp &lastErrorTime) const { - /*buffSize is the size fo rxBuffer*/ - const char *functionName = "readHeaderLine"; - int eomReason; - bool threw = false; - size_t nBytesIn; - asynStatus status = asynTimeout; - //check to see if rxBuffer is - while (status == asynTimeout) { - status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, - buffSize, &nBytesIn, &eomReason); - } +asynStatus ADPandABlocks::readHeaderLine(char *rxBuffer, const size_t buffSize, epicsTimeStamp &lastErrorTime) const +{ + /*buffSize is the size fo rxBuffer*/ + const char *functionName = "readHeaderLine"; + int eomReason; + bool threw = false; + size_t nBytesIn; + asynStatus status = asynTimeout; + //check to see if rxBuffer is + while (status == asynTimeout) + { + status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, + buffSize, &nBytesIn, &eomReason); + } epicsTimeStamp currentTime; epicsTimeGetCurrent(¤tTime); - if(status && pandaResponsive) { - if (epicsTimeDiffInSeconds(¤tTime, &lastErrorTime) > 0.5) { + if (status && pandaResponsive) + { + if (epicsTimeDiffInSeconds(¤tTime, &lastErrorTime) > 0.5) + { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error reading data: %s'\n", driverName, functionName, errorMsg[status].c_str()); threw = true; } - } - if (eomReason != ASYN_EOM_EOS && pandaResponsive) { - if (epicsTimeDiffInSeconds(¤tTime, &lastErrorTime) > 0.5) { + } + if (eomReason != ASYN_EOM_EOS && pandaResponsive) + { + if (epicsTimeDiffInSeconds(¤tTime, &lastErrorTime) > 0.5) + { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: failed on 'bt%.*s'\n", driverName, functionName, (int) nBytesIn, rxBuffer); } lastErrorTime = currentTime; - return asynError; - } - if (threw) lastErrorTime = currentTime; - return status; -} - -asynStatus ADPandABlocks::readDataBytes(char* rxBuffer, const size_t nBytes, bool &responsive) const { - const char *functionName = "readDataBytes"; - int eomReason; - size_t nBytesIn = 0; - asynStatus status = asynTimeout; - - while (status == asynTimeout) { - status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, - nBytes, &nBytesIn, &eomReason); - } - - if(status) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,"%s:%s: Error reading data: %s'\n", - driverName, functionName, errorMsg[status].c_str()); - } - if(nBytes != nBytesIn) { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Only got %d bytes, not %d bytes with EOM reason %d\n", - driverName, functionName, (int)nBytesIn, (int)nBytes, eomReason); - return asynError; - } - if (!responsive && nBytesIn != 0) { - responsive = true; - } - return status; + return asynError; + } + if (threw) lastErrorTime = currentTime; + return status; +} + +asynStatus ADPandABlocks::readDataBytes(char *rxBuffer, const size_t nBytes, bool &responsive) const +{ + const char *functionName = "readDataBytes"; + int eomReason; + size_t nBytesIn = 0; + asynStatus status = asynTimeout; + + while (status == asynTimeout) + { + status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, + nBytes, &nBytesIn, &eomReason); + } + + if (status) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error reading data: %s'\n", + driverName, functionName, errorMsg[status].c_str()); + } + if (nBytes != nBytesIn) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Only got %d bytes, not %d bytes with EOM reason %d\n", + driverName, functionName, (int) nBytesIn, (int) nBytes, eomReason); + return asynError; + } + if (!responsive && nBytesIn != 0) + { + responsive = true; + } + return status; } /*this function reads from the data port*/ -void ADPandABlocks::readDataPort() { - asynStatus status = asynSuccess; - char rxBuffer[N_BUFF_DATA]; - std::string header; - try{ - while (true) { - switch(state) { - case waitHeaderStart: - status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); - if(status == asynError){ - state = waitHeaderStart; - setIntegerParam(ADPandABlocksIsResponsive, 0); - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - if (strcmp(rxBuffer, "
\0") == 0) { - //we have a header so we have started acquiring - setIntegerParam(ADAcquire, 1); +void ADPandABlocks::readDataPort() +{ + asynStatus status = asynSuccess; + char rxBuffer[N_BUFF_DATA]; + std::string header; + try + { + while (true) + { + switch (state) + { + case waitHeaderStart: + status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); + if (status == asynError) + { + state = waitHeaderStart; + setIntegerParam(ADPandABlocksIsResponsive, 0); + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + if (strcmp(rxBuffer, "
\0") == 0) + { + //we have a header so we have started acquiring + setIntegerParam(ADAcquire, 1); setIntegerParam(ADStatus, ADStatusAcquire); setStringParam(ADStatusMessage, "Acquiring"); - header.append(rxBuffer); - header.append("\n"); - callParamCallbacks(); - state = waitHeaderEnd; - } - break; - - case waitHeaderEnd: - status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); - if(status == asynError){ - header.clear(); - state = dataEnd; - setIntegerParam(ADPandABlocksIsResponsive, 0); - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - /*accumulate the header until we reach the end, then process*/ - header.append(rxBuffer); - header.append("\n"); - if (strcmp(rxBuffer, "
\0") == 0) { - headerValues = parseHeader(header); - // Read the last line of the header - status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); - if(status == asynError){ - header.clear(); - state = dataEnd; - setIntegerParam(ADPandABlocksIsResponsive, 0); - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - //change the input eos as the data isn't terminated with a newline - pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "", 0); - state = waitDataStart; - } - break; - - case waitDataStart: - // read "BIN " or "END " - status = readDataBytes(rxBuffer, 4, pandaResponsive); - if(status == asynError){ - setIntegerParam(ADPandABlocksIsResponsive, 0); - state = dataEnd; - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - - if (strncmp(rxBuffer, "BIN ", 4) == 0) { - state = receivingData; - } - else if (strncmp(rxBuffer, "END ", 4) == 0) { - state = dataEnd; - } - break; - - case receivingData: - // read next four bytes to get the packet size - { - uint32_t message_length; - status = readDataBytes(reinterpret_cast(&message_length), 4, pandaResponsive); - if(status == asynError){ - setIntegerParam(ADPandABlocksIsResponsive, 0); - state = dataEnd; - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - uint32_t dataLength = message_length - 8; // size of the packet prefix information is 8 - // read the rest of the packet - status = readDataBytes(rxBuffer, dataLength, pandaResponsive); - if(status == asynError){ - setIntegerParam(ADPandABlocksIsResponsive, 0); - state = dataEnd; - break; - } else { - setIntegerParam(ADPandABlocksIsResponsive, 1); - } - - std::vector dataPacket; - dataPacket.insert(dataPacket.begin(), rxBuffer, rxBuffer + dataLength); - parseData(dataPacket, dataLength); - state = waitDataStart; - } - break; - - case dataEnd: - //reset the header string - header = ""; - //change the input eos back to newline for the header - pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "\n", 1); - //set the acquire light to 0 + header.append(rxBuffer); + header.append("\n"); + callParamCallbacks(); + state = waitHeaderEnd; + } + break; + + case waitHeaderEnd: + status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); + if (status == asynError) + { + header.clear(); + state = dataEnd; + setIntegerParam(ADPandABlocksIsResponsive, 0); + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + /*accumulate the header until we reach the end, then process*/ + header.append(rxBuffer); + header.append("\n"); + if (strcmp(rxBuffer, "
\0") == 0) + { + headerValues = parseHeader(header); + // Read the last line of the header + status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); + if (status == asynError) + { + header.clear(); + state = dataEnd; + setIntegerParam(ADPandABlocksIsResponsive, 0); + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + //change the input eos as the data isn't terminated with a newline + pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "", 0); + state = waitDataStart; + } + break; + + case waitDataStart: + // read "BIN " or "END " + status = readDataBytes(rxBuffer, 4, pandaResponsive); + if (status == asynError) + { + setIntegerParam(ADPandABlocksIsResponsive, 0); + state = dataEnd; + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + + if (strncmp(rxBuffer, "BIN ", 4) == 0) + { + state = receivingData; + } else if (strncmp(rxBuffer, "END ", 4) == 0) + { + state = dataEnd; + } + break; + + case receivingData: + // read next four bytes to get the packet size + { + uint32_t message_length; + status = readDataBytes(reinterpret_cast(&message_length), 4, pandaResponsive); + if (status == asynError) + { + setIntegerParam(ADPandABlocksIsResponsive, 0); + state = dataEnd; + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + uint32_t dataLength = message_length - 8; // size of the packet prefix information is 8 + // read the rest of the packet + status = readDataBytes(rxBuffer, dataLength, pandaResponsive); + if (status == asynError) + { + setIntegerParam(ADPandABlocksIsResponsive, 0); + state = dataEnd; + break; + } else + { + setIntegerParam(ADPandABlocksIsResponsive, 1); + } + + std::vector dataPacket; + dataPacket.insert(dataPacket.begin(), rxBuffer, rxBuffer + dataLength); + parseData(dataPacket, dataLength); + state = waitDataStart; + } + break; + + case dataEnd: + //reset the header string + header = ""; + //change the input eos back to newline for the header + pasynOctet_data->setInputEos(octetPvt_data, pasynUser_data, "\n", 1); + //set the acquire light to 0 setIntegerParam(ADStatus, ADStatusIdle); setStringParam(ADStatusMessage, "Idle"); callParamCallbacks(); - setIntegerParam(ADAcquire, 0); - status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); - setStringParam(ADPandABlocksDataEnd, rxBuffer); - callParamCallbacks(); - state = waitHeaderStart; - break; - } - } - } - catch(const std::runtime_error& e){ - //return to beginning state if there is an exception - state = waitHeaderStart; - status = asynError; - } - callParamCallbacks(); -} - - -ADPandABlocks::headerMap ADPandABlocks::parseHeader(const std::string& headerString) -{ - /**return a map containing the header data corresponding to each xml node - * the first map will always be the 'data' node, - * then each field will be pushed onto the vector sequentially - */ - //Array to store how many of each data type present in header - //dataTypes[0] = doubles - //datatypes[1] = uint32_t - int dataTypes[2]; - - std::map tmpValues; - headerMap tmpHeaderValues; - - //set the header parameter - setStringParam(ADPandABlocksHeader, headerString.c_str()); - - asynStatus status = asynSuccess; - xmlTextReaderPtr xmlreader = xmlReaderForMemory(headerString.c_str(), (int)headerString.length(), NULL, NULL, 0); - - if (xmlreader == NULL){ - //do some error handling - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "ERROR PARSING HEADER\n"); - status = asynError; - } - if(status == asynSuccess) - { - /*walk the xml nodes*/ - while ((xmlTextReaderRead(xmlreader)) == 1) - { - /*get the node names*/ - const xmlChar* xmlNodeName = xmlTextReaderConstName(xmlreader); - if (xmlNodeName != NULL) - { - std::string name((const char*)xmlNodeName); - /*get the attributes for the data and field nodes and put on vector*/ - if( name == "data" || name == "field") - { - extractHeaderData(xmlreader, tmpValues); - tmpHeaderValues.push_back(tmpValues); - } - } - } - } - xmlFreeTextReader(xmlreader); - - /* - * As this method is only called before the first frame it makes sense to any map travsering here, once. - * What it in the map is stored in arrays for fast access on every frame. - */ - typeArray[0]=0; - nameCaptArray[0]=""; - setLen=0; - headerArraySize=tmpHeaderValues.size(); - - - /* - * Depending on the data type populate typeArray with a 1 for a double and a 2 for an int. - * Also keep setLen up to date with the total size so far. - */ - for(int j = 0; j < tmpHeaderValues.size()-1; j++){; - if(tmpHeaderValues[j+1].find("type")->second=="double") { - typeArray[j+1]=1; - setLen += sizeof(double); - } - else { - typeArray[j+1]=2; - setLen += sizeof(uint32_t); - } - - nameCaptArray[j+1]=strdup(tmpHeaderValues[j+1].find("name")->second.c_str()); - nameCaptArray[j+1]=strcat(nameCaptArray[j+1],"."); - nameCaptArray[j+1]=strcat(nameCaptArray[j+1],tmpHeaderValues[j+1].find("capture")->second.c_str()); - } - - callParamCallbacks(); - return tmpHeaderValues; -} - -asynStatus ADPandABlocks::extractHeaderData(const xmlTextReaderPtr xmlreader, std::map& values)const -{ - /*Get the attribute values for a node and place in the map values*/ - xmlNodePtr node= xmlTextReaderCurrentNode(xmlreader); - if (xmlTextReaderNodeType(xmlreader)==1 && node && node->properties) { - xmlAttr* attribute = node->properties; - while(attribute && attribute->name && attribute->children) - { - xmlChar* xvalue = xmlNodeListGetString(node->doc, attribute->children, 1); - - /*Insert the values into the data_value map*/ - std::string value((const char*)xvalue); - std::string name((const char*)attribute->name); - values[name] = value; - - xmlFree(xvalue); - attribute = attribute->next; - } - } - return asynSuccess; -} - -std::string ADPandABlocks::getHeaderValue(const int index, const std::string attribute)const -{ - /*return the value of an attribute of a given element*/ - //first check index (do find on headerValues - if (headerValues[index].find(attribute) != headerValues[index].end()) - { - return headerValues[index].find(attribute)->second; - } - else - { - throw std::out_of_range("attribute not in map"); - } -} - -void ADPandABlocks::getAllData(std::vector& inBuffer, const int dataLen, const int buffLen)const -{ - const char *functionName = "getAllData"; - size_t nBytesIn; - size_t readBytes = dataLen - buffLen; - int eomReason; - asynStatus status = asynSuccess; - // asynUser *pasynUserRead = pasynManager->duplicateAsynUser(pasynUser_data, 0, 0); - char rxBuffer[readBytes]; - status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, readBytes, - &nBytesIn, &eomReason); - inBuffer.insert(inBuffer.end(), rxBuffer, rxBuffer+nBytesIn); - if(status) - { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,"%s:%s: Error reading data: %d'\n", - driverName, functionName, status); - } -} - -void ADPandABlocks::parseData(std::vector dataBuffer, const int dataLen){ - int buffLen = dataBuffer.size(); //actual length of received input data stream (could be multiple lines) - int dataNo = headerValues.size() - 1; //number of received data points (total header fields - 'data' = number of captured fields) - //check to see if we have read all the data in, and do another read if we haven't - if(dataLen > buffLen) - { - getAllData(dataBuffer, dataLen, buffLen); - } - outputData(dataLen, dataNo, dataBuffer); + setIntegerParam(ADAcquire, 0); + status = readHeaderLine(rxBuffer, N_BUFF_DATA, lastHeaderErrorTime); + setStringParam(ADPandABlocksDataEnd, rxBuffer); + callParamCallbacks(); + state = waitHeaderStart; + break; + } + } + } + catch (const std::runtime_error &e) + { + //return to beginning state if there is an exception + state = waitHeaderStart; + status = asynError; + } + callParamCallbacks(); +} + + +ADPandABlocks::headerMap ADPandABlocks::parseHeader(const std::string &headerString) +{ + /**return a map containing the header data corresponding to each xml node + * the first map will always be the 'data' node, + * then each field will be pushed onto the vector sequentially + */ + //Array to store how many of each data type present in header + //dataTypes[0] = doubles + //datatypes[1] = uint32_t + int dataTypes[2]; + + std::map tmpValues; + headerMap tmpHeaderValues; + + //set the header parameter + setStringParam(ADPandABlocksHeader, headerString.c_str()); + + asynStatus status = asynSuccess; + xmlTextReaderPtr xmlreader = xmlReaderForMemory(headerString.c_str(), (int) headerString.length(), NULL, NULL, 0); + + if (xmlreader == NULL) + { + //do some error handling + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "ERROR PARSING HEADER\n"); + status = asynError; + } + if (status == asynSuccess) + { + /*walk the xml nodes*/ + while ((xmlTextReaderRead(xmlreader)) == 1) + { + /*get the node names*/ + const xmlChar *xmlNodeName = xmlTextReaderConstName(xmlreader); + if (xmlNodeName != NULL) + { + std::string name((const char *) xmlNodeName); + /*get the attributes for the data and field nodes and put on vector*/ + if (name == "data" || name == "field") + { + extractHeaderData(xmlreader, tmpValues); + tmpHeaderValues.push_back(tmpValues); + } + } + } + } + xmlFreeTextReader(xmlreader); + + /* + * As this method is only called before the first frame it makes sense to any map travsering here, once. + * What it in the map is stored in arrays for fast access on every frame. + */ + typeArray[0] = 0; + nameCaptArray[0] = ""; + setLen = 0; + headerArraySize = tmpHeaderValues.size(); + + + /* + * Depending on the data type populate typeArray with a 1 for a double and a 2 for an int. + * Also keep setLen up to date with the total size so far. + */ + for (int j = 0; j < tmpHeaderValues.size() - 1; j++) + { ; + if (tmpHeaderValues[j + 1].find("type")->second == "double") + { + typeArray[j + 1] = 1; + setLen += sizeof(double); + } else + { + typeArray[j + 1] = 2; + setLen += sizeof(uint32_t); + } + + nameCaptArray[j + 1] = strdup(tmpHeaderValues[j + 1].find("name")->second.c_str()); + nameCaptArray[j + 1] = strcat(nameCaptArray[j + 1], "."); + nameCaptArray[j + 1] = strcat(nameCaptArray[j + 1], tmpHeaderValues[j + 1].find("capture")->second.c_str()); + } + + callParamCallbacks(); + return tmpHeaderValues; +} + +asynStatus +ADPandABlocks::extractHeaderData(const xmlTextReaderPtr xmlreader, std::map &values) const +{ + /*Get the attribute values for a node and place in the map values*/ + xmlNodePtr node = xmlTextReaderCurrentNode(xmlreader); + if (xmlTextReaderNodeType(xmlreader) == 1 && node && node->properties) + { + xmlAttr *attribute = node->properties; + while (attribute && attribute->name && attribute->children) + { + xmlChar *xvalue = xmlNodeListGetString(node->doc, attribute->children, 1); + + /*Insert the values into the data_value map*/ + std::string value((const char *) xvalue); + std::string name((const char *) attribute->name); + values[name] = value; + + xmlFree(xvalue); + attribute = attribute->next; + } + } + return asynSuccess; +} + +std::string ADPandABlocks::getHeaderValue(const int index, const std::string attribute) const +{ + /*return the value of an attribute of a given element*/ + //first check index (do find on headerValues + if (headerValues[index].find(attribute) != headerValues[index].end()) + { + return headerValues[index].find(attribute)->second; + } else + { + throw std::out_of_range("attribute not in map"); + } +} + +void ADPandABlocks::getAllData(std::vector &inBuffer, const int dataLen, const int buffLen) const +{ + const char *functionName = "getAllData"; + size_t nBytesIn; + size_t readBytes = dataLen - buffLen; + int eomReason; + asynStatus status = asynSuccess; + // asynUser *pasynUserRead = pasynManager->duplicateAsynUser(pasynUser_data, 0, 0); + char rxBuffer[readBytes]; + status = pasynOctet_data->read(octetPvt_data, pasynUser_data, rxBuffer, readBytes, + &nBytesIn, &eomReason); + inBuffer.insert(inBuffer.end(), rxBuffer, rxBuffer + nBytesIn); + if (status) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error reading data: %d'\n", + driverName, functionName, status); + } +} + +void ADPandABlocks::parseData(std::vector dataBuffer, const int dataLen) +{ + int buffLen = dataBuffer.size(); //actual length of received input data stream (could be multiple lines) + int dataNo = headerValues.size() - + 1; //number of received data points (total header fields - 'data' = number of captured fields) + //check to see if we have read all the data in, and do another read if we haven't + if (dataLen > buffLen) + { + getAllData(dataBuffer, dataLen, buffLen); + } + outputData(dataLen, dataNo, dataBuffer); } void ADPandABlocks::outputData(const int dataLen, const int dataNo, const std::vector data) { - try{ + try + { int linecount = 0; //number of lines of data received and parsed //get the length of an individual dataSet int writeAttributes = 0; int endOfFrame = 0; - const char* ptridx = &data.front(); + const char *ptridx = &data.front(); int noDataSets = data.size() / setLen; //number of data sets in the received binary data //find other possible data types.. //loop over the data sets in the received data - for (int j = 0; j < noDataSets; ++j) { + for (int j = 0; j < noDataSets; ++j) + { writeAttributes = 0; endOfFrame = 0; - if(numExposuresCounter == 0){ + if (numExposuresCounter == 0) + { endOfFrame = 0; writeAttributes = 1; - }else{ + } else + { writeAttributes = 0; } // are we still acquiring? int acquiring; getIntegerParam(ADAcquire, &acquiring); - if (!acquiring) { + if (!acquiring) + { return; } // allocate a frame for each data set - if(writeAttributes) { + if (writeAttributes) + { allocateFrame(); } - if (pArray != NULL) { + if (pArray != NULL) + { //loop over each data point in the data set - for (int i = 0; i < dataNo; ++i) { + for (int i = 0; i < dataNo; ++i) + { // NDAttributes are used to store the actual captured data //find out what type the individual point is //from the header and assign the appropriate pointer. - if(typeArray[i+1] == 1) { + if (typeArray[i + 1] == 1) + { // Create the NDAttributes and initialise them with data value (headerValue[0] is the data info) - if(writeAttributes) { + if (writeAttributes) + { pArray->pAttributeList->add( - (nameCaptArray[i+1]), - "sample value", - NDAttrFloat64, - (double *) ptridx); + (nameCaptArray[i + 1]), + "sample value", + NDAttrFloat64, + (double *) ptridx); } // Write data for every trigger but if using multiple exposures don't overwrite data from a previous exposure - ((double *) pArray->pData)[i+(dataNo*numExposuresCounter)] = *(double *) ptridx; + ((double *) pArray->pData)[i + (dataNo * numExposuresCounter)] = *(double *) ptridx; ptridx += sizeof(double); - } else if (typeArray[i+1] == 2) { + } else if (typeArray[i + 1] == 2) + { // Create the NDAttributes and initialise them with data value (headerValue[0] is the data info) - if(writeAttributes) { + if (writeAttributes) + { pArray->pAttributeList->add( - (nameCaptArray[i+1]), + (nameCaptArray[i + 1]), "sample value", NDAttrUInt32, (uint32_t *) ptridx); } uint32_t value = *(uint32_t *) ptridx; // Write data for every trigger but if using multiple exposures don't overwrite data from a previous exposure - ((double *) pArray->pData)[i+(dataNo*numExposuresCounter)] = (double) value; + ((double *) pArray->pData)[i + (dataNo * numExposuresCounter)] = (double) value; //Determine if we need to populate the Bit Mask value - std::string headerLabel = nameCaptArray[i+1]; + std::string headerLabel = nameCaptArray[i + 1]; size_t bitsFound = headerLabel.find("BITS"); - if (bitsFound != std::string::npos) { + if (bitsFound != std::string::npos) + { int blockNum = atoi(headerLabel.substr(bitsFound + 4, 1).c_str()); uint8_t maskPtr; - if (pArray != NULL) { - for (int maski = 0; maski < 32; maski++) { + if (pArray != NULL) + { + for (int maski = 0; maski < 32; maski++) + { //shift and mask the value and push into individual NDAttrs maskPtr = (value >> maski) & 0x01; pArray->pAttributeList->add( @@ -1156,12 +1320,14 @@ void ADPandABlocks::outputData(const int dataLen, const int dataNo, const std::v } numExposuresCounter++; - if(numExposuresCounter == numExposures){ + if (numExposuresCounter == numExposures) + { numExposuresCounter = 0; endOfFrame = 1; } /* Ship off the NDArray*/ - if(endOfFrame) { + if (endOfFrame) + { wrapFrame(); } @@ -1171,405 +1337,499 @@ void ADPandABlocks::outputData(const int dataLen, const int dataNo, const std::v } } - catch(const std::out_of_range& e){ + catch (const std::out_of_range &e) + { //if attribute is not in header map, go back to beginning ? } } -void ADPandABlocks::allocateFrame() { - // Release the old NDArray if it exists - if (pArray != NULL) { - pArray->release(); - pArray = NULL; - } - // Allocate a new NDArray - int arraysize = headerValues.size() -1; - size_t dims[2]; - int nDims = 2; - dims[0] = arraysize; - dims[1] = numExposures; - pArray = pNDArrayPool->alloc(nDims, dims, NDFloat64, 0, NULL); - //clear the attribute list to get rid of previous scans - if (pArray != NULL) { - pArray->pAttributeList->clear(); - } -} - -void ADPandABlocks::wrapFrame() { - this->lock(); - getIntegerParam(NDArrayCounter, &(arrayCounter)); - getIntegerParam(ADNumImagesCounter, &(numImagesCounter)); - // Set the time stamp - epicsTimeStamp arrayTime; - epicsTimeGetCurrent(&arrayTime); - if (pArray != NULL) { - pArray->timeStamp = arrayTime.secPastEpoch; - pArray->timeStamp +=0.000000001*arrayTime.nsec; - // Save the NDAttributes if there are any - getAttributes(pArray->pAttributeList); - } - // Update statistics - arrayCounter++; - numImagesCounter++; - //send disarm signal if we are in a mode that requires it - if ((imgMode == ADImageSingle && arrayCounter == 1) || - (imgMode == ADImageMultiple && numImagesCounter == imgNo)) { - sendCtrl("*PCAP.DISARM="); +void ADPandABlocks::allocateFrame() +{ + // Release the old NDArray if it exists + if (pArray != NULL) + { + pArray->release(); + pArray = NULL; + } + // Allocate a new NDArray + int arraysize = headerValues.size() - 1; + size_t dims[2]; + int nDims = 2; + dims[0] = arraysize; + dims[1] = numExposures; + pArray = pNDArrayPool->alloc(nDims, dims, NDFloat64, 0, NULL); + //clear the attribute list to get rid of previous scans + if (pArray != NULL) + { + pArray->pAttributeList->clear(); + } +} + +void ADPandABlocks::wrapFrame() +{ + this->lock(); + getIntegerParam(NDArrayCounter, &(arrayCounter)); + getIntegerParam(ADNumImagesCounter, &(numImagesCounter)); + // Set the time stamp + epicsTimeStamp arrayTime; + epicsTimeGetCurrent(&arrayTime); + if (pArray != NULL) + { + pArray->timeStamp = arrayTime.secPastEpoch; + pArray->timeStamp += 0.000000001 * arrayTime.nsec; + // Save the NDAttributes if there are any + getAttributes(pArray->pAttributeList); + } + // Update statistics + arrayCounter++; + numImagesCounter++; + //send disarm signal if we are in a mode that requires it + if ((imgMode == ADImageSingle && arrayCounter == 1) || + (imgMode == ADImageMultiple && numImagesCounter == imgNo)) + { + sendCtrl("*PCAP.DISARM="); setIntegerParam(ADStatus, ADStatusIdle); setStringParam(ADStatusMessage, "Idle"); callParamCallbacks(); - setIntegerParam(ADAcquire, 0); + setIntegerParam(ADAcquire, 0); callParamCallbacks(); - } - // Set the unique ID - if (pArray != NULL) { - pArray->uniqueId = arrayCounter; - } - // Update the counters - setIntegerParam(NDArrayCounter, arrayCounter); - setIntegerParam(ADNumImagesCounter, numImagesCounter); - //callParamCallbacks(); - this->unlock(); - if (pArray != NULL) { - // Ship the array off - doCallbacksGenericPointer(pArray, NDArrayData, 0); - } + } + // Set the unique ID + if (pArray != NULL) + { + pArray->uniqueId = arrayCounter; + } + // Update the counters + setIntegerParam(NDArrayCounter, arrayCounter); + setIntegerParam(ADNumImagesCounter, numImagesCounter); + //callParamCallbacks(); + this->unlock(); + if (pArray != NULL) + { + // Ship the array off + doCallbacksGenericPointer(pArray, NDArrayData, 0); + } } /* Send helper function * called with lock taken */ -asynStatus ADPandABlocks::sendData(const std::string txBuffer){ - asynStatus status = send(txBuffer, pasynOctet_data, octetPvt_data, pasynUser_data); - return status; -} - -asynStatus ADPandABlocks::sendCtrl(const std::string txBuffer){ - asynStatus status = send(txBuffer, pasynOctet_ctrl, octetPvt_ctrl, pasynUser_ctrl_tx); - return status; -} - -asynStatus ADPandABlocks::send(const std::string txBuffer, asynOctet *pasynOctet, void* octetPvt, asynUser* pasynUser) { - const char *functionName = "send"; - asynStatus status = asynSuccess; - int connected; - size_t nBytesOut; - pasynUser->timeout = TIMEOUT; - status = pasynOctet->write(octetPvt, pasynUser, txBuffer.c_str(), txBuffer.length(), - &nBytesOut); - asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, - "%s:%s: Send: '%.*s'\n", driverName, functionName, (int)txBuffer.length(), txBuffer.c_str()); - getIntegerParam(ADPandABlocksIsConnected, &connected); - if (status != asynSuccess && connected) { - // Can't write, port probably not connected - setIntegerParam(ADPandABlocksIsConnected, 0); +asynStatus ADPandABlocks::sendData(const std::string txBuffer) +{ + asynStatus status = send(txBuffer, pasynOctet_data, octetPvt_data, pasynUser_data); + return status; +} + +asynStatus ADPandABlocks::sendCtrl(const std::string txBuffer) +{ + asynStatus status = send(txBuffer, pasynOctet_ctrl, octetPvt_ctrl, pasynUser_ctrl_tx); + return status; +} + +asynStatus ADPandABlocks::send(const std::string txBuffer, asynOctet *pasynOctet, void *octetPvt, asynUser *pasynUser) +{ + const char *functionName = "send"; + asynStatus status = asynSuccess; + int connected; + size_t nBytesOut; + pasynUser->timeout = TIMEOUT; + status = pasynOctet->write(octetPvt, pasynUser, txBuffer.c_str(), txBuffer.length(), + &nBytesOut); + asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, + "%s:%s: Send: '%.*s'\n", driverName, functionName, (int) txBuffer.length(), txBuffer.c_str()); + getIntegerParam(ADPandABlocksIsConnected, &connected); + if (status != asynSuccess && connected) + { + // Can't write, port probably not connected + setIntegerParam(ADPandABlocksIsConnected, 0); setIntegerParam(ADStatus, ADStatusDisconnected); setStringParam(ADStatusMessage, "Disconnected"); - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: Can't write to ADPandABlocks: '%.*s'\n", driverName, functionName, (int)txBuffer.length(), txBuffer.c_str()); - } else if (status == asynSuccess && !connected) { - setIntegerParam(ADPandABlocksIsConnected, 1); + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Can't write to ADPandABlocks: '%.*s'\n", driverName, functionName, (int) txBuffer.length(), + txBuffer.c_str()); + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "PandA box appears to be disconnected. Polling errors will be suppressed.\n"); + } else if (status == asynSuccess && !connected) + { + setIntegerParam(ADPandABlocksIsConnected, 1); setIntegerParam(ADStatus, ADStatusIdle); setStringParam(ADStatusMessage, "Idle"); - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "Reconnected to ADPandABlocks'\n"); - } - return status; + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "Reconnected to ADPandABlocks\n"); + } + return status; } // Get encoder number from name of position bus int ADPandABlocks::getEncoderNumberFromName(std::string posBusName) { - // Check if encoder - std::string encoderStringName("INENC"); - if (posBusName.find(encoderStringName) != std::string::npos) - { - // Parse and return encoder number - int encoderNumber; - std::stringstream encoderNumberSS; - encoderNumberSS << posBusName[5]; - encoderNumberSS >> encoderNumber; - return encoderNumber; - } - else - { - return -1; - } + // Check if encoder + std::string encoderStringName("INENC"); + if (posBusName.find(encoderStringName) != std::string::npos) + { + // Parse and return encoder number + int encoderNumber; + std::stringstream encoderNumberSS; + encoderNumberSS << posBusName[5]; + encoderNumberSS >> encoderNumber; + return encoderNumber; + } else + { + return -1; + } } // Calibrate encoder position by number (1..4) void ADPandABlocks::calibrateEncoderPosition(int encoderNumber) { - if(encoderNumber > 0 && encoderNumber < NENC) - { - // Increment value to cause record to be processed - int currentValue; - getIntegerParam(ADPandABlocksMCalibrate[encoderNumber-1], ¤tValue); - setIntegerParam(ADPandABlocksMCalibrate[encoderNumber-1], currentValue + 1); - } + if (encoderNumber > 0 && encoderNumber < NENC) + { + // Increment value to cause record to be processed + int currentValue; + getIntegerParam(ADPandABlocksMCalibrate[encoderNumber - 1], ¤tValue); + setIntegerParam(ADPandABlocksMCalibrate[encoderNumber - 1], currentValue + 1); + } } // Set position of encoder (1..4) to custom value void ADPandABlocks::setEncoderPosition(int encoderNumber, int value) { - if(encoderNumber > 0 && encoderNumber < NENC) - { - setIntegerParam(ADPandABlocksMSetpos[encoderNumber-1], value); - } + if (encoderNumber > 0 && encoderNumber < NENC) + { + setIntegerParam(ADPandABlocksMSetpos[encoderNumber - 1], value); + } } // Erase substring from string in place void ADPandABlocks::removeSubString(std::string &string, std::string &subString) { - // Search and only remove if it exists in string - size_t pos = string.find(subString); - if (pos != std::string::npos) - { - string.erase(pos, subString.length()); - } + // Search and only remove if it exists in string + size_t pos = string.find(subString); + if (pos != std::string::npos) + { + string.erase(pos, subString.length()); + } } // Send setpos (SETP) command to PandA for homing a position bus -void ADPandABlocks::setPandASetPos(std::string posBusName, int value){ +void ADPandABlocks::setPandASetPos(std::string posBusName, int value) +{ - // Remove .VAL from posBusName for correct command - std::string valSuffix(".VAL"); - removeSubString(posBusName, valSuffix); + // Remove .VAL from posBusName for correct command + std::string valSuffix(".VAL"); + removeSubString(posBusName, valSuffix); - // Build and send command - std::stringstream setPosCmd; - setPosCmd << posBusName << ".SETP=" << value; - sendCtrl(setPosCmd.str()); + // Build and send command + std::stringstream setPosCmd; + setPosCmd << posBusName << ".SETP=" << value; + sendCtrl(setPosCmd.str()); } // Check motor offset and scale bool ADPandABlocks::checkIfMotorFloatParams(int reason, double value) { - bool motorParamChanged = false; - if (checkIfReasonIsMotorOffset(reason, value)) - { - motorParamChanged = true; - } - else if (checkIfReasonIsMotorScale(reason, value)) - { - motorParamChanged = true; - } - return motorParamChanged; + bool motorParamChanged = false; + if (checkIfReasonIsMotorOffset(reason, value)) + { + motorParamChanged = true; + } else if (checkIfReasonIsMotorScale(reason, value)) + { + motorParamChanged = true; + } + return motorParamChanged; } // Check if motor offset has changed bool ADPandABlocks::checkIfReasonIsMotorOffset(int reason, double value) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMOffset[i]) - { - updatePandAMotorParam(i+1, offset, value); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMOffset[i]) + { + updatePandAMotorParam(i + 1, offset, value); + return true; + } + } + return false; } // Check if motor record scaling has changed bool ADPandABlocks::checkIfReasonIsMotorScale(int reason, double value) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMScale[i]) - { - updatePandAMotorParam(i+1, scale, value); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMScale[i]) + { + updatePandAMotorParam(i + 1, scale, value); + return true; + } + } + return false; } // Check if motor record units have changed bool ADPandABlocks::checkIfReasonIsMotorUnit(int reason, std::string value) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMUnits[i]) - { - updatePandAMotorParam(i+1, units, value); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMUnits[i]) + { + updatePandAMotorParam(i + 1, units, value); + return true; + } + } + return false; } // Check if motor record setpos have changed bool ADPandABlocks::checkIfReasonIsMotorSetpos(int reason, int value) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMSetpos[i]) - { - updatePandAMotorParam(i+1, setpos, value); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMSetpos[i]) + { + updatePandAMotorParam(i + 1, setpos, value); + return true; + } + } + return false; } // Change embedded window to readOnly if using motorSync template bool ADPandABlocks::checkIfReasonIsMotorScreenType(int reason, int value) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMScreenType[i]) - { - embeddedScreenType screenType = static_cast(value); - updatePandAMotorParam(i+1, screen, screenType); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMScreenType[i]) + { + embeddedScreenType screenType = static_cast(value); + updatePandAMotorParam(i + 1, screen, screenType); + return true; + } + } + return false; } // Set motor name param bool ADPandABlocks::checkIfReasonIsMotorName(int reason, std::string name) { - for (int i=0; i<4; i++) - { - if (reason == ADPandABlocksMMotorName[i]) - { - updatePandAMotorParam(i+1, motorName, name); - return true; - } - } - return false; + for (int i = 0; i < 4; i++) + { + if (reason == ADPandABlocksMMotorName[i]) + { + updatePandAMotorParam(i + 1, motorName, name); + return true; + } + } + return false; +} + +// Set custom param +bool ADPandABlocks::checkIfReasonIsCustomParam(int reason, std::string name) +{ + for (int i = 0; i < NCUSTOM; i++) + { + if (reason == ADPandABlocksCustomParamBlock[i]) + { + updateCustomParamLookup(i, name, ""); + return true; + } + if (reason == ADPandABlocksCustomParamField[i]) + { + updateCustomParamLookup(i, "", name); + return true; + } + + if(reason == ADPandABlocksCustomParamDemand[i]) + { + std::string block, field; + getStringParam(ADPandABlocksCustomParamBlock[i], block); + getStringParam(ADPandABlocksCustomParamField[i], field); + std::stringstream setParamCmd; + setParamCmd << block << "." << field << "=" << name; + sendCtrl(setParamCmd.str()); + return true; + } + + } + return false; +} + +void ADPandABlocks::updateCustomParamLookup(int paramIndex, std::string paramName, std::string paramNameEnd) +{ + int *demandParamInd = &ADPandABlocksCustomParamDemand[paramIndex]; + int *rbvParamInd = &ADPandABlocksCustomParamRBV[paramIndex]; + if (paramNameEnd.length() == 0) + { + if (paramName.length() > 0) + { + // New block was set, wipe field & return + setStringParam(ADPandABlocksCustomParamField[paramIndex], ""); + return; + } + getStringParam(ADPandABlocksCustomParamField[paramIndex], paramNameEnd); + } + if (paramName.length() == 0) + { + getStringParam(ADPandABlocksCustomParamBlock[paramIndex], paramName); + } + std::map < std::string, std::map < std::string, std::pair < int *, int * > > > ::iterator + it = customParamLookup.find(paramName); + if (it == customParamLookup.end()) + { + customParamLookup.insert(std::pair < std::string, std::map < std::string, + std::pair < int * , + int * > > > (paramName, std::map < std::string, std::pair < int *, int * > > ())); + it = customParamLookup.find(paramName); + } + for (it = customParamLookup.begin(); it != customParamLookup.end(); ++it) + { + for (std::map < std::string, std::pair < int *, int * > > ::iterator it2 = it->second.begin(); + it2 != it->second.end(); + ++it2) + { + if (*demandParamInd == *it2->second.first) + { + customParamLookup[paramName].erase(it2); + } + } + } + customParamLookup[paramName].insert(std::pair < std::string, std::pair < int * , + int * > > (paramNameEnd, std::pair(demandParamInd, rbvParamInd))); } // Search and update lookup table parameter, sending command to PandA if required template asynStatus ADPandABlocks::UpdateLookupTableParamFromWrite(int param, T value) { - return asynSuccess; + return asynSuccess; } + template<> asynStatus ADPandABlocks::UpdateLookupTableParamFromWrite(int param, int value) { - const char *functionName = "UpdateLookupTableParamFromWrite"; - if(posBusLookup.empty() == true) return asynSuccess; - for(std::map >::iterator it = posBusLookup.begin(); - it != posBusLookup.end(); ++it) - { - for(std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) - { - if(param == *it2->second) - { - if(it2->first == "CAPTURE") - { - // Update Capture enum - std::stringstream cmdStr; - cmdStr << it->first <<"."<< it2->first <<"="<first == "CALIBRATE") - { - // Trigger manual copy of motor encoder position to PandA position - // Only calibrate once per button press - if(value == 1) - { - int encoderNumber = getEncoderNumberFromName(it->first); - calibrateEncoderPosition(encoderNumber); - } - return asynSuccess; - } - else if(it2->first == "SETPOS") - { - // Update PandA setpos to new value - setPandASetPos(it->first, value); - } - // Regular parameter update - else - { - std::stringstream cmdStr; - cmdStr << it->first <<"."<< it2->first <<"="< > ::iterator it = posBusLookup.begin(); + it != posBusLookup.end(); + ++it) + { + for (std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) + { + if (param == *it2->second) + { + if (it2->first == "CAPTURE") + { + // Update Capture enum + std::stringstream cmdStr; + cmdStr << it->first << "." << it2->first << "=" << captureStrings[value]; + sendCtrl(cmdStr.str()); + std::string field; + asynStatus status = readPosBusValues(&field); + if (status == asynError) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting: %s '\n", + driverName, functionName, cmdStr.str().c_str()); + } + return status; + } else if (it2->first == "CALIBRATE") + { + // Trigger manual copy of motor encoder position to PandA position + // Only calibrate once per button press + if (value == 1) + { + int encoderNumber = getEncoderNumberFromName(it->first); + calibrateEncoderPosition(encoderNumber); + } + return asynSuccess; + } else if (it2->first == "SETPOS") + { + // Update PandA setpos to new value + setPandASetPos(it->first, value); + } + // Regular parameter update + else + { + std::stringstream cmdStr; + cmdStr << it->first << "." << it2->first << "=" << value; + sendCtrl(cmdStr.str()); + std::string field; + asynStatus status = readPosBusValues(&field); + if (status == asynError) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting: %s '\n", + driverName, functionName, cmdStr.str().c_str()); + } + return status; + } + } + } + } + return asynSuccess; } + template<> asynStatus ADPandABlocks::UpdateLookupTableParamFromWrite(int param, double value) { - const char *functionName = "UpdateLookupTableParamFromWrite"; - if(posBusLookup.empty() == true) return asynSuccess; - for(std::map >::iterator it = posBusLookup.begin(); - it != posBusLookup.end(); ++it) - { - for(std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) - { - if(param == *it2->second) - { - std::stringstream cmdStr; - cmdStr << it->first <<"."<< it2->first <<"="< > ::iterator it = posBusLookup.begin(); + it != posBusLookup.end(); + ++it) + { + for (std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) + { + if (param == *it2->second) + { + std::stringstream cmdStr; + cmdStr << it->first << "." << it2->first << "=" << value; + sendCtrl(cmdStr.str()); + std::string field; + asynStatus status = readPosBusValues(&field); + if (status == asynError) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting: %s '\n", + driverName, functionName, cmdStr.str().c_str()); + } + callParamCallbacks(); + return status; + } + } + } + return asynSuccess; } + template<> asynStatus ADPandABlocks::UpdateLookupTableParamFromWrite(int param, std::string value) { - const char *functionName = "UpdateLookupTableParamFromWrite"; - if(posBusLookup.empty() == true) return asynSuccess; - for(std::map >::iterator it = posBusLookup.begin(); - it != posBusLookup.end(); ++it) - { - for(std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) - { - if(param == *it2->second) - { - std::stringstream cmdStr; - cmdStr << it->first <<"."<< it2->first <<"="< > ::iterator it = posBusLookup.begin(); + it != posBusLookup.end(); + ++it) + { + for (std::map::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) + { + if (param == *it2->second) + { + std::stringstream cmdStr; + cmdStr << it->first << "." << it2->first << "=" << value; + sendCtrl(cmdStr.str()); + std::string field; + asynStatus status = readPosBusValues(&field); + if (status == asynError) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting: %s '\n", + driverName, functionName, cmdStr.str().c_str()); + } + callParamCallbacks(); + return status; + } + } + } + return asynSuccess; } /** Called when asyn clients call pasynFloat64->write(). @@ -1579,26 +1839,26 @@ asynStatus ADPandABlocks::UpdateLookupTableParamFromWrite(int param * \param[in] value Value to write. */ asynStatus ADPandABlocks::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { - const char *functionName = "writeFloat64"; - asynStatus status = asynError; - /* Any work we need to do */ - int param = pasynUser->reason; - status = setDoubleParam(param, value); - - // Check if motor parameters have changed - if (checkIfMotorFloatParams(param, value)); - // Otherwise check lookup table - else status = UpdateLookupTableParamFromWrite(param, value); - - if(status) - { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,"%s:%s: Error setting values'\n", - driverName, functionName); - } + const char *functionName = "writeFloat64"; + asynStatus status = asynError; + /* Any work we need to do */ + int param = pasynUser->reason; + status = setDoubleParam(param, value); + + // Check if motor parameters have changed + if (checkIfMotorFloatParams(param, value)); + // Otherwise check lookup table + else status = UpdateLookupTableParamFromWrite(param, value); + + if (status) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting values'\n", + driverName, functionName); + } - /* Do callbacks so higher layers see any changes */ - callParamCallbacks(); - return status; + /* Do callbacks so higher layers see any changes */ + callParamCallbacks(); + return status; } @@ -1609,57 +1869,54 @@ asynStatus ADPandABlocks::writeFloat64(asynUser *pasynUser, epicsFloat64 value) * \param[in] value Value to write. */ asynStatus ADPandABlocks::writeInt32(asynUser *pasynUser, epicsInt32 value) { - const char *functionName = "writeInt32"; - asynStatus status = asynError; - - // Update parameter value - int param = pasynUser->reason; - status = setIntegerParam(param, value); - - // Check if image configuration has been updated - if(param == ADImageMode) - { - imgMode = value; - } - else if(param == ADNumImages) - { - imgNo = value; - } - else if(param == ADAcquire) - { - if(value) - { - //set the current array number - asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, - "SEND ARM CMD:\n"); - sendCtrl("*PCAP.ARM="); - setIntegerParam(ADNumImagesCounter, 0); - getIntegerParam(ADNumExposures,&numExposures); - if (numExposures < 1) numExposures = 1; - numExposuresCounter=0; - } - else - { - asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, - "SEND DISARM CMD:\n"); - sendCtrl("*PCAP.DISARM="); - } - } - // Check if setpos has been called due to homing motor - else if(checkIfReasonIsMotorSetpos(param, value)); - // Check if need to change embedded screen type if using motorsync - else if(checkIfReasonIsMotorScreenType(param, value)); - // Otherwise update lookup table - else status = UpdateLookupTableParamFromWrite(param, value); - - if(status) - { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,"%s:%s: Error setting values'\n", - driverName, functionName); - } - - callParamCallbacks(); - return status; + const char *functionName = "writeInt32"; + asynStatus status = asynError; + + // Update parameter value + int param = pasynUser->reason; + status = setIntegerParam(param, value); + + // Check if image configuration has been updated + if (param == ADImageMode) + { + imgMode = value; + } else if (param == ADNumImages) + { + imgNo = value; + } else if (param == ADAcquire) + { + if (value) + { + //set the current array number + asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, + "SEND ARM CMD:\n"); + sendCtrl("*PCAP.ARM="); + setIntegerParam(ADNumImagesCounter, 0); + getIntegerParam(ADNumExposures, &numExposures); + if (numExposures < 1) numExposures = 1; + numExposuresCounter = 0; + } else + { + asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, + "SEND DISARM CMD:\n"); + sendCtrl("*PCAP.DISARM="); + } + } + // Check if setpos has been called due to homing motor + else if (checkIfReasonIsMotorSetpos(param, value)); + // Check if need to change embedded screen type if using motorsync + else if (checkIfReasonIsMotorScreenType(param, value)); + // Otherwise update lookup table + else status = UpdateLookupTableParamFromWrite(param, value); + + if (status) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting values'\n", + driverName, functionName); + } + + callParamCallbacks(); + return status; } /** Called when asyn clients call pasynOctet->write(). @@ -1667,58 +1924,74 @@ asynStatus ADPandABlocks::writeInt32(asynUser *pasynUser, epicsInt32 value) * For all parameters it sets the value in the parameter library and calls any registered callbacks.. * \param[in] pasynUser pasynUser structure that encodes the reason and address. * \param[in] value Value to write. */ -asynStatus ADPandABlocks::writeOctet(asynUser *pasynUser, const char* value, size_t nChars, size_t* nActual) { - /* This will check if a user has changed a value and attempt to update the - * panda. It will also update the readback values */ - const char *functionName = "writeOctet"; - asynStatus status = asynError; - /* Any work we need to do */ - int param = pasynUser->reason; - status = setStringParam(param, value); - *nActual = nChars; - - // Parse to stringStream for string conversion - std::stringstream valueStream; - valueStream << value; - - // Check if motor units have changed - if (checkIfReasonIsMotorUnit(param, valueStream.str())); - // Check if motor name is being set - else if (checkIfReasonIsMotorName(param, valueStream.str())); - // Otherwise check lookup table - else status = UpdateLookupTableParamFromWrite(param, valueStream.str()); +asynStatus ADPandABlocks::writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual) +{ + /* This will check if a user has changed a value and attempt to update the + * panda. It will also update the readback values */ + const char *functionName = "writeOctet"; + asynStatus status = asynError; + /* Any work we need to do */ + int param = pasynUser->reason; + status = setStringParam(param, value); + *nActual = nChars; + + // Parse to stringStream for string conversion + std::stringstream valueStream; + valueStream << value; + + // Check if motor units have changed + if (checkIfReasonIsMotorUnit(param, valueStream.str())); + // Check if motor name is being set + else if (checkIfReasonIsMotorName(param, valueStream.str())); + // Check if is setting for custom param + else if (checkIfReasonIsCustomParam(param, valueStream.str())); + + // Otherwise check lookup table + else status = UpdateLookupTableParamFromWrite(param, valueStream.str()); + + if (param < FIRST_PARAM) + { + // call parent class if attribute isn't ours + status = asynNDArrayDriver::writeOctet(pasynUser, value, nChars, nActual); + } - if(status) - { - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,"%s:%s: Error setting values'\n", - driverName, functionName); - } + if (status) + { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Error setting values'\n", + driverName, functionName); + } - callParamCallbacks(); - return status; + callParamCallbacks(); + return status; } -extern "C" int ADPandABlocksConfig(const char *portName, const char* pandaAddress, int maxPts, int maxBuffers, int maxMemory) { - new ADPandABlocks(portName, pandaAddress, maxPts, maxBuffers, maxMemory); - return (asynSuccess); +extern "C" int +ADPandABlocksConfig(const char *portName, const char *pandaAddress, int maxPts, int maxBuffers, int maxMemory) +{ + new ADPandABlocks(portName, pandaAddress, maxPts, maxBuffers, maxMemory); + return (asynSuccess); } /** Code for iocsh registration */ -static const iocshArg ADPandABlocksConfigArg0 = { "Port name", iocshArgString }; -static const iocshArg ADPandABlocksConfigArg1 = { "Panda address", iocshArgString }; -static const iocshArg ADPandABlocksConfigArg2 = { "Max number of points to capture in position compare", iocshArgInt }; -static const iocshArg ADPandABlocksConfigArg3 = { "maxBuffers for areaDetector", iocshArgInt }; -static const iocshArg ADPandABlocksConfigArg4 = { "maxMemory for areaDetector", iocshArgInt }; -static const iocshArg* const ADPandABlocksConfigArgs[] = { &ADPandABlocksConfigArg0, - &ADPandABlocksConfigArg1, &ADPandABlocksConfigArg2, &ADPandABlocksConfigArg3, &ADPandABlocksConfigArg4 }; -static const iocshFuncDef configADPandABlocks = { "ADPandABlocksConfig", 5, ADPandABlocksConfigArgs }; -static void configADPandABlocksCallFunc(const iocshArgBuf *args) { - ADPandABlocksConfig(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival); -} - -static void ADPandABlocksRegister(void) { - iocshRegister(&configADPandABlocks, configADPandABlocksCallFunc); +static const iocshArg ADPandABlocksConfigArg0 = {"Port name", iocshArgString}; +static const iocshArg ADPandABlocksConfigArg1 = {"Panda address", iocshArgString}; +static const iocshArg ADPandABlocksConfigArg2 = {"Max number of points to capture in position compare", iocshArgInt}; +static const iocshArg ADPandABlocksConfigArg3 = {"maxBuffers for areaDetector", iocshArgInt}; +static const iocshArg ADPandABlocksConfigArg4 = {"maxMemory for areaDetector", iocshArgInt}; +static const iocshArg *const ADPandABlocksConfigArgs[] = {&ADPandABlocksConfigArg0, + &ADPandABlocksConfigArg1, &ADPandABlocksConfigArg2, + &ADPandABlocksConfigArg3, &ADPandABlocksConfigArg4}; +static const iocshFuncDef configADPandABlocks = {"ADPandABlocksConfig", 5, ADPandABlocksConfigArgs}; + +static void configADPandABlocksCallFunc(const iocshArgBuf *args) +{ + ADPandABlocksConfig(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival); +} + +static void ADPandABlocksRegister(void) +{ + iocshRegister(&configADPandABlocks, configADPandABlocksCallFunc); } extern "C" { diff --git a/ADPandABlocksApp/src/ADPandABlocks.h b/ADPandABlocksApp/src/ADPandABlocks.h index 7d4afa0..b7b7d7c 100644 --- a/ADPandABlocksApp/src/ADPandABlocks.h +++ b/ADPandABlocksApp/src/ADPandABlocks.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,9 @@ /* This is the number of encoders (motors) */ #define NENC 4 +/* This is the number of custom params */ +#define NCUSTOM 10 + /* The timeout waiting for a response from ADPandABlocks */ #define TIMEOUT 1.0 @@ -106,7 +110,11 @@ class ADPandABlocks: public ADDriver { int ADPandABlocksMUnits[NENC]; // string write - motor units from GeoBrick int ADPandABlocksMScreenType[NENC]; // int32 write - Applies writeOnly embedded screen if using MotorSync int ADPandABlocksMCalibrate[NENC]; // int32 read - Calibrates encoder position - int ADPandABlocksMMotorName[NENC]; // string write - motor name from GeoBrick + int ADPandABlocksMMotorName[NENC]; // string write - motor name from GeoBrick + int ADPandABlocksCustomParamBlock[NCUSTOM]; // string write - Custom Panda param to read/write (Block name) + int ADPandABlocksCustomParamField[NCUSTOM]; // string write - Custom Panda param to read/write (Field name) + int ADPandABlocksCustomParamDemand[NCUSTOM]; // string write - Custom Panda param demand value + int ADPandABlocksCustomParamRBV[NCUSTOM]; // string read - Custom Panda param readback value #define NUM_PARAMS (&LAST_PARAM - &FIRST_PARAM + 1 + NPOSBUS*9 + NENC*6) private: @@ -127,7 +135,7 @@ class ADPandABlocks: public ADDriver { bool posBusInUse(std::string posBusName); void createLookup(std::string paramName, std::string paramNameEnd, int* paramInd, int posBusInd); std::vector stringSplit(const std::string& s, char delimiter); - void processChanges(std::string cmd, bool posn); + void processChanges(std::string cmd); void updateScaledPositionValue(std::string posBusName); int getEncoderNumberFromName(std::string posBusName); void calibrateEncoderPosition(int encoderNumer); @@ -140,8 +148,10 @@ class ADPandABlocks: public ADDriver { bool checkIfReasonIsMotorSetpos(int reason, int value); bool checkIfReasonIsMotorScreenType(int reason, int value); bool checkIfReasonIsMotorName(int reason, std::string name); + bool checkIfReasonIsCustomParam(int reason, std::string name); + void updateCustomParamLookup(int paramIndex, std::string paramName, std::string paramNameEnd); template - void updatePandAParam(std::string name, std::string field, T value); + bool updatePandAParam(std::string name, std::string field, T value); template void updatePandAMotorParam(int motorIndex, motorField field, T value); template @@ -149,7 +159,8 @@ class ADPandABlocks: public ADDriver { double stringToDouble(std::string str); int stringToInteger(std::string str); std::string doubleToString(double value); - void removeSubString(std::string &string, std::string &subString); + void removeSubString(std::string &string, std::string &subString); + bool checkIfCustomParam(std::string paramName, std::string fieldName, std::string val); private: NDArray *pArray; asynUser *pasynUser_ctrl_tx; @@ -178,6 +189,9 @@ class ADPandABlocks: public ADDriver { //Lookup table for posbus params std::map > posBusLookup; + //Lookup table for custom params (pair of demand param & rbv param) + std::map > > customParamLookup; + //Capture type map std::map captureType; std::vector captureStrings; diff --git a/ADPandABlocksApp/src/Makefile b/ADPandABlocksApp/src/Makefile index 29c07f5..af5df03 100644 --- a/ADPandABlocksApp/src/Makefile +++ b/ADPandABlocksApp/src/Makefile @@ -3,6 +3,7 @@ TOP=../.. include $(TOP)/configure/CONFIG #USR_CXXFLAGS += -Werror +USR_CXXFLAGS += -g USR_CXXFLAGS += -Wall -Wextra USR_CXXFLAGS += -Wno-unused-parameter diff --git a/configure/RELEASE b/configure/RELEASE index 89660c8..cfe1d73 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -25,11 +25,11 @@ SUPPORT=/dls_sw/prod/R3.14.12.7/support WORK=/dls_sw/work/R3.14.12.7/support # ASYN - for device/driver support -ASYN=$(SUPPORT)/asyn/4-34 +ASYN=$(SUPPORT)/asyn/4-41 # areaDetector for plugins like NDPluginStats -ADCORE=$(SUPPORT)/ADCore/3-4dls2 -MOTOR=$(SUPPORT)/motor/6-10-1dls1-1 +ADCORE=$(SUPPORT)/ADCore/3-10dls1 +MOTOR=$(SUPPORT)/motor/7-0dls7 # If using the sequencer, point SNCSEQ at its top directory: #SNCSEQ=$(EPICS_BASE)/../modules/soft/seq diff --git a/etc/builder.py b/etc/builder.py index 095d31c..6f69af3 100644 --- a/etc/builder.py +++ b/etc/builder.py @@ -2,26 +2,87 @@ from iocbuilder.modules.asyn import Asyn, AsynPort, AsynIP from iocbuilder.modules.ADCore import ADCore, ADBaseTemplate, includesTemplates, \ makeTemplateInstance +from iocbuilder.modules.calc import Calc from iocbuilder.arginfo import * from iocbuilder.modules.motor import MotorLib -# Peform template subsitution + +# Perform template subsitution @includesTemplates(ADBaseTemplate) -class _MainDbFile (AutoSubstitution): +class _MainDbFile(AutoSubstitution): TemplateFile = 'ADPandABlocks.template' -class _PosBusTemplate (AutoSubstitution): + +class _PosBusTemplate(AutoSubstitution): TemplateFile = 'ADPandABlocksPosBus.template' -class _MotorSyncTemplate (AutoSubstitution): + +class _MotorSyncTemplate(AutoSubstitution): """Synchronises motor record MRES, OFFSET, UNITS with PandABlocks INENC and sets position after home""" TemplateFile = 'ADPandABlocksMotorSync.template' -class MotorSync (Device): - def __init__( self, MOTOR, P, R, PORT, ENC_IND, - DIR="+", MULT=1, READONLY=True, HOMESETTLE=5): +class _CustomParamTemplate(AutoSubstitution): + TemplateFile = 'ADPandABlocksCustomParam.template' + + +class _TTLControl(AutoSubstitution): + TemplateFile = 'ADPandABlocksTTLControl.template' + + +class _TTLReadback(AutoSubstitution): + TemplateFile = 'ADPandABlocksTTLOutRBV.template' + + +numTTLControl = 0 + + +class TTLControl(Device): + Dependencies = (Calc, ) + def __init__(self, PORT, P, R, TTL_IND=1): + global numTTLControl + PARAM1_IND = "%02d" % ((numTTLControl * 2) + 1) + PARAM2_IND = "%02d" % ((numTTLControl * 2) + 2) + self.template = _TTLControl(PORT=PORT, P=P, R=R, TTL_IND=TTL_IND, PARAM1_IND=PARAM1_IND, + PARAM2_IND=PARAM2_IND) + numTTLControl += 1 + + ArgInfo = makeArgInfo(__init__, + P=Simple("Device prefix", str), + R=Simple("Device suffix", str), + PORT=Simple("Asyn port", str), + TTL_IND=Choice("TTL output index", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) + + +class TTLReadback(Device): + Dependencies = (Calc, ) + def __init__(self, PORT, P, R, TTL_IND=1): + global numTTLControl + PARAM1_IND = "%02d" % ((numTTLControl * 2) + 1) + PARAM2_IND = "%02d" % ((numTTLControl * 2) + 2) + self.template = _TTLReadback(PORT=PORT, P=P, R=R, TTL_IND=TTL_IND, PARAM1_IND=PARAM1_IND, + PARAM2_IND=PARAM2_IND) + numTTLControl += 1 + + ArgInfo = makeArgInfo(__init__, + P=Simple("Device prefix", str), + R=Simple("Device suffix", str), + PORT=Simple("Asyn port", str), + TTL_IND=Choice("TTL output index", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) + + + +# Use TTL output for software triggering a detector with a gated pulse +# Probably not something we should encourage, just use malcolm! +# class SoftTTLTrigger(AutoSubstitution): +# TemplateFile = 'ADPandABlocksSoftTTLTrigger.template' + + +class MotorSync(Device): + + def __init__(self, MOTOR, P, R, PORT, ENC_IND, + DIR="+", MULT=1, READONLY=True, HOMESETTLE=5): self.__super.__init__() self.MOTOR = MOTOR self.P = P @@ -40,27 +101,26 @@ def __init__( self, MOTOR, P, R, PORT, ENC_IND, R=self.R, PORT=self.PORT, ENC_IND=self.ENC_IND, - DIR = self.DIR, - MULT = self.MULT, - READONLY = self.READONLY_VALUE, - HOMESETTLE = self.HOMESETTLE + DIR=self.DIR, + MULT=self.MULT, + READONLY=self.READONLY_VALUE, + HOMESETTLE=self.HOMESETTLE ) ArgInfo = _MotorSyncTemplate.ArgInfo + makeArgInfo(__init__, - MOTOR = Simple("PV of motor to sync with", str), - P = Simple("Device prefix", str), - R = Simple("Device suffix", str), - PORT = Simple("Asyn port", str), - ENC_IND = Choice("Motor encoder index", [1, 2, 3, 4]), - DIR = Choice("Motor direction", ["+", "-"]), - MULT = Simple("Scale factor multiplier", float), - READONLY = Simple("Should embedded screen be read-only", bool), - HOMESETTLE = Simple("Calibration delay after homing", int)) + MOTOR=Simple("PV of motor to sync with", str), + P=Simple("Device prefix", str), + R=Simple("Device suffix", str), + PORT=Simple("Asyn port", str), + ENC_IND=Choice("Motor encoder index", [1, 2, 3, 4]), + DIR=Choice("Motor direction", ["+", "-"]), + MULT=Simple("Scale factor multiplier", float), + READONLY=Simple("Should embedded screen be read-only", bool), + HOMESETTLE=Simple("Calibration delay after homing", int)) # Main class for the ADPandABlocks device class ADPandABlocks(AsynPort): - # We depend upon Asyn Dependencies = (ADCore, Asyn) @@ -72,9 +132,10 @@ class ADPandABlocks(AsynPort): UniqueName = "PORT" N_POSBUS = 32 + N_CUSTOM = 0 def __init__(self, PORT, HOST, MAXBUF=1000, MAXMEM=0, **args): - + # create asyn ports for PandA self.control_port = AsynIP('%s:8888' % HOST, '%s_CTRL' % PORT) self.data_port = AsynIP('%s:8889' % HOST, '%s_DATA' % PORT) @@ -87,30 +148,32 @@ def __init__(self, PORT, HOST, MAXBUF=1000, MAXMEM=0, **args): self.HOST = HOST self.MAXBUF = MAXBUF self.MAXMEM = MAXMEM - self.NELM = 100000 - + self.NELM = 100000 + # Perform template subsitutions to create our DB file makeTemplateInstance(_MainDbFile, locals(), args) locals().update(args) # Create the templates for the position bus entries for i in range(self.N_POSBUS): - makeTemplateInstance(_PosBusTemplate, locals(), {'POSBUS_IND' : ("%d" % i)}) + makeTemplateInstance(_PosBusTemplate, locals(), {'POSBUS_IND': ("%d" % i)}) + + # Create templates for custom params + for i in range(self.N_CUSTOM): + makeTemplateInstance(_CustomParamTemplate, locals(), {'PARAM_IND': ("%02d" % (i + 1))}) def Initialise(self): # Print the command to create the device in the startup script - #print "# Create AsynIP ports for PandA" - #print 'drvAsynIPPortConfigure("%(PORT)s_CTRL", "%(HOST)s:8888", 100, 0, 0)' % self.__dict__ - #print 'drvAsynIPPortConfigure("%(PORT)s_DATA", "%(HOST)s:8889", 100, 0, 0)' % self.__dict__ + # print "# Create AsynIP ports for PandA" + # print 'drvAsynIPPortConfigure("%(PORT)s_CTRL", "%(HOST)s:8888", 100, 0, 0)' % self.__dict__ + # print 'drvAsynIPPortConfigure("%(PORT)s_DATA", "%(HOST)s:8889", 100, 0, 0)' % self.__dict__ print "# Create driver" print 'ADPandABlocksConfig("%(PORT)s", "%(HOST)s", ' \ - '%(NELM)d, %(MAXBUF)d, %(MAXMEM)d, 0)' % self.__dict__ + '%(NELM)d, %(MAXBUF)d, %(MAXMEM)d, 0)' % self.__dict__ # tell xmlbuilder what args to supply ArgInfo = _MainDbFile.ArgInfo + makeArgInfo(__init__, - PORT = Simple("Asyn port name for the created driver", str), - HOST = Simple("PandA Box - can be hostname or IP address", str), - MAXBUF = Simple("Maximum number of buffers (areaDetector)", int), - MAXMEM = Simple("Maximum memory (areaDetector)", int)) - - + PORT=Simple("Asyn port name for the created driver", str), + HOST=Simple("PandA Box - can be hostname or IP address", str), + MAXBUF=Simple("Maximum number of buffers (areaDetector)", int), + MAXMEM=Simple("Maximum memory (areaDetector)", int))