/*
 *  Copyright (c) 1998 Regents of the University of California.
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. All advertising materials mentioning features or use of this software
 *     must display the following acknowledgment:
 *       This product includes software developed by the Imaging and
 *       Distributed Collaboration Group at Lawrence Berkeley 
 *       National Laboratory.
 *  4. Neither the name of the University nor of the Laboratory may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 * 
 *  @(#) $Header: devserv.cpp,v 0.4.3 98/20/10 12:07:23 mperry Exp $ (LBL)
 */

#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include <assert.h>
#include "udpnet.h"
#include "ipnet.h"
#ifdef SSL
#include "ssltcp.h"
#include "SecurityInfo.h"
#endif
#include "serialport.h"
#include "sonyevid30.h"
#include "panasonicwjmx50.h"
// The Panasonic WJ-MX20 is not implemented yet.
//#include "panasonicwjmx20.h"
#include "canonvcc3.h"
#include "canonvcc1.h" 

#ifdef CIF
#include "CIF.H"
#endif

#define MAX_BUFSIZE		2000
#define SECS_ELAPSED  	450	

typedef struct config *CONFIGPTR;
typedef struct config {
	char port[60];
	char make[30];
	char model[30];
	int devnum;
	char type[10];
	int unitnum;
	short lens;
	Device *objptr;
	CONFIGPTR next;
} CONFIG;

typedef struct ports *PORTLISTPTR;
typedef struct ports {
	char port[80];
	Serialport *portptr;
	PORTLISTPTR next;
} PORTS;
	
// These probably should be List objects--change in later version.
CONFIGPTR configptr = NULL;
PORTLISTPTR portlistptr = NULL;

/* powerOff and debugLevel can be set by user in command line arg.
   If user doesn't want devices to be powered off at program termination,
   powerOff is set to 0.  
   debugLevel: 0:  no display at all;  
	1: display initialization/termination status and error messages;
	2: display network stuff 
	3: display device communication
	4: display all of the above
*/
	     
short powerOff=1; 
short debugLevel=1;
short udpport=5555, ipport=5556;
char ipaddr[40], cif_uumaddr[16], cif_udpport[8], cif_uumport[8];
char timestamp[25], devserver[80], video[30], text[80], video_host[80];
char lastcmd[256];
long starttime;

void sighandler(int sig);
void doHardwareSetup();
void readConfigFile(short flag);
void insertDevice(char *line);
void insertPort(char *port);
void doDefaults();
void setAddrs(char *buf);
void setVideoAddr(char *buf);
void setText(char *buf);
void setDeviceHost(char *buf);
int timeElapsed(long secs);
void processRequest(char *msg, int len, short cif_flag, short ip_flag);
int processLine(char *line,char *time,short *flag,short cif_flag,short ip_flag);
void sendDescription(short cif_flag, short ip_flag);
void formatDescription(char *desc, short cif_flag);
void createDevObject();
Device *getDevice(char *s, int unit);
void tellAllDevices(char *cmd);
void usage();

// Instantiate unicast (UDP) and multicast (IP) objects. 
UDPNet udp;
IPNet ip; 

// If SSL is supported, instantiate an SSLtcp object.
#ifdef SSL
SSLtcp ssltcp;
#endif

#ifdef CIF
  int timeout = 0;
  long cif_starttime;
  CIF_Listener *cif_listener[2];
  CIF_Connection *cif_conn[2];
  CIF_Bool rc;
  CIF_Byte buf[MAX_BUFSIZE];
  char url[80];
  CIF_Message message;
#endif


int main(int argc, char *argv[])
{
  int n=1;  
  short video_flag=0, ipsock=1, udpsock=0, ssl=1;
  char *msg; 
#ifdef WIN32
  WSADATA WSAData;
#endif

  signal(SIGINT, sighandler);
  signal(SIGTERM, sighandler);
  signal(SIGABRT, sighandler);
  signal(SIGSEGV, sighandler);
#ifdef SIGBUS
  signal(SIGBUS, sighandler);
#endif
#ifdef SIGHUP
  signal(SIGHUP, sighandler);
#endif
#ifdef SIGQUIT
  signal(SIGQUIT, sighandler);
#endif
#ifdef SIGPIPE
  signal(SIGPIPE, sighandler);
#endif

  strcpy(video, "0.0.0.0/0/0");
  text[0] = ' ';
  text[1] = '\0';
  udp.getLocalname(video_host);
  lastcmd[0] = '\0';

  while (n < (argc-1))
  {
      if (!strcmp(argv[n], "-d"))
	  debugLevel = atoi(argv[++n]);
      else if (!strcmp(argv[n], "-s"))
      {
	n++;
	if ((argv[n][0]=='n') || (argv[n][0]=='N'))
	    powerOff = 0;
      }
      else if (!strcmp(argv[n], "-t"))
	ip.setTTL(atoi(argv[++n]));
      else if (!strcmp(argv[n],"-v")||!strcmp(argv[n],"-video")) 
      {	
	strcpy(video, argv[++n]);
	video_flag = 1;
      }
      else if (!strcmp(argv[n], "-h"))
	strcpy(video_host, argv[++n]);
      else
	  usage();
      n++;
  }
      	
  udp.setDebugLevel(debugLevel);
  ip.setDebugLevel(debugLevel);

#ifdef SSL
  ssltcp.setDebugLevel(debugLevel); 
#endif

  strcpy(ipaddr, "224.35.36.37");
  strcpy(cif_udpport, "7775");
  strcpy(cif_uumaddr, "224.35.36.37");
  strcpy(cif_uumport, "7776");

  readConfigFile(video_flag);
  doHardwareSetup();
  ip.setAddr(ipaddr);
  ip.setPort(ipport);
  udp.setPort(udpport);

  if ((debugLevel==2) || (debugLevel==4)) 
  {
    printf("DEVSERV: network addresses are:\n\t");
    printf("udpport: %d, ipport: %d, cif_udpport: %s, cif_uumport: %s\n\t",
		udpport,ipport,cif_udpport,cif_uumport);
    printf("ipaddr: %s, cif_uumaddr: %s\n",ipaddr, cif_uumaddr);
  }
    
#ifdef WIN32
  n = WSAStartup(MAKEWORD(2,0), &WSAData);
#endif

#ifdef SSL
  n = ssltcp.openSocket();
  if (n < 0)
  {
     if (debugLevel > 0)  	
		fprintf(stderr,"DEVSERV: ssltcp.openSocket() failed\n");
     ssl = 0;	
  }
#endif

  if (ssl)
  {
      n = udp.openSocket();
      if (n < 0)
      {
     	if (debugLevel > 0)  	
			fprintf(stderr,"DEVSERV: udp.openSocket() failed\n");
      }
      else
        udpsock = 1;
  }

  n = ip.openSocket();
  if (n < 0)
  {
     if (debugLevel > 0)  	
		fprintf(stderr,"DEVSERV: IP multicast failed on openSocket()\n");
     ipsock = 0;	
  }
  if (udpsock && ipsock)
  {
     if (debugLevel > 0)  	
        fprintf(stderr, "DEVSERV: OPENED UDP AND IP SOCKETS\n"); 
  }
  else
  {
#ifndef CIF
  exit(1); 
#endif
  }
 
  /* If we support CIF communication, instantiate Listener,
     Connection, Message and other needed objects.  Then open
     a uum (multicast) connection.  */ 
#ifdef CIF
  if ((debugLevel==2) || (debugLevel==4))
     printf("devserv: CIF version\n");
  CIF::activate();
  strcpy(url, "x-cif-uum://");
  strcat(url, cif_uumaddr);
  strcat(url, ":");
  strcat(url, cif_uumport);
  cif_conn[0] = CIF_Factory::get_Connection(url);
  assert(CIF::status == CIF::STATUS_SUCCESS);
  assert(cif_conn[0] != CIF_NULL);
  if ((debugLevel==2) || (debugLevel==4))
     printf("devserv: CIF multicast connection established for URL: %s\n",url);

  strcpy(url, "x-cif-udp://");
  strcat(url, cif_udpport);
  cif_listener[0] = CIF_Factory::get_Listener(url);
  assert(CIF::status == CIF::STATUS_SUCCESS);
  assert(cif_listener[0] != CIF_NULL);
  if ((debugLevel==2) || (debugLevel==4))
     printf("devserv: Listening at %s\n\n", cif_listener[0]->get_URL()); 
#endif 
     
  msg = new char[MAX_BUFSIZE];
  assert(msg != NULL);

/* Get the starting time and store the number of seconds.  Each pass  
   of the while loop, get the current time.  If 7.5 minutes have 
   elapsed, send a description message.  */ 

#ifndef WIN32
  struct timeval t;
  struct timezone tz;
  gettimeofday(&t, &tz);
  starttime = t.tv_sec;
#else
  struct _timeb t;
  _ftime(&t);
  starttime = t.time;
#endif

#ifdef CIF
  cif_starttime = starttime;
#endif

  /* Initialize the timestamp  */
  strcpy(timestamp, "0");
    
  while (1)
  {  
    /* If the number of SECS_ELAPSED have elapsed, send description message. */
    if (timeElapsed(starttime))
		sendDescription(0, ipsock);
#ifdef CIF
    if (timeElapsed(cif_starttime))
        sendDescription(1, ipsock);
#endif

#ifdef SSL
    if (ssl)
    {
		n = ssltcp.doAccept();
		ssltcp.closeSockets();
    } 	
#endif

    /* If we have a udp socket (udpsock is 1), receive request over UDP.
       If we support CIF, see and there's a CIF udp connection, receive 
	   the CIF UDP request.   */
 
    if (udpsock)
    {  	
      memset(msg, 0, MAX_BUFSIZE); 
      if ((debugLevel==2) || (debugLevel==4))
        printf("devserv: calling udp.doRecv()\n");
      n = udp.doRecv(msg, (long)MAX_BUFSIZE);
      if ((debugLevel==2) || (debugLevel==4))
         printf("DEVSERV: udp.doRecv returned: %d\n", n); 
      if ((n > 0) && ((debugLevel==2) || (debugLevel==4)))
         printf("DEVSERV: REQUEST: %s\n", msg); 
      if ((n > 0) && (n < MAX_BUFSIZE))
  		processRequest(msg, n, 0, ipsock);
      else if ((n < 0) && (debugLevel > 0))
		printf("DEVSERV: udp.doRecv() failed\n"); 
    }  

#ifdef CIF
    cif_conn[1] = cif_listener[0]->get_Connection(timeout);
    // Give the CPU to some other process.
    #ifdef WIN32
    Sleep(200);		
    #else
    usleep(200);
    #endif
    if (cif_conn[1] != CIF_NULL)
    {
       rc = cif_conn[1]->receive(message, 5000);
       if (rc == true)
       {
          if ((debugLevel==2) || (debugLevel==4))
             printf("devserv: CIF MSG: %s\n", message.get_Data());
		  sprintf(msg, "%s", message.get_Data());
		  int num = message.get_Info().size; 
		  processRequest(msg, num, 1, ipsock);
       }
       cif_conn[1]->close();
    }
    delete cif_conn[1];
#endif       
  } 
#ifdef CIF
  cif_listener[0]->close();
  delete cif_listener[0];
  CIF::deactivate();
#endif
  delete [] msg;
  return 0;
}


void sighandler(int sig) 
{
   if (debugLevel > 0)
   {
     fprintf(stderr,"DEVSERV: SIGNAL %d\n", sig);	
     fprintf(stderr,"DEVSERV: SHUTTING DOWN ALL ATTACHED DEVICES...THIS MAY TAKE A WHILE\n");
   }
   if (powerOff)
   {
     tellAllDevices("shutdown");	
     if (debugLevel > 0)
        fprintf(stderr, "SHUTDOWN COMPLETE\n");
   }
   exit(1);
}

void doHardwareSetup()
{
  // Instantiate objects for supported devices.
  if (debugLevel > 0) 
      fprintf(stderr, "INITIALIZING DEVICES...THIS MAY TAKE A FEW SECONDS\n");
  if (configptr)
      createDevObject();
  else
  {
      if (debugLevel > 0)
      {
          fprintf(stderr,"devserv can't find a configuration file...");
		  fprintf(stderr,"using defaults\n");
      }
      doDefaults();
  }
  // Tell devices to turn on power.
  if (debugLevel > 0)
      fprintf(stderr, "TURNING ON POWER\n");
  tellAllDevices("power A 1");
  // Send all devices to home position.
  if (debugLevel > 0)
      fprintf(stderr,"SENDING DEVICES TO HOME POSITION\n");
  tellAllDevices("home");
  if (debugLevel > 0)
      fprintf(stderr,"DEVICE INITIALIZATION PROCESS FINISHED\n");
}

void readConfigFile(short flag)
{
   /* Read the configuration file; create a linked list of supported devices,
      createDevObject will instantiate an object for each device type. */

   char buf[80], label[20]; 
   char *home = getenv("HOME");
   FILE *fp; 

   strcpy(buf, home);
#ifndef WIN32
   strcat(buf, "/.devserv.config");
#else
   strcat(buf, "devconfig");
#endif
    
   fp = fopen(buf, "r");
   if (fp)
   {
	while (fgets(buf, 80, fp) != NULL)
	{
	   if ((buf[0] == '#') || (buf[0] == '\n'))
		continue;
	   sscanf(buf, "%s", label);
   	   if ((label[0]=='d')&&(label[1]=='e')&&(label[2]=='v'))
	    insertDevice(buf);
	   else if ((label[0]=='a')&&(label[1]=='d')&&(label[2]=='d')) 
	    setAddrs(buf);
	   else if (!flag&&((!strcmp(label,"video")||!strcmp(label,"video:"))))
		setVideoAddr(buf);
	   else if ((label[0]=='t')&&(label[1]=='e')&&(label[2]=='x'))
		setText(buf);
	   else if (!strcmp(label,"video_host")||!strcmp(label,"video_host:"))
	 	setDeviceHost(buf);
	}
	fclose(fp);
   }
}


void insertDevice(char *line)
{
   // Insert entry for device into front of device configuration list. 

   char buf[10], make[25], model[30], type[15], port[60];
   int devnum, unitnum;
   char lens[15];

   lens[0] = '\0';
   CONFIGPTR temp = (CONFIGPTR)malloc(sizeof(CONFIG));
   sscanf(line,"%s%s%s%d%s%d%s%s",buf,make,model,&devnum,type,&unitnum,port,lens);
   strcpy(temp->make, make);
   strcpy(temp->model, model);
   strcpy(temp->type, type); 
   strcpy(temp->port, port);
   temp->devnum = devnum;
   temp->unitnum = unitnum;  
   if ((lens[0]=='W') || (lens[0]=='w'))
	temp->lens = 1;
   else
	temp->lens = 0;
   temp->objptr = NULL; 
   temp->next = configptr;
   configptr = temp; 
   if (temp->devnum == 1)
      insertPort(port);	
}


void insertPort(char *port)
{
   // Insert port into ports list and instantiate a Serialport object.
   PORTLISTPTR temp = portlistptr;
   static int counter=0;

   /* If the port is in the list, there is either a duplicate entry
      in the config file or the user is trying to connect different
      devices to the same port.  Return in either case.*/

   while (temp)
   {
	if (!strcmp(temp->port, port))
	    return;
	temp = temp->next;
   }		
   temp = (PORTLISTPTR)malloc(sizeof(PORTS));
   strcpy(temp->port, port);
   temp->portptr = new Serialport(counter, debugLevel);
   temp->next = portlistptr;
   portlistptr = temp;
   ++counter;	
}


void doDefaults()
{ 
   Serialport *portp;
   char port[40];
   
   // This should probably be changed to prompt the user for this info.
#ifdef __sun__
	strcpy(port, "/dev/ttya");  
#endif
#ifdef __sgi 
	strcpy(port, "/dev/ttym1");
#endif
#ifdef __bsd__
	strcpy(port, "/dev/ttyd1");
#endif
#ifdef WIN32
	strcpy(port, "COM1");
#endif

   configptr = (CONFIGPTR)malloc(sizeof(CONFIG));
   assert(configptr != NULL);
   strcpy(configptr->port, port);
   strcpy(configptr->make, "Sony");
   strcpy(configptr->model,"EVI-D30");
   strcpy(configptr->type, "cam");
   configptr->devnum = 1;
   configptr->unitnum = 1;
   configptr->lens = 0; 	
   portp = new Serialport(0, debugLevel);
   configptr->objptr = new SonyEVID30(portp, port, 1, 0);
   configptr->next = NULL;
}
   

void setAddrs(char *buf)
{
   // Set the network addresses (CIF and non-CIF).
   char label[20], name[20], value[20] ;

   sscanf(buf, "%s%s%s", label, name, value);
   if (!strcmp(name,"multicastPort"))
	ipport = atoi(value);
   else if (!strcmp(name, "udpPort"))
	udpport = atoi(value);
   else if (!strcmp(name, "cifUDPPort"))
	strcpy(cif_udpport, value);
   else if (!strcmp(name, "cifUUMPort"))
	strcpy(cif_uumport, value);
   else if (!strcmp(name, "multicastAddress"))
	strcpy(ipaddr, value);
   else if (!strcmp(name, "CIFUUMAddress"))
	strcpy(cif_uumaddr, value);
}

void setVideoAddr(char *buf)
{
   // Set the video transmission address/port/ttl.
   char label[20], value[30];

   value[0] = '\0';
   sscanf(buf, "%s%s", label, value);
   if (value[0]!='\0')
   	strcpy(video, value); 
}

void setText(char *buf)
{
   // Set the text description of devserv.

   int i, j;
   for (i=0; ((buf[i] != ' ')&&(buf[i] != '\0')); i++);
   for (; buf[i]==' '; i++);
   for (j=0; ((buf[i]!='\0') && (buf[i]!='\n')); i++,j++)
	text[j] = buf[i];
   text[j] = '\0'; 
}
	
void setDeviceHost(char *buf)
{
   char host[80], label[20]; 

   /*  If video_host is our own IP address, there was no command
       line argument to override this value.  So use the entry in 
       the config file.  Otherwise, use the existing value (the 
       default or the machine name given on the command line).  */
	     
   udp.getLocalname(host);
   if (!strcmp(host, video_host))
   {
	host[0] = '\0';
	sscanf(buf, "%s%s", label, host); 
	if (host[0] != '\0')
	    strcpy(video_host, host);
   }
}
	

int timeElapsed(long secs)
{
#ifndef WIN32
   struct timeval t;
   struct timezone tz;    
   gettimeofday(&t, &tz);
   if ((t.tv_sec-secs) >= SECS_ELAPSED)
	return 1;
   else
	return 0;
#else
   struct _timeb t;
   _ftime(&t);
   if ((t.time-secs) >= SECS_ELAPSED) 
 	return 1;
   else 
 	return 0;
 #endif
}

void processRequest(char *msg, int len, short cif_flag, short ip_flag)
{
   char *temp, *cmd, *time;
   short i, j, n;

   temp = new char[80];
   cmd = new char[256]; 
   time = new char[25];

   if (!temp || !cmd || !time)
   {
	if (debugLevel > 0)
	   fprintf(stderr,"DEVSERV: CAN'T PROCESS REQUEST--OUT OF MEMORY\n");
	if (temp)
	   delete [] temp;
	if (cmd)
	   delete [] cmd;
	if (time)
	   delete [] time; 
	return;
   }
   
   /* Get line 1, the header; parse fields of header and determine further
      processing.  (Lines are separated by "#".)   */
 
   for (i=0,j=0; ((msg[i] != ' ')&&(msg[i]!='\0')); i++,j++)
	temp[j] = msg[i];
   temp[j] = '\0';

   if (len < atoi(temp))	
   {
        // We didn't get a complete message--don't process.
	if (debugLevel > 0)
	  fprintf(stderr,"DEVSERV: Received an incomplete message...ignoring it\n");  
	delete [] temp;
	delete [] cmd;
	delete [] time;
	return;
   } 

   /* If we're supporting SSL, the message is in the format:
               header#request#...#SecurityInfo
      header = length timestamp devserv_host 	
      All messages are null-terminated.
      Strip off "length" from the header and strip off SecurityInfo. 
      Call checkAccess() with 2 args:
      the original message (w/o the security info and w/o "length") and the 
      security info.
      If checkAccess returns FALSE, return (the client is not authorized).
   */ 

#ifdef SSL
   for (n=strlen(msg); ((msg[n]!='#')&&(n>0)); n--);
   msg[n] = '\0';
   for (++n, j=0; msg[n]!='\0'; n++, j++)
	temp[j] = msg[n];
   temp[j] = '\0';
   if (!checkAccess(temp, &msg[i+1]))
   {
   	delete [] temp;
	delete [] cmd;
	delete [] time; 	
	return;
   }
#endif
   
   for (++i,j=0; ((msg[i]!=' ')&&(msg[i]!='\0')); i++,j++)
        time[j] = msg[i];
   time[j] = '\0';
 
   for (++i,j=0; ((msg[i]!='#')&&(msg[i]!='\0')); i++,j++)
        devserver[j] = msg[i];
   devserver[j] = '\0';

   /* For each request, processLine() will return the device reference
      ("cam 1", "camera 2", etc) for use in description messages.  These
      device references will be concatenated if there are > 1 references. */ 
	     
   cmd[0] = '\0';
 	
   // Get line 2, the minimum number of lines in a message from a client.  
   while ((msg[i]!='\0')&&(msg[i]!='\n'))
   {
        /* Process each line. processLine() indicates whether it sent a 
           description message --call with out-parameter (&n).  */
        for (++i,j=0; ((msg[i]!='#')&&(msg[i]!='\0')); i++,j++)
	     temp[j] = msg[i];
	temp[j] = '\0';
        n = 0;

        /* If a device reference was returned by processLine, copy
           'temp' into 'cmd' if it is the first reference or add it if 
	   there were previous commands in this request.   */

        if (processLine(temp, time, &n, cif_flag, ip_flag))
	{ 
	   if (cmd[0] == '\0')
		strcpy(cmd, temp);
	   else	
	   {
		strcat(cmd, "@");
		strcat(cmd, temp);
	   }
        }
   } 
   /* If there was >= 1 device reference, update the 'lastcmd' variable. */
   if (cmd[0] != '\0')
	strcpy(lastcmd, cmd);
   if (!n)
        sendDescription(cif_flag, ip_flag);
   delete [] temp;
   delete [] cmd;
   delete [] time;
}


int processLine(char *line,char *time,short *flag,short cif_flag,short ip_flag)
{
   char *field1, *field2;
   int i,j;
   Device *obj;

   field1 = new char[15];
   field2 = new char[15];

   if (!field1 || !field2)  
   {
       if (debugLevel > 0)
	   fprintf(stderr,"DEVSERV: CAN'T PROCESS REQUEST--OUT OF MEMORY\n");
       if (field1)
	   delete [] field1;
       if (field2)
	   delete [] field2;
       return -1;
   } 
   	
   /* The first two fields are either a device and number (e.g., "cam 1" or
      "camera 1") or a request to send a description.  If it is a 
      device, return these values to caller.  */  

   for (i=0,j=0; line[i]!=' '; i++,j++)
	field1[j] = line[i];
   field1[j] = '\0';

   for (++i,j=0; ((line[i]!=' ')&&(line[i]!='\0')); i++,j++)
	field2[j] = line[i];
   field2[j] = '\0'; 	

   if (!strcmp(field1, "devserv"))
   {
	if (!strcmp(field2, "description"))
	{
	    // multicast description or status message 
            sendDescription(cif_flag, ip_flag);
	    *flag = 1;
	    delete [] field1;
	    delete [] field2;
  	    return 0; 
        }
   }

   /* Return if timestamp in current request is not greater than
      timestamp of last request accepted.  */

   if ((double)atof(time) < (double)atof(timestamp))
   {
	if (debugLevel > 0)
	    fprintf(stderr,"DEVSERV: Rejecting request...old timestamp\n");	
	delete [] field1;
	delete [] field2;
	return 0;
   }
   strcpy(timestamp, time);	

   // Call function to return a pointer to an object, based on the
   // client handle. Then tell the object to move. 
  
   obj = getDevice(field1, atoi(field2));
   if (obj)
       obj->move(&line[++i]); 

   sprintf(line, "%s %s", field1, field2);  
   delete [] field1;
   delete [] field2;
   return 1;
}	


void sendDescription(short cif_flag, short ip_flag)
{
   char *desc = new char[1024];

   memset(desc, 0, 1024); 
   formatDescription(desc, cif_flag);
   if (!cif_flag && ip_flag)
   {
      if ((debugLevel==2) || (debugLevel==4))
         fprintf(stderr,"devserv: calling ip.doSend for desc: %s\n", desc);	
      ip.doSend(desc, (long)strlen(desc));
   }
#ifdef CIF
   if (cif_flag)
   {
      CIF_Byte buf[1024];
      sprintf((char *)buf, "%s", desc);
      rc = cif_conn[0]->send(buf,strlen((char *)buf)+1);
      assert(rc==true);
      assert(CIF::status == CIF::STATUS_SUCCESS);
   }
#endif 
   delete [] desc;
}


void formatDescription(char *desc, short cif_flag)
{
   char *temp;
   long i,j;
   CONFIGPTR listp = configptr;
#ifndef WIN32
   struct timeval t;
   struct timezone tz;
#else
   struct _timeb t;
#endif

   temp = new char[1024];
   if (!temp)
   {
	if (debugLevel > 0)
	   fprintf(stderr,"CAN'T FORMAT DESCRIPTION--OUT OF MEMORY\n");
	return;
   } 

   /* Line 1 of message (per Camera Remote Control Command Language).

      If we're sending a non-CIF message, set starttime to the current
      time.  If we're sending a CIF message, set cif_starttime to the
      current time.  */

#ifndef WIN32
   gettimeofday(&t,&tz);
   if (!cif_flag)
      starttime = t.tv_sec;
   else
   {
      #ifdef CIF
      	cif_starttime = t.tv_sec;	
      #endif
   }
   sprintf(temp,"%ld%ld ",t.tv_sec, (t.tv_usec/1000)); 
#else
   _ftime(&t);
   if (!cif_flag)
      starttime = t.time;
   else
   {
      #ifdef CIF
         cif_starttime = t.time;
      #endif
   }
   sprintf(temp, "%d%d ", t.time, t.millitm);
#endif

   udp.getLocalname(&temp[strlen(temp)]);

   // Line 2 - message type
   strcat(temp, "#description#");

   // Line 3 - text describing this devserv (can be empty).
 	strcat(temp, text); 
    
   // Line 4 - video address/port/ttl and host with devices attached
   sprintf(&temp[strlen(temp)], "#%s %s#", video, video_host);

   // Line 5 - identification of last cmd accepted including requestor
   //          and timestamp

   strcat(temp, "\""); 
   if (lastcmd[0] == '\0')
	strcat(temp, "NONE");
   else 
        strcat(temp, lastcmd);
   strcat(temp, "\" ");
   if (!cif_flag)
       udp.getRemoteHost(&temp[strlen(temp)]);
   else
   {
       /*change this when CIF can give this info*/	
       strcpy(&temp[strlen(temp)], "CIF_client"); 
   }	
   strcat(temp, " ");
   strcat(temp, timestamp);
  
   // Status lines returned by each device
   while (listp)
   {
       sprintf(&temp[strlen(temp)],"#%s %d ",listp->type,listp->unitnum);
       if (listp->objptr)
	   listp->objptr->getDesc(&temp[strlen(temp)]);
       else
	   strcat(temp, "-1");
       listp = listp->next;
   } 
   i = strlen(temp);
   sprintf(desc, "%ld", i);
   j = strlen(desc);
   i += j;
   sprintf(desc, "%ld ", i);
   strcat(desc, temp); 
   delete [] temp;
}


void createDevObject()
{
   CONFIGPTR temp = configptr;
   PORTLISTPTR portp;

   // Instantiate one object for each hardware device.
   while (temp)
   {
       portp = portlistptr;
       // Match the port names on the hardware-configuration
       // and port lists.

       while (portp && (strcmp(temp->port,portp->port)!=0))
	   portp = portp->next;
       if (portp && portp->portptr)
       {
	   if (!strcmp(temp->make,"Sony")&&(!strcmp(temp->model,"EVI-D30")||!strcmp(temp->model,"EVI D30")))
	     temp->objptr = new SonyEVID30(portp->portptr,portp->port,temp->devnum,debugLevel,temp->lens);
	   else if (!strcmp(temp->make,"Canon")&&(!strcmp(temp->model,"VC-C3")||!strcmp(temp->model,"VC C3")))
	     temp->objptr = new CanonVCC3(portp->portptr,portp->port,temp->devnum,debugLevel,temp->lens);
	   else if (!strcmp(temp->make,"Canon")&&(!strcmp(temp->model,"VC-C1")||!strcmp(temp->model,"VC C1")))
	     temp->objptr = new CanonVCC1(portp->portptr,portp->port,temp->devnum,debugLevel,temp->lens);
	   else if (!strcmp(temp->make,"Panasonic")&&(!strcmp(temp->model,"WJ-MX50")||!strcmp(temp->model,"WJ MX50")))
	     temp->objptr = new PanasonicWJMX50(portp->portptr,portp->port,temp->devnum,debugLevel);
	   temp = temp->next;
       }

   }
   // Remove the ports list.
   portp = portlistptr;
   while (portp)
   {
	portlistptr = portlistptr->next;
	free(portp);
	portp = portlistptr;
   }
}

Device *getDevice(char *s, int unit)
{
   /* Map client handle to device object by walking linked list and
      comparing 'type' and 'unitnum' fields of structs.  Return a
      pointer to the device object associated with that type and
      unitnum.  If client handle is invalid, return NULL.  */

   CONFIGPTR temp = configptr;
   while (temp)
   {
      if ((temp->unitnum==unit)&&(temp->type[0]==s[0])&&(temp->type[1]==s[1])&&(temp->type[2]==s[2]))
         return temp->objptr;
      temp = temp->next;
   }
   return NULL;
}

void tellAllDevices(char *cmd)
{
	CONFIGPTR temp = configptr;
	while (temp)
	{
	    if (temp->objptr)
		temp->objptr->move(cmd);
	    temp = temp->next;
	}
}

void usage()
{
 fprintf(stderr,"Usage: devserv [-d <debug level>] [-s N] [-t <ttl>] ");
 fprintf(stderr,"[-h <video_host>] [-v <video_address/video_port/video_ttl]\n");
}

