/*--------------------------------------------------------------
 This is a driver for the newer Astrium == Dornier chopper 
 systems which use a TCP/IP server for communication.
 This driver has to take care of some ugliness:
 - As of december 2005, the communication is in unicode!
   To go from ASCII to unicode and back one has to 
   add a 0x00 before each character or to remove it.
 - The controller is slow in responding and the controller
   must be watched in the environment monitor. This is taken
   care of by a special SICS task which updates the status
   regularly and returning cached values anytime else.


 Inititial Implementation: Mark Koennecke, December 2005
 ------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <fortify.h>
#include <sics.h>
#include <stringdict.h>
#include <rs232controller.h>
#include <codri.h>
#include <stptok.h>
#include <commandlog.h>  
/*========================================================================
  Our private data structure
  ========================================================================*/
#define ASYNMODE 0
#define SYNCMODE 1

typedef struct{
    prs232 controller;
    int iRefreshIntervall;
    time_t nextRefresh;
    long lTask;
    pStringDict parameters;
    int lastError;
    int numChoppers;
    int mode;
    int timeout;
    char user[132];
    char pword[132];
    int stop;
    int busy;
    char *config;
}TcpDoCho, *pTcpDoCho;
/*-----------------------------------------------------------------------
Error codes:
-----------------------------------------------------------------------*/
#define WRONGMODE       -8301
#define BADCONVERSION   -8302
#define FAILEDCOMMAND   -8303
#define BADWRITE        -8304
#define BADRESPONSE     -8306
#define UNDRIVABLE      -8307
#define BADPAR          -8308
#define ESTOP           -8309

extern char *trim(char *str);
#define ABS(x) (x < 0 ? -(x) : (x)) 

#define SPEEDTOL 2
#define PHASETOL .5
/*=============== support functions ===================================*/
static int asciiToUnicode(char *input, char **output){
    int len, i;
    char *result = NULL;

    len = strlen(input);
    result = (char *)malloc(2*len*sizeof(char));
    if(result == NULL){
	return 0;
    }
    memset(result,0,2*len*sizeof(char));
    for(i = 0; i < len; i++){
	result[i*2] = input[i];
    }
    *output = result; 
    return 2*len;
} 
/*-------------------------------------------------------------------*/
static int unicodeToAscii(char *input, int len, char **output){
    int i, alen;
    char *result = NULL;

    alen = len/2;

    result = (char *)malloc((alen+1)*sizeof(char));
    if(result == NULL){
	return 0;
    }
    memset(result,0,(alen+1)*sizeof(char));
    for(i = 0; i < alen; i++){
	result[i] = input[i*2];
    }
    *output = result;
    return alen;
}
/*----------------------------------------------------------------*/
static int tcpDoChoSend(pTcpDoCho self, char *command, int choNum,
			char *value){
    char buffer[1024];
    int sendlen, status;

    /*
      format and send command
    */
    self->lastError = 0;
    if(choNum < 0){
	snprintf(buffer,1023,"#SOS#%s \r\n",command);
    } else if(value != NULL){
	snprintf(buffer,1023,"#SOS#%s%1.1d: %s\r\n",command, choNum,
		 value);
    } else {
	snprintf(buffer,1023,"#SOS#%s%1.1d:\r\n",command, choNum);
    }
    sendlen = strlen(buffer);
    if(sendlen == 0){
	self->lastError = BADCONVERSION;
	return 0;
    }
    status = send(self->controller->pSock->sockid,buffer,sendlen,0);
    if(status < 0){
	self->lastError = BADWRITE;
	return 0;
    }
    return 1;
}
/*----------------------------------------------------------------*/
static int statusComplete(char *statusMessage){
    char *pPtr = NULL;

    /*
      currently the status message has no terminator. I try to find
      the last component of the message which happens to be TIME and
      the last # after that
    */
    pPtr = strstr(statusMessage,"TIME");
    if(pPtr != NULL){
	pPtr = strstr(pPtr+6,"#");
	if(pPtr != NULL){
	    return 1;
	} else {
	    return 0;
	}
    } else {
	return 0;
    }
}
/*---------------------------------------------------------------*/
static int readChopperNum(char *entry){
    int num;
    char *pPtr = NULL;

    pPtr = strstr(entry,"CH");
    if(pPtr == NULL){
	return -1;
    }
    sscanf(pPtr+3,"%d",&num);
    return num;
}
/*---------------------------------------------------------------*/
static void addEntry(pTcpDoCho self, int choNum, char *entry){
    char name[80], value[80], *pPtr = NULL;
    char num[5];

    memset(name,0,80);
    memset(value,0,80);
    snprintf(num,5,"_%1.1d",choNum);
    pPtr = strstr(entry,"=");
    if(pPtr != NULL){
	strncpy(name,entry,pPtr -entry);
	strncat(trim(name),num,80-strlen(trim(name)));
	strcpy(value,pPtr+1);
	if(StringDictExists(self->parameters,name)){
	    StringDictUpdate(self->parameters,name,trim(value));
	} else {
	    StringDictAddPair(self->parameters,name,trim(value));
	}
    }
} 
/*----------------------------------------------------------------*/
static int parseStatus(pTcpDoCho self, char *statusMessage){
    int choNum;
    char entry[80], *pPtr;
    
    pPtr = statusMessage;

    /* skip over SOS */
    pPtr = stptok(pPtr,entry,79,"#");
    pPtr = stptok(pPtr,entry,79,"#");
    pPtr = stptok(pPtr,entry,79,"#");
    
    choNum = readChopperNum(entry);
    if(choNum < 0){
	self->lastError = BADRESPONSE;
	return 0;
    }
    while((pPtr = stptok(pPtr,entry,79,"#")) != NULL){
	addEntry(self,choNum,entry);
	memset(entry,0,80);
    }
    return 1;
}
/*-----------------------------------------------------------------*/
static int tcpDoChoReceive(pTcpDoCho self){
    char buffer[1024];
    int len, bytesRead, bufferStart, status;
    time_t endTime;

    endTime = time(NULL) + self->timeout;
    bufferStart = 0;
    bytesRead = 0;
    memset(buffer,0,1024*sizeof(char));
    while(time(NULL) < endTime){
	if(availableRS232(self->controller)){
	    bytesRead += recv(self->controller->pSock->sockid,buffer+bufferStart,
			     1024 - bytesRead,0);
	    if(bytesRead < 0){
		self->lastError = BADREAD;
		return 0;
	    }
      if(strstr(buffer,"State") != NULL){
		if(statusComplete(buffer) == 0) {
		    continue;
		} else {
		    status = parseStatus(self,buffer);
		    return status;
		}
	    }
	    if(strstr(buffer,"ACCEPT") != NULL){
		return 1;
            } else if(strstr(buffer,"NCCEPT") != NULL){
		self->lastError = FAILEDCOMMAND;
		return 0;
            } else {
		self->lastError = BADRESPONSE;
		return 0;
	    }
        } else {
	    SicsWait(1);
	}
    }
    self->lastError = TIMEOUT;
    return 0;
}
/*-----------------------------------------------------------------*/
static int tcpDoChoCommand(pTcpDoCho self, char *command, int choNum,
			   char *value){
    int status;
    
    status = tcpDoChoSend(self,command,choNum,value);
    if(status == 0){
	return 0;
    }
    return tcpDoChoReceive(self);
}
/*---------------------------------------------------------------*/
static int TcpDoChoConnect(pTcpDoCho self){
    int status, sendLen, readLen;
    char buffer[256];

    status = initRS232(self->controller);
    if(status != 1){
	self->lastError = status;
	return 0;
    }
    setRS232Timeout(self->controller,5);

    readLen = 255;
    readRS232(self->controller,(void *)buffer,&readLen);

    /*
      user name
    */
    snprintf(buffer,255,"user:%s\r\n",self->user);
    sendLen = strlen(buffer);
    if(sendLen == 0){
	self->lastError = BADCONVERSION;
        return 0;
    }
    status = send(self->controller->pSock->sockid,buffer,sendLen,0);
    if(status < 0){
	self->lastError = BADSEND;
	return 0;
    }
    readLen = 255;
    readRS232(self->controller,(void *)buffer,&readLen);

    /* 
       password
    */
    snprintf(buffer,255,"password:%s\r\n",self->pword);
    sendLen = strlen(buffer);
    if(sendLen == 0){
	self->lastError = BADCONVERSION;
        return 0;
    }
    status = send(self->controller->pSock->sockid,buffer,sendLen,0);
    if(status < 0){
	self->lastError = BADSEND;
	return 0;
    }
    readLen = 255;
    readRS232(self->controller,(void *)buffer,&readLen);

    /*
      TODO: responses should be checked to test for a valid login. 
      I do not know at this time how the controller reacts upon a
      bad login.
    */

    return 1;
}
/*==================== actual driver implementation code ==================*/
static int TcpChopperTask(void *pData){
    pCodri self = NULL;
    pTcpDoCho pPriv = NULL;
    int status, code, i;
    char buffer[80];
    char error[512];

    self = (pCodri)pData;
    assert(self);
    pPriv = (pTcpDoCho)self->pPrivate;
    assert(pPriv);

    if(pPriv->stop == 1){
	return 0;
    }
    
    if(time(NULL) > pPriv->nextRefresh){
	if(pPriv->lastError != 0){
	    self->GetError(self,&code,buffer,79);
	    snprintf(error,511,"WARNING: chopper tries to fix: %s",buffer);
	    WriteToCommandLog("Chopper-task:>>", error);
            status = self->TryFixIt(self,code);
	    if(status == CHFAIL){
		pPriv->nextRefresh = time(NULL) + pPriv->iRefreshIntervall;
		return 1;
	    }
	} else {
	    pPriv->busy = 1;
	    for(i = 0; i < pPriv->numChoppers; i++){
 		status = tcpDoChoCommand(pPriv,"STATE ", i+1,NULL);
		if(status != 1){
		    /*
		      force error correction
		    */
		    return 1;
		}
            }
	    pPriv->nextRefresh = time(NULL) + pPriv->iRefreshIntervall;
	    pPriv->busy = 0;
        }
    }
    return 1;
}
/*------------------------------------------------------------------------------*/
static int  TcpDoChoKill(pCodri self){
     pTcpDoCho pPriv = NULL;

     pPriv = (pTcpDoCho)self->pPrivate;
     if(!pPriv)
       return 1;

     if(pPriv->controller != NULL){
	 KillRS232(pPriv->controller);
     }
     if(pPriv->parameters != NULL){
	 DeleteStringDict(pPriv->parameters);
     }
     if(pPriv->config != NULL){
	 free(pPriv->config);
     }
     free(pPriv);
     return 1;
}
/*-------------------------------------------------------------------*/
static int TcpDoChoConfigure(pCodri pDriv){
    pTcpDoCho self = NULL;
    int status;
    char *pPtr = NULL;
    char command[80];

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    if(self->config != NULL){
	pPtr = self->config;
	while( (pPtr = stptok(pPtr,command,79,"\n")) != NULL){
	    status = tcpDoChoCommand(self,command,-1,NULL);
	    if(status != 1){
		return 0;
            }
        }
    }
    return 1;
}
/*---------------------------------------------------------------------*/
static int TcpDoChoInit(pCodri pDriv){
    pTcpDoCho self = NULL;
    int status;

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);
    
    self->lastError = 0;
    self->stop = 0;
    self->nextRefresh = 0;
    
    status = TcpDoChoConnect(self);
    if(status != 1){
	return 0;
    }

    status = TcpDoChoConfigure(pDriv);
    if(status != 1){
	return 0;
    }

     /* start the update task */
     if(self->lTask == 0){
	 self->lTask = TaskRegister(pServ->pTasker,
				      TcpChopperTask,
				      NULL,
				      NULL,
				      pDriv,
				      1);                             
     }
     return 1;     
}
/*-------------------------------------------------------------------*/
static void waitForBusy(pTcpDoCho self){
    time_t endTime;

    endTime = time(NULL) + 10 *60; /* max 10 min */
    while(time(NULL) < endTime){
	if(self->busy == 1){
	    SicsWait(3);
        } else {
	    return;
	}
    }
    WriteToCommandLog("Chopper-task>> ","WARNING: timeout on busy flag, flag forced");
    self->busy = 0;
}
/*----------------------------------------------------------------------*/
static int TcpDoChoClose(pCodri pDriv){
    pTcpDoCho self = NULL;
    int status;

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    self->stop = 1;
    if(self->controller != NULL){
	KillRS232(self->controller);
	self->controller = NULL;
    }
    return 1;
}
/*----------------------------------------------------------------------*/
static int dissectName(char *name, char par[80], int *num){
    char *pPtr = NULL;

    pPtr = strrchr(name,(int)'_');
    if(pPtr == NULL){
	return 0;
    }
    memset(par,0,80*sizeof(char));
    strncpy(par,name,pPtr - name);
    if(sscanf(pPtr+1,"%d",num) != 1){
	return 0;
    }
    return 1;
}
/*----------------------------------------------------------------------*/
static int TcpDoChoSetPar2(pCodri pDriv, char *parname, char *value){
    
    pTcpDoCho self = NULL;
    int status, choNum;
    char par[80], buffer[80], state[80]; 

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    
    /*
      force status requests right after setting something in order
      to make the stored status represent the new target values
    */
    if(dissectName(parname,par,&choNum)){
	/*
	  check for emergency stop
	*/
	snprintf(buffer,79,"State_%1.1d",choNum);
	memset(state,0,80*sizeof(char));
	StringDictGet(self->parameters,buffer,state,79);
	if(strstr(state,"E-Stop") != NULL){
	    self->lastError = ESTOP;
	    return 0;
        }

	if(strcmp(par,"speed") == 0){
	    waitForBusy(self);
	    status = tcpDoChoCommand(self,"SPEED ",choNum,trim(value));
	    tcpDoChoCommand(self,"STATE ",choNum,NULL);
	    if(status != 1){
		return 0;
	    } else {
		return 1;
	    }
	} else if(strcmp(par,"phase") == 0){
	    waitForBusy(self);
	    status = tcpDoChoCommand(self,"PHASE ",choNum,trim(value));
	    tcpDoChoCommand(self,"STATE ",choNum,NULL);
	    if(status != 1){
		return 0;
	    } else {
		return 1;
	    }
        }
    }
    self->lastError = UNDRIVABLE;
    return 0;
}
/*-----------------------------------------------------------------------*/
static int TcpDoChoHalt(pCodri pDriv){
    
    pTcpDoCho self = NULL;
    int status;

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    waitForBusy(self);
    tcpDoChoCommand(self,"ESTOP :",-1,NULL);
    return 1;
}
/*-----------------------------------------------------------------------*/
static int TcpDoChoSetPar(pCodri pDriv, char *parname, float fValue){
    
    pTcpDoCho self = NULL;
    int status, choNum;
    char value[80];
    char par[80];


    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    if(dissectName(parname,par,&choNum)){
	if(strcmp(par,"speed") == 0){
	    snprintf(value,79,"%5.1f", fValue);
	    return TcpDoChoSetPar2(pDriv,parname,value);
	} else if(strcmp(par,"phase") == 0){
	    snprintf(value,79,"%6.2f",fValue);
	    return TcpDoChoSetPar2(pDriv,parname,value);
        }
    }   
    if(strcmp(parname,"updateintervall") == 0){
         sprintf(value,"%d",(int)fValue);
         StringDictUpdate(self->parameters,"updateintervall",value);
         self->iRefreshIntervall = (int)fValue;
         return 1;
    } else {
	snprintf(value,79,"%f",fValue);
	return TcpDoChoSetPar2(pDriv,parname,value);
    }
}
/*---------------------------------------------------------------------*/
static int TcpDoChoGetPar(pCodri pDriv, char *parname, 
			  char *pBuffer, int iBuflen){
    pTcpDoCho self = NULL;
    int status = 0, choNum;
    char par[80], buffer[80];
    float val;

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);
    
    memset(par,0,80);
    dissectName(parname,par,&choNum);
    if(strcmp(par,"speed") == 0){
	snprintf(buffer,80,"ASPEED_%1.1d", choNum);
	status = StringDictGet(self->parameters,buffer,pBuffer,iBuflen);
    } else if(strcmp(par,"phase") == 0){
	snprintf(buffer,80,"APHASE_%1.1d", choNum);
	status = StringDictGet(self->parameters,buffer,pBuffer,iBuflen);
    } else {
	status = StringDictGet(self->parameters,parname,pBuffer,iBuflen);
    }
    return status;
}
/*----------------------------------------------------------------------*/
static int TcpDoChoCheckPar(pCodri pDriv, char *parname){
    pTcpDoCho self = NULL;
    int status = 0, choNum;
    float val, soll, delta;
    char value[80], csoll[80], par[80], buffer[80], state[80];

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    /*
      check for flags first
    */
    if(self->busy){
	return HWBusy;
    }
    if(self->lastError != 0) {
	return HWFault;
    }
    
    /*
      updateintervall is always Idle
    */
    if(strcmp(parname,"updateintervall") == 0){
       return HWIdle;
    }

     /*
       check for emergency stop
     */
    snprintf(buffer,79,"State_%1.1d",choNum);
    memset(state,0,80*sizeof(char));
    StringDictGet(self->parameters,buffer,state,79);
    if(strstr(state,"E-Stop") != NULL){
	self->lastError = HWFault;
	return 0;
    }
    
    memset(par,0,80);
    dissectName(parname,par,&choNum);
    if(strcmp(par,"speed") == 0){
        snprintf(buffer,79,"RSPEED_%1.1d", choNum);
	StringDictGet(self->parameters,buffer,csoll,79);
	sscanf(csoll,"%f",&soll);
        snprintf(buffer,79,"ASPEED_%1.1d", choNum);
	StringDictGet(self->parameters,buffer,value,79);
	sscanf(value,"%f",&val);
	delta = ABS(soll - val);
	if(delta > SPEEDTOL){
	    return HWBusy;
	} else {
	    return HWIdle;
	}
    } else if(strcmp(par,"phase") == 0){
        snprintf(buffer,79,"RPHASE_%1.1d", choNum);
	StringDictGet(self->parameters,buffer,csoll,79);
	sscanf(value,"%f",&soll);
        snprintf(buffer,79,"APHASE_%1.1d", choNum);
	StringDictGet(self->parameters,buffer,value,79);
	sscanf(value,"%f",&val);
	delta = ABS(soll - val);
	if(delta > PHASETOL){
	    return HWBusy;
	} else {
	    return HWIdle;
	}
    }
    self->lastError = BADPAR;
    return HWFault;
}
/*---------------------------------------------------------------------------*/
static int TcpDoChoError(pCodri pDriv, int *iCode, char *pError, int iLen){
    pTcpDoCho self = NULL;
    int status = 0;
    float val, soll, delta;
    char value[80];

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    *iCode = self->lastError;
    switch(self->lastError){
	case WRONGMODE:
	    strncpy(pError,"Chopper in wrong mode",iLen);
	    break;
        case BADCONVERSION:
	    strncpy(pError,"Bad ASCII to unicode conversion",iLen);
            break;
        case FAILEDCOMMAND:
	    strncpy(pError,"Command not accepted",iLen);
            break;
        case BADWRITE:
	    strncpy(pError,"Failed to write to chopper controller",iLen);
            break;
        case BADRESPONSE:
	    strncpy(pError,"Chopper controller send invalid command",iLen);
            break;
        case UNDRIVABLE:
	    strncpy(pError,"Parameter cannot be changed",iLen);
            break;
        case BADPAR:
	    strncpy(pError,"No such parameter",iLen);
            break;
	case ESTOP:
	    strncpy(pError,"Emergency stop is engaged",iLen);
            break;
	default:
	    getRS232Error(self->lastError,pError,iLen);
	    break;
    }
    return 1;
}
/*---------------------------------------------------------------------*/
static int TcpDoChoFix(pCodri pDriv, int iCode){
    pTcpDoCho self = NULL;
    int status = 0;
    float val, soll, delta;
    char value[80];

    assert(pDriv != NULL);
    self = (pTcpDoCho)pDriv->pPrivate;
    assert(self != NULL);

    self->lastError = 0;
    switch(iCode){
	case BADCONVERSION:
        case BADRESPONSE:
	    return CHREDO;
	    break;
        case WRONGMODE:
        case FAILEDCOMMAND:
        case UNDRIVABLE:
        case BADPAR:
        case ESTOP:
            return CHFAIL;
	    break;
        default:
	    closeRS232(self->controller);
	    status = TcpDoChoConnect(self);
	    if(status == 1){
		return CHREDO;
            } else {
		return CHFAIL;
	    }
	    break;
    }
    return CHFAIL;
}
/*-------------------------------------------------------------------*/
pCodri MakeTcpDoChoDriver(char *tclArray, SConnection *pCon){
    pCodri pNew = NULL;
    pTcpDoCho self = NULL;
    const char *pPtr = NULL;
    char buffer[132];
    int port, i, count;
    Tcl_DString pars;
    char *parnames[] = {"State",
			"ASPEED",
			"RSPEED",
			"APHASE",
			"RPHASE",
			"AVETO",
			"DIR",
			"MONIT",
			"FLOWR",
			"WTEMP",
			"MTEMP",
			"MVIBR",
			"MVACU",
			"speed",
			"phase",
			NULL,
    };

    /*
      allocate memory
    */
    pNew = (pCodri)malloc(sizeof(Codri));
    self = (pTcpDoCho)malloc(sizeof(TcpDoCho));
    if(!pNew || !self){
        return NULL;
    }
    memset(pNew,0,sizeof(Codri));
    memset(self,0,sizeof(TcpDoCho));
    
    /* port and host name */
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"port",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: port not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    sscanf(pPtr,"%d",&port);
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"host",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: host not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    memset(buffer,0,132);
    strncpy(buffer,pPtr, 131);
    self->controller = createRS232(buffer,port);

    /* number of choppers */
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"nchopper",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: nchopper  not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    sscanf(pPtr,"%d",&port);
    if(port < 0 || port > 8){
	SCWrite(pCon,"ERROR: number of choppers not in range 1 - 8",eError);
        free(pNew);
	free(self);
    }
    self->numChoppers = port;

    /* timeout */
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"timeout",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: timeout not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    sscanf(pPtr,"%d",&port);
    self->timeout = port;

    /* username and password */
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"user",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: user not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    strncpy(self->user,pPtr, 131);
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"password",TCL_GLOBAL_ONLY);
    if(!pPtr){
	SCWrite(pCon,"ERROR: password not found in configuration array for TCP Dornier Chopper",
		eError);
        free(pNew);
	free(self);
        return NULL;
    }
    strncpy(self->pword,pPtr, 131);

    /*
      chopper configuration
    */
    pPtr = Tcl_GetVar2(pServ->pSics->pTcl,tclArray,"config",TCL_GLOBAL_ONLY);
    if(pPtr != NULL){
	self->config = strdup(pPtr);
    }


    /* initialize some more */
    self->parameters = CreateStringDict();
    if(self->parameters == NULL || self->controller == NULL){
	SCWrite(pCon,"ERROR: out of memory in MakeTcpDoCho",eError);
        free(pNew);
	free(self);
        return NULL;
    }
    self->iRefreshIntervall = 60;
    pNew->Init      =  TcpDoChoInit;
    pNew->Close     =  TcpDoChoClose;
    pNew->Delete    =  TcpDoChoKill;
    pNew->SetPar    =  TcpDoChoSetPar;
    pNew->SetPar2   =  TcpDoChoSetPar2;
    pNew->GetPar    =  TcpDoChoGetPar;
    pNew->CheckPar  =  TcpDoChoCheckPar;
    pNew->GetError  =  TcpDoChoError;
    pNew->TryFixIt  =  TcpDoChoFix;
    pNew->Halt      =  TcpDoChoHalt;
    StringDictAddPair(self->parameters,"updateintervall","60");
    pNew->pPrivate = self;

    /*
      create parameter list
    */
    Tcl_DStringInit(&pars);
    count = 0;
    Tcl_DStringAppend(&pars,"updateintervall",15);
    while(parnames[count] != NULL){
	for(i = 0; i < self->numChoppers; i++){
	    snprintf(buffer,131,",%s_%1.1d", parnames[count], i + 1);
	    Tcl_DStringAppend(&pars,buffer,strlen(buffer));
        }
	count++;
    }
    pNew->pParList = strdup(Tcl_DStringValue(&pars));
    Tcl_DStringFree(&pars);
    return pNew;
}
