ISISComputingGroup / IBEX

Top level repository for IBEX stories
5 stars 2 forks source link

IOC: Eurotherm Add2 and above not updating #1903

Closed John-Holt-Tessella closed 7 years ago

John-Holt-Tessella commented 7 years ago

On Larmor and IRIS we have seen that the address 2 and 3 often don't update. For LARMOR (where we had some debug time) when you log the communications you do not see 0022PV being sent. The value appears as "TIMEOUT INVALID"

Acceptance Testing

Test both models 2000 and 3000 series for: temp readback and setting over the whole capable range (should be documented in wiki) Ensuring minus numbers and close to 0 and fractional reading if possible. reading on auxilary functions remain stable even when there are the maximum number of eurotherms working.

John-Holt-Tessella commented 7 years ago

Debugging log:

Can see monitor not updating. When add debugging on output then can see that command is not sent to the eurotherm If the record is asked to process it rerturns a value and then the next time errors

Conclusion is that it is trying to send too many commands to the eurotherm all at once and then some of these are timing out. We reduce the repeation of the fan out read and set the temp to read on convertion read. This seems to have made it more reliable but it is still erroring occastionally.

Also at the same time we get errors for negative number because the eurotherm returns them as "- 9.9" (with a space in) this is fixed by adding a # in the protocol %f -> %#f.

John-Holt-Tessella commented 7 years ago

Plan of action:

  1. Try to reproduce the error on local hardware using a moxa if needed
  2. Edit the fanout to be a sequence of fast and slow records. So that the commands are sent one after another and slower commands are updated less often.
  3. Add in the conversion from small negative numbers.
John-Holt-Tessella commented 7 years ago

Final DB file on system

#FileList for ramp
###################

record(waveform, "$(P)RAMP_DIRBASE:SP")
{
   field(DESC, "Set directory of ramp files")
   field(DTYP, "asynOctetWrite")
   field(PINI, "NO")
   field(INP, "@asyn($(RAMPLIST),0,1)DIRBASE")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

record(waveform, "$(P)RAMP_DIRBASE")
{
   field(DESC, "Get directory of ramp files")
   field(DTYP, "asynOctetRead")
   field(INP, "@asyn($(RAMPLIST),0,1)DIRBASE")
   field(SCAN, "I/O Intr")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

record(waveform, "$(P)RAMP_FILES")
{
    field(DESC, "Compressed JSON list of ramp files")
    field(FTVL, "CHAR")
    field(DTYP, "asynOctetRead")
    field(INP, "@asyn($(RAMPLIST),0,1)JARR")    
    field(NELM, 16384)
    field(SCAN, "I/O Intr")
}

record(waveform, "$(P)RAMP_PATTERN:SP")
{
   field(DESC, "Set regex search of ramp files")  
   field(DTYP, "asynOctetWrite")
   field(INP, "@asyn($(RAMPLIST),0,1)SEARCH")
   field(PINI, "NO")
   field(FTVL, "CHAR")
   field(NELM, 256)
}
record(waveform, "$(P)RAMP_PATTERN")
{
   field(DESC, "Get regex search of ramp files")
   field(DTYP, "asynOctetRead")
   field(INP, "@asyn($(RAMPLIST),0,1)SEARCH")
   field(PINI, "NO")
   field(SCAN, "I/O Intr")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

#ReadASCII
###################
record(waveform, "$(P)READ_RAMP_DIRBASE:SP")
{
   field(DTYP, "asynOctetWrite")
   field(INP, "@asyn($(READ),0,1)DIRBASE")
   field(PINI, "NO")
   field(FTVL, "CHAR")
   field(NELM, 256)

}

record(waveform, "$(P)READ_RAMP_DIRBASE")
{
   field(DTYP, "asynOctetRead")
   field(INP, "@asyn($(READ),0,1)DIRBASE")
   field(PINI, "YES")
   field(FTVL, "CHAR")
   field(NELM, 256)
   field(SCAN, "I/O Intr")
}

record(stringin, "$(P)RAMP_FILE")
{
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(READ),0,1)DIR")
   field(SCAN, "I/O Intr")
}

record(stringout, "$(P)RAMP_FILE:SP")
{
   field(DTYP, "asynOctetWrite")
   field(OUT,  "@asyn($(READ),0,1)DIR")
   field(SCAN, "I/O Intr")
}

record(ao, "$(P)TEMP:SP")
{
   field(DESC, "Temperature setpoint")
   field(DTYP, "asynFloat64")
   field(OUT, "@asyn($(READ),0,1)TGT")
   field(FLNK, "$(P)OUT_SP")
   field(EGU, "K")
   info(INTEREST, "HIGH")
   info(archive, "VAL")
}

record(ao, "$(P)TEMP:SP:RBV")
{
   field(DESC, "Temperature setpoint readback")
   field(DTYP, "asynFloat64")
   field(OUT, "@asyn($(READ),0,1)TGT:RBV")
   field(EGU, "K")
   info(INTEREST, "MEDIUM")
   info(archive, "VAL")
}

record(ao, "$(P)RATE:SP")
{
   field(DESC, "Rate the ramp increases/decreases")
   field(DTYP, "asynFloat64")
   field(OUT, "@asyn($(READ),0,1)RATE")
   field(EGU, "K/s")
}

record(ai, "$(P)RATE")
{
   field(DESC, "Rate the ramp increases/decreases")
   field(DTYP, "asynFloat64")
   field(INP, "@asyn($(READ),0,1)RATE")
   field(EGU, "K/s")
   field(SCAN, "I/O Intr")
}

record(bi, "$(P)RAMPING")
{
   field(DESC, "High if ramp is executing")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(READ),0,1)CURRMP")
   field(SCAN, "I/O Intr")   
}

record(ao, "$(P)CURRENT_TEMP")
{
    field(DESC, "The current temperature of the sensor")
    field(DOL, "$(P)TEMP")
    field(OMSL, "closed_loop")
    field(DTYP, "asynFloat64")
    field(OUT, "@asyn($(READ),0,1)CUR")
    field(EGU, "K")
}

record(bo, "$(P)RAMPON:SP")
{
   field(DESC, "Toggles the SP ramp")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(READ),0,1)RMP")
}

record(bi, "$(P)RAMPON")
{
   field(DESC, "Toggles the SP ramp")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(READ),0,1)RMP")
   field(SCAN, "I/O Intr")
}

record(bo, "$(P)LUTON:SP")
{
   field(DESC, "Toggles the PID lookup")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(READ),0,1)LUT")
}

record(bi, "$(P)LUTON")
{
   field(DESC, "Toggles the PID lookup")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(READ),0,1)LUT")
   field(SCAN, "I/O Intr")
}

record(ai, "$(P)OUT_SP")
{
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(READ),0,1)SP")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
   field(FLNK, "$(P)RAMP_SP.PROC PP")
}

record(ai, "$(P)OUT_P")
{
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(READ),0,1)P")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
   field(FLNK, "$(P)RAMP_P")
}

record(ai, "$(P)OUT_I")
{
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(READ),0,1)I")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
   field(FLNK, "$(P)RAMP_I")
}

record(ai, "$(P)OUT_D")
{
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(READ),0,1)D")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
   field(FLNK, "$(P)RAMP_D")
}

record(ai, "$(P)OUT_MAX")
{
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(READ),0,1)MH")
   field(PREC, "5")
   field(SCAN, "I/O Intr")
   field(FLNK, "$(P)RAMP_MAXOUT")
  }

###########

#Pass on PID ramp
record(ao, "$(P)RAMP_SP")
{
    field(DTYP, "Soft Channel")
    field(DOL, "$(P)OUT_SP")
    field(FLNK, "$(P)TEMP:SP:CONV.PROC PP") 
    field(OMSL, "closed_loop")
    field(OIF, "Full")
}

record(ao, "$(P)RAMP_P")
{
    field(DTYP, "Soft Channel")
    field(DOL, "$(P)OUT_P")
    field(OMSL, "closed_loop")
    field(OIF, "Full")
    field(OUT, "$(P)P:SP PP")
}

record(ao, "$(P)RAMP_I")
{
    field(DTYP, "Soft Channel")
    field(DOL, "$(P)OUT_I")
    field(OMSL, "closed_loop")
    field(OIF, "Full")
    field(OUT, "$(P)I:SP PP")
}

record(ao, "$(P)RAMP_D")
{
    field(DTYP, "Soft Channel")
    field(DOL, "$(P)OUT_D")
    field(OMSL, "closed_loop")
    field(OIF, "Full")
    field(OUT, "$(P)D:SP PP")
}

record(ao, "$(P)RAMP_MAXOUT")
{
    field(DTYP, "Soft Channel")
    field(DOL, "$(P)OUT_MAX")
    field(OMSL, "closed_loop")
    field(OIF, "Full")
    field(OUT, "$(P)MAX_OUTPUT:SP PP")
}

##################

#Fanout reads so that they can be interrupted

record(fanout, "$(P)READ") {
    field(SCAN, "10 second")
    field(LNK1, "$(P)P PP")
    field(LNK2, "$(P)I PP")
    field(LNK3, "$(P)D PP")
    field(LNK4, "$(P)OUTPUT PP")    
    field(LNK5, "$(P)MAX_OUTPUT PP")
    field(LNK6, "$(P)READ1 PP")
    field(SELM, "All")
}

record(fanout, "$(P)READ1") {
    field(SCAN, "Passive")
    field(LNK1, "$(P)AUTOTUNE PP")
    field(LNK2, "$(P)SENSOR PP")
    field(LNK3, "$(P)RBV PP")
    field(LNK4, "$(P)SP:RBV PP")
    field(LNK5, "$(P)HILIM PP")
    field(LNK6, "$(P)LOWLIM PP")    
    field(SELM, "All")
}

####
record(longin, "$(P)ADDRESS") {
    field(DTYP, "Soft Channel")
    field(DESC, "Eurotherm address")
    field(SCAN, "Passive")
    field(PINI, "YES")
    field(VAL, "$(ADDR)")
}

record(longin, "$(P)GAD") {
    field(DESC, "Eurotherm greater address")
    field(SCAN, "Passive")
    field(PINI, "YES")
    field(VAL, "$(GAD)")
}

record(longin, "$(P)LAD") {
    field(DESC, "Eurotherm lesser address")
    field(SCAN, "Passive")
    field(PINI, "YES")
    field(VAL, "$(LAD)")
}

record(ai, "$(P)OUTPUT") {
    info(INTEREST, "MEDIUM")
    field(DESC, "Output Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),OP,$(GAD),$(LAD)) $(PORT)")
    field(EGU, "%")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:OUTPUT")
    field(SDIS, "$(Q)DISABLE")
    info(archive, "VAL")
}

record(ai, "$(P)MAX_OUTPUT") {
    field(DESC, "Max Output Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),HO,$(GAD),$(LAD)) $(PORT)")
    field(EGU, "%")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:MAX_OUTPUT")
    field(SDIS, "$(Q)DISABLE")
    info(archive, "VAL")
}

record(ao, "$(P)MAX_OUTPUT:SP") {
    field(DESC, "D Setpoint")
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),HO,$(GAD),$(LAD)) $(PORT)")
    field(DRVH, "100")
    field(DRVL, "0")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:MAX_OUTPUT:SP")
    field(SDIS, "$(Q)DISABLE")
    field(FLNK, "$(P)MAX_OUTPUT.PROC PP")
    info(archive, "VAL")
}

alias("$(P)MAX_OUTPUT", "$(P)MAX_OUTPUT:SP:RBV")

record(mbbi, "$(P)ERR_READ") {
    field(DESC, "Current error message")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto readhex($(P),EE,$(GAD),$(LAD)) $(PORT)")
    field(ZRVL, "0")
    field(ZRST, "No Error")
    field(ONVL, "1")
    field(ONST, "Invalid Param")
    field(TWVL, "2")
    field(TWST, "Read Only")
    field(SVVL, "7")
    field(SVST, "Incorrect Msg")
    field(EIVL, "8")
    field(EIST, "Limit Error")
    field(PINI, "YES")
    field(FLNK, "$(P)ERR_READ_PUSH")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:ERR")
    field(SDIS, "$(Q)DISABLE")
    info(archive, "VAL")
}

# This error record is usually written to by an error that happens during writing to the device 
# However when err_read is processed this also write to here.
record(mbbi, "$(P)ERR") {
    field(DESC, "Last error message")
    field(ZRVL, "0")
    field(ZRST, "No Error")
    field(ONVL, "1")
    field(ONST, "Invalid Param")
    field(TWVL, "2")
    field(TWST, "Read Only")
    field(SVVL, "7")
    field(SVST, "Incorrect Msg")
    field(EIVL, "8")
    field(EIST, "Limit Error")
    field(PINI, "YES")
    info(archive, "VAL")
    info(INTEREST, "MEDIUM")
}

record(mbbo, "$(P)ERR_READ_PUSH") {
    field(DESC, "Push error from device to error record")
    field(DOL, "$(P)ERR_READ")
    field(OUT, "$(P)ERR")
    field(OMSL, "closed_loop")
}

record(ai, "$(P)P") {
    info(INTEREST, "MEDIUM")
    field(DESC, "P Param Readback")
    field(SCAN, "Passive")      
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),XP,$(GAD),$(LAD)) $(PORT)")    
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:P")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    info(archive, "VAL")
}

record(ao, "$(P)P:SP") {
    field(DESC, "P Setpoint")
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),XP,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:P:SP")
    field(SDIS, "$(Q)DISABLE")
    field(FLNK, "$(P)P.PROC")
}

alias("$(P)P", "$(P)P:SP:RBV")

record(ai, "$(P)I") {
    info(INTEREST, "MEDIUM")
    field(DESC, "I Param Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),TI,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:I")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    info(archive, "VAL")
}

record(ao, "$(P)I:SP") {
    field(DESC, "I Setpoint")
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),TI,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:I:SP")
    field(SDIS, "$(Q)DISABLE")
    field(FLNK, "$(P)I.PROC")
}

alias("$(P)I", "$(P)I:SP:RBV")

record(ai, "$(P)D") {
    info(INTEREST, "MEDIUM")
    field(DESC, "D Param Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),TD,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:D")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    info(archive, "VAL")
}

record(ao, "$(P)D:SP") {
    field(DESC, "D Setpoint")
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),TD,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:D:SP")
    field(SDIS, "$(Q)DISABLE")
    field(FLNK, "$(P)D.PROC")
}

alias("$(P)D", "$(P)D:SP:RBV")

record(ai, "$(P)AUTOTUNE") {
    field(DESC, "Autotune Readback")
    field(DTYP, "stream")
    field(SCAN, "Passive")
    field(INP, "@eurotherm2k.proto read($(P),AT,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:AUTOTUNE")
    field(SDIS, "$(Q)DISABLE")
}

record(ao, "$(P)AUTOTUNE:SP") {
    field(DESC, "D Setpoint")
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),AT,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:AUTOTUNE:SP")
    field(SDIS, "$(Q)DISABLE")
    field(FLNK, "$(P)AUTOTUNE.PROC")
}

alias("$(P)AUTOTUNE", "$(P)AUTOTUNE:SP:RBV")

record(ai, "$(P)SENSOR") {
    info(INTEREST, "MEDIUM")
    field(DESC, "Sensor Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),PV,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:SENSOR")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
}

record(stringin, "$(P)CAL:SEL:RBV") {
    info(INTEREST, "MEDIUM")
    field(DESC, "Calibration file")
    field(DTYP, "Soft Channel")
    field(INP, "$(P)TEMP.NSPE MS")
    field(PINI, "YES")
    field(SCAN, "5 second")
    info(archive, "VAL")
}

record(stringin, "$(P)CAL:RBV")
{
    field(DESC, "Current Calibration file")
    field(DTYP, "Soft Channel")
    field(INP, "$(P)TEMP.SPEC MS")
    field(PINI, "YES")
    field(SCAN, "5 second")
    info(archive, "VAL")
}

record(bi, "$(P)CAL:APPLY")
{
    field(DTYP, "Soft Channel")
    field(ZNAM, "WAIT")
    field(ONAM, "APPLY")
    field(VAL, "0")
    field(FLNK, "$(P)CAL:APPLY:UPDATE.PROC")
}

record(bo, "$(P)CAL:APPLY:RST")
{
    field(DTYP, "Soft Channel")
    field(ZNAM, "STOP APPLY")
    field(ONAM, "")
    field(VAL, "0")
    field(OUT, "$(P)CAL:APPLY")
}

record(stringout, "$(P)CAL:SEL")
{
    field(DESC, "Set Calibration file")
    field(DTYP, "Soft Channel")
    field(FLNK, "$(P)CAL:SEL:UPDATE.PROC")
    field(PINI, "YES")
    field(VAL, "None.txt")
    info(archive, "VAL")
    info(autosaveFields, "VAL")
#    info(autosaveFields_pass0, "VAL")
}

record(scalcout, "$(P)CAL:SEL:FMT")
{
    field(INPA, "$(P)CAL:SEL:VAL")
    field(CALC, "$P('%s.test',A)")
}

record(sseq, "$(P)CAL:SEL:UPDATE")
{
    field(DOL1, "$(P)CAL:SEL.VAL")
    field(LNK1, "$(P)TEMP.NSPE")
    field(DOL2, "$(P)CAL:SEL")
    field(LNK2, "$(P)TEMP:SP:RBV:CONV.NSPE")
    field(DOL3, "$(P)CAL:SEL")
    field(LNK3, "$(P)TEMP:HIGHLIMIT.NSPE")
    field(DOL4, "$(P)CAL:SEL")
    field(LNK4, "$(P)TEMP:LOWLIMIT.NSPE")
    field(DOL5, "$(P)CAL:SEL")
    field(LNK5, "$(P)TEMP:SP:CONV.NSPE")
    field(DOL6, "1")
    field(LNK6, "$(P)CAL:SEL:UPDATE:NOW.PROC")
}

record(dfanout, "$(P)CAL:SEL:UPDATE:NOW")
{
    field(VAL, "1")
    field(OUTA, "$(P)CAL:APPLY.VAL")
    field(OUTB, "$(P)CAL:APPLY.PROC")
}

record(seq, "$(P)CAL:APPLY:UPDATE")
{
    field(DOL1, "$(P)CAL:APPLY")
    field(LNK1, "$(P)TEMP.INIT NPP")
    field(DOL2, "$(P)CAL:APPLY")
    field(LNK2, "$(P)TEMP:SP:RBV:CONV.INIT NPP")
    field(DOL3, "$(P)CAL:APPLY")
    field(LNK3, "$(P)TEMP:HIGHLIMIT.INIT NPP")
    field(DOL4, "$(P)CAL:APPLY")
    field(LNK4, "$(P)TEMP:LOWLIMIT.INIT NPP")
    field(DOL5, "$(P)CAL:APPLY")
    field(LNK5, "$(P)TEMP:SP:CONV.INIT NPP")
    field(DOL6, "0")
    field(DLY6, "2")
    field(LNK6, "$(P)CAL:APPLY:RST.PROC")
}

# FileList for sensor calibration
#######################

record(waveform, "$(P)SENSOR_DIRBASE:SP")
{
   field(DESC, "Set directory of sensor calib files")
   field(DTYP, "asynOctetWrite")
   field(PINI, "NO")
   field(INP, "@asyn($(SENSORLIST),0,1)DIRBASE")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

record(waveform, "$(P)SENSOR_DIRBASE")
{
   field(DESC, "Get directory of sensor calib files")
   field(DTYP, "asynOctetRead")
   field(INP, "@asyn($(SENSORLIST),0,1)DIRBASE")
   field(SCAN, "I/O Intr")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

record(waveform, "$(P)SENSOR_FILES")
{
    field(DESC, "Compressed JSON list of sensor files")
    field(FTVL, "CHAR")
    field(DTYP, "asynOctetRead")
    field(INP, "@asyn($(SENSORLIST),0,1)JARR")  
    field(NELM, 16384)
    field(SCAN, "I/O Intr")
}

record(waveform, "$(P)SENSOR_PATTERN:SP")
{
   field(DESC, "Set regex search of sensor calib files")  
   field(DTYP, "asynOctetWrite")
   field(INP, "@asyn($(SENSORLIST),0,1)SEARCH")
   field(FTVL, "CHAR")
   field(NELM, 256)
}
record(waveform, "$(P)SENSOR_PATTERN")
{
   field(DESC, "Get regex search of sensor calib files")
   field(DTYP, "asynOctetRead")
   field(INP, "@asyn($(SENSORLIST),0,1)SEARCH")
   field(PINI, "NO")
   field(SCAN, "I/O Intr")
   field(FTVL, "CHAR")
   field(NELM, 256)
}

record(cvt, "$(P)TEMP")
{
    field(SCAN, "1 second")
    field(DESC, "Temperature")
    info(INTEREST, "HIGH")
    field(INPY, "$(P)RBV PP")
    field(YSLO, 1)
    field(METH, "1D TABLE INVERTED")
    field(SPEC, "None.txt")
    field(TDIR, "$(SDIR)")
    field(BDIR, "$(CALIB_BASE_DIR)")
    field(PREC, "3")
    field(FLNK, "$(P)CURRENT_TEMP PP")
    info(archive, "VAL")
}

record(cvt, "$(P)TEMP:SP:RBV:CONV")
{
    field(SCAN, "1 second")
    field(INPY, "$(P)SP:RBV PP")
    field(OUT, "$(P)TEMP:SP:RBV PP")
    field(YSLO, 1)
    field(METH, "1D TABLE INVERTED")
    field(SPEC, "None.txt") 
    field(PREC, "3")
    field(TDIR, "$(SDIR)")
    field(BDIR, "$(CALIB_BASE_DIR)")
}

record(cvt, "$(P)TEMP:HIGHLIMIT")
{
    field(SCAN, "10 second")
    field(INPY, "$(P)HILIM PP")
    field(YSLO, 1)
    field(METH, "1D TABLE INVERTED")
    field(SPEC, "None.txt")
    field(TDIR, "$(SDIR)")
    field(BDIR, "$(CALIB_BASE_DIR)")
    field(PREC, "3")
    info(archive, "VAL")
}

record(cvt, "$(P)TEMP:LOWLIMIT")
{
    field(SCAN, "10 second")
    field(INPY, "$(P)LOWLIM PP")
    field(YSLO, 1)
    field(METH, "1D TABLE INVERTED")
    field(SPEC, "None.txt")
    field(TDIR, "$(SDIR)")
    field(BDIR, "$(CALIB_BASE_DIR)")
    field(PREC, "3")
    info(archive, "VAL")
}

record(cvt, "$(P)TEMP:SP:CONV")
{
    field(OUT, "$(P)SP PP")
    field(INPX, "$(P)RAMP_SP")
    field(YSLO, 1)
    field(METH, "1D TABLE")
    field(SPEC, "None.txt")
    field(TDIR, "$(SDIR)")
    field(BDIR, "$(CALIB_BASE_DIR)")
    field(PREC, "3")
}

record(ai, "$(P)RBV") {
    info(INTEREST, "HIGH")
    field(DESC, "Temperature Readback")
    field(DTYP, "stream")
    field(SCAN, "Passive")
    field(INP, "@eurotherm2k.proto read($(P),PV,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:TEMP")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    field(FLNK, "$(P)TEMP.PROC PP")
    info(archive, "VAL")
    info(alarm, "Euro")
}

record(ai, "$(P)SP:RBV") {
    info(INTEREST, "MEDIUM")
    field(DESC, "Temperature Setpoint Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),SP,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:TEMP:SP:RBV")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    info(archive, "VAL")
    info(alarm, "Euro")
}

record(ao, "$(P)SP") {
    info(INTEREST, "MEDIUM")
    field(DESC, "Temperature Setpoint")
    field(OUT, "$(P)_SP NPP")
    field(EGU, "")
    field(PREC, "3")
    info(archive, "VAL")
    field(FLNK, "$(P)_DOSP.PROC PP")
}

record(longout, "$(P)_DOSP") {
    field(VAL, "0")
    field(OUT, "$(P)SP:RETRYCNT PP")
    field(FLNK, "$(P)_SP.PROC PP")
}

record(ao, "$(P)_SP") {
    field(DTYP, "stream")
    field(OUT, "@eurotherm2k.proto write($(P),SL,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:TEMP:SP")
    field(SDIS, "$(Q)DISABLE")
    field(EGU, "")
    field(PREC, "3")
    field(FLNK, "$(P)_CHECKSP.PROC PP")
    info(archive, "VAL")
    info(alarm, "Euro")
}

## how many times to resend a setpoint if it doesn't match readback
record(longout, "$(P)SP:NRETRY") {
    field(VAL, "5")
    field(OUT, "$(P)SP:RETRYCNT.HIGH")
    field(PINI, "YES")
}

## current setpoint retry number
## HIGH alarm value should be same as retry count NRETRY  
record(longout, "$(P)SP:RETRYCNT") {
    field(DESC, "Last setpoint retry count")
    field(VAL, "0")
    field(PINI, "YES")
    field(HIGH, "5")
    field(HSV, "MINOR")
    info(archive, "VAL")
    info(alarm, "Euro")
}

## difference between sent SP and readback SP to trigger a resend of SP
## note this is a tolerance on the raw value sent to the eurotherm via the stream device command
record(ai, "$(P)SP:CHKTOL") {
    field(DESC, "Setpoint check tolerance")
    field(VAL, "0.001")
}

## check difference between sent and readback SP and re-issue send if necessary
## the PP on INPB forces a setpoint readback
## request resend setpoint after a 1 second delay
record(calcout, "$(P)_CHECKSP") {
    field(DESC, "Check SP received or resend")
    field(INPA, "$(P)_SP NPP")
    field(INPB, "$(P)SP:RBV PP")
    field(INPC, "$(P)SP:CHKTOL NPP")
    field(INPD, "$(P)SP:NRETRY NPP")
    field(INPE, "$(P)SP:RETRYCNT NPP")
    field(CALC, "(ABS(A-B)>C)&&(E<D)?1:0")
    field(OOPT, "When Non-zero")
    # we need to use CA on OUT as otherwise SP will not reprocess as we are currently part of its existing processing chain
    field(OUT, "$(P)_DORETRY.PROC PP")
    field(HIGH, "0.5")
    field(HSV, "MINOR")
    field(ODLY, 1)
   info(archive, "VAL")
}

record(calcout, "$(P)_DORETRY") {
    field(INPA, "$(P)SP:RETRYCNT NPP")
    field(CALC, "A+1")
    field(OOPT, "Every Time")
    field(OUT, "$(P)SP:RETRYCNT PP")
    # we need to use CA on OUT as otherwise SP will not reprocess as we are currently part of its existing processing chain
    field(FLNK, "$(P)_SP.PROC CA")
}

record(ai, "$(P)HILIM") {
    field(DESC, "Setpoint Highlimit Readback")
    field(SCAN, "Passive")
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),HS,$(GAD),$(LAD)) $(PORT)")
    field(FLNK, "$(P)LOWLIM.PROC")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:TEMP:HIGHLIMIT")
    field(SDIS, "$(Q)DISABLE")
    field(PREC, "3")
}

record(ai, "$(P)LOWLIM") {
    field(DESC, "Setpoint Lowlimit Readback")
    field(SCAN, "Passive")    
    field(DTYP, "stream")
    field(INP, "@eurotherm2k.proto read($(P),LS,$(GAD),$(LAD)) $(PORT)")
    field(SIML, "$(Q)SIM")
    field(SIOL, "$(P)SIM:TEMP:LOWLIMIT")
    field(SDIS, "$(Q)DISABLE")
    field(PREC, "3")
}

### SIMULATION RECORDS ###

record(ai, "$(P)SIM:MAX_OUTPUT")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:MAX_OUTPUT","$(P)SIM:MAX_OUTPUT:SP")

alias("$(P)SIM:MAX_OUTPUT","$(P)SIM:MAX_OUTPUT:SP:RBV")

record(ai, "$(P)SIM:TEMP:LOWLIMIT")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

record(ai, "$(P)SIM:D")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:D","$(P)SIM:D:SP")

alias("$(P)SIM:D","$(P)SIM:D:SP:RBV")

record(mbbi, "$(P)SIM:ERR")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

record(ai, "$(P)SIM:TEMP")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:TEMP","$(P)SIM:TEMP:SP")

alias("$(P)SIM:TEMP","$(P)SIM:TEMP:SP:RBV")

record(ai, "$(P)SIM:I")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:I","$(P)SIM:I:SP")

alias("$(P)SIM:I","$(P)SIM:I:SP:RBV")

record(ai, "$(P)SIM:AUTOTUNE")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:AUTOTUNE","$(P)SIM:AUTOTUNE:SP")

alias("$(P)SIM:AUTOTUNE","$(P)SIM:AUTOTUNE:SP:RBV")

record(ai, "$(P)SIM:SENSOR")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

record(ai, "$(P)SIM:P")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

alias("$(P)SIM:P","$(P)SIM:P:SP")

alias("$(P)SIM:P","$(P)SIM:P:SP:RBV")

record(ai, "$(P)SIM:OUTPUT")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")
}

record(ai, "$(P)SIM:TEMP:HIGHLIMIT")
{
    field(SCAN, "Passive")
    field(DTYP, "Soft Channel")

}
John-Holt-Tessella commented 7 years ago

Set point not updating for values > 99.99.

Protocol file added # to %#f in read_once and read_init and .2 to %.2f in write.

## \file
## Stream Device Protocol for eurotherm 2000 series EI Bisynch
## will look for the record \$1ERR which will process any errors

locktimeout = 5000;

OutTerminator   = "";

# Uses a timeout when there is no termination character (i.e. for reads)
replytimeout = 200;
readtimeout  = 100;
extrainput   = Error;

# time to wait after read (see below)
waittime = 50;

#STX = "\x02"
#ETX = "\x03"
#EOT = "\x04"
#ENQ = "\x05"
#ACK = "\x06"

## Read value
## \code
## send: [EOT](GAD)(GAD)(LAD)(LAD)(CHAN)(C1)(C2)[ENQ]
## reply: [STX](CHAN)(C1)(C2)<DATA>[ETX](BCC)
## \endcode
##
## - $1 = device prefix, \$(P)
## - $2 = command mnemonic
## - $3 = GAD = First char of address, e.g. address = 1, GAD = 0, address = 12, GAD = 1
## - $4 = LAD = Second char of address, e.g. address = 1, LAD = 1, address = 12, LAD = 2
#############################################################################

## read a normal value using the \x03 as the termination character on read
##   this means there is an extra BCC character left in the buffer this is 
##   either read as part of the next read (hence the regex for a random character) or is got rid of as part 
##   of the buffer flush done by stream. The eurotherm will not respond if it is still writting the last character (BCC)
##   to the buffer so we wait for a small amount of time to allow it to finish writing this character (without this there are may time out warnings)
read_once  {InTerminator = "\x03"; out "\x04\$3\$3\$4\$4\$2\x05"; in "%*/.?\x02/\$2%#f"; wait $waittime;}
readhex_once  {out "\x04\$3\$3\$4\$4\$2\x05"; in "%*/.?\x02/\$2>%x"; wait $waittime;}

## read the value (if it times out re read it)
read { InTerminator = "\x03"; read_once; @readtimeout { read_once; } }

## Read value, but in hex (if it times out re read it)
readhex { InTerminator = "\x03"; readhex_once; @readtimeout { readhex_once; } }

## Write value
## \code
## send: [EOT](GAD)(GAD)(LAD)(LAD)[STX](CHAN)(C1)(C2)<DATA>[ETX](BCC)
## reply: [ACK] or [NAK], discarded as no terminator
## \endcode
## - $1 = device prefix, \$(P)
## - $2 = command mnemonic
## - $3 = GAD = First char of address, e.g. address = 1, GAD = 0, address = 12, GAD = 1
## - $4 = LAD = Second char of address, e.g. address = 1, LAD = 1, address = 12, LAD = 2
#############################################################################

## read the error and put it in error record. There are no termination characters for this
##   because it is a write so get back both the \x03 and BCC
read_error { out "\x04\$3\$3\$4\$4EE\x05"; in "%*/.?\x02/EE>%(\$1ERR)x\x03%*c"; } 

## read the value on init, because it is part of write there is no terminator character so get back both the \x03 and BCC
##  no need for regex on beinging since everything is read correctly
read_init     { InTerminator = ""; out "\x04\$3\$3\$4\$4\$2\x05";  in "\x02\$2%#f\x03%*c";}
read_init_int { InTerminator = ""; out "\x04\$3\$3\$4\$4\$2\x05";  in "\x02\$2%i\x03%*c";}

## write value to the eurotherm. The reply is a single character so InTerminator must be blank and we reply on the timeout
## on an error (e.g. NAK mismatches ACK) read the error code immediately from the device
write {
    InTerminator = ""; 
    out "\x04\$3\$3\$4\$4\x02\$2%.2f\x03%6<xor>"; 
    in "\x06";
    @init{ read_init; }; 
    @mismatch{ read_error; }; 
}

## Write a value in int rather than float. The reply is a single character so InTerminator must be blank and we reply on the timeout
## on an error (e.g. NAK mismatches ACK) read the error code immediately from the device
writeint {  
    InTerminator = ""; 
    out "\x04\$3\$3\$4\$4\x02\$2%i\x03%6<xor>"; 
    in "\x06"; 
    @init{ read_init_int; }; 
    @mismatch{ read_error; }; 
}
John-Holt-Tessella commented 7 years ago

Can not reproduce timeout errors with 3 sensors running but with 7 running (relable addresses 4,5,6,7 to 1,2,3,1) I do get problems on sensors 2+ fairly consistently.

FreddieAkeroyd commented 7 years ago

You mention .2 to %.2f above - don't the new eurotherms take 3 decimal places?

John-Holt-Tessella commented 7 years ago

They don't seem to no; I will test more today. I suspect that it will take 10.123 but it does not allow 101.123 I think it might be fixed width instead of fixed dp. Restricting it to the above on Larmor fixed their problems and this is how it looks in the labview code (to my best labview reading ability).

FreddieAkeroyd commented 7 years ago

sscanf (read) ignore the number of decimal places, so you would need to use a max width instead. printf (write) defaults to %6.2 unless otherwise specified

John-Holt-Tessella commented 7 years ago

It is on write that the problem is being caused and suspect but not sure that it is because the precision is set on the set point write so stream is outputting 3dp; this means the eurotherm ignore the setpoint being requested and so we can not set anything above 99.999.

FreddieAkeroyd commented 7 years ago

this is testing on a 3000 series and you get no error raised? I though Isabella did a test with a eurotherm 2000 at one stage and it was quite happy getting 1.234567 and just ignored extra, maybe the 3000 series are different. It's not that the eurotherm has internal limits set that we need to change?

John-Holt-Tessella commented 7 years ago

Yes this is testing on a 3000 series, no there is no error raised that I can see. Yes the 2000 seems happy to ignore the trailing dps but the 300 (I have a 3504 on my desk) is not. It seems to ignore the request, No the internal limit was the first thing I checked; in fact I thought that it was limited to 1000. The lab view code does the same so I suspect this was a thing found when coding for them too. I will put a reminder in the acceptance criteria that it should work for both the 2000 and 300 series.

FreddieAkeroyd commented 7 years ago

So I guess we need to check at the DB end for > 99 and then adjust the number of DP used in the protocol file accordingly. It might be possible to use %.*f format to pass the number of DP as a protocol argument from the DB record rather than needing two functions

John-Holt-Tessella commented 7 years ago

Useful command to monitor records:

camonitor %MYPVPREFIX%EUROTHRM_01:A01:RBV %MYPVPREFIX%EUROTHRM_01:A01:SENSOR %MYPVPREFIX%EUROTHRM_01:A01:SP:RBV %MYPVPREFIX%EUROTHRM_01:A01:OUTPUT %MYPVPREFIX%EUROTHRM_01:A02:RBV %MYPVPREFIX%EUROTHRM_01:A02:SENSOR %MYPVPREFIX%EUROTHRM_01:A02:SP:RBV %MYPVPREFIX%EUROTHRM_01:A02:OUTPUT %MYPVPREFIX%EUROTHRM_01:A03:RBV %MYPVPREFIX%EUROTHRM_01:A03:SENSOR %MYPVPREFIX%EUROTHRM_01:A03:SP:RBV %MYPVPREFIX%EUROTHRM_01:A03:OUTPUT %MYPVPREFIX%EUROTHRM_01:A04:RBV %MYPVPREFIX%EUROTHRM_01:A04:SENSOR %MYPVPREFIX%EUROTHRM_01:A04:SP:RBV %MYPVPREFIX%EUROTHRM_01:A04:OUTPUT %MYPVPREFIX%EUROTHRM_01:A05:RBV %MYPVPREFIX%EUROTHRM_01:A05:SENSOR %MYPVPREFIX%EUROTHRM_01:A05:SP:RBV %MYPVPREFIX%EUROTHRM_01:A05:OUTPUT %MYPVPREFIX%EUROTHRM_01:A06:RBV %MYPVPREFIX%EUROTHRM_01:A06:SENSOR %MYPVPREFIX%EUROTHRM_01:A06:SP:RBV %MYPVPREFIX%EUROTHRM_01:A06:OUTPUT %MYPVPREFIX%EUROTHRM_01:A07:RBV %MYPVPREFIX%EUROTHRM_01:A07:SENSOR %MYPVPREFIX%EUROTHRM_01:A07:SP:RBV %MYPVPREFIX%EUROTHRM_01:A07:OUTPUT
John-Holt-Tessella commented 7 years ago

I have created another ticket (#1969) to address the issues it is important that these changes are made.