/* $Id: comm.cpp 2.5 1997/10/08 10:38:34 leon Exp $
 */
#define Uses_TProgram

#define Uses_MsgBox

#include <tvision/tv.h>


#include "setup.h"

#include "serial.h"

#include "comm.h"

#include "log.h"

#include "gisel.h"

#include "translat.h"


#include <stdio.h>    // sprintf()

#include <string.h>   // strlen()

#include <ctype.h>    // isdigit()


extern "C"
{
    int getccb(void);
    int setSerial(int port, int speed, int parity, int bits, int stopBit);
    int serialOut(char byte);
    void initSerial(void);
    void closeSerial(void);
    void flushComm(void);
}


/** Translate window for Job Status **/
extern Translate *trans;
/** Current Position **/
extern long position[3];

comData settings(B9600, COM1, P_NONE, 1, 8); // overriden by .ini file



/**
 ** Creates new comm channel
 **/
Comm::Comm(TLogWindow **log)
{
  logWindow = log;
  reply = 0;
  commIsOpen = False;
  receiver = NULL;
  enqueued = False;
  dequeued = False;
  paused = False;
  depaused = False;
  relativeSpeed[0] =
  relativeSpeed[1] =
  relativeSpeed[2] = 1.0;
}

/**
 ** Cheks for presence of the reply character. This function is intended
 ** to be called from TProgram::idle()
 **/
void Comm::update()
{
  static Boolean inPausedPosition = False; // Is paused and in pause position?

  int c;
  if (dequeued)
    {
      dequeued = False;
      enqueued = False;
      depaused = False;
      if (trans)
	      {
           message(trans, evCommand, cmCancel, 0);
           messageBox( "\n\003Job dequeued", mfOKButton | mfInformation);
      	}
    }

  if ( ((c = getccb()) != -1) || depaused)
    {
      reply = c;

      if (*logWindow ) // && c != '0')

          (*logWindow)->insertErrMsg(reply);

      if (enqueued)
        {
          depaused = False;
          if (paused)
            {
              if (!inPausedPosition)
                {
                  inPausedPosition = True;
                  sendCommand("@0a0,1000,0,1000,-1600,3000,0,30");
                }
              return;
            }
          else
            {
              if (inPausedPosition)
                {
                  inPausedPosition = False;
                  sendCommand("@0a0,1000,0,1000,1600,3000,0,30");
                  return;
                }
              sendNextLine();
            }
         }

      if (trans)
        trans->update();
    }

}

/**
 ** Returns reply char and clears internal reply char;
 ** If returns 0 this means that line was empty and no reply was expected
 ** If reply is -1 there was no ack 
 **/
int Comm::getReply()
{
  if (reply > 0)
    {
      static int c;
      c = reply;
      reply = 0;
      return c;
    }
  return reply;
}

/**
 ** Returns curent queue line number
 **/
long Comm::getLineNumber()
{
  return lineNumber;
}

/**
 ** Pause queue. Returns negative number ob error.
 **/
short Comm::pauseQueue()
{
  if (paused)
    {
      messageBox("\n\003Job already paused", 
                 mfInformation | mfOKButton);
      return -1;
    }
  if (!enqueued)
    {
      messageBox("\n\003No job to pause", 
                 mfInformation | mfOKButton);
      return - 2;
    }

  paused = True;
  if (*logWindow ) 
     (*logWindow)->insertLine("\nJob is currently paused...");
  return 0;
}


/**
 ** Continue queue
 **/
short Comm::continueQueue()
{
  if (paused && enqueued)
    {
      if (*logWindow ) 
        (*logWindow)->insertLine("\n Continuing with job execution.");
      paused = False;
      depaused = True;
      return 0;
    }
  else
    messageBox("\n\003No job available to continue", 
               mfInformation | mfOKButton);
  return -1;
}


/**
 ** Sets relative speed for a given axis
 **/
void Comm::setRelativeSpeed(short axis, double factor)
{
  if (axis > 2)
    return;

  relativeSpeed[axis] = factor;
}

/**
 ** Opens buffered communication line over serial port
 ** If comm channel is alredy open it is reopened (closed and opened).
 **/
Comm::open(int port, int speed, int parity, int bits, int stopBit)
{
  if (commIsOpen == True)
    close();

  if(setSerial(port, speed, parity, bits, stopBit) != 0)
    return -1;

  initSerial();
  commIsOpen = True;
  return 0;
}

/**
 ** Opens comm channel with default settings
 **/
Comm::open()
{
  return open(settings.getComPort(), settings.getBaud(),
           settings.getParity(), settings.getDataBits(),
           settings.getStopBits());
}

/**
 ** Returns True if comm channel is open
 **/
Boolean Comm::isOpen()
{
  return commIsOpen;
}

/**
 ** Closes communication channel
 **/
void Comm::close()
{
  if (commIsOpen)
    closeSerial();
  commIsOpen = False;
}

/**
 ** Sends /count/ bytes to the communication line
 ** Appends CR at the end of line
 **/
void Comm::sendCommand(const char *s, int count)
{
  reply = -1;   // line must be acknewledged or error will occur

  flushComm();  // discard input buffer

  for(int i = 0; i < count; i++)
       serialOut( s[i] );
  serialOut('\r');
  serialOut('\n');
  if (*logWindow)
    {
      (*logWindow)->insertLine("\n");
      (*logWindow)->insertChars(s, count);
    }
}


/**
 ** Sends string to the communication line
 ** Appends CR at the end of line
 **/
void Comm::sendCommand(const char *s)
{
  sendCommand(s, strlen(s));
}

/**
 ** Sends string. When reply is available /evBroadcast/ will be
 ** send to the /owner/ view with /cmReplyReceived/.
 **/
void Comm::sendCommand(const char *s, TView *owner)
{
  receiver = owner;
  sendCommand(s, strlen(s));
}


/**
 ** Enqueues input stream. Be shure that /inStream/ lasts for 
 ** a long time (at least for time of queue).
 **/
int Comm::enqueue(istream& inStream, const char *jobName)
{

  inStream.seekg(0L);

  is = inStream;
  Comm::jobName = jobName;

  if (*logWindow)
    {
      (*logWindow)->insertLine("Starting ");
      (*logWindow)->insertLine(jobName);
    }

  lineNumber = 0;
  enqueued = True;
  sendNextLine();

  return 0;
}


/**
 ** Enqueues file 
 **/
int Comm::enqueue(const char *fileName)
{
  ifs.open(fileName);
  return enqueue(ifs);
}

/**
 ** Clears queue
 **/
void Comm::dequeue()
{
  if (enqueued)
    {
      dequeued = True;
      paused = False;
    }
}


// TODO

#define SPEEDCHANGE(axis, upperLimit)                                   \

   ( (tmpSpeed = long(double(speed[axis])*relativeSpeed[axis])) < 30 ?  \
      30 : ( tmpSpeed > upperLimit ? 4000 : tmpSpeed))

/**
 ** Sends next line from queue
 **/
void Comm::sendNextLine()
{
#define BUFFER_SIZE 100

  char cmd[BUFFER_SIZE];
  size_t cmdLen;
  while ( is.getline(cmd, BUFFER_SIZE) )
    {

      //@0m000,2000,0001,2000,0002,3000,0,30

      long pos[3], speed[3], lastArg;
      char cmdPrefix, cmdType;
      short device;
      if ( sscanf(cmd, "%c%d%c%ld,%ld,%ld,%ld,%ld,%ld,%ld",
                  &cmdPrefix, &device, &cmdType, 
                  &pos[0], &speed[0],
                  &pos[1], &speed[1],
                  &pos[2], &speed[2],
                  &lastArg
                 ) == 10 )
        {
          if (cmdPrefix == '@')
            {
              long tmpSpeed;

              switch (cmdType)
                {
                  case 'm':
                  case 'M':
                            position[0] = pos[0];
                            position[1] = pos[1];
                            position[2] = pos[2];
                  case 'a':
                  case 'A':

                    sprintf(cmd, "@%d%c%ld,%ld,%ld,%ld,%ld,%ld,0,30",
                                  device, cmdType, 
                                  pos[0], SPEEDCHANGE(0, 4000),
                                  pos[1], SPEEDCHANGE(1, 4000),
                                  pos[2], SPEEDCHANGE(2, 2000)
                          );
                    break;
                  case 'y':
                  case 'Y':
                    sprintf(cmd, "@%d%c%ld,%ld,%ld,%ld,%ld,%ld,%ld",
                                  device, cmdType, 
                                  pos[0], SPEEDCHANGE(0, 4000),
                                  pos[1], speed[1],
                                  pos[2], speed[2],
                                  lastArg);
                    break;
                }
            }
        }
      sendCommand(cmd);
      lineNumber++;
      return;
    }

  // The following lines are in effect only at end of stream


  enqueued = False;
  if (trans)
    {
      message(trans, evCommand, cmCancel, 0);
      messageBox( "\n\003Job completed", mfOKButton | mfInformation);
    }
  else
    {
      if (*logWindow)
        {
          (*logWindow)->insertLine("\n");
          (*logWindow)->insertLine(jobName);
          (*logWindow)->insertLine(" Completed.\n");
        }
    }
  return;
}


/**
 ** Checks if channel is open. If it's not it tries to open it.
 ** Then checks if it is safe for enqueuing. On any error shows messageBox()
 ** and retruns False. If there is no problem it returns True.
 **/
Boolean Comm::clearToSend()
{
  if (!commIsOpen)                                             
    {                                                                 
     if (open() != 0)                                           
       {                                                              
        messageBox("Cannot open communication channel", 
                   mfOKButton | mfError);                       
        return False;                                                       
       }
    }                                                                 
  if (enqueued)                                         
     {                                                                
        if ( messageBox( "\003Previous command has not completed!\n"
                    "\003OK to discard the previous command?",
		    mfOKCancel | mfConfirmation) == cmOK)
          {
      	     dequeued = True;
	           return True;
      	  }
       return False;                                                        
     }
  return True;
}




syntax highlighted by Code2HTML, v. 0.9.1