#include #include #include #include #include #include #include #include "err_handling.h" #include "coc_server.h" #include "coc_logfile.h" #include "str_util.h" #include "tecs_lsc.h" #include "tecs_dlog.h" #define TABLE_FILE "lsc.tab" #define NO_CODE 999 static SerChannel *ser=NULL; static char *serverId=NULL; static char *binDir=NULL; static char *logDir=NULL; static DlogSet dset; typedef struct { float temp, t1, t2; /* calc, high, low temperature */ int dirty; /* input config to be reloaded */ int try; /* trial count */ int manual; /* manual device */ int code, code1; /* device code, buffer for device code */ int nSens; /* number of sensors */ int codChanged; /* code has changed */ int codDefined; /* code is not yet confirmed */ char ch1[2], ch2[2]; /* channels for high/low T */ char device[16]; /* device name */ } Part; Part /* C standard guarantees initialization to zero */ cryo, /* data for main sensors (on heat exchanger, or the only sensors) */ samp, /* data for extra sensors of sample stick */ *parts[2]={&cryo, &samp}, *part=&cryo; static float tempC, /* set T */ tempH, /* set T on heater */ htr, /* heat power */ tLimit, power, /* heater parameters */ tLow=0, tHigh=0, /* lower limit of high-T sensor, upper limit of low-T sensor */ tShift=0, /* setpoint shift */ tInt=0; /* integral time (sec.) for setpoint shift */ static int logPeriod=10, /* data logging period (sec.) */ period=5000, /* default read interval (msec.) */ logTime, /* next logging time */ setFlag, /* temperature to be set */ remoteFlag, /* to be set to remote mode */ saveTime, /* time for a CRVSAV command */ noResp=1, /* no response */ quit, /* quit server */ controlMode=2, /* 0: control on heater, 1: control on sample, 3: 2nd loop for difference heater-sample */ mode, /* 0: local, 2: remote */ maxfld, /* last used display field */ busy, /* busy after CRVSAV */ deviceFlag, /* device given via net */ num, /* curve number */ fld, /* field number */ key, /* key status */ serialNo, resist, cod1, cod2, out1, out2, /* codes read from digital input/output */ iRange, iAmp, /* max. range and max. current code */ per; /* effective period */ static time_t auto_remote_time, /* time for automatic reload */ tim, /* actual time */ tableTime; /* last time when table was read */ static int decod[8]={21,20,17,16,5,4,1,0}; /* for code conversion */ static char device[32], /* concatenated device names */ buf1[256], buf2[256], buf3[256], buf4[256], /* buffers for temporary use */ head[64], /* curve header */ intype[64], /* input configuration */ chan[2], /* actual channel */ xxxx; static char *table=NULL, /* environment devices table */ *cache=NULL; /* curve list cache */ struct timeb tim0; int logMask; void idleHdl(int tmo, int fd) { int iRet; iRet=CocHandleRequests(tmo, fd); if (iRet<0) logfileShowErr("CocHandleRequests"); } void concatDevice() { str_copy(device, cryo.device); if (0!=strcmp(cryo.device, samp.device) && (samp.device[0]!='\0' || samp.manual)) { str_append(device, "/"); str_append(device, samp.device); } } int instCurve(char *nam, char *channel) { /* install sensor nam on channel */ char buf[256], chead[64], nbuf[256], lbuf[16]; char *crv, *entry, *points, *start, *s, /* start of found entry */ *e, /* cache part after found entry */ *res, *t; int i, n; char used[60]; FILE *fil; int retstat; str_copy(chan, channel); logfileOut(LOG_MAIN, "install curve %s\n", nam); fil=NULL; crv=NULL; /* read curve file */ str_copy(nbuf, binDir); str_append(nbuf, nam); retstat=-2; /* an error would be severe */ ERR_P(crv=str_read_file(nbuf)); t=str_split(chead, crv, '\n'); if (t==NULL) { points=NULL; } else { points=str_split(intype, t, '\n'); } if (points==NULL) ERR_MSG("illegal curve file"); if (points[0]=='$') { /* standard curve */ points++; num=atoi(points); if (num>20) ERR_MSG("illegal standard curve number"); retstat=-1; /* an error could be fixed */ ERR_P(LscCmd(ser, "CRVHDR?[num]>head")); } else { strcpy(buf, ":"); str_append(buf, nam); str_append(buf, " "); str_upcase(buf, buf); if (cache==NULL) { cache=malloc(1); *cache='\0'; } /* create empty cache if undefined */ start=strchr(cache, '\n'); /* skip device names */ if (start==NULL) { start=cache; } else { start++; } entry=strstr(start, buf); if (entry==NULL) { /* sensor not found in cache */ entry=start; for (i=60;i>20;i--) used[i]=0; n=40; num=0; while (entry!=NULL && n>0) { i=0; sscanf(entry, "%d", &i); if (i>20 && i<=60) { num=i; s=entry; used[i]=1; n--; } entry=strchr(entry, '\n'); if (entry !=NULL) entry++; } if (n>0) { for (num=60;num>20;num--) { if (used[num]==0) break; } s=NULL; e=NULL; } else { e=strchr(s, '\n'); if (e!=NULL) { *e='\0'; e++; } if (s>start) { s--; *s='\0'; } } head[0]='\0'; } else { s=entry; entry++; while (s>start && *s!='\n') s--; if (s>start) { *s='\0'; s++; } num=0; sscanf(s, "%d", &num); if (num<21 || num>60) ERR_MSG("illegal curve number"); retstat=-1; /* an error could be fixed */ ERR_P(LscCmd(ser, "CRVHDR?[num]>head")); e=strchr(entry, '\n'); if (e!=NULL) { *e='\0'; e++; } } } fld=chan[0]-'A'+1; if (fld>maxfld) maxfld=fld; if (head[0]!='\0' && LscEqPar(head, chead)) { /* header matches: select sensor type and curve */ retstat=-1; /* an error could be fixed */ ERR_P(LscCmd(ser, "INTYPE [chan]:[intype];INCRV [chan]:[num];DISPFLD [fld],[chan],1;DISPLAY:[maxfld]")); logfileOut(LOG_MAIN, "curve %d on channel %s selected\n", num, chan); } else { /* header does not match -> download */ retstat=-2; /* an error would be severe */ if (num<=20) ERR_MSG("standard curve does not match"); retstat=-1; /* an error could be fixed */ if (busy) ERR_MSG("busy"); logfileOut(LOG_MAIN, "download curve %d\n", num); /* select sensor type first to display sensor units */ ERR_P(LscCmd(ser, "INTYPE [chan]:[intype];DISPFLD [fld],[chan],3;DISPLAY:[maxfld]")); n=3; do { ERR_P(LscCmd(ser, "CRVDEL [num];CRVHDR?[num]>buf1,")); buf1[4]='\0'; i=strcmp(buf1, "User"); n--; } while (i!=0 && n!=0); if (i!=0) ERR_MSG("can not delete curve"); sprintf(lbuf, "CRVPT %d", num); do { /* download curve */ t=str_split(nbuf, points, '\n'); if (nbuf[0]!='\0') { ERR_I(str_substitute(buf, nbuf, "#0", lbuf)); ERR_P(LscCmd(ser, buf)); } points=t; } while (t!=NULL); /* write header, select curve */ str_upcase(head, chead); ERR_P(LscCmd(ser, "CRVHDR [num]:[head];INCRV [chan]:[num];DISPFLD [fld],[chan],1")); logfileOut(LOG_MAIN, "curve selected on channel %s\n", chan); saveTime=tim+30; } free(crv); crv=NULL; if (num<=20) return(0); /* standard curve, do not touch cache */ /* rewrite cache with actual entry at beginning */ retstat=-2; /* errors in following section are severe */ sprintf(lbuf, "lsc.%d", serialNo); str_copy(nbuf, logDir); str_append(nbuf, lbuf); fil=fopen(nbuf, "r+"); if (fil==NULL) ERR_SP(fil=fopen(nbuf, "w")); if (cryo.manual) ERR_SI(fputs(cryo.device, fil)); ERR_SI(fputs("/", fil)); if (samp.manual) ERR_SI(fputs(samp.device, fil)); ERR_SI(fputs("\n", fil)); sprintf(buf, "%d:%s", num, head); ERR_SI(fputs(buf, fil)); /* write actual entry */ if (start!=s) { /* write content before replaced entry */ ERR_SI(fputs("\n", fil)); ERR_SI(fputs(start, fil)); } if (e!=NULL) { /* write content after replaced entry */ ERR_SI(fputs("\n", fil)); ERR_SI(fputs(e, fil)); } ERR_SI(fclose(fil)); fil=NULL; free(cache); /* re-read it */ ERR_P(cache=str_read_file(nbuf)); return(0); OnError: if (crv!=NULL) free(crv); if (fil!=NULL) fclose(fil); return(retstat); } int configInput() { char *t; char buf[80], nam[16], nbuf[256]; int i, n, nn; int retstat; char *ext; retstat=-2; /* errors in following section are severe */ if (part->manual) { sprintf(buf, ".%s.", part->device); } else { sprintf(buf, "%+d ", part->code); if (part->code==0) return(0); } if (table!=NULL && tim>tableTime+60) { free(table); table=NULL; }; /* clear old table */ if (table==NULL) { /* read table */ str_copy(nbuf, binDir); str_append(nbuf, TABLE_FILE); ERR_P(table=str_read_file(nbuf)); tableTime=tim; str_replace_char(table, 9, ' '); /* replace TAB (9) by space in order to find codes terminated by tab */ } t=strstr(table, buf); if (t==NULL) ERR_MSG("device not found"); i=sscanf(t, "%79[^\n!]", buf); /* read line */ t=strchr(buf, '.'); if (t==NULL) ERR_MSG("missing '.' in table file"); t++; n=1; if (part==&samp) { samp.nSens=0; i=sscanf(t, "%12s%d%d", nam, &nn, &n); if (i<1) ERR_MSG("missing sensor name"); ext="s"; } else { cryo.nSens=0; tLow=0; tHigh=0; controlMode=0; i=sscanf(t, "%12s%d%d%d%f%d%f%f%f", nam, &n, &nn, &controlMode, &tLimit, &resist, &power, &tLow, &tHigh); if (i<7) ERR_MSG("missing some sensor parameters"); ext="x"; } if (n<0 || n>2) ERR_MSG("illegal value for nsensor"); if (n==0) return(0); if (!part->manual) { /* set device name */ str_copy(part->device, nam); part->device[strlen(part->device)-1]='\0'; /* strip off '.' */ concatDevice(); } str_append(nam, ext); ERR_I(retstat=instCurve(nam, part->ch1)); if (n==2) { str_append(nam, "l"); ERR_I(retstat=instCurve(nam, part->ch2)); } part->nSens=n; return(0); OnError: return(retstat); } int loadCache() { int i, j, k; char *res; char buf[256], nbuf[256], lbuf[16]; char *bufi[4]; FILE *fil; fil=NULL; sprintf(lbuf, "lsc.%d", serialNo); str_copy(nbuf, logDir); str_append(nbuf, lbuf); ERR_SP(fil=fopen(nbuf, "w")); if (cryo.manual) ERR_SI(fputs(cryo.device, fil)); ERR_SI(fputs("/", fil)); if (samp.manual) ERR_SI(fputs(samp.device, fil)); ERR_SI(fputs("\n", fil)); bufi[0]=buf1; bufi[1]=buf2; bufi[2]=buf3; bufi[3]=buf4; for (i=60; i>21; i-=4) { sprintf(buf, "CRVHDR?%d>buf1;CRVHDR?%d>buf2;CRVHDR?%d>buf3;CRVHDR?%d>buf4", i, i-1, i-2, i-3); ERR_P(LscCmd(ser, buf)); k=0; for (j=i; j>i-4; j--) { res=bufi[k]; k++; if (res[1]=='s') { i=0; break;} /* s of "User", must be empty slot, lowercase letters are not programmable */ sprintf(buf, "%d:", j); ERR_SI(fputs(buf, fil)); ERR_SI(fputs(res, fil)); ERR_SI(fputs("\n", fil)); } } ERR_SI(fclose(fil)); /* re-read cache */ ERR_P(cache=str_read_file(nbuf)); return(0); OnError: if (fil!=NULL) fclose(fil); return(-1); } float WeightedAverage(int n, float t1, float t2) { float p,q; if (n==0) { return(0.0); } else if (n<2) { return(t1); } else { if (t21 && tempC<(tLow+tHigh)/2) ch=cryo.ch2; if (samp.nSens>0) { if (controlMode==1) { /* control directly on sample sensor */ tShift=0; ch=samp.ch1; if (cryo.nSens>1 && tempC<(tLow+tHigh)/2) ch=samp.ch2; } else if (controlMode!=2) { tShift=0; } } else { tShift=0; } str_copy(chan, ch); tempH=tempC+tShift; if (switchOn) { ERR_P(LscCmd(ser, "CSET 1:[chan],1,1,0;RANGE:[iRange];SETP 1:[tempH]")); } else { ERR_P(LscCmd(ser, "CSET 1:[chan],1;SETP 1:[tempH]")); } return(0); OnError: return(-1); } int ReadTemp() { char buf[256], lbuf[16]; char *res; int i, k; float t2[2], p, d; ERR_P(LscCmd(ser, "DIOST?>cod1,out1;DOUT 3,29;HTR?>htr;BUSY?>busy")); if (cryo.codDefined) { per=period; } else { per=1; /* advance fast when initializing */ } if (noResp) { /* check serial number */ k=serialNo; ERR_P(LscCmd(ser, "*IDN?>buf1,buf2,serialNo,")); if (0!=strcmp(buf1, "LSCI") || 0!=strcmp(buf2, "MODEL340") || serialNo==0) return(0); if (k!=serialNo) { if (cryo.manual || cryo.code!=0) cryo.dirty=1; if (samp.manual || samp.code!=0) samp.dirty=1; ERR_P(LscCmd(ser, "RANGE:0")); /* switch off heater */ /* reload curve cache: */ if (cache!=NULL) free(cache); sprintf(lbuf, "lsc.%d", serialNo); str_copy(buf, logDir); str_append(buf, lbuf); cache=str_read_file(buf); if (cache==NULL && 0==strcmp(ErrMessage, "file not found")) ERR_I(loadCache()); cryo.device[0]='\0'; samp.device[0]='\0'; sscanf(cache, "%15[^/!]%*c%15[^\n!]", cryo.device, samp.device); /* read device names separated by '/' */ if (cryo.device[0]!='\0') cryo.manual=1; if (samp.device[0]!='\0') samp.manual=1; concatDevice(); } noResp=0; } ERR_P(LscCmd(ser, "KRDG?[cryo.ch1]>cryo.t1;KRDG?[cryo.ch2]>cryo.t2;KRDG?[samp.ch1]>samp.t1;KRDG?[samp.ch2]>samp.t2")); cryo.temp=WeightedAverage(cryo.nSens, cryo.t1, cryo.t2); samp.temp=WeightedAverage(samp.nSens, samp.t1, samp.t2); if (samp.temp==0.0) samp.temp=cryo.temp; if (cryo.dirty==0 && samp.dirty==0 && noResp==0 && tim>logTime) { t2[0]=cryo.temp; t2[1]=samp.temp; DlogPut(&dset, tim, 2, t2); DlogUpd(&dset); logTime=(tim/logPeriod+1)*logPeriod; } if (samp.nSens>0 && cryo.nSens>0 && controlMode==2 && tempC!=0) { d=tempH-cryo.temp; d=exp(-d*d); /* if d is small, we are far from setpoint */ if (tInt<60000/period) tInt+=d; /* increase integral time until 60 sec. */ if (tInt>d) { p=d/tInt; } else { p=1.0; } tShift=tShift*(1.0-p)+p*(cryo.temp-samp.temp); ERR_I(SetTemp(0)); } ERR_P(LscCmd(ser, "KEYST?>key;DIOST?>cod2,out2;DOUT 3,30")); if (busy==0) { if (out1!=30) { ERR_P(LscCmd(ser, "DOUT:3,30")); ERR_P(LscCmd(ser, "DIOST?>cod1,out1")); } if (out2!=29) { ERR_P(LscCmd(ser, "DOUT:3,29")); ERR_P(LscCmd(ser, "DIOST?>cod2,out2;DOUT 3,30")); } if (out1==30 && out2==29) { /* code conversion */ cryo.code1=3*decod[cod2 % 8] ^ 2*decod[cod1 % 8]; /* ^ is exclusive OR */ samp.code1=-(3*decod[cod2 / 8] ^ 2*decod[cod1 / 8]); for (i=0; i<2; i++) { part=parts[i]; if (part->code1!=part->code) { part->code=part->code1; part->codChanged=1; } else { if (part->codChanged) { part->codChanged=0; if (part->code1==0) { logfileOut(LOG_MAIN, "unplugged X\n"); } else { logfileOut(LOG_MAIN, "plugged (%d)\n", part->code1); } if (part->codDefined) { part->manual=0; } part->dirty=1; } part->codDefined=1; } } } } if (key!=0) { auto_remote_time=tim+600; if (!(cryo.dirty || samp.dirty)) { logfileOut(LOG_MAIN ,"user touched keys\n"); } if (cryo.manual || cryo.code!=0) cryo.dirty=1; if (samp.manual || samp.code!=0) samp.dirty=1; mode=0; ERR_P(LscCmd(ser, "MODE?>mode")); if (mode==2) auto_remote_time=tim; /* user switched to remote mode */ } return(0); OnError: return(-1); } int inputSettings(Part *this) { part=this; if (part->dirty && samp.codDefined) { if (busy==0 || cryo.dirty>=0 && part->dirty>=0) { /* do not enter when busy and cryo.dirty/P indicates error on last time */ logfileOut(LOG_MAIN ,"configure inputs for "); if (part->manual) { logfileOut(LOG_MAIN ,"%s\n", part->device); } else { logfileOut(LOG_MAIN ,"%+d\n", part->code); } if (part->dirty>0) part->try=0; part->nSens=0; if (!part->manual) { part->device[0]='\0'; concatDevice(); } part->dirty=configInput(); if (part->dirty<0) { part->try++; if (part->dirty!=-1 || part->try>3) { logfileShowErr("fatal error P"); part->dirty=0; part->device[0]='\0'; concatDevice(); } else { logfileShowErr("try again P"); } } else { ERR_P(LscCmd(ser, "ALARM [part.ch1]:[part.nSens],1,[tLimit],0,0,1;ALARM [part.ch2]:0;RELAY 1:1;BEEP:0")); } } } return(0); OnError: return(-1); } int Settings() { int i, j, k; float pa, pr, pw, dif; if (cryo.dirty && cryo.codDefined || samp.dirty && samp.codDefined) { for (i=0; i<2; i++) { part=parts[i]; if (part->dirty) { ERR_P(LscCmd(ser, "DISPFLD 2,[part.ch1],1;DISPFLD 4,[part.ch2],1")); } } inputSettings(&cryo); inputSettings(&samp); if (cryo.nSens>0) { /* control settings */ ERR_P(LscCmd(ser, "CDISP 1:1,[resist],1;MOUT 1:0;CMODE 1:1")); iAmp=1; iRange=0; if (power>0) { pa=resist*4; /* max. power */ pw=0; dif=1.0e6; for (i=4; i>0; i--) { pr=pa; for (j=5; j>0; j--) { if (pr>power) { if (pr/power=cryo.nSens) { maxfld=2*samp.nSens; } else { maxfld=2*cryo.nSens-1; } if (maxfld>0) { ERR_P(LscCmd(ser, "MODE:2;DISPLAY:[maxfld]")); } else { maxfld=1; ERR_P(LscCmd(ser, "MODE:2;DISPLAY:1;DISPFLD 1,A,3")); } mode=2; if (saveTime!=0 && tim>saveTime) { ERR_P(LscCmd(ser, "CRVSAV;BUSY?>busy")); while (!busy) { idleHdl(200, 0); /* wait 200 ms */ ERR_P(LscCmd(ser, "BUSY?>busy")); } saveTime=0; } } return(0); OnError: return(-1); } int ExecuteRequest() { char *t, *res; struct CocClient *client; if (tim>auto_remote_time || setFlag) ERR_I(Settings()); if (setFlag) { if (cryo.nSens>0) { tInt=0; /* reset integral time */ ERR_I(SetTemp(1)); } setFlag=0; } client=CocGetNextCmd(); if (client!=NULL) { if (NULL!=strchr(client->cmd, '>') || NULL!=strchr(client->cmd, '[')) ERR_MSG("no variables allowed"); res=LscCmd(ser, client->cmd); if (res==NULL && ErrMessage!=NULL) { str_copy(client->res, ErrMessage); } else { str_copy(client->res, res); } client->cmd[0]='\0'; } if (deviceFlag) { t=strchr(device, '/'); if (t==NULL) { if (0==strcmp(device, "0")) { cryo.manual=0; cryo.dirty=1; samp.manual=0; samp.dirty=1; } else { str_copy(cryo.device, device); str_copy(samp.device, device); cryo.manual=1; cryo.dirty=1; samp.manual=1; samp.dirty=1; } } else { if (t!=device) { *t='\0'; str_copy(cryo.device, device); *t='/'; cryo.manual=1; cryo.dirty=1; } t++; if (*t!='\0') { str_copy(samp.device, t); samp.manual=1; samp.dirty=1; } } concatDevice(); deviceFlag=0; } return(0); OnError: return(-1); } int mainBody(void) { int i, iret, tdif; struct timeb tim1; /* read & control temp */ ERR_I(ReadTemp()); if (tim>=auto_remote_time) ERR_I(Settings()); logfileWrite(logMask); while (!quit) { ftime(&tim1); tdif=per-((tim1.time-tim0.time)*1000+tim1.millitm-tim0.millitm); if (tdif<0) { tim=tim1.time; break; } /* timeout */ ERR_I(iret=CocHandleRequests(tdif, 0)); if (iret==0) { time(&tim); break; } /* timeout */ tim=tim1.time; ERR_I(ExecuteRequest()); logfileWrite(logMask); } ftime(&tim1); tdif=((tim1.time-tim0.time)*1000+tim1.millitm-tim0.millitm)/period; i=period*tdif+tim0.millitm; tim0.time+=i / 1000; tim0.millitm=i % 1000; if (tdif>1) { logfileOut(LOG_MAIN ,"%d cycles lost\n", tdif-1); } return(0); OnError: if (0==strcmp(ErrMessage, "timeout")) { if (!noResp) logfileOut(LOG_MAIN ,"no response\n"); per=period+10000; /* long period if no response */ noResp=1; return(0); } return(-1); } int main(int argc, char *argv[]) { int logIt=0; int i, iret, use_stdout=0; char *host; char buf[256], opt; int port, msecTmo; str_copy(cryo.ch1,"A"); str_copy(cryo.ch2,"B"); str_copy(samp.ch1,"C"); str_copy(samp.ch2,"D"); cryo.codChanged=1; samp.codChanged=1; logMask=LOG_MAIN+LOG_WARN; binDir="bin/"; logDir="log/"; serverId="tecs"; host="lnsp26"; port=0; msecTmo=0; logfileOut(LOG_MAIN ,"%s ", argv[0]); for (i=1;i