new version of tecs_dlog (now in fortran)
This commit is contained in:
236
tecs/tecs.c
236
tecs/tecs.c
@@ -21,7 +21,6 @@ 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 */
|
||||
@@ -54,7 +53,7 @@ static float
|
||||
tInt=0; /* integral time (sec.) for setpoint shift */
|
||||
|
||||
static int
|
||||
logPeriod=10, /* data logging period (sec.) */
|
||||
logPeriod=0, /* data logging period (sec.) */
|
||||
period=5000, /* default read interval (msec.) */
|
||||
logTime, /* next logging time */
|
||||
setFlag, /* temperature to be set */
|
||||
@@ -62,7 +61,7 @@ static int
|
||||
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=2, /* 0: local, 2: remote */
|
||||
remoteMode, /* 1: local, 2: remote */
|
||||
maxfld, /* last used display field */
|
||||
busy, /* busy after CRVSAV */
|
||||
deviceFlag, /* device given via net */
|
||||
@@ -78,7 +77,6 @@ static int
|
||||
per; /* effective period */
|
||||
|
||||
static time_t
|
||||
auto_remote_time, /* time for automatic reload */
|
||||
tim, /* actual time */
|
||||
tableTime; /* last time when table was read */
|
||||
|
||||
@@ -86,11 +84,12 @@ static int decod[8]={21,20,17,16,5,4,1,0}; /* for code conversion */
|
||||
|
||||
static char
|
||||
status[132], /* status buffer */
|
||||
device[32], /* concatenated device names */
|
||||
device[64], /* 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 */
|
||||
chan[2], /* actual channel */
|
||||
dlogfile[128];
|
||||
|
||||
static char
|
||||
*table=NULL, /* environment devices table */
|
||||
@@ -112,6 +111,38 @@ void concatDevice(void) {
|
||||
str_append(device, "/");
|
||||
str_append(device, samp.device);
|
||||
}
|
||||
if (cryo.manual) {
|
||||
str_append(device, " (manual");
|
||||
} else {
|
||||
str_append(device, " (auto");
|
||||
}
|
||||
if (samp.manual==cryo.manual) {
|
||||
str_append(device, ")");
|
||||
} else if (samp.manual) {
|
||||
str_append(device, "/manual)");
|
||||
} else {
|
||||
str_append(device, "/auto)");
|
||||
}
|
||||
}
|
||||
|
||||
int putPermanentData(FILE *fil) {
|
||||
char buf[256];
|
||||
char *d1, *d2;
|
||||
|
||||
if (cryo.manual) {
|
||||
d1="*";
|
||||
} else {
|
||||
d1="";
|
||||
}
|
||||
if (samp.manual) {
|
||||
d2="*";
|
||||
} else {
|
||||
d2="";
|
||||
}
|
||||
sprintf(buf, "%s%s/%s%s/%d/%d\n", d1, cryo.device, d2, samp.device, cryo.code, samp.code);
|
||||
ERR_SI(fputs(buf, fil));
|
||||
return(0);
|
||||
OnError: return(-1);
|
||||
}
|
||||
|
||||
int instCurve(char *nam, char *channel) {
|
||||
@@ -123,7 +154,7 @@ int instCurve(char *nam, char *channel) {
|
||||
*s, /* start of found entry */
|
||||
*e, /* cache part after found entry */
|
||||
*res, *t;
|
||||
int i, n;
|
||||
int i, n, c1, c2;
|
||||
char used[60];
|
||||
FILE *fil;
|
||||
int retstat;
|
||||
@@ -208,7 +239,8 @@ int instCurve(char *nam, char *channel) {
|
||||
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]"));
|
||||
ERR_P(LscCmd(ser, "RANGE:0;INTYPE [chan]:[intype];INCRV [chan]:[num]"));
|
||||
ERR_P(LscCmd(ser, "DISPFLD [fld],[chan],1;DISPLAY:[maxfld]"));
|
||||
logfileOut(LOG_MAIN, "curve %d on channel %s selected\n", num, chan);
|
||||
Progress(100);
|
||||
|
||||
@@ -220,7 +252,8 @@ int instCurve(char *nam, char *channel) {
|
||||
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]"));
|
||||
ERR_P(LscCmd(ser, "RANGE:0;INTYPE [chan]:[intype]"));
|
||||
ERR_P(LscCmd(ser, "DISPFLD [fld],[chan],3;DISPLAY:[maxfld]"));
|
||||
Progress(1);
|
||||
|
||||
n=3;
|
||||
@@ -264,10 +297,7 @@ int instCurve(char *nam, char *channel) {
|
||||
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));
|
||||
ERR_I(putPermanentData(fil));
|
||||
sprintf(buf, "%d:%s", num, head);
|
||||
ERR_SI(fputs(buf, fil)); /* write actual entry */
|
||||
if (start!=s) { /* write content before replaced entry */
|
||||
@@ -365,10 +395,7 @@ int loadCache(void) {
|
||||
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));
|
||||
ERR_I(putPermanentData(fil));
|
||||
bufi[0]=buf1;
|
||||
bufi[1]=buf2;
|
||||
bufi[2]=buf3;
|
||||
@@ -436,15 +463,15 @@ int SetTemp(int switchOn) {
|
||||
tShift=0;
|
||||
}
|
||||
str_copy(chan, ch);
|
||||
if (scale!=1.0) {
|
||||
if (scale!=1.0) { /* show set point on display (for rdrn) */
|
||||
ERR_P(LscCmd(ser, "LINEAR C,1,0,1,1,[tempC]"));
|
||||
}
|
||||
if (tempC==0) {
|
||||
ERR_P(LscCmd(ser, "RANGE:0;SETP 1:0"));
|
||||
return(0);
|
||||
}
|
||||
tempH=(tempC+tShift)/scale;
|
||||
if (switchOn) {
|
||||
if (tempC==0) {
|
||||
ERR_P(LscCmd(ser, "CSET 1:[chan],1,1,0;RANGE:0;SETP 1:0"));
|
||||
} else if (remoteMode==1) { /* local mode: do not switch on heater */
|
||||
ERR_P(LscCmd(ser, "SETP 1:[tempH]"));
|
||||
} else 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]"));
|
||||
@@ -496,7 +523,7 @@ int ReadTemp(void) {
|
||||
&& !samp.dirty && samp.codDefined && !samp.codChanged
|
||||
&& !cryo.dirty && cryo.codDefined && !cryo.codChanged) {
|
||||
configuring=0;
|
||||
} else if (configuring==0) {
|
||||
} else if (configuring==0 && remoteMode==2) {
|
||||
str_copy(status, "configuring");
|
||||
configuring=1;
|
||||
}
|
||||
@@ -506,31 +533,26 @@ int ReadTemp(void) {
|
||||
|
||||
int PeriodicTask(void) {
|
||||
char buf[256], lbuf[16];
|
||||
char *res;
|
||||
char *next;
|
||||
int i, k;
|
||||
time_t putTim;
|
||||
float t2[2], p, d, w;
|
||||
float t3[3], p, d, w;
|
||||
|
||||
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 (cryo.codDefined && samp.codDefined) {
|
||||
per=period; /* no timeout on above command and codes are defined: normal period */
|
||||
}
|
||||
|
||||
if (noResp) { /* check serial number */
|
||||
k=serialNo;
|
||||
if (noResp) { /* there was no response on an earlier command, or we are initializing */
|
||||
k=serialNo; /* check serial number */
|
||||
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 (k!=serialNo) { /* controller exchanged or we are initializing */
|
||||
if (!configuring) {
|
||||
str_copy(status, "controller connected");
|
||||
}
|
||||
configuring++;
|
||||
if (remoteMode==2) configuring++;
|
||||
tempC=0;
|
||||
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) my_free(cache);
|
||||
sprintf(lbuf, "lsc.%d", serialNo);
|
||||
@@ -538,12 +560,40 @@ int PeriodicTask(void) {
|
||||
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';
|
||||
/* get device names and last codes separated by '/' */
|
||||
str_split(buf, cache, '\n'); /* read 1 line */
|
||||
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;
|
||||
cryo.code=0;
|
||||
samp.code=0;
|
||||
next=str_split(buf1, buf, '/');
|
||||
if (next!=NULL) {
|
||||
next=str_split(buf2, next, '/');
|
||||
if (next!=NULL) {
|
||||
next=str_split(buf3, next, '/');
|
||||
cryo.code=atoi(buf3);
|
||||
cryo.codChanged=0;
|
||||
if (next!=NULL) {
|
||||
next=str_split(buf3, next, '\n');
|
||||
samp.code=atoi(buf3);
|
||||
samp.codChanged=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buf1[0]=='*') {
|
||||
str_copy(cryo.device, buf1+1);
|
||||
cryo.manual=1;
|
||||
} else {
|
||||
str_copy(cryo.device, buf1);
|
||||
}
|
||||
if (buf2[0]=='*') {
|
||||
str_copy(samp.device, buf2+1);
|
||||
samp.manual=1;
|
||||
} else {
|
||||
str_copy(samp.device, buf2);
|
||||
}
|
||||
concatDevice();
|
||||
if (cryo.manual || cryo.code!=0) { cryo.dirty=1; }
|
||||
if (samp.manual || samp.code!=0) { samp.dirty=1; }
|
||||
}
|
||||
noResp=0;
|
||||
}
|
||||
@@ -551,15 +601,16 @@ int PeriodicTask(void) {
|
||||
ERR_I(ReadTemp());
|
||||
|
||||
if (cryo.dirty==0 && samp.dirty==0 && noResp==0 && tim>logTime) {
|
||||
t2[0]=cryo.temp;
|
||||
t2[1]=samp.temp;
|
||||
t3[0]=cryo.temp;
|
||||
t3[1]=samp.temp;
|
||||
t3[2]=htr*htr*power*1e-4;
|
||||
if (t3[2]==0.0) t3[2]=1e-20;
|
||||
|
||||
time(&putTim);
|
||||
/* DlogPut(&dset, putTim, 2, t2); DlogUpd(&dset); */
|
||||
i=3;
|
||||
dlog_put_(&putTim, &i, t3);
|
||||
|
||||
logTime=(putTim/logPeriod+1)*logPeriod;
|
||||
time(&tim);
|
||||
if (tim-putTim>1) {
|
||||
logfileOut(LOG_MAIN, "needed %d sec. for filling in dlog\n", tim-putTim);
|
||||
}
|
||||
}
|
||||
if (samp.nSens>0 && cryo.nSens>0 && controlMode==2 && tempC!=0) {
|
||||
d=(tempH-cryo.temp)/cryo.temp-1.0; /* relative difference */
|
||||
@@ -593,11 +644,11 @@ int PeriodicTask(void) {
|
||||
samp.code1=-(3*decod[cod2 / 8] ^ 2*decod[cod1 / 8]);
|
||||
for (i=0; i<2; i++) {
|
||||
tpoint=tpoints[i];
|
||||
if (tpoint->code1!=tpoint->code) {
|
||||
if (tpoint->code1!=tpoint->code) { /* code has changed -> wait for a confirmation */
|
||||
tpoint->code=tpoint->code1;
|
||||
tpoint->codChanged=1;
|
||||
} else {
|
||||
if (tpoint->codChanged) {
|
||||
if (tpoint->codChanged) { /* code change confirmed */
|
||||
tpoint->codChanged=0;
|
||||
Progress(1);
|
||||
if (tpoint->code1==0) {
|
||||
@@ -605,10 +656,10 @@ int PeriodicTask(void) {
|
||||
} else {
|
||||
logfileOut(LOG_MAIN, "plugged %d on %s\n", tpoint->code1, tpoint->tname);
|
||||
}
|
||||
if (tpoint->codDefined) {
|
||||
tpoint->manual=0;
|
||||
}
|
||||
tpoint->manual=0;
|
||||
tempC=0;
|
||||
remoteMode=2; /* set to remote mode */
|
||||
LscCmd(ser, "MODE:[remoteMode]");
|
||||
tpoint->dirty=1;
|
||||
if (!configuring) configuring=1;
|
||||
}
|
||||
@@ -619,15 +670,26 @@ int PeriodicTask(void) {
|
||||
}
|
||||
|
||||
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 */
|
||||
remoteMode=1;
|
||||
ERR_P(LscCmd(ser, "MODE?>remoteMode"));
|
||||
if (remoteMode==2) { /* user switched to remote mode */
|
||||
ERR_P(LscCmd(ser, "RANGE?>iRange;SETP?1>tempC"));
|
||||
setFlag=(iRange>0);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -716,12 +778,11 @@ int Settings(void) {
|
||||
maxfld=2*cryo.nSens-1;
|
||||
}
|
||||
if (maxfld>0) {
|
||||
ERR_P(LscCmd(ser, "MODE:2;DISPLAY:[maxfld]"));
|
||||
ERR_P(LscCmd(ser, "DISPLAY:[maxfld]"));
|
||||
} else {
|
||||
maxfld=1;
|
||||
ERR_P(LscCmd(ser, "MODE:2;DISPLAY:1;DISPFLD 1,A,3"));
|
||||
ERR_P(LscCmd(ser, "DISPLAY:1;DISPFLD 1,A,3"));
|
||||
}
|
||||
mode=2;
|
||||
|
||||
str_copy(nbuf, binDir);
|
||||
str_append(nbuf, cryo.device);
|
||||
@@ -738,14 +799,6 @@ int Settings(void) {
|
||||
my_free(cfg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ERR_I(ReadTemp());
|
||||
}
|
||||
return(0);
|
||||
@@ -759,7 +812,7 @@ int ExecuteRequest(void) {
|
||||
struct CocClient *client;
|
||||
|
||||
if (readTemp) ReadTemp();
|
||||
if (tim>auto_remote_time || setFlag) ERR_I(Settings());
|
||||
if (remoteMode==2) ERR_I(Settings());
|
||||
if (setFlag) {
|
||||
if (cryo.nSens>0) {
|
||||
tInt=0; /* reset integral time */
|
||||
@@ -780,13 +833,15 @@ int ExecuteRequest(void) {
|
||||
}
|
||||
if (deviceFlag) {
|
||||
tempC=0;
|
||||
remoteMode=2; /* set to remote mode */
|
||||
LscCmd(ser, "MODE:[remoteMode]");
|
||||
if (!configuring) {
|
||||
str_copy(status, "configuring");
|
||||
configuring=1;
|
||||
}
|
||||
t=strchr(device, '/');
|
||||
if (t==NULL) {
|
||||
if (0==strcmp(device, "0")) {
|
||||
if (0==strcmp(device, "0") || 0==strcasecmp(device, "auto")) {
|
||||
cryo.manual=0; cryo.dirty=1;
|
||||
samp.manual=0; samp.dirty=1;
|
||||
} else {
|
||||
@@ -821,7 +876,7 @@ int mainBody(void)
|
||||
struct timeb tim1;
|
||||
|
||||
ERR_I(PeriodicTask());
|
||||
if (tim>=auto_remote_time) ERR_I(Settings());
|
||||
if (remoteMode==2) ERR_I(Settings());
|
||||
logfileWrite(logMask);
|
||||
|
||||
while (!quit) {
|
||||
@@ -913,6 +968,9 @@ int main(int argc, char *argv[])
|
||||
} else if ('p'==opt) {
|
||||
i++;
|
||||
port=atoi(argv[i]);
|
||||
} else if ('f'==opt) {
|
||||
i++;
|
||||
logPeriod=atoi(argv[i]);
|
||||
} else {
|
||||
logfileOut(LOG_INFO ,"?");
|
||||
}
|
||||
@@ -922,13 +980,15 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
if (port==0) port=9753;
|
||||
if (msecTmo==0) msecTmo=1000;
|
||||
if (logPeriod==0) logPeriod=10;
|
||||
if (logPeriod*1000<period) period=logPeriod*1000;
|
||||
|
||||
str_copy(buf, logDir);
|
||||
str_append(buf, serverId);
|
||||
|
||||
str_copy(status, "starting up");
|
||||
logfileStatusBuf(status);
|
||||
logfileInit(buf, logIt, use_stdout, logIt);
|
||||
logfileInit(buf, logIt, use_stdout, logIt && use_stdout);
|
||||
logfileOut(LOG_INFO ,"\n");
|
||||
logfileWrite(logMask);
|
||||
|
||||
@@ -966,6 +1026,7 @@ int main(int argc, char *argv[])
|
||||
CocDefStr(chan, CocRD);
|
||||
CocDefStr(intype, CocRD);
|
||||
CocDefStr(status, CocRD);
|
||||
CocDefStr(dlogfile, CocRD);
|
||||
|
||||
CocDefInt(cod1, CocRD);
|
||||
CocDefInt(cod2, CocRD);
|
||||
@@ -978,7 +1039,7 @@ int main(int argc, char *argv[])
|
||||
CocDefInt(resist, CocRD);
|
||||
CocDefInt(iAmp, CocRD);
|
||||
CocDefInt(iRange, CocRD);
|
||||
CocDefInt(mode, CocRD);
|
||||
CocDefInt(remoteMode, CocRD);
|
||||
|
||||
CocDefInt(readTemp, CocWR);
|
||||
CocDefInt(controlMode, CocWR);
|
||||
@@ -991,21 +1052,17 @@ int main(int argc, char *argv[])
|
||||
ERR_I(iret=CocHandleRequests(100, 0));
|
||||
ftime(&tim0);
|
||||
tim=tim0.time;
|
||||
/*
|
||||
str_copy(buf, logDir);
|
||||
str_append(buf, serverId);
|
||||
str_append(buf, ".dlog");
|
||||
logfileOut(LOG_INFO, "open data log file: %s\n", buf);
|
||||
ERR_I(iret=DlogOpenWrt(&dset, buf, tim0.time, 2, 7*24*60*60/logPeriod, logPeriod, 0.0));
|
||||
if (iret==0) {
|
||||
logfileOut(LOG_INFO, "created\n");
|
||||
} else {
|
||||
logfileOut(LOG_INFO, "opened\n");
|
||||
}
|
||||
|
||||
str_copy(dlogfile, logDir);
|
||||
str_append(dlogfile, serverId);
|
||||
str_append(dlogfile, ".dlog");
|
||||
logfileOut(LOG_INFO, "open data log file: %s\n", dlogfile);
|
||||
dlog_open_write_(dlogfile, 2000); /* max size of 2 MB */
|
||||
logfileWrite(logMask);
|
||||
*/
|
||||
LscCmd(ser, "MODE:[mode]");
|
||||
per=period;
|
||||
|
||||
LscCmd(ser, "MODE?>remoteMode");
|
||||
if (remoteMode!=2) configuring=0;
|
||||
per=1; /* advance fast when initializing */
|
||||
cntError=0;
|
||||
while (!quit) {
|
||||
iret=mainBody();
|
||||
@@ -1017,14 +1074,11 @@ int main(int argc, char *argv[])
|
||||
if (cntError>0) cntError--;
|
||||
}
|
||||
}
|
||||
ERR_P(LscCmd(ser, "MODE:1"));
|
||||
logfileWrite(logMask);
|
||||
ERR_MSG("got quit command");
|
||||
OnError:
|
||||
logfileShowErr("exit TecsServer");
|
||||
/*
|
||||
DlogClose(&dset);
|
||||
*/
|
||||
dlog_close_w_();
|
||||
SerClose(ser);
|
||||
CocCloseServer();
|
||||
return(0);
|
||||
|
||||
Reference in New Issue
Block a user