/**
* This is the implementation of a chopper driver for the MARS Chopper cascade
* as provided by the Forschungszentrum Juelich. The original host protocol 
* from Juelich has been changed on our request; Gerd Theidel did some of that 
* Turbo Pascal coding.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, June-July 2006
* 
* Parameter updating is to  slow, therefore it happens in two groups now: 
* actspeed and actphase are requested more frequently, so they go separate
* 
* Mark Koennecke, October 2006
*/
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <sics.h>
#include <unistd.h>
#define CHOCOINTERNAL
#include <choco.h>
#include <SCinter.h> 
#include <rs232controller.h>
#include <sicshipadaba.h>
#include <stptok.h>
#include <dynstring.h>
/*----------------------------- error codes  -----------------------------*/
#define FRAMEERROR -801
#define CKERROR    -802
#define BADCOMMAND -803
#define WRONGNOPAR -804
#define ILLPAR     -805
#define PARRANGE   -806
#define BADERR     -807
#define BADREPLY   -808
#define NOTPAR     -809
#define BADTEXT    -810
#define ROPAR      -811
#define NOMEM      -812
#define HALT       -813
/*--------------------- update flags -------------------------------------*/
#define ALL 1
#define SPEED 2
/*--------------------- wait after set -----------------------------------*/
#define STWAIT 5
/*------------------------- chopper internal names -----------------------*/
#define CH1N "snail"
#define CH2N "master"
#define CH3N "rabbit"
#define CH4N "four"
#define CH5N "five"
/*=============== Juelich chopper private data structure ================== */
typedef struct {
            prs232 controller;
            pHdb parNode;
            int errorCode;
            time_t lastUpdate;
            int updateIntervall;
            int speedUpdate;
            int halt;
}JulCho, *pJulCho;
/*------------------------------------------------------------------------*/
typedef struct {
        char prefix[10];
        char postfix[10];
        char comCode[10];
        pJulCho pDriv;
} julCBData, *pJulCBData;
/*================= internal support functions ============================*/
static int calculateJulCheckSum(char *realCommand){
    int i, checkSum = 0;
    
    for(i = 1; i < strlen(realCommand); i++){
        checkSum += (int)realCommand[i];
    }   
    return checkSum;
}
/*------------------------------------------------------------------------*/
static int testJulError(char *reply){
    char *pPtr = NULL, *pEnd = NULL;
    int code, status;
    
    if(strstr(reply,"ERR") == NULL){
        return 1;
    }
    pPtr = &reply[9]; /* #ERR:CCC: */
    pEnd = strchr(pPtr,'{');
    if(pEnd == NULL){
        return BADERR;
    }
    *pEnd = '\0';
    code = atoi(pPtr);
    switch(code){
        case 1:
            status = FRAMEERROR;
            break;
        case 2:
            status = CKERROR;
            break;
        case 3:
            status = BADCOMMAND;
            break;
        case 4:
            status = WRONGNOPAR;
            break;
        case 5:
            status = ILLPAR;
            break;
        case 6:
            status = PARRANGE;
            break;
        default:
            status = BADERR;
            break;
    }
    return status;
}
/*------------------------------------------------------------------------*/
static void readClean(prs232 controller){
    char buffer[1024];
    int count = 0, bufSize;
    
    while(availableRS232(controller) == 1 && count < 20){
        bufSize = 1024;
        readRS232(controller,buffer, &bufSize);
    }   
}
/*-------------------------------------------------------------------------*/
static int JulChoTransact(pJulCho self, char *command, 
    char *reply, int replyLen){
    int status, length, checkSum;
    char realCommand[1024], checkString[30];
    
    strcpy(realCommand,"#");
    strcat(realCommand,command);
    
    checkSum = calculateJulCheckSum(realCommand);
    snprintf(checkString,30,"{%d}$", checkSum);
    strcat(realCommand,checkString);
    
    /*
     * clean the socket: If someone whacked the keyboard and the
     * controller did not repsond, the line might be full of shit
     */
    readClean(self->controller);
    
    status = transactRS232(self->controller, realCommand,strlen(realCommand), 
        (void *)reply, replyLen);
    if(status <= 0){
        return status;
    }

    status = testJulError(reply);    
                 
    return status;
} 
/*---------------------------------------------------------------------------*/
static hdbCallbackReturn JulChoSetCallback(pHdb node, void *userData, 
		pHdbMessage message){
    pJulCBData cbData = NULL;
    char command[256], reply[256];
    pHdbDataMessage mm = NULL;
    int status;
    
    cbData = (pJulCBData)userData;
    if(cbData == NULL){
        return hdbContinue;
    }
 
    /*
     * Is this for us?
     */
    mm = GetHdbSetMessage(message);
    if(mm == NULL){
    	return hdbContinue;
    }
    
    
    if(mm->v->dataType == HIPINT){
        snprintf(command,255,"%s %s%d%s",cbData->comCode, cbData->prefix,
            (int)mm->v->v.intValue,cbData->postfix);
    } else if(mm->v->dataType == HIPFLOAT){
        snprintf(command,255,"%s %s%f%s",cbData->comCode, cbData->prefix,
            (float)mm->v->v.doubleValue,cbData->postfix);
    } else {
        assert(0); /* this is a programming error */
    }
    
    cbData->pDriv->halt = 0;
    status = JulChoTransact(cbData->pDriv, command, reply, 255);
    if(status < 0) {
            cbData->pDriv->errorCode = status;
            return hdbAbort;
    }
    return hdbContinue;
}
/*------------------------------------------------------------------------------*/
static pHdbCallback MakeJulChoSetCallback(pJulCho driv, char *command, 
    char *prefix, char *postfix){
    pJulCBData cbData = NULL;
    pHdbCallback hdbCB = NULL;
    
    hdbCB = malloc(sizeof(hdbCallback));
    cbData = malloc(sizeof(julCBData));
    if(cbData == NULL || hdbCB == NULL){
        return NULL;
    }
    cbData->pDriv = driv;
    strncpy(cbData->comCode,command,9);        
    strncpy(cbData->prefix,prefix,9);        
    strncpy(cbData->postfix,postfix,9);   
    hdbCB->next = NULL;     
    hdbCB->previous = NULL;     
    hdbCB->killFunc = free;
    hdbCB->userCallback = JulChoSetCallback;
    hdbCB->userData = cbData;
    return hdbCB;     
}
/*--------------------------------------------------------------------------*/
static int splitJulChoInt(char *reply, int data[5]){
    char number[10];
    char *pPtr = NULL;
    int count = 0;
    
    pPtr = stptok(reply,number,10,":");
    pPtr = stptok(pPtr,number,10,":");
    while(pPtr != NULL && count < 5){
        data[count] = atoi(number);
        count++;
        pPtr = stptok(pPtr,number,10,":");
    }
    if(count < 4){
        return 0;
    } 
    return 1;
}
/*--------------------------------------------------------------------------*/
static int splitJulChoDouble(char *reply, double data[5]){
    char number[10];
    char *pPtr = NULL;
    int count = 0;
    
    pPtr = stptok(reply,number,10,":");
    pPtr = stptok(pPtr,number,10,":");
    while(pPtr != NULL && count < 5){
        data[count] = (double)atof(number);
        count++;
        pPtr = stptok(pPtr,number,10,":");
    }
    if(count < 4){
        return 0;
    } 
    return 1;
}
/*--------------------------------------------------------------------------*/
static int setJulChoIntPar(pHdb root, char *par, int data[5]){
    char path[256];
    hdbValue v;
    pHdb node = NULL;
    
    memset(&v,0,sizeof(hdbValue));
    v.dataType = HIPINT;
    
    snprintf(path,255,"%s/%s",CH1N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.intValue = (long)data[0];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH2N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.intValue = (long)data[1];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH3N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.intValue = (long)data[2];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH4N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.intValue = (long)data[3];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH5N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.intValue = (long)data[4];
    UpdateHipadabaPar(node,v,NULL);
    
    return 1;
}
/*--------------------------------------------------------------------------*/
static int setJulChoDoublePar(pHdb root, char *par, double data[5]){
    char path[256];
    hdbValue v;
    pHdb node = NULL;
    
    memset(&v,0,sizeof(hdbValue));
    v.dataType = HIPFLOAT;
    
    snprintf(path,255,"%s/%s",CH1N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.doubleValue = data[0];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH2N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.doubleValue = data[1];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH3N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.doubleValue = data[2];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH4N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.doubleValue = data[3];
    UpdateHipadabaPar(node,v,NULL);

    snprintf(path,255,"%s/%s",CH5N,par);
    node = GetHipadabaNode(root,path);
    assert(node != NULL);
    v.v.doubleValue = data[4];
    UpdateHipadabaPar(node,v,NULL);
    
    return 1;
}
/*--------------------------------------------------------------------------*/
static void updateJulChoFlag(pHdb node, char *choppername, char *parname,
    int code, int mask){
    char path[256];
    hdbValue v;
    pHdb target = NULL;
    
    v.dataType = HIPINT;
    snprintf(path,255,"%s/%s",choppername,parname);
    if((mask & code) > 0) {
        v.v.intValue = 1;
    } else {
        v.v.intValue = 0;
    }
    target = GetHipadabaNode(node,path);
    assert(target != NULL);
    UpdateHipadabaPar(target,v,NULL);
}
/*---------------------------------------------------------------------------*/
static int setJulChoFlags(pHdb node, int intData[5]){
    char *chNames[] = {CH1N,
                       CH2N,
                       CH3N,
                       CH4N,
                       CH5N,
                       NULL};
    char path[256];
    int i, code;
    hdbValue v;
    
    memset(&v,0,sizeof(hdbValue));
    v.dataType = HIPINT;
    for(i = 0; i < 5; i++){
        code = intData[i];
        updateJulChoFlag(node,chNames[i],"microok",code,1);
        updateJulChoFlag(node,chNames[i],"atspeed",code,2);
        updateJulChoFlag(node,chNames[i],"atphase",code,4);
        updateJulChoFlag(node,chNames[i],"magneton",code,8);
        updateJulChoFlag(node,chNames[i],"dcon",code,16);
        updateJulChoFlag(node,chNames[i],"driveon",code,32);
        updateJulChoFlag(node,chNames[i],"currentdc",code,64);
        updateJulChoFlag(node,chNames[i],"lockopen",code,128);
        updateJulChoFlag(node,chNames[i],"diskopen",code,256);
        updateJulChoFlag(node,chNames[i],"diskclosed",code,512);
        updateJulChoFlag(node,chNames[i],"speedoverflow",code,1024);
        updateJulChoFlag(node,chNames[i],"bearingfailed",code,2048);
        updateJulChoFlag(node,chNames[i],"voltagetohigh",code,4096);
    }
    
    return 1;
}
/*--------------------------------------------------------------------------*/
static int ReadJulChoFlags(pJulCho self){
    int status, intData[5];
    char reply[256];
    
    status = JulChoTransact(self,"RSC",reply,255);
/*    fprintf(stdout,"Chopper flags = %s\n", reply);*/
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoInt(reply,intData)){
        self->errorCode = BADREPLY;
    }
    setJulChoFlags(self->parNode, intData);
    return 1;
}
/*---------------------------------------------------------------------------*/
static int UpdateJulChoParameters(pJulCho self, int what){
    int status, intData[5];
    double doubleData[5];
    char reply[255];
    
    assert(what == ALL || what == SPEED);
    
    if(what == SPEED){
        if(time(NULL) < self->speedUpdate + self->updateIntervall){
            return 1;
        }
    } else{
        if(time(NULL) < self->lastUpdate + self->updateIntervall){
            return 1;
        }
    }
    
    status = JulChoTransact(self,"RAS",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoInt(reply,intData)){
        self->errorCode = BADREPLY;
    }
    setJulChoIntPar(self->parNode,"actspeed",intData);

    status = JulChoTransact(self,"RAP",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"actphase",doubleData);

    if(what != ALL){
        self->speedUpdate = time(NULL);
        return 1;
    }
    
    status = JulChoTransact(self,"RNS",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoInt(reply,intData)){
        self->errorCode = BADREPLY;
    }
    setJulChoIntPar(self->parNode,"nomspeed",intData);
    
    
    status = JulChoTransact(self,"RNP",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"nomphase",doubleData);
    
    
    status = JulChoTransact(self,"RGW",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"gatewidth",doubleData);
    
    status = JulChoTransact(self,"RNC",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"nomcurrent",doubleData);
    
    
    status = JulChoTransact(self,"RAC",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"actcurrent",doubleData);
    
    status = JulChoTransact(self,"RAV",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"voltage",doubleData);
    
    status = JulChoTransact(self,"RIT",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"inverter_temperature",doubleData);

    status = JulChoTransact(self,"RST",reply,255);
    if(status < 0){
        self->errorCode = status;
        return 0;
    }
    if(!splitJulChoDouble(reply,doubleData)){
        self->errorCode = BADREPLY;
    }
    setJulChoDoublePar(self->parNode,"stator_temperature",doubleData);

    status = ReadJulChoFlags(self);
    
    self->lastUpdate = time(NULL);
    self->speedUpdate = time(NULL);
    return status;
}
/*------------------------------------------------------------------------*/
static void JulChoErrorcodeToString(int code, char *pError, int iLen){
    switch(code){
        case FRAMEERROR:
            strncpy(pError,"Frame error",iLen);
            break;
        case CKERROR:
            strncpy(pError,"Checksum error",iLen);
            break;
        case BADCOMMAND:
            strncpy(pError,"Bad Command",iLen);
            break;
        case WRONGNOPAR:
            strncpy(pError,"Wrong number of parameters",iLen);
            break;
        case ILLPAR:
            strncpy(pError,"Illegal parameter",iLen);
            break;
        case PARRANGE:
            strncpy(pError,"Parameter out of range",iLen);
            break;
        case BADERR:
            strncpy(pError,"Controller error not recognised",iLen);
            break;
        case BADREPLY:
            strncpy(pError,"Unexpected reply",iLen);
            break;
        case NOTPAR:
            strncpy(pError,"Unsupported parameter",iLen);
            break;
        case BADTEXT:
            strncpy(pError,"Failed to convert text to number",iLen);
            break;
        case ROPAR:
            strncpy(pError,"Read only Parameter",iLen);
            break;
        case NOMEM:
            strncpy(pError,"Out of memory formatting parameter",iLen);
            break;
        case HALT:
            strncpy(pError,"User requested HALT; choppers status undefined ",iLen);
            break;
        case SICSCBRANGE:
            strncpy(pError,"Parameter value out of range",iLen);
            break;
        case SICSCBRO:
            strncpy(pError,"Parameter is READ-ONLY",iLen);
            break;
        default:
            getRS232Error(code, pError, iLen);
            break;    
    }
}
/*------------------------------------------------------------------------*/
static int testParGroup(char *name){
    if(strstr(name,"actspeed") != NULL || 
        strstr(name,"actphase") != NULL){
            return SPEED;
        } else {
            return ALL;
        }
}
/*-------------------------------------------------------------------------*/
static hdbCallbackReturn JulChoGetCallback(pHdb currentNode, 
		void *userData, pHdbMessage message){
    pJulCho self = NULL;
    SConnection *pCon = NULL;
    int status;
    char error[128], buffer[256];
    pHdbDataMessage mm = NULL;
    
    self = (pJulCho)userData;
    assert(self != NULL);
    
    mm = GetHdbGetMessage(message);
    if(mm == NULL){
    	return hdbContinue;
    }
    pCon = (SConnection *)mm->callData;
    
    status = UpdateJulChoParameters(self,testParGroup(currentNode->name));
    if(status != 1 && pCon != NULL){
        JulChoErrorcodeToString(self->errorCode, error,127);
        snprintf(buffer,255,"ERROR: %s occurred reading par",error);
        SCWrite(pCon,buffer,eError);
    }
    return hdbContinue;
}
/*--------------------------------------------------------------------------*/
static int AppendJulChoROPar(pHdb parent, char *name, int type){
	pHdb child = NULL;
    child = AddSICSHdbROPar(parent,name,makeHdbValue(type,1));
    if(child != NULL){
    	return 1;
    } else {
    	return 0;
    }
}
/*---------------------------------------------------------------------------*/
static int ConfigureSingleJulCho(pHdb parent, pJulCho driv, 
    char *prefix, char *postfix){
    pHdb child = NULL;
    pHdbCallback pCb = NULL;
    
    /*
     * write parameters
     */
        
    child = MakeHipadabaNode("nomphase",HIPFLOAT,0);
    if(child == NULL){
        return 0;
    }
    pCb = MakeCheckPermissionCallback(usUser);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(child,pCb); 
    pCb = MakeFloatRangeCallback(5.0, 355.);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(child,pCb); 
    pCb = MakeJulChoSetCallback(driv,"SPH", prefix,postfix);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(child,pCb); 
    AddHipadabaChild(parent,child,NULL);
    
    child = MakeHipadabaNode("gatewidth",HIPFLOAT,0);
    if(child == NULL){
        return 0;
    }
    pCb = MakeCheckPermissionCallback(usUser);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(child,pCb); 
    pCb = MakeJulChoSetCallback(driv,"SGW", prefix,postfix);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(child,pCb); 
    AddHipadabaChild(parent,child,NULL);
    
    if(!AppendJulChoROPar(parent,"nomspeed",HIPINT)){
        return 0;
    }
 
    if(!AppendJulChoROPar(parent,"actspeed",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"actphase",HIPFLOAT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"nomcurrent",HIPFLOAT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"actcurrent",HIPFLOAT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"voltage",HIPFLOAT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"inverter_temperature",HIPFLOAT)){
        return 0;
    }

    if(!AppendJulChoROPar(parent,"stator_temperature",HIPFLOAT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"microok",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"atspeed",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"atphase",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"magneton",HIPINT)){
        return 0;
    }

    if(!AppendJulChoROPar(parent,"dcon",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"driveon",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"currentdc",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"lockopen",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"diskopen",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"diskclosed",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"speedoverflow",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"bearingfailed",HIPINT)){
        return 0;
    }
    
    if(!AppendJulChoROPar(parent,"voltagetohigh",HIPINT)){
        return 0;
    }
    
    /*
     * append get callbacks 
     */
   child = parent->child;
   while(child != NULL){
        AppendHipadabaCallback(child,
            MakeHipadabaCallback(JulChoGetCallback,driv,NULL)); 
        child = child->next;
   }
   return 1;     
}
/*--------------------------------------------------------------------------*/
static int InitJulChoPar(pJulCho driv){
    pHdb child = NULL, parChild = NULL;    
    pHdbCallback pCb = NULL;
        
     
    child = MakeHipadabaNode(CH1N,HIPNONE,0); 
    if(child == NULL){
        return 0;
    }  
    if(!ConfigureSingleJulCho(child,driv,"","::::")){
        return 0;
    }
    AddHipadabaChild(driv->parNode,child,NULL);

    child = MakeHipadabaNode(CH2N,HIPNONE,0); 
    if(child == NULL){
        return 0;
    }  
    if(!ConfigureSingleJulCho(child,driv,":",":::")){
        return 0;
    }
    /**
     * the master speed can be set, the slaves not, thus remove the read only 
     * set callback and replace by a speed setting callback
     */
    parChild = GetHipadabaNode(child,"nomspeed");
    assert(parChild != NULL);
    /*
     * delete the callback cahin in order to remove the 
     * read only callback
     */
    DeleteCallbackChain(parChild->callBackChain);
    parChild->callBackChain = NULL;

    pCb = MakeCheckPermissionCallback(usUser);
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(parChild,pCb); 
    pCb = MakeJulChoSetCallback(driv,"SMS","","");
    if(pCb == NULL){
        return 0;
    }
    AppendHipadabaCallback(parChild,pCb); 
    AppendHipadabaCallback(parChild,
        MakeHipadabaCallback(JulChoGetCallback,driv,NULL)); 
 
    
    AddHipadabaChild(driv->parNode,child,NULL);
    
    child = MakeHipadabaNode(CH3N,HIPNONE,0); 
    if(child == NULL){
        return 0;
    }  
    if(!ConfigureSingleJulCho(child,driv,"::","::")){
        return 0;
    }
    AddHipadabaChild(driv->parNode,child,NULL);

    child = MakeHipadabaNode(CH4N,HIPNONE,0); 
    if(child == NULL){
        return 0;
    }  
    if(!ConfigureSingleJulCho(child,driv,":::",":")){
        return 0;
    }
    AddHipadabaChild(driv->parNode,child,NULL);

    child = MakeHipadabaNode(CH5N,HIPNONE,0); 
    if(child == NULL){
        return 0;
    }  
    if(!ConfigureSingleJulCho(child,driv,"::::","")){
        return 0;
    }
    AddHipadabaChild(driv->parNode,child,NULL);
    
    return 1;            
}
/*================= actual interface functions ==============================*/
static int JulChoInit(pCodri pDriv){
    pJulCho self = NULL;
    int status;
    
    self = (pJulCho)pDriv->pPrivate;
    status = initRS232(self->controller);
    if(status < 1){
        self->errorCode = status;
    }
    setRS232SendTerminator(self->controller,"$");
    setRS232ReplyTerminator(self->controller,"$");
    setRS232Timeout(self->controller,2000);
    return 1;
}
/*---------------------------------------------------------------------------*/
static int JulChoClose(pCodri pDriv){
    pJulCho self = NULL;
    
    self = (pJulCho)pDriv->pPrivate;
    closeRS232(self->controller);
    return 1;
}
/*---------------------------------------------------------------------------*/
static int JulChoKill(pCodri pDriv){
    pJulCho self = NULL;

    if(pDriv == NULL){
        return 1;
    }
    self = (pJulCho)pDriv->pPrivate;
    JulChoClose(pDriv);
    if(self->controller != NULL){
        KillRS232(self->controller);
        self->controller = NULL;
    }    
    free(self);
    return 1;
}
/*---------------------------------------------------------------------------
 * The set and get routines introduce phase and speed values which map to
 * nomspeed when setting and actspeed when reading. This ugly hack saves me
 * to introduce another drive adapter which set one parameter and reads 
 * another
 *---------------------------------------------------------------------------*/
static int JulChoSetPar2(pCodri pDriv, char *parname, char *pValue){
    pJulCho self = NULL;
    pHdb target = NULL;
    hdbValue v;
    char error[64], *pPtr = NULL;
    int status;
    
    if(strcmp(parname,"master/speed") == 0){
        status = JulChoSetPar2(pDriv,"master/nomspeed", pValue);
        if(status == 1) {
            SicsWait(10);
        }
        return status;
    } else if(strcmp(parname,"master/phase") == 0){
        return JulChoSetPar2(pDriv,"master/nomphase", pValue);
    } else if(strcmp(parname,"snail/phase") == 0){
        return JulChoSetPar2(pDriv,"snail/nomphase", pValue);
    } else if(strcmp(parname,"rabbit/phase") == 0){
        return JulChoSetPar2(pDriv,"rabbit/nomphase", pValue);
    } else if(strcmp(parname, "four/phase") == 0){
        return JulChoSetPar2(pDriv,"four/nomphase", pValue);
    } else if(strcmp(parname,"five/phase") == 0){
        return JulChoSetPar2(pDriv,"five/nomphase", pValue);
    }
    
    self = (pJulCho)pDriv->pPrivate;
    target = GetHipadabaNode(self->parNode,parname);
    if(target == NULL){
        self->errorCode = NOTPAR;
        return 0;
    }
    v.dataType = target->value.dataType;
    if(!readHdbValue(&v,pValue,error,63)){
        self->errorCode = BADTEXT;
        return 0;
    }    
    self->errorCode = 0;
    status = SetHipadabaPar(target,v,NULL);
    if(status == 0 && self->errorCode == 0){
        self->errorCode = status;
        return 0;
    }
    /*
     * The SicsWait is here to allow the chopper to update his status flags.
     * There were occurrences where the chopper was still driving but the 
     * status flag did not reflect this.
     */
    SicsWait(STWAIT);
    self->lastUpdate = 0;
    return status;
}
/*-------------------------------------------------------------------------------*/
static int JulChoSetPar(pCodri pDriv, char *parname, float fValue){
    pJulCho self = NULL;
    pHdb target = NULL;
    hdbValue v;
    char error[64];
    int status;
 
    if(strcmp(parname,"master/speed") == 0){
        status = JulChoSetPar(pDriv,"master/nomspeed", fValue);
        if(status == 1) {
            SicsWait(10);
        }
        return status;
    } else if(strcmp(parname,"master/phase") == 0){
        return JulChoSetPar(pDriv,"master/nomphase", fValue);
    } else if(strcmp(parname,"snail/phase") == 0){
        return JulChoSetPar(pDriv,"snail/nomphase", fValue);
    } else if(strcmp(parname,"rabbit/phase") == 0){
        return JulChoSetPar(pDriv,"rabbit/nomphase", fValue);
    } else if(strcmp(parname, "four/phase") == 0){
        return JulChoSetPar(pDriv,"four/nomphase", fValue);
    } else if(strcmp(parname,"five/phase") == 0){
        return JulChoSetPar(pDriv,"five/nomphase", fValue);
    }
    
    self = (pJulCho)pDriv->pPrivate;
    target = GetHipadabaNode(self->parNode,parname);
    if(target == NULL){
        self->errorCode = NOTPAR;
        return 0;
    }
    v.dataType = target->value.dataType;
    if(v.dataType == HIPINT){
        v.v.intValue = (int)fValue;
    } else {
        v.v.doubleValue = (double)fValue;
    } 
    self->errorCode = 0;
    status = SetHipadabaPar(target,v,NULL);
    if(status == 0 && self->errorCode == 0){
        self->errorCode = status;
        return 0;
    }
    /*
     * The SicsWait is here to allow the chopper to update his status flags.
     * There were occurrences where the chopper was still driving but the 
     * status flag did not reflect this.
     */
    SicsWait(STWAIT);
    self->lastUpdate = 0;
    return status;
}
/*---------------------------------------------------------------------------*/
static int JulChoHalt(pCodri pDriv){
    pJulCho self = NULL;
    
    self = (pJulCho)pDriv->pPrivate;
    self->halt = 1;
    /*
     * empty function on Philips request
     */
    return 1;
}
/*--------------------------------------------------------------------------*/
static int JulChoGetPar(pCodri pDriv, char *parname, 
                         char *pBuffer, int iBufLen){
    pJulCho self = NULL;
    pHdb target = NULL;
    pDynString val = NULL;

    if(strcmp(parname,"master/speed") == 0){
        return JulChoGetPar(pDriv,"master/actspeed", pBuffer,iBufLen);
    } else if(strcmp(parname,"master/phase") == 0){
        return JulChoGetPar(pDriv,"master/actphase", pBuffer,iBufLen);
    } else if(strcmp(parname,"snail/phase") == 0){
        return JulChoGetPar(pDriv,"snail/actphase", pBuffer,iBufLen);
    } else if(strcmp(parname,"rabbit/phase") == 0){
        return JulChoGetPar(pDriv,"rabbit/actphase", pBuffer,iBufLen);
    } else if(strcmp(parname, "four/phase") == 0){
        return JulChoGetPar(pDriv,"four/actphase", pBuffer,iBufLen);
    } else if(strcmp(parname,"five/phase") == 0){
        return JulChoGetPar(pDriv,"five/actphase", pBuffer,iBufLen);
    }

    
    self = (pJulCho)pDriv->pPrivate;
    target = GetHipadabaNode(self->parNode,parname);
    if(target == NULL){
        self->errorCode = NOTPAR;
        return 0;
    }
    if(!UpdateJulChoParameters(self,testParGroup(parname))){
        return 0;
    }
    val = formatValue(target->value, target);
    if(val == NULL){
        self->errorCode = NOMEM;
        return 0;
    }
    strncpy(pBuffer,GetCharArray(val), iBufLen);
    DeleteDynString(val);
    return 1;                            
}
/*---------------------------------------------------------------------------*/
static int JulChoCheckPar(pCodri pDriv, char *parname){
    pJulCho self = NULL;
    int i, status;
    char path[256], reply[256];
    pHdb target = NULL;
    char *chNames[] = {CH1N,
                       CH2N,
                       CH3N,
                       CH4N,
                       CH5N,
                       NULL};

    self = (pJulCho)pDriv->pPrivate;
    
    if(self->halt == 1){
        self->errorCode = HALT;
        return HWFault;
    }
    /* 
     * For the Juelich chopper this means checking the atspeed, atphase
     * flags
     */
    if(!ReadJulChoFlags(self)){
        return HWFault;
    }
    for(i = 0; i < 5; i++){
        snprintf(path,255,"%s/atspeed", chNames[i]);
        target = GetHipadabaNode(self->parNode,path);
        assert(target != NULL);
        if(target->value.v.intValue == 0){
            return HWBusy;
        }
        snprintf(path,255,"%s/atphase",chNames[i]);
        target = GetHipadabaNode(self->parNode,path);
        assert(target != NULL);
        if(target->value.v.intValue == 0){
            return HWBusy;
        }
    }
    status = JulChoTransact(self,"RSC",reply,255);
    if(status < 0){
        self->errorCode = status;
        return HWFault;
    }
/*    fprintf(stdout,"Chopper Flags at finish: %s\n", reply);*/
    return HWIdle;
}
/*---------------------------------------------------------------------------*/
static int JulChoError(pCodri pDriv, int *iCode, char *pError, int iLen){
    pJulCho self = NULL;

    self = (pJulCho)pDriv->pPrivate;

    *iCode = self->errorCode;
    JulChoErrorcodeToString(self->errorCode, pError, iLen);
    return 1;       
}
/*---------------------------------------------------------------------------*/
static int JulChoFix(pCodri pDriv, int  code){
    pJulCho self = NULL;

    self = (pJulCho)pDriv->pPrivate;
    
    switch(code){
        case TIMEOUT:
        case CKERROR:
        case BADERR:
        case BADREPLY:
            return CHREDO;
            break;
        case  NOTCONNECTED:
            if(initRS232(self->controller) == 1){
                return CHREDO;
            } else {
                return CHFAIL;
            }
            break;
        case INCOMPLETE:
        case BADSEND:
        case BADREAD:
            closeRS232(self->controller);
            if(initRS232(self->controller) == 1){
                return CHREDO;
            } else {
                return CHFAIL;
            }
            break;
        default:
            return CHFAIL;
            break;            
    }
}
/*---------------------------------------------------------------------------*/
static pCodri MakeJulChoDriver(char *pHost, int port, pHdb parNode){
    pCodri pDriv = NULL;
    pJulCho self = NULL;
    int i;
    pDynString names = NULL;
    char *chNames[] = {CH1N,
                       CH2N,
                       CH3N,
                       CH4N,
                       CH5N,
                       NULL};
    char path[256], *par = NULL;
    
    pDriv = malloc(sizeof(Codri));
    self = malloc(sizeof(JulCho));
    names = CreateDynString(256,256);
    
    if(pDriv == NULL && self == NULL && names == NULL){
        return NULL;
    }
    memset(pDriv,0,sizeof(Codri));
    memset(self,0,sizeof(JulCho));
    
    self->parNode = parNode;
    self->controller = createRS232(pHost,port);
    if(self->controller == NULL){
       free(self);
       free(pDriv);
       return NULL;
    }
    self->updateIntervall = 10;
    if(!InitJulChoPar(self)){
        free(self);
        free(pDriv);
        return NULL;
    }
    
    pDriv->pPrivate = self;
    
    pDriv->Init      =  JulChoInit;
    pDriv->Close     =  JulChoClose;
    pDriv->Delete    =  JulChoKill;
    pDriv->SetPar    =  JulChoSetPar;
    pDriv->SetPar2   =  JulChoSetPar2;
    pDriv->GetPar    =  JulChoGetPar;
    pDriv->CheckPar  =  JulChoCheckPar;
    pDriv->GetError  =  JulChoError;
    pDriv->TryFixIt  =  JulChoFix;
    pDriv->Halt      =  JulChoHalt;
    
    for(i = 0; i < 5; i++){
        snprintf(path,255,"%s/nomspeed,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/actspeed,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/nomphase,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/actphase,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/gatewidth,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/nomcurrent,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/actcurrent,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/voltage,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/inverter_temperature,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/stator_temperature,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/microok,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/atspeed,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/atphase,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/magneton,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/dcon,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/driveon,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/currentdc,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/lockopen,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/diskopen,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/diskclosed,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/speedoverflow,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/bearingfailed,",chNames[i]);
        DynStringConcat(names,path);    
        snprintf(path,255,"%s/voltagetohigh,",chNames[i]);
        DynStringConcat(names,path);    
    } 
    par = GetCharArray(names);
    par[strlen(par) -1] = '\0';
    pDriv->pParList = strdup(par);
    DeleteDynString(names);
    if(pDriv->pParList == NULL){
        JulChoKill(pDriv);
        free(pDriv);
        return NULL;
    }

    return pDriv;    
}
/*--------------------------------------------------------------------------*/
int JulChoFactory(SConnection *pCon, SicsInterp *pSics, void *pData,
                  int argc, char *argv[]){
      pChoco pNew = NULL;
      pCodri pDriv = NULL;
      pObjectDescriptor pDes = NULL;
      char pBueffel[132];
      int iRet, iPort, iChannel;
      int iSingle = 0;

      if(argc < 4)
      {
         SCWrite(pCon,
            "ERROR: Insufficient number of arguments to MakeJulCho",
                 eError);
         return 0;
      }
    

      /* first try to get everything done */
      pNew = (pChoco)malloc(sizeof(Choco));
      pDes = CreateDescriptor("Chopper");
      pDes->parNode = MakeHipadabaNode(argv[1], HIPNONE,0);
      /* do driver */
      
      pDriv = MakeJulChoDriver(argv[2],atoi(argv[3]),pDes->parNode);
      if(pDriv == NULL){
          sprintf(pBueffel,"ERROR: Failed to initialize JulCho Driver");
        SCWrite(pCon,pBueffel,eError);
        return 0;
      }
      if( (pNew == NULL) || (pDes == NULL) || (pDriv == NULL) )
      {
        SCWrite(pCon,"ERROR: No memory left to create controller",eError);
        return 0;
      }
      pNew->pDes = pDes;
      pNew->pDriv = pDriv;

      /* initialize driver */
      iRet = pDriv->Init(pDriv);
      if(!iRet){
        SCWrite(pCon,"ERROR: Failed to initialize driver",eError);
        KillChoco(pNew);
        return 0;
      }

      /* install as command */
      iRet = AddCommand(pSics, argv[1],ChocoAction,KillChoco,pNew);
      if(!iRet){
        sprintf(pBueffel,"ERROR: duplicate command %s NOT created",
        argv[1]);
        SCWrite(pCon,pBueffel,eError);
        return 0;
      }
      return 1;
}


