#include #include #include #include #include "server.h" #include "logfile.h" #include "util.h" #include "lsc.h" #include "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; static float tempP=0, /* sample T */ tempX=0, /* heat exch. T */ tempC=0, /* set T */ tempH=0, /* set T on heater */ htr; /* heat power */ static int logPeriod=10, /* logging period (sec.) */ period=1000, /* default read interval (msec.) */ logTime=0, /* next logging time */ setFlag=0, /* temperature to be set */ remoteFlag=0, /* to be set to remote mode */ dirtyX=0, /* reload input config for heat exch. */ dirtyP=0, /* reload input config for sample */ tryP=0, /* trial count for sample */ tryX=0, /* trial count for heat exch */ saveIt=0, /* CRVSAV command */ noResp=1, /* no response */ manualX=0, /* X device was given manually */ manualP=0, /* P device was given manually */ quit=0, /* quit server */ controlMode=2, /* 0: control on heater, 1: control on sample, 3: 2nd loop for difference heater-sample */ serialNo=0; static int busy, mode=0; static int nX=0, nP=0; /* number of sensors */ static int maxfld; /* last used display field */ static float tLimit, resist=0.0, power; /* heater parameters */ static float tLow=0, tHigh=0; /* lower limit of high-T sensor, upper limit of low-T sensor */ static float tShift=0; /* setpoint shift */ static float tInt=0; /* integral time (sec.) for setpoint shift */ static time_t auto_remote_time=0; /* time for automatic reload */ static time_t tim; /* actual time */ static time_t tableTime; /* last time when table was read */ static int codX=-1, codP=0; /* codes for heat exchanger and sample */ static int decod[8]={21,20,17,16,5,4,1,0}; /* for code conversion */ static char channel[5]="ACBD"; /* sensor channels for heater and sample (main, lowT) */ static char device[32], deviceX[16], deviceP[16]; static int deviceFlag=0; static char *table=NULL; /* environment devices table */ static char *cache=NULL; /* curve list cache */ void idleHdl(int tmo, int fd) { int iRet; iRet=CocHandleRequests(tmo, fd); if (iRet<0) logfileShowErr("CocHandleRequests"); } void concatDevice() { str_copy(device, deviceX); if (0!=strcmp(deviceX, deviceP)) { str_append(device, "/"); str_append(device, deviceP); } } int instCurve(char *nam, char chan) { /* install sensor nam on channel chan */ char *args[2]; char buf[256], head[64], nbuf[256], lbuf[16]; char *crv, *entry, *points, *intype, *start, *s, /* start of found entry */ *e, /* cache part after found entry */ *res, *t; int i, num, n, fld; char used[60]; FILE *fil; int retstat; logfileOut("install curve %s\n", nam); retstat=-2; /* errors in following section are severe */ fil=NULL; crv=NULL; strcpy(buf, ":"); str_append(buf, nam); str_append(buf, " "); str_upcase(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"); sprintf(buf, "CRVHDR?%d", num); retstat=-1; /* errors in following section are not severe */ ERR_P(res=SerCmd(ser, buf)); e=strchr(entry, '\n'); if (e!=NULL) { *e='\0'; e++; } str_copy(head, res); } /* read and substitute curve file */ sprintf(buf, "CRVPT %d", num); args[0]=buf; retstat=-2; /* errors in following section are severe */ str_copy(nbuf, binDir); str_append(nbuf, nam); ERR_P(crv=str_read_arg(nbuf, args, 1)); intype=str_split1(crv, '\n'); if (intype==NULL) { points=NULL; } else { points=str_split1(intype, '\n'); } if (points==NULL) { ERR_MSG("illegal curve file"); } res=strchr(channel, chan); if (res!=NULL) { fld=res-channel+1; } else { fld=1; } if (fld>maxfld) maxfld=fld; retstat=-1; /* errors in following section are not severe */ if (LscEqPar(crv, head)) { /* header matches: select sensor type and curve */ sprintf(buf, "INTYPE %c:%s;INCRV %c:%d;DISPFLD %d:%c,1;DISPLAY:%d" , chan, intype, chan, num, fld, chan, maxfld); ERR_I(LscCmdChk(ser, buf)); logfileOut("curve %d on channel %c selected\n", num, chan); } else { /* header does not match -> download */ if (busy) ERR_MSG("busy"); logfileOut("download curve %d\n", num); sprintf(buf, "INTYPE %c:%s;DISPFLD %d:%c,3;DISPLAY:%d" , chan, intype, fld, chan, maxfld); /* select sensor type first to display sensor units */ ERR_I(LscCmdChk(ser, buf)); sprintf(buf, "CRVDEL %d;CRVHDR?%d", num, num); n=3; do { ERR_P(res=SerCmd(ser, buf)); res[4]='\0'; i=strcmp(res, "User"); n--; } while (i!=0 && n!=0); if (i!=0) ERR_MSG("can not delete curve"); do { /* download curve */ t=str_split1(points, '\n'); if (*points!='\0') ERR_I(LscCmdChk(ser, points)); points=t; } while (t!=NULL); sprintf(buf, "CRVHDR %d:%s;INCRV %c:%d;DISPFLD %d:%c,1", num, crv, chan, num, fld, chan); /* write header, select curve */ ERR_I(LscCmdChk(ser, buf)); logfileOut("curve selected on channel %c\n", chan); str_copy(head, crv); str_upcase(head); saveIt=1; } free(crv); crv=NULL; /* 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 (manualX) ERR_SI(fputs(deviceX, fil)); ERR_SI(fputs("/", fil)); if (manualP) ERR_SI(fputs(deviceP, 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_arg(nbuf, NULL, 0)); return(0); OnError: if (crv!=NULL) free(crv); if (fil!=NULL) fclose(fil); return(retstat); } int configInput(int sampleStick) { char *t; char buf[80], nam[16], nbuf[256]; int i, j, n, nn; int retstat; retstat=-2; /* errors in following section are severe */ if (sampleStick) { if (manualP) { sprintf(buf, ".%s.", deviceP); } else { sprintf(buf, "%+d ", codP); if (codP==0) return(0); } } else { if (manualX) { sprintf(buf, ".%s.", deviceX); } else { sprintf(buf, "%+d ", codX); if (codX==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_arg(nbuf, NULL, 0)); 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 (sampleStick) { nP=0; i=sscanf(t, "%12s%d%d", nam, &nn, &n); if (i<1) ERR_MSG("missing sensor name"); j=1; if (n<0 || n>2) ERR_MSG("illegal value for nsensor"); if (n==0) return(0); if (!manualP) { /* set device name */ str_copy(deviceP, nam); deviceP[strlen(deviceP)-1]='\0'; /* strip off '.' */ concatDevice(); } str_append(nam,"s"); } else { nX=0; tLow=0; tHigh=0; controlMode=0; i=sscanf(t, "%12s%d%d%d%f%f%f%f%f", nam, &n, &nn, &controlMode, &tLimit, &resist, &power, &tLow, &tHigh); if (i<7) ERR_MSG("missing some sensor parameters"); j=0; if (n<0 || n>2) ERR_MSG("illegal value for nsensor"); if (n==0) return(0); if (!manualX) { /* set device name */ str_copy(deviceX, nam); deviceX[strlen(deviceX)-1]='\0'; /* strip off '.' */ concatDevice(); } str_append(nam, "x"); } ERR_I(retstat=instCurve(nam, channel[j])); if (n==2) { str_append(nam, "l"); ERR_I(retstat=instCurve(nam, channel[j+2])); } if (sampleStick) { nP=n; } else { nX=n; } return(0); OnError: return(retstat); } int loadCache() { int i, j; char *res, *p; char buf[256], nbuf[256], lbuf[16]; FILE *fil; fil=NULL; sprintf(lbuf, "lsc.%d", serialNo); str_copy(nbuf, logDir); str_append(nbuf, lbuf); ERR_SP(fil=fopen(nbuf, "w")); if (manualX) ERR_SI(fputs(deviceX, fil)); ERR_SI(fputs("/", fil)); if (manualP) ERR_SI(fputs(deviceP, fil)); ERR_SI(fputs("\n", fil)); for (i=60; i>21; i-=4) { sprintf(buf, "CRVHDR?%d;CRVHDR?%d;CRVHDR?%d;CRVHDR?%d", i, i-1, i-2, i-3); ERR_P(res=SerCmd(ser, buf)); for (j=i; j>i-4; j--) { p=str_split1(res, ';'); if (res[1]=='s') { i=0; break;} /* s of "User", must be empty slot, lowercase letters are not programmable */ sprintf(buf, "%d:%s", j, res); ERR_SI(fputs(buf, fil)); ERR_SI(fputs("\n", fil)); if (p==NULL) break; res=p; } } ERR_SI(fclose(fil)); /* re-read cache */ ERR_P(cache=str_read_arg(nbuf, NULL, 0)); 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=channel[2]; if (nP>0) { if (controlMode==1) { /* control directly on sample sensor */ tShift=0; ch=channel[1]; if (nX>1 && tempC<(tLow+tHigh)/2) ch=channel[3]; } else if (controlMode!=2) { tShift=0; } } else { tShift=0; } tempH=tempC+tShift; if (switchOn) { sprintf(buf, "CSET 1:%c,1;RANGE:5;SETP 1:%f", ch, tempH); } else { sprintf(buf, "CSET 1:%c,1;SETP 1:%f", ch, tempH); } ERR_I(LscCmdChk(ser, buf)); return(0); OnError: return(-1); } int ReadTemp() { char buf[256], lbuf[16]; char *res; int i, key, k; int cod1, cod2, out1, out2; /* codes read from digital input/output */ int codX1, codP1; float t2[2], t4[4], p, d; ERR_P(res=SerCmdC(ser, "DIOST?;HTR?;BUSY?;DOUT 3,29")); if (0==strcmp(res, "?TMO")) { if (0==noResp) logfileOut("no response\n"); noResp=1; return(0); } i=sscanf(res, "%d%*c%d%*c%f%*c%d", &cod1, &out1, &htr, &busy); if (i!=4) ERR_MSG("bad answer 0"); if (noResp) { /* check serial number */ ERR_P(res=SerCmdC(ser, "*IDN?")); if (0!=strncmp(res, "LSCI,MODEL340,", 14)) return(0); res+=14; k=0; sscanf(res, "%d", &k); if (k!=0 && k!=serialNo) { serialNo=k; dirtyX=1; dirtyP=1; ERR_I(LscCmdChkC(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_arg(buf, NULL, 0); if (cache==NULL && 0==strcmp(ErrMessage, "file not found")) ERR_I(loadCache()); deviceX[0]='\0'; deviceP[0]='\0'; sscanf(cache, "%15[^/!]%*c%15[^\n!]", deviceX, deviceP); /* read device names separated by '/' */ if (deviceX[0]!='\0') manualX=1; if (deviceP[0]!='\0') manualP=1; concatDevice(); } noResp=0; } sprintf(buf, "KRDG?%c;KRDG?%c;KRDG?%c;KRDG?%c", channel[0], channel[1], channel[2], channel[3]); ERR_P(res=SerCmd(ser, buf)); i=sscanf(res, "%f%*c%f%*c%f%*c%f", &t4[0], &t4[1], &t4[2], &t4[3]); if (i!=4) ERR_MSG("bad answer 1"); tempX=WeightedAverage(nX, t4[0], t4[2]); tempP=WeightedAverage(nP, t4[1], t4[3]); if (dirtyX==0 && dirtyP==0 && noResp==0 && tim>logTime) { t2[0]=tempX; t2[1]=tempP; DlogPut(&dset, tim, 2, t2); DlogUpd(&dset); logTime=(tim/logPeriod+1)*logPeriod; } if (nP>0 && nX>0 && controlMode==2) { d=tempH-tempX; 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. */ p=d/tInt; tShift=tShift*(1.0-p)+p*(tempX-tempP); ERR_I(SetTemp(0)); } ERR_P(res=SerCmdC(ser, "KEYST?;DIOST?;DOUT 3,30")); i=sscanf(res, "%d%*c%d%*c%d", &key, &cod2, &out2); if (i!=3) ERR_MSG("bad answer 2"); if (busy==0) { while (out1!=30) { ERR_I(LscCmdChkC(ser, "DOUT:3,30")); ERR_P(res=SerCmdC(ser, "DIOST?")); i=sscanf(res, "%d%*c%d", &cod1, &out1); } while (out2!=29) { ERR_I(LscCmdChkC(ser, "DOUT:3,29")); ERR_P(res=SerCmdC(ser, "DIOST?;DOUT 3,30")); i=sscanf(res, "%d%*c%d", &cod2, &out2); } /* code conversion */ codX1=3*decod[cod2 % 8] ^ 2*decod[cod1 % 8]; /* ^ is exclusive OR */ codP1=-(3*decod[cod2 / 8] ^ 2*decod[cod1 / 8]); if (codX<0) { codX=codX1; codP=codP1; } /* first time after restart */ if (codP1!=codP) { dirtyP=1; codP=codP1; manualP=0; } if (codX1!=codX) { dirtyX=1; codX=codX1; manualX=0; } } if (key!=0) { auto_remote_time=tim+600; if (!(dirtyX || dirtyP)) { logfileOut("user touched keys\n"); } dirtyX=1; dirtyP=1; ERR_P(res=SerCmdC(ser, "MODE?")); mode=0; sscanf(res, "%d", &mode); if (mode==2) auto_remote_time=tim; /* user switched to remote mode */ } return(0); OnError: return(-1); } int Settings() { char buf[256]; char *res; int i, j, k, ia, ir; float pa, pr, pw, dif; if (dirtyX || dirtyP) { if (busy==0 || dirtyX>=0 && dirtyP>=0) { /* do not enter when busy and dirtyX/P indicates error on last time */ if (device[0]=='\0') { logfileOut("configure inputs for codes %d %d\n", codX, codP); } else { logfileOut("configure inputs for %s\n", device); } if (dirtyP) { sprintf(buf, "DISPFLD 2:%c,1;DISPFLD 4:%c,1", channel[1], channel[3]); ERR_I(LscCmdChk(ser, buf)); } if (dirtyX) { if (dirtyX>0) tryX=0; sprintf(buf, "DISPFLD 1:%c,1;DISPFLD 3:%c,1", channel[0], channel[2]); ERR_I(LscCmdChk(ser, buf)); nX=0; if (!manualX) { deviceX[0]='\0'; concatDevice(); } dirtyX=configInput(0); if (dirtyX<0) { tryX++; if (dirtyX!=-1 || tryX>3) { logfileShowErr("fatal error X"); dirtyX=0; deviceX[0]='\0'; concatDevice(); } else { logfileShowErr("try again X"); } } else { /* control settings */ sprintf(buf, "CDISP 1:1,%d,1;MOUT 1:0;CMODE 1:1", (int)(resist+0.5)); ERR_I(LscCmdChk(ser, buf)); ia=1; ir=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/power0), tLimit, channel[2]); ERR_I(LscCmdChk(ser, buf)); } } if (dirtyP) { if (dirtyP>0) tryP=0; nP=0; if (!manualP) { deviceP[0]='\0'; concatDevice(); } dirtyP=configInput(1); if (dirtyP<0) { tryP++; if (dirtyP!=-1 || tryP>3) { logfileShowErr("fatal error P"); dirtyP=0; deviceP[0]='\0'; concatDevice(); } else { logfileShowErr("try again P"); } } else { sprintf(buf, "ALARM %c:%d,1,%f,0,0,1;ALARM %c:0;RELAY 1:1;BEEP:0" , channel[1], (nP>0), tLimit, channel[3]); ERR_I(LscCmdChk(ser, buf)); } } if (nP>=nX) { maxfld=2*nP; } else { maxfld=2*nX-1; } if (maxfld>0) { sprintf(buf, "MODE:2;DISPLAY:%d", maxfld); } else { maxfld=1; sprintf(buf, "MODE:2;DISPLAY:1;DISPFLD 1:A,3", maxfld); } ERR_I(LscCmdChkC(ser, buf)); mode=2; if (saveIt) { ERR_P(res=SerCmdC(ser, "CRVSAV;BUSY?")); do { idleHdl(200, 0); /* wait 200 ms */ ERR_P(res=SerCmdC(ser, "BUSY?")); sscanf(res, "%d", &busy); } while (!busy); saveIt=0; } } } return(0); OnError: return(-1); } int ExecuteRequest() { char *res, *t; int iRet; struct CocClient *client; if (tim>auto_remote_time || setFlag) ERR_I(Settings()); if (setFlag) { if (nX>0) { tInt=0; /* reset integral time */ ERR_I(SetTemp(1)); logfileOut("set point\n"); } else { logfileOut("flag reset\n"); } setFlag=0; } client=CocGetNextCmd(); if (client!=NULL) { if (NULL==strchr(client->cmd, ':')) { res=SerCmdC(ser, client->cmd); if (res==NULL) { res=ErrMessage; if (res==NULL) res="empty error message"; } str_copy(client->res, res); } else { iRet=LscCmdChkC(ser, client->cmd); if (iRet<0 && ErrMessage!=NULL) { str_copy(client->res, ErrMessage); } else { str_copy(client->res, "o.k."); } } client->cmd[0]='\0'; } if (deviceFlag) { t=strchr(device, '/'); if (t==NULL) { str_copy(deviceX, device); str_copy(deviceP, device); manualX=1; dirtyX=1; manualP=1; dirtyP=1; } else { if (t!=device) { *t='\0'; str_copy(deviceX, device); *t='/'; manualX=1; dirtyX=1; } t++; if (*t!='\0') { str_copy(deviceP, t); manualP=1; dirtyP=1; } } concatDevice(); deviceFlag=0; } return(0); OnError: return(-1); } int main(int argc, char *argv[]) { int logIt=0; struct timeb tim1, tim0; int i, tdif, iret; char *host; char buf[256], opt; int port, msecTmo; binDir=""; logDir=""; serverId="tecs"; host="lnsp26"; port=0; msecTmo=0; logfileOutTmp("%s ", argv[0]); for (i=1;i=auto_remote_time) { iret=Settings(); if (iret<0) logfileShowErr("Settings"); } while (!quit) { ftime(&tim1); tdif=period-((tim1.time-tim0.time)*1000+tim1.millitm-tim0.millitm); if (tdif<0) { tim=tim1.time; break; } /* timeout */ iret=CocHandleRequests(tdif, 0); if (iret!=2) { time(&tim); break; } /* timeout */ if (iret<0) logfileShowErr("CocHandleRequests"); tim=tim1.time; iret=ExecuteRequest(); if (iret<0) logfileShowErr("ExecuteRequest"); } 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("%d cycles lost\n", tdif-1); } } logfileOut("%s got quit command\n", serverId); DlogClose(&dset); return(1); OnError: SerClose(ser); CocCloseServer(); ERR_EXIT("TECS"); }