 /*---------------------------------------------------------------------------
  SICS object for driving a couple of motors along a tabulated given path.

  copyright: see file COPYRIGHT

  Mark Koennecke, July 2005
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <math.h>
#include <lld.h>
#include "tabledrive.h"
/*--------------------------------------------------------------------------*/
#define OUTOFSYNC   100
#define STARTING    101
#define STEPPING    102
#define WAITING     103
#define WAITFINISH  104
#define IDLE        105

#define ABS(x) (x < 0 ? -(x) : (x))
#define SIGN(x) (x < .0 ? (-1) : (1))
extern char *trim(char *txt);
/*
 * copied from motor.c!!
 */
#define SLOW 0
#define SUPP 1

/*=================== the drivable interface functions =====================*/
static int TableDriveHalt(void *pData)
{
  pTableDrive self = (pTableDrive) pData;
  int status;
  tdMotor moti;

  if (self == NULL || self->motorTable < 0) {
    return 0;
  }

  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    if (moti.pMot != NULL) {
      moti.pMot->pDrivInt->Halt(moti.pMot);
    }
    status = LLDnodePtr2Next(self->motorTable);
  }
  self->state = OUTOFSYNC;
  return 1;
}

/*-------------------------------------------------------------------------*/
static int TableDriveCheckLimits(void *pData, float fVal, char *error,
                                 int iErrLen)
{
  pTableDrive self = (pTableDrive) pData;
  tdMotor moti;

  if (self == NULL || self->motorTable < 0) {
    strlcpy(error, "Path Table Not Defined!", 25);
    return 0;
  }
  if (fVal < 1. || fVal > self->tableLength) {
    strlcpy(error, "Out of Range", 25);
    return 0;
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static long TableDriveSetValue(void *pData, SConnection * pCon, float fVal)
{

  pTableDrive self = (pTableDrive) pData;
  char pError[132];

  if (self == NULL || self->motorTable < 0) {
    SCWrite(pCon, "ERROR: no path  defined", eError);
    return HWFault;
  }
  strcpy(pError, "ERROR:");
  if (!TableDriveCheckLimits(self, fVal, &pError[6], 120)) {
    SCWrite(pCon, pError, eError);
    return HWFault;
  }
  self->state = STARTING;
  self->targetPosition = fVal;
  return OKOK;
}

/*-------------------------------------------------------------------------*/
static int findOrientingMotor(pTableDrive self, ptdMotor moti)
{
  int status;

  if (!self->oriInvalid) {
    *moti = self->oriMotor;
    return 1;
  }
  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, moti);
    if (strcmp(moti->motorName, self->orientMotor) == 0) {
      self->oriMotor = *moti;
      self->oriInvalid = 0;
      return 1;
    }
    status = LLDnodePtr2Next(self->motorTable);
  }
  return 0;
}

/*-------------------------------------------------------------------------*/
static float locatePosition(tdMotor moti, SConnection * pCon)
{
  int status, begin = 1;
  float value;
  double diff;
  tdEntry lower, upper;

  status = MotorGetSoftPosition(moti.pMot, pCon, &value);
  if (!status) {
    return -9999.99;
  }
  status = LLDnodePtr2First(moti.table);
  while (status != 0) {
    LLDnodeDataTo(moti.table, &lower);
    status = LLDnodePtr2Next(moti.table);
    if (status != 0) {
      LLDnodeDataTo(moti.table, &upper);
    } else {
      /*
       * end of table
       */
      return lower.tablePos;
    }
                /**
		 * we have to deal with ascending and descending tables. This is
		 * why we have to have some funny logic here
		 */
    if (upper.position > lower.position) {
      /*
       * ascending table 
       */
      if (value >= lower.position && value < upper.position) {
        diff = upper.position - lower.position;
        return lower.tablePos + (value - lower.position) / diff;
      }
      if (begin == 1 && value < lower.position) {
        return lower.tablePos;
      }
    } else {
      /*
       * descending table
       */
      if (value <= lower.position && value > upper.position) {
        diff = lower.position - upper.position;
        return lower.tablePos + ABS(value - lower.position) / diff;
      }
      if (begin == 1 && value > lower.position) {
        return lower.tablePos;
      }
    }
    begin = 0;
  }
  return -999.99;
}

/*-------------------------------------------------------------------------*/
static void findBracket(tdMotor moti, float value, ptdEntry upper,
                        ptdEntry lower)
{
  int status, targetCount;
  float diff;

  targetCount = (int) floor(value);
  status = LLDnodePtr2First(moti.table);
  while (status != 0) {
    LLDnodeDataTo(moti.table, lower);
    if (lower->tablePos == targetCount) {
      LLDnodeDataTo(moti.table, upper);
      if (LLDnodePtr2Next(moti.table)) {
        LLDnodeDataTo(moti.table, upper);
      }
      return;
    }
    status = LLDnodePtr2Next(moti.table);
  }
}

/*-------------------------------------------------------------------------*/
static float findTarget(tdMotor moti, float value)
{
  int targetCount;
  tdEntry lower, upper;
  float diff;

  targetCount = (int) floor(value);
  findBracket(moti, value, &upper, &lower);
  if (ABS(targetCount - value) < .00001) {
    return lower.position;
  } else {
    diff = upper.position - lower.position;
    return (float) lower.position + (value - targetCount) * diff;
  }
}

/*-------------------------------------------------------------------------*/
static void checkSync(pTableDrive self, SConnection * pCon, float value)
{
  int status, test;
  float motorPosition, targetPosition, tolerance, diff;
  tdMotor moti;
  char pBueffel[256];

  if (self->state != IDLE) {
    return;
  }

  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    test = MotorGetSoftPosition(moti.pMot, pCon, &motorPosition);
    if (!test) {
      snprintf(pBueffel, 255, "ERROR: failed to read motor %s",
               moti.motorName);
      SCWrite(pCon, pBueffel, eError);
    } else {
      MotorGetPar(moti.pMot, "precision", &tolerance);
      targetPosition = findTarget(moti, value);
      if (ABS(targetPosition - motorPosition) > tolerance) {
        snprintf(pBueffel, 256,
                 "WARNING: motor %s out of sync by %f",
                 moti.motorName, ABS(targetPosition - motorPosition));
        SCWrite(pCon, pBueffel, eWarning);
      }
    }
    status = LLDnodePtr2Next(self->motorTable);
  }
}

/*--------------------------------------------------------------------------*/
static float TableDriveGetValue(void *pData, SConnection * pCon)
{
  pTableDrive self = (pTableDrive) pData;
  tdMotor orient;
  float value;

  if (self == NULL || self->motorTable < 0) {
    SCWrite(pCon, "ERROR: no path  defined", eError);
    return -9999.99;
  }
  if (!findOrientingMotor(self, &orient)) {
    SCWrite(pCon, "ERROR: table corrupted or orienting motor not found",
            eError);
    return -9999.99;
  }
  value = locatePosition(orient, pCon);
  checkSync(self, pCon, value);

  self->currentPosition = value;
  return value;
}

/*-------------------------------------------------------------------------*/
static void tableInfo(pTableDrive self, SConnection * pCon)
{
  int status, test;
  float motorPosition, targetPosition, tolerance, diff, value;
  tdMotor moti;
  char pBueffel[256];

  value = TableDriveGetValue(self, pCon);
  snprintf(pBueffel, 255, " Triffid Position: %8.2f", value);
  SCWrite(pCon, pBueffel, eValue);

  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    test = MotorGetSoftPosition(moti.pMot, pCon, &motorPosition);
    if (!test) {
      snprintf(pBueffel, 255, "ERROR: failed to read motor %s",
               moti.motorName);
      SCWrite(pCon, pBueffel, eError);
    } else {
      MotorGetPar(moti.pMot, "precision", &tolerance);
      targetPosition = findTarget(moti, value);
      snprintf(pBueffel, 256,
               "Motor %10s, should: %8.2f, is %8.2f, diff = %8.2f",
               moti.motorName, targetPosition, motorPosition,
               ABS(targetPosition - motorPosition));
      SCWrite(pCon, pBueffel, eValue);
    }
    status = LLDnodePtr2Next(self->motorTable);
  }
}

/*------------------------------------------------------------------------*/
static void tableSetPar(pMotor pMot, char *name, float value)
{
  ObPar *ob = NULL;

  ob = ObParFind(pMot->ParArray, name);
  assert(ob != NULL);
  ob->fVal = value;
}

/*------------------------------------------------------------------------
 * Before I can drive the motors, I have to release the software limits..
 -------------------------------------------------------------------------*/
static void liberateMotors(pTableDrive self, SConnection * pCon)
{
  int status;
  tdMotor moti;
  float upper, lower;

  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    MotorGetPar(moti.pMot, "hardupperlim", &upper);
    MotorGetPar(moti.pMot, "hardlowerlim", &lower);

    tableSetPar(moti.pMot, "softupperlim", upper);
    tableSetPar(moti.pMot, "softlowerlim", lower);
    status = LLDnodePtr2Next(self->motorTable);
  }
}

/*------------------------------------------------------------------------
 * after driving the motors, we have to set the software limits in order
 * to prevent the user from manually crashing components 
------------------------------------------------------------------------ */
static void closeMotors(pTableDrive self, SConnection * pCon)
{
  int status;
  tdMotor moti;
  tdEntry tdLower, tdUpper;
  float upper, lower, diff, targetCount;

  targetCount = floor(self->currentPosition);
  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    findBracket(moti, self->currentPosition, &tdUpper, &tdLower);
    diff = tdUpper.upper - tdLower.upper;
    upper = tdLower.upper + (self->currentPosition - targetCount) * diff;
    diff = tdUpper.lower - tdLower.lower;
    lower = tdLower.lower + (self->currentPosition - targetCount) * diff;
    tableSetPar(moti.pMot, "softupperlim", upper);
    tableSetPar(moti.pMot, "softlowerlim", lower);
    status = LLDnodePtr2Next(self->motorTable);
  }
}

/*------------------------------------------------------------------------*/
static float calculateNextStep(pTableDrive self)
{
  float step;

  step = self->targetPosition - self->currentPosition;
  if (ABS(step) > 1.) {
    step = SIGN(step) * 1.0;
  }
  return step;
}

/*------------------------------------------------------------------------*/
static int runToNextStep(pTableDrive self, SConnection * pCon, float value)
{
  int status;
  tdMotor moti;
  float target;
  long motID;

  self->groupID = GetTaskGroupID(pServ->pTasker);
  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    target = findTarget(moti, value);
    motID = StartDriveTask(moti.pMot,pCon,moti.motorName,target);
    if(motID < 0){
      return HWFault;
    }
    AddTaskToGroup(pServ->pTasker,motID,self->groupID);
    status = LLDnodePtr2Next(self->motorTable);
  }
  return 1;
}

/*------------------------------------------------------------------------*/
static int checkRunning(pTableDrive self, SConnection * pCon)
{
  int status;
  int test;
  tdMotor moti;

  if(isTaskGroupRunning(pServ->pTasker,self->groupID)){
    return HWBusy;
  } 
  return HWIdle;
}
/*------------------------------------------------------------------------*/
static void showPositions(pTableDrive self, SConnection * pCon,
                          char *pBueffel, int buffLen)
{
  float value;
  int status, test, len;
  tdMotor moti;

  value = TableDriveGetValue(self, pCon);
  snprintf(pBueffel, buffLen, " %8.2f : ", value);

  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    test = MotorGetSoftPosition(moti.pMot, pCon, &value);
    len = strlen(pBueffel);
    snprintf(&pBueffel[len], buffLen - len,
             "%s = %8.2f ", moti.motorName, value);
    status = LLDnodePtr2Next(self->motorTable);
  }
}

/*-------------------------------------------------------------------------
 * This is the old scheme: drive table positions tablestep by tablestep.
 * I leave this code here: may be it is useful in another scenario then
 * MARS. Then some means must be found to select the table drive 
 * algorithm.
 * -----------------------------------------------------------------------*/
static int TableDriveCheckStatusOld(void *pData, SConnection * pCon)
{
  pTableDrive self = (pTableDrive) pData;
  int status;
  float step;
  char pBueffel[1024];

  switch (self->state) {
  case STARTING:
    /*
     * make sure that our current position is up-to-date
     */
    TableDriveGetValue(self, pCon);
    liberateMotors(self, pCon);
    self->state = STEPPING;
    return HWBusy;
    break;
  case STEPPING:
    step = calculateNextStep(self);
    status = runToNextStep(self, pCon, self->currentPosition + step);
    if (status == OKOK) {
      if (ABS(step) < 1.) {
        self->state = WAITFINISH;
      } else {
        self->state = WAITING;
      }
      return HWBusy;
    } else {
      TableDriveHalt(self);
      return HWFault;
    }
    break;
  case WAITING:
    status = checkRunning(self, pCon);
    switch (status) {
    case HWIdle:
    case OKOK:
      self->state = STEPPING;
      if (self->debug > 0) {
        showPositions(self, pCon, pBueffel, 1023);
        SCWrite(pCon, pBueffel, eValue);
      } else {
        self->currentPosition += calculateNextStep(self);
      }
      return HWBusy;
      break;
    case HWBusy:
      return HWBusy;
      break;
    default:
      TableDriveHalt(self);
      self->state = WAITFINISH;
      return HWBusy;
      break;
    }
  case WAITFINISH:
    status = checkRunning(self, pCon);
    if (status != HWBusy) {
      TableDriveGetValue(self, pCon);
      closeMotors(self, pCon);
      self->state = IDLE;
      return status;
    } else {
      return HWBusy;
    }
    break;
  case OUTOFSYNC:
    SCWrite(pCon, "WARNING: tabledrive out of sync", eWarning);
    self->state = IDLE;
    return HWFault;
    break;
  default:
    SCWrite(pCon,
            "ERROR: programming error in tabledrive, invalid state",
            eError);
    return HWFault;
    break;
  }
  return HWFault;
}

/*===========================================================================
 * This is code for the new scheme of driving MARS tables:
 * 1) drive ds and as to 0
 * 2) drive all other motors to targets directly
 * 3) drive ds and as to desired positions.
 * =========================================================================*/
#define WAITDSAS  110
#define WAITOTHER 111
#define ASDSTO0      200
#define ASDSTOTARGET 201
/*-------------------------------------------------------------------------*/
static int startASDS(pTableDrive self, SConnection * pCon, int target)
{
  tdMotor moti;
  int status;
  char name[132];
  float targetValue;
  long motID;

  if (self == NULL || self->motorTable < 0) {
    return 0;
  }

  self->groupID = GetTaskGroupID(pServ->pTasker);
  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    strlcpy(name, moti.motorName, 131);
    strtolower(name);
    if (strstr(name, "ds") != NULL || strstr(name, "as") != NULL) {
      if (target == ASDSTO0) {
        targetValue = 0.0;
      } else {
        targetValue = findTarget(moti, self->targetPosition);
      }
      motID = StartDriveTask(moti.pMot,pCon,moti.motorName,targetValue);
      if(motID < 0){
	return HWFault;
      }
      AddTaskToGroup(pServ->pTasker,motID,self->groupID);

    }
    status = LLDnodePtr2Next(self->motorTable);
  }
  return OKOK;
}

/*-------------------------------------------------------------------------*/
static int startOthers(pTableDrive self, SConnection * pCon)
{
  tdMotor moti;
  int status;
  char name[132];
  float targetValue;
  long motID;

  if (self == NULL || self->motorTable < 0) {
    return 0;
  }

  self->groupID = GetTaskGroupID(pServ->pTasker);
  status = LLDnodePtr2First(self->motorTable);
  while (status != 0) {
    LLDnodeDataTo(self->motorTable, &moti);
    strlcpy(name, moti.motorName, 131);
    strtolower(name);
    if (strstr(name, "ds") == NULL && strstr(name, "as") == NULL) {
      targetValue = findTarget(moti, self->targetPosition);
      motID = StartDriveTask(moti.pMot,pCon,moti.motorName,targetValue);
      if(motID < 0){
	return HWFault;
      }
      AddTaskToGroup(pServ->pTasker,motID,self->groupID);
    }
    status = LLDnodePtr2Next(self->motorTable);
  }
  return OKOK;
}

/*-------------------------------------------------------------------------*/
static int TableDriveCheckStatus(void *pData, SConnection * pCon)
{
  pTableDrive self = (pTableDrive) pData;
  int status;
  float step;
  char pBueffel[1024];

  switch (self->state) {
  case STARTING:
    /*
     * make sure that our current position is up-to-date
     */
    TableDriveGetValue(self, pCon);
    liberateMotors(self, pCon);
    status = startASDS(self, pCon, ASDSTO0);
    if (status != OKOK) {
      TableDriveHalt(self);
      return HWFault;
    }
    self->state = WAITDSAS;
    return HWBusy;
    break;
  case WAITDSAS:
    status = checkRunning(self, pCon);
    switch (status) {
    case HWIdle:
    case OKOK:
      if (self->debug > 0) {
        showPositions(self, pCon, pBueffel, 1023);
        SCWrite(pCon, pBueffel, eValue);
      }
      status = startOthers(self, pCon);
      if (status != OKOK) {
        TableDriveHalt(self);
        return HWFault;
      }
      self->state = WAITOTHER;
      return HWBusy;
      break;
    case HWBusy:
      return HWBusy;
      break;
    default:
      TableDriveHalt(self);
      self->state = WAITFINISH;
      return HWBusy;
      break;
    }
    break;
  case WAITOTHER:
    status = checkRunning(self, pCon);
    switch (status) {
    case HWIdle:
    case OKOK:
      if (self->debug > 0) {
        showPositions(self, pCon, pBueffel, 1023);
        SCWrite(pCon, pBueffel, eValue);
      }
      status = startASDS(self, pCon, ASDSTOTARGET);
      if (status != OKOK) {
        TableDriveHalt(self);
        return HWFault;
      }
      self->state = WAITFINISH;
      return HWBusy;
      break;
    case HWBusy:
      return HWBusy;
      break;
    default:
      TableDriveHalt(self);
      self->state = WAITFINISH;
      return HWBusy;
      break;
    }
    break;
  case WAITFINISH:
    status = checkRunning(self, pCon);
    if (status != HWBusy) {
      if (self->debug > 0) {
        showPositions(self, pCon, pBueffel, 1023);
        SCWrite(pCon, pBueffel, eValue);
      }
      TableDriveGetValue(self, pCon);
      closeMotors(self, pCon);
      self->state = IDLE;
      return status;
    } else {
      return HWBusy;
    }
    break;
  case OUTOFSYNC:
    SCWrite(pCon, "WARNING: tabledrive out of sync", eWarning);
    self->state = IDLE;
    return HWFault;
    break;
  default:
    SCWrite(pCon,
            "ERROR: programming error in tabledrive, invalid state",
            eError);
    return HWFault;
    break;
  }
  return HWFault;
}

/*================== live and death ========================================*/
static void *TableDriveInterface(void *pData, int ID)
{
  pTableDrive self = (pTableDrive) pData;
  if (self == NULL) {
    return NULL;
  }

  if (ID == DRIVEID) {
    return self->pDriv;
  } else {
    return NULL;
  }
}

/*--------------------------------------------------------------------------*/
static void clearTable(int table)
{
  int status;
  tdMotor moti;

  status = LLDnodePtr2First(table);
  while (status != 0) {
    LLDnodeDataTo(table, &moti);
    LLDdelete(moti.table);
    status = LLDnodePtr2Next(table);
  }
}

/*------------------------------------------------------------------------*/
static int loadTable(pTableDrive self, char *filename, SConnection * pCon)
{
  FILE *fd = NULL;
  tdMotor moti;
  tdEntry entry;
  char pBueffel[512];
  int lineCount, tableCount = -1, read;

  if (self->motorTable >= 0) {
    clearTable(self->motorTable);
  }
  fd = fopen(filename, "r");
  if (fd == NULL) {
    snprintf(pBueffel, 511, "ERROR: failed to open %s for reading",
             filename);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }
  self->motorTable = LLDcreate(sizeof(tdMotor));
  if (self->motorTable < 0) {
    SCWrite(pCon, "ERROR: failed to allocate motor table", eError);
    fclose(fd);
    return 0;
  }

  while (fgets(pBueffel, 511, fd) != NULL) {
    if (pBueffel[0] == '#') {
      strcpy(moti.motorName, trim(&pBueffel[1]));
      moti.pMot = FindCommandData(pServ->pSics, moti.motorName, "Motor");
      if (moti.pMot == NULL) {
        snprintf(pBueffel, 511, "ERROR: motor %s NOT found!",
                 moti.motorName);
        SCWrite(pCon, pBueffel, eError);
        fclose(fd);
        return 0;
      }
      moti.table = LLDcreate(sizeof(tdEntry));
      if (moti.table < 0) {
        SCWrite(pCon, "ERROR: failed to allocate table data", eError);
        fclose(fd);
        return 0;
      }
      lineCount = 1;
    } else {
      read = sscanf(pBueffel, "%lf %lf %lf", &entry.lower, &entry.position,
                    &entry.upper);
      if (read >= 3) {
        entry.tablePos = lineCount;
        LLDnodeAppendFrom(moti.table, &entry);
        lineCount++;
      } else {
        /*
         * we are on a break line
         */
        if (tableCount < 0) {
          tableCount = lineCount;
        } else {
          if (lineCount != tableCount) {
            SCWrite(pCon,
                    "ERROR: bad table file, table length mismatch",
                    eError);
            fclose(fd);
            return 0;
          }
        }
        LLDnodeAppendFrom(self->motorTable, &moti);
      }
    }
  }
  self->tableLength = tableCount - 1;
  fclose(fd);
  return 1;
}

/*-------------------------------------------------------------------------*/
static void killTableDrive(void *pData)
{
  pTableDrive self = (pTableDrive) pData;
  if (self == NULL) {
    return;
  }
  if (self->pDes != NULL) {
    DeleteDescriptor(self->pDes);
  }
  if (self->motorTable >= 0) {
    clearTable(self->motorTable);
    LLDdelete(self->motorTable);
    self->motorTable = -1;
  }
  if (self->pDriv != NULL) {
    free(self->pDriv);
  }
  free(self);
}

/*-------------------------------------------------------------------------*/
int TableDriveFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
                      int argc, char *argv[])
{
  pTableDrive pNew = NULL;
  char pBueffel[256];
  int status;

  if (argc < 3) {
    SCWrite(pCon, "ERROR: insufficient parameters to TableDriveFactory",
            eError);
    return 0;
  }

  pNew = (pTableDrive) malloc(sizeof(TableDrive));
  if (pNew == NULL) {
    SCWrite(pCon, "ERROT: out of memory creating table drive", eError);
    return 0;
  }
  memset(pNew, 0, sizeof(TableDrive));

  pNew->pDes = CreateDescriptor("TableDrive");
  pNew->pDriv = CreateDrivableInterface();
  if (pNew->pDes == NULL || pNew->pDriv == NULL) {
    SCWrite(pCon, "ERROT: out of memory creating table drive", eError);
    return 0;
  }
  pNew->pDes->GetInterface = TableDriveInterface;
  pNew->pDriv->CheckLimits = TableDriveCheckLimits;
  pNew->pDriv->CheckStatus = TableDriveCheckStatus;
  pNew->pDriv->GetValue = TableDriveGetValue;
  pNew->pDriv->Halt = TableDriveHalt;
  pNew->pDriv->SetValue = TableDriveSetValue;
  pNew->motorTable = -1;
  pNew->state = OUTOFSYNC;
  pNew->tableLength = 0;
  pNew->targetPosition = 0;
  pNew->oriInvalid = 1;

  if (!loadTable(pNew, argv[2], pCon)) {
    killTableDrive(pNew);
    return 0;
  }
  pNew->state = IDLE;

  status = AddCommand(pSics, argv[1],
                      TableDriveAction, killTableDrive, pNew);
  if (!status) {
    snprintf(pBueffel, 256, "ERROR: duplicate command %s not created",
             argv[1]);
    SCWrite(pCon, pBueffel, eError);
    killTableDrive(pNew);
    return 0;
  }
  return 1;
}

/*====================== interpreter interface ============================*/
int TableDriveAction(SConnection * pCon, SicsInterp * pSics, void *pData,
                     int argc, char *argv[])
{
  pTableDrive self = (pTableDrive) pData;
  int status;
  char pBueffel[1024];
  float value;
  pDynString print = NULL;

  if (argc < 2) {
    value = TableDriveGetValue(self, pCon);
    snprintf(pBueffel, 255, "%s = %f", argv[0], value);
    SCWrite(pCon, pBueffel, eValue);
    if (value > -999) {
      return 1;
    } else {
      return 0;
    }
  } else {
    strtolower(argv[1]);
    if (strcmp(argv[1], "load") == 0) {
      if (!SCMatchRights(pCon, usMugger)) {
        return 0;
      }
      if (argc < 3) {
        SCWrite(pCon, "ERROR: require filename to load", eError);
        return 0;
      }
      status = loadTable(self, argv[2], pCon);
      if (status == 1) {
        SCSendOK(pCon);
      }
      return status;
    } else if (strcmp(argv[1], "show") == 0) {
      SCStartBuffering(pCon);	
      showPositions(self, pCon, pBueffel, 1023);
      SCWrite(pCon, pBueffel, eValue);
      print = SCEndBuffering(pCon);
      if(print != NULL){
    	  SCWrite(pCon,GetCharArray(print), eValue);
      }
      return 1;
    } else if (strcmp(argv[1], "info") == 0) {
      SCStartBuffering(pCon);
      tableInfo(self, pCon);
      print = SCEndBuffering(pCon);
      if (print != NULL) {
        SCWrite(pCon, GetCharArray(print), eValue);
      }
      return 1;
    } else if (strcmp(argv[1], "orient") == 0) {
      if (argc > 2) {
        if (!SCMatchRights(pCon, usMugger)) {
          return 0;
        }
        strlcpy(self->orientMotor, argv[2], 80);
        self->oriInvalid = 1;
        SCSendOK(pCon);
        return 1;
      } else {
        snprintf(pBueffel, 255, "%s.orient = %s",
                 argv[0], self->orientMotor);
        SCWrite(pCon, pBueffel, eValue);
        return 1;
      }
    } else if (strcmp(argv[1], "debug") == 0) {
      if (self->debug == 0) {
        self->debug = 1;
      } else {
        self->debug = 0;
      }
      SCSendOK(pCon);
      return 1;
    } else {
      SCWrite(pCon, "ERROR: sub command not understood", eError);
      return 0;
    }
  }
  return 1;
}
