//  File Name:             IadsDS_SimStyle.cpp
//  Project:               IadsDS
//  Author:                Mattingly, P.
//  Date:                  Nov-2005
//  Update:                Nov-2015
//
// Symvionics Inc. ("SYMVIONICS") grants a non-exclusive license for you to use
// this source code and the accompanying documentation, if any, free of charge,
// provided that this paragraph, and the following two paragraphs appear in all
// copies and in all derivative works.  In no event shall this source code or
// this license thereto impact upon or fall within the scope of any other
// agreement that you have with Symvionics.
// 
// BY USING THIS SOFTWARE, YOU FURTHER AGREE THAT, IN NO EVENT, SHALL SYMVIONICS
// BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
// SOFTWARE, THE ACCOMPANYING DOCUMENTATION, IF ANY, OR ANY DERIVATIVE WORKS.
// YOU AGREE TO DEFEND, INDEMNIFY, AND HOLD HARMLESS SYMVIONICS FROM AND AGAINST
// ALL FINES, PENALTIES, LIABILITIES, DAMAGES, COSTS, AND EXPENSES INCURRED
// BY SYMVIONICS AS A RESULT OF YOUR USE OF THIS SOFTWARE INCLUDING THIRD PARTY
// USE OF ANY DERIVATIVE WORKS CREATED BY YOU.
// 
// SYMVIONICS SPECIFICALLY DISCLAIMS ANY AND ALL WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.  THE SOFTWARE AND ACCOMPANYING DOCUMENTATION LICENSED
// HEREUNDER ARE PROVIDED "AS IS" WITHOUT WARRANTY OR OBLIGATION OF SUPPORT OR 
// MAINTENANCE.
//
//  PURPOSE: 
//    1. This is a demonstration program for the IADS Data Source developer to show
//       what is required to send data to the IADS Server (also know as the CDS) using
//       the IADS Custom data source ethernet protocol.
//
//  NOTES:  (This is a repeat of the "readme.txt" file that comes with this
//           Developers Kit.  Also read the IADS Real-time Data Source Interface
//           document for more complete information.)
//
//  What is this Project ?
//
//    1. The IadsDS is a Microsoft Dev Studio project that contains three different programs that
//       demonstrate sending data to the IADS Server using differnet techniques with varying
//       levels of complexity. The main purpose is for training and not production. I purposely
//       left out more complex structures so as not to hinder the understanding of the key ideas
//       of making the appropriate data packets for the IADS Server and the initial handshake required.
//    2. This source is the "Simulator" style program which creates data for Time and (4) parameters
//       all at the same sample rate, typical of Simulator systems output.  We recommend starting
//       with this exmample because it is the least complex.
//    3. There are two other data source example programs in this project, they are the
//       "IadsDS_DecomStyle" and "IadsDS_BlobStyle" styles. The Decom style creates a PCM-like memory
//       frame strucutre that replicates a buffer from a typical Decom card. This is the most complex
//       example which shows how to properly interleave data of disimilar sample rates. The Blob style
//       simulates a packet or blob of data as a single parameter. It demonstrates using a differnet
//       format (protocal) to the IADS Server.
//
//  What else is there?
//
//    1. IADS Real-time Station (RT Station) - In order to do end-to-end testing you will need to
//       either have purchased RT Station or requested from SYMVIONICS the IADS Server Developers Kit
//       which contains RT Station. This program needs to be installed on the system you are developing
//       your data source on. You will run RT Station to make a connection to your data source and display
//       data with the IADS Client, which also comes with RT Station.
//    2. IADS Real-time Data Source Interface document. This document contains a description of the protocol
//       required to send data to the IADS Server. You can get this document from our Web site along with
//       this project. It is also contained in the IADS Server Developers kit.
//
//  How do I get it?
//
//    1. You get this project and the IADS Real-time Data Source Interface document from our web site: 
//       http://iads.symvionics.com/downloads/iadsDataSource.zip
//    2. You get the Data Source Developers Kit from SYMVIONICS by request.  We can either upload it to 
//       an account for you on our FTP site: iadsfiles.symvionics.com or mail it.
//       Contact Pat Mattingly at: pmattingly@symvionics.com for the kit.
//
//  What is the The IADS Server?
//
//    The IADS Server is mentioned throughout this example; it is the software component contained
//    within the RT Station program that your data source connects to.
//
//  What are the key points?
//
//    1. Determine packet frequency.  Every data source must maintain a relatively constant
//       packet frequency of 50 packets per second. Packet rates significantly slower may
//       adversely affect the display Clients in the form of a visible ‘stutter’ during screen
//       refresh. Packet frequencies greater than 100 may affect overall throughput performance
//       and degrade the IADS Servers ability to process data.
//    2. Packet Construction - The IADS Custom data source supports two types of protocols called
//       "Frame" types.  Frame type (100) uses a "Tag/Tag/Data/Data" construction, Frame type (101)
//       uses a "Tag/Size/Data" construction useful for Blob data . At this time, data packets
//       may be comprised of only one Frame type.
//    3. It is important to build packets that interleave time words such that each sample of a
//       particular data parameter has a unique time stamp.

#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include "resource.h"

#pragma warning( disable: 4244 ) // Double to float conversion
#pragma comment(lib, "wsock32.lib")

//
// Function Prototypes
//
void DataAcq(void) ;
void FillSendPacket(void) ;
void OpenSocketInterface( void ) ;

//
// Data Structures = Please see the IadsInterface.doc for further definition of fields
//
typedef struct
{
  int packetSize ;    // non-inclusive
  int packetCounter ; // packet counter to determine out of order packets
  int totalSent ;     // Total Packets sent
  int totalDropped ;  // Total Packets dropped
  int reserved[4] ;   // reserved for future use
} PACKET_HEADER ;

//
// Global variables
//
__int64            gIrigTime = 0 ; // a vlaue of 0 represents day 1 to the IADS Server
int                gPortId = 49000 ; // This is the default for the IADS Server
char               gEndian = 1 ;
int                gFrameType = 100 ; // "tag/tag/data/data" packet construction with IADS time in total nanoseconds
char*              gSendBuf = '\0' ;
PACKET_HEADER      gHeader ;
int                gPacketSize = 0 ;
float              gParam[4] = { 0.0, 0.0, 0.0, 0.0 } ;
SOCKET             gAcceptSocket ;
SOCKET             gSendSocket ;
struct sockaddr_in gSocketAddr ;

//
// Routines
//
void Shutdown( void )
{
  try  
  {
    if( gSendBuf ) delete [] gSendBuf ;
    if( gAcceptSocket != INVALID_SOCKET ) closesocket( gAcceptSocket ) ;
    if( gSendSocket != INVALID_SOCKET ) closesocket( gSendSocket ) ;
  } catch(...) {} 

  WSACleanup() ;

  printf("Hit any key to exit...\n") ;

  getchar() ;
  exit(0) ;
}

//---------------------------------------------------------------------------//
//  Function:  main                                                          //
//  Author:    Mattingly, P.                                                 //
//  Date:      Nov-2005                                                      //
//  Update:    Nov-2015                                                      //
//---------------------------------------------------------------------------//
//  PURPOSE:  IadsDS main program
//
//  NOTES:   
//
//  *** END FUNCTION HEADER

void main( int argc, char* argv[] )
{
  printf("*******************************************************************************\n") ;
  printf("*                                                                             *\n") ;
  printf("*          Welcome to the IADS Data Source Demonstration Program              *\n") ;
  printf("*                                                                             *\n") ;
  printf("*******************************************************************************\n\n\n") ;

	if( argc == 2 )
  {
    gPortId = atoi(argv[1]) ;
  }

//
// Build the Socket Interface
//
  OpenSocketInterface() ;

// Determine Packet size in bytes
//--[ Simulator Style - Packet Format 100 ]-------------------------------------------------
//  Packet Header =                           32 +
//  Time_High =   50 sps, 1 sample in 20 ms    6 +
//  Time_Low  =   50 sps, 1 sample in 20 ms    6 +
//  Param_1   =   50 sps, 1 sample in 20 ms    6 + 
//  Param_2   =   50 sps, 1 sample in 20 ms    6 + 
//  Param_3   =   50 sps, 1 sample in 20 ms    6 + 
//  Param_4   =   50 sps, 1 sample in 20 ms    6 + 
//------------------------------------------------------------------------------------------
//  Total Samples = 6(samples) * 6(bytes:tag+data) = 36(bytes) + header(32) = 68(bytes)

  gPacketSize = 68 ;
  
  // Allocate the send packet
  gSendBuf = new char[gPacketSize] ;
  if( gSendBuf == NULL )
  {
    printf("*- Error: Unable to allocate memory for send buffer - quiting... \n") ;
    Shutdown() ;
  }

//
// Pre-Fill the essential header elements
//
  gHeader.packetSize = gPacketSize - 4 ;
  gHeader.packetCounter = 0 ;
  gHeader.totalSent = 0 ;
  gHeader.totalDropped = 0 ;

  int KeepRunning = 1 ;

//
// Connect to the IADS Server loop
//
  do 
  {
    // ***************************************************************
    // *
    // *             Wait for Connect from the IADS Server
    // *
    // ***************************************************************
    printf("Sim Style - Blocked waiting for connect on Port: ( %d )\n", gPortId ) ;

    int addrLen = sizeof( struct sockaddr_in ) ;
    gSendSocket = accept( gAcceptSocket, (struct sockaddr*)&gSocketAddr, &addrLen ) ;
    if( gSendSocket == INVALID_SOCKET) 
    {
      printf("Bad Socket on Accept - quiting...\n" ) ;
      Shutdown() ;
    }

    // *************************************************************************************
    // * Handshake -  this is a one-way communication.
    // *   Step 1: Send the Endian or Byte Order, Little Endian = 1, Big Endian = 2 
    // *   Step 2: Send data format type; 100=tag/tag/data/data
    // *************************************************************************************
    if( send( gSendSocket, (char*)&gEndian, 1, 0 ) == SOCKET_ERROR )
    {
      printf("Connection Terminated - Start again...\n" ) ;
      continue ;
    }

    if( send( gSendSocket, (char*)&gFrameType, 4, 0 ) == SOCKET_ERROR )
    {
      printf("Connection Terminated - Start again...\n" ) ;
      continue ;
    }

    // ***************************************************************
    // *
    // *                Data Aquisition Loop  
    // *
    // ***************************************************************
    printf("- Start data source transfer...\n\n") ;

    do 
    {
      // You will replace this routine with your actual method of getting data
      DataAcq() ;

      // This takes the internal array that contains the data and serializes it into a packet
      FillSendPacket() ;

      if( send( gSendSocket, (char*)&gSendBuf[0], gPacketSize, 0 ) == SOCKET_ERROR )
      {
        break ;
      }

      // This is a quick way to simulate a 20ms stream - not perfect but good enough for demonstration
      ::Sleep(20) ;

    } while(!::_kbhit()) ;

    printf("- IADS Server terminated the connection - go to 'wait for connect' state...\n\n") ;

  } while( KeepRunning ) ;

  printf("Ciao...\n") ;
  Shutdown() ;
}

//---------------------------------------------------------------------------//
//  Function:  DataAcq                                                       //
//  Author:    Mattingly, P.                                                 //
//  Date:      Nov-2005                                                      //
//  Update:    Nov-2015                                                      //
//---------------------------------------------------------------------------//
//  PURPOSE:  This routine fills the data array with ramp data
//
//  NOTES:   
//
//  *** END FUNCTION HEADER
void DataAcq(void)
{
  // All the data is at same 50sps rate.  We will create differnet values
  // to verify parameters are working properly in an IADS Strip Chart.
  gParam[0] = (++gParam[0] > 49.0  ? 0.0 : gParam[0]) ;
  gParam[1] = (++gParam[1] > 99.0  ? 0.0 : gParam[1]) ;
  gParam[2] = (++gParam[2] > 199.0 ? 0.0 : gParam[2]) ;
  gParam[3] = (++gParam[3] > 399.0 ? 0.0 : gParam[3]) ;

  // Because our packet represents 20ms of data, our single Time value in the packet(Time is also 50sps as defined)
  // will increment by 20ms in nanosecond units
  gIrigTime += 20000000 ; 
}

//---------------------------------------------------------------------------//
//  Function:  FillSendPacket                                                //
//  Author:    Mattingly, P.                                                 //
//  Date:      11-Nov-05                                                     //
//---------------------------------------------------------------------------//
//  PURPOSE:  This routine fills the send packet from the data aray
//
//  NOTES:   
void FillSendPacket(void) 
{
  // Update those header elements that we will use in our sample program - A production version
  // could fill in the data acquisition hardware status more fully.
  gHeader.packetCounter++ ;
  gHeader.totalSent++ ;

  int bufPtr = 0 ;

  memcpy( (char*)&gSendBuf[bufPtr], &gHeader, sizeof(PACKET_HEADER)) ;
  bufPtr = sizeof(PACKET_HEADER) ;

  // IADS Time words are 64 bit in length and represent both Major and Minor time in units of nanoseconds.
  // They are sent to the IADS Server as two 32 bit words using successive tag Ids as shown here.  The
  // Time value of 0 represents Day 1 or 001:00:00:00.000 Please see the IADS Real-time Data Source Interface
  // Document for more information.
  unsigned short tag = 1 ;
  memcpy( (char*)&gSendBuf[bufPtr], (char*)&tag, sizeof(unsigned __int16)) ;
  bufPtr += sizeof(unsigned __int16) ;

  tag = 2 ;
  memcpy( (char*)&gSendBuf[bufPtr], (char*)&tag, sizeof(unsigned __int16)) ;
  bufPtr += sizeof(unsigned __int16) ;

  unsigned __int32 timeHigh = (unsigned __int32)(gIrigTime >> 32) ;
  unsigned __int32 timeLow = (unsigned __int32)(gIrigTime & 0xFFFFFFFF) ;

  memcpy( (char*)&gSendBuf[bufPtr], (char*)&timeHigh, sizeof(unsigned __int32)) ;
  bufPtr += sizeof(unsigned __int32) ;

  memcpy( (char*)&gSendBuf[bufPtr], (char*)&timeLow, sizeof(unsigned __int32)) ;
  bufPtr += sizeof(unsigned __int32) ;

  // The 4 data parameters
  for(int n=0; n < 3; n+=2 )
  {
    // Tag/Tag
    unsigned short tag = 3+n ;
    memcpy( (char*)&gSendBuf[bufPtr], (char*)&tag, sizeof(unsigned _int16)) ;
    bufPtr += sizeof(unsigned __int16) ;
    tag = 4+n ;
    memcpy( (char*)&gSendBuf[bufPtr], (char*)&tag, sizeof(unsigned __int16)) ;
    bufPtr += sizeof(unsigned __int16) ;

    // Data/Data
    memcpy( (char*)&gSendBuf[bufPtr], (char*)&gParam[n], sizeof(float)) ;
    bufPtr += sizeof(float) ;
    memcpy( (char*)&gSendBuf[bufPtr], (char*)&gParam[n+1], sizeof(float)) ;
    bufPtr += sizeof(float) ;
  }
}

//---------------------------------------------------------------------------//
//  Function:  OpenSocketInterface                                           //
//  Author:    Mattingly, P.                                                 //
//  Date:      Nov-2015                                                      //
//---------------------------------------------------------------------------//
//  PURPOSE:  This routine Opens the socket interface on Windows OS
//
//  NOTES:   
//
//  *** END FUNCTION HEADER
void OpenSocketInterface( void )
{
  // Open on Windows
  WSADATA data ;
  int status = WSAStartup( MAKEWORD(1, 1), &data ) ;
  if( status != 0 )
  {
    printf( "Socket - socket init failed...\n" ) ;
    Shutdown() ;
  }
  // Associate the socket with the address
  memset( &gSocketAddr, 0, sizeof(gSocketAddr) )  ;
  gSocketAddr.sin_port        = htons( gPortId ) ;
  gSocketAddr.sin_family      = AF_INET ;
  gSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY ) ;

  // Create the socket
  gAcceptSocket = socket( AF_INET, SOCK_STREAM, 0 ) ;
  if( gAcceptSocket == INVALID_SOCKET )
  {
    int error = WSAGetLastError() ;
    printf( "Socket - socket called failed (%d)...\n", error ) ;
    WSACleanup() ;
    Shutdown() ;
  }

  // For reconnections
  BOOL enable = 1 ;
  setsockopt( gAcceptSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable) );

  // Bind
  status = bind( gAcceptSocket, (struct sockaddr*)&gSocketAddr, sizeof(struct sockaddr) ) ;
  if( status != 0 )
  {
    printf( "Socket - socket init failed...\n" ) ;
    Shutdown() ;
  }

  // Listen
  status = listen( gAcceptSocket, 1 ) ;
  if( status != 0 )
  {
    printf( "Socket - socket init failed...\n" ) ;
    Shutdown() ;
  }
}
