// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: comserv.cpp 
// Compiler Used: MSVC40, egcs-2.90.29, HP CPP 10.24
// Produced By: Doug Gaer   
// File Creation Date: 09/20/1999
// Date Last Modified: 06/21/2000
// Copyright (c) 2000 Dougas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBS C++ classes are copyright (c) 2000, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

The ComSock server is used to connect a stream socket to a
serial port.
*/
// ----------------------------------------------------------- // 
#include <iostream.h>
#include <stdlib.h>
#include "comfile.h"
#include "systime.h"

// --------------------------------------------------------------
// Constants 
// --------------------------------------------------------------
const double comservVersionNumber = 1034.101;
const char *comservProgramName = "ComSock Server";
const int __RESTART_SERVER__ = -1;
const int __SHUTDOWN_SERVER__ = 0;
// --------------------------------------------------------------

int CopyStreamSocketToSerialPort(vbsCommFileCache *dev, int port,
				 char *tty_device, int baud)
// Copies data from a stream socket to the memory cache
// and flushes the cache buffers to a disk file.
{
  vbStream server;
  vbSerialComm serial_port;
  unsigned byte_count = 0;

  // Initialize the serial port
  int status = serial_port.OpenSerialPort(tty_device);
  if(status < 0) {
    cout << "Cannot open " << tty_device << endl;
    cout << serial_port.SerialCommExceptionMessage() << endl;
    cout << "Exiting..." << endl;
    return __SHUTDOWN_SERVER__;
  }
      
  if(status == vbSerialComm::scommREAD_ONLY) {
    cout << endl;
    cout << tty_device << " is open for read only access." << endl;
    cout << "You do not have permission to write to this device!" << endl;
    return __SHUTDOWN_SERVER__;
  }

  serial_port.SetBaudRate(baud);
  if(serial_port.InitSerialPort() < 0) {
    cout << "Cannot initalize " << tty_device << endl;
    cout << serial_port.SerialCommExceptionMessage() << endl;
    return __SHUTDOWN_SERVER__;
  }

  if(server.StreamServer(port) != 0) {
    cout << server.SocketExceptionMessage() << endl;
    return __SHUTDOWN_SERVER__;
  }
  
  // Get the host name assigned to this machine
  char hostname[vbsMAX_NAME_LEN];
  if(server.HostName(hostname) != 0) {
    cout << server.SocketExceptionMessage() << endl;
    return __SHUTDOWN_SERVER__;
  }
  
  cout << "Opening comsock stream server on host " << hostname << endl;
  cout << "Listening on port " << port << endl;
  cout << "Writing to " << tty_device << " at " << baud << ":N,8,1" << endl;

  SysTime systime; // Systime object used for logging functions
 
 
  cout << "Listening on port " << port << endl;
  vbsSocket_t remote_socket = server.Accept();

  if(remote_socket < 0) {
    cout << server.SocketExceptionMessage() << endl;
    return __SHUTDOWN_SERVER__;
  }

  char client_name[vbsMAX_NAME_LEN]; int r_port = -1;

  while(1) {
    // Read the block following a client connection
    vbBlockHeader vb;
    if(server.ReadClientHeader(vb) != 0) {
      cout << server.SocketExceptionMessage() << endl;
      return __RESTART_SERVER__;     
    }

    // Read the status byte to determine what to do with this block
    __ULWORD__ block_status = vb.Status;
    __SBYTE__ status = (__SBYTE__)((block_status & 0xFF00)>>8);

    if(status == vbKillServer) {
      dev->Flush(); // Flush all the cache buffers before exiting
      return __SHUTDOWN_SERVER__;
    }

    switch(status) { 
      // Process each block of data
      case vbSendBlock : {
	server.GetClientInfo(client_name, r_port);
	cout << "Reading " << vb.Length << " bytes from " << client_name 
	     << " remote port " << r_port << endl << flush;
	
	if(!dev->CopyStreamSocketToSerialPort(&server, &serial_port, vb)) {
	  cout << "Error copying from port " << port << " to "
	       << tty_device << endl << flush;
	  return __RESTART_SERVER__;
	}

	byte_count += (__ULWORD__)vb.Length;
	break;
      }
      case vbCloseConnection :
	cout << "Client closed connection at: " << systime.GetSystemDateTime()
	     << endl << flush;
	dev->Flush(); // Flush all the cache buffers before closing
	return __RESTART_SERVER__;

	cout << byte_count << " bytes written to " << tty_device
	     << endl << flush;
	byte_count = 0;
	server.CloseRemoteSocket();
	cout << "Comm server listening on port " << port << endl << flush;
	remote_socket = server.Accept();
	if(remote_socket < 0) {
	  cout << server.SocketExceptionMessage() << endl;
	  return __RESTART_SERVER__;
	}
	break;

      default:
	cout << "Only accepting raw data blocks" << endl;
	cout << "The \"" << status << "\" command was rejected" << endl;
	server.CloseRemoteSocket();
	break;
    }
  }

  dev->Flush(); // Flush all the cache buffers before exiting
  return __SHUTDOWN_SERVER__;
}

// Programs main thread of execution
int main(int argc, char **argv)
{
  // Check arguments 
  if(argc != 4) {
    cout << endl;
    cout.setf(ios::showpoint | ios::fixed);
    cout.precision(3);
    cout << comservProgramName << " version " << comservVersionNumber << endl;
    cout << "Usage: " << argv[0] << " port_number tty_device baud_rate"
	 << endl;
    cout << endl;
    cout << "port_number = The port number used to listen for requests."
	 << endl;
    cout << "tty_device  = The serial port to write received data to."
	 << endl; 
    cout << "baud_rate   = The serial port speed that will be used."
	 << endl;
    cout << endl;
    return 0;
  }

  unsigned short port = (unsigned short) atoi(argv[1]);
  int short baud = (unsigned short) atoi(argv[3]);
  char *tty_device = argv[2];
  
  vbsCommFileCache dev(__CACHE_SIZE__); // Device cache used to process a file
  SysTime systime;
  int rv = 1;
  cout << endl;
  cout << "Starting the " << comservProgramName << " server at: "
       << systime.GetSystemDateTime() << endl;
  
  while(rv != __SHUTDOWN_SERVER__) {
    rv = CopyStreamSocketToSerialPort(&dev, port, tty_device, baud);
    if(rv == __RESTART_SERVER__) cout << "Restarting the server at: "
				      << systime.GetSystemDateTime() << endl;
  }
  
  cout << "Shutting down the server at: " << systime.GetSystemDateTime()
       << endl;
  cout << endl;
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //