// ----------------------------------------------------------------------------------------------------
//  Dearborn Group Technology - RP1210 Example Source Code
// ----------------------------------------------------------------------------------------------------
// 
//  Name:           SampleSourceCode.c  Version 2.0
// 
//  Date:           October 20, 2008
// 
//  Written By:     Kenneth L. DeGrant II
//                  Field Applications Engineering Manager
//                  Dearborn Group Technology, Inc.
//                  TMC S.12 RP1210 Task Force Chairman
//                  kdegrant@dgtech.com
//                  859-358-1485 (cell)
// 
//  Description:    This code is provided by Dearborn Group to assist application developers in 
//                  developing RP1210A and RP1210B applications for the Dearborn Protocol Adapter
//                  (DPA) family of products.  It allows connecting to a DG DPA and the J1708/J1587,
//                  CAN@250k, CAN@500k, J1939 and J1850 VPW  protocols (the most common heavy-duty protocols).  
//                  It displays all message traffic seen/sent and also sends out request messages
//                  every 5 seconds.  
//
// CAN Notes:       The code also sends a 29-bit CANID of 0x18EA00F9 with three bytes
//                  of data [C5][FD][00]; which correlates to the J1939 PGN of 59904 (the request PGN) 
//                  requesting the PGN 0x00FDC5 (64965 - ECU Identification Information - ECUID).
//                  The receive routine for the CAN protocols handles both standard CAN (11-bit) 
//                  as well as extended (29-bit) CAN traffic.  GMLAN (protocol name = IESCAN) which 
//                  is 500k CAN (J2284) is also included, but no specific GMLAN messages are presented.
//                  GMLAN is being treated as simply "CAN" in this example code.
//
// J1850 Notes:     The code sends out a request for MODE=1, PID=0 OBDII request as the periodic
//                  message.  This code was a port from a J2534 sample application and has not been tested.  
//                  No code is written to decode any specific J1850 messages.
//
// Code Notes:      All necessary code for this project is in one file (this file).  Because this
//                  program runs on a DOS command prompt, it can be readily adapted to do any basic 
//                  functionality that a developer unfamiliar with RP1210 might want to do without
//                  having to look at complex MFC or Visual Basic code or dialog boxes, etc.  
//
// Logging Notes:   This code also presents the user the option to write all data that appears
//                  on the screen (messages, errors, etc) to a disk file.  This makes this code
//                  a very simple data logger.  You will see where two file pointers are passed to 
//                  most every function.  Passing "stdout" as the first FILE * argument to these
//                  functions causes the data to get printed to the screen, and the second FILE *
//                  is for logging to the file.  Note that some functions do not use the second
//                  argument, as you may not want "translated" or just information data writtent to a file.
//
// Compile Notes:   This code was successfully compiled and executed using Microsoft Visual C++ 6.0.
//
//                  It will take modification to compile easily under the "free" Microsoft 8.0
//                  Enterprise Edition compiler, but has been done.  The main problem with going
//                  from C++ 6.0 to 8.0 was the call to LoadLibrary.  It seems that a wide character
//                  string was needed.  Here is the code that was donated by Jesse Stanley of the US
//                  Army Corps of Engineers to solve that problem.  Note that you will get many 
//                  warnings where Microsoft wants you to use their "safe" string functions instead
//                  of the standard ANSI C like sprintf, etc.  These warnings can safely be ignored.
//
//            [//   Quick fix for char * to LPSWSTR issue.
//            [//   
//            [//   // Added variables at start of main ( void )
//            [//   int a;
//            [//   BSTR unicodestr;
//            [//   
//            [//   // After assigning szDLLName
//            [//   
//            [//   a = lstrlenA(szDLLName);
//            [//   unicodestr = SysAllocStringLen(NULL, a);
//            [//   MultiByteToWideChar(CP_ACP, 0, szDLLName, a, unicodestr, a);
//            [//   
//            [//   LoadLibraryAndFunctions( unicodestr );
//            [//   
//            [//   // Now should free the BSTR
//            [//   SysFreeString(unicodestr);
//                  
//
//  Requirements:   This program requires that you have downloaded and installed the latest DPA
//                  drivers for either the DPA 4+ (and prior), or the DPA 5.  These drivers are 
//                  readily available from the Dearborn Group Technology website as follows:
//
//                               http://www.dgtech.com/download.php
//
//  No Liability:   This code is example code and is NOT warranted to be fit for any particular use 
//                  or purpose.  Dearborn Group Technology will not be held liable for any use or 
//                  misuse of this code "under any circumstances whatsoever".  
// 
//  Notes:          Be sure to have your DOS command prompt set to a width of at least 150, as the 
//                  program displays about 140 contiguous characters from a J1939 message.
// 
//                  It is possible to develop a completely RP1210A/B-compliant application that
//                  will not connect, send or read messages to or from a particular databus.  
//                  To ensure success, please read the documentation for the protocol you want to use 
//                  thoroughly along with reading the "complete" RP1210 document before trying to 
//                  adapt this code to a particular purpose.
//
//  Copyright:      This code is Copyrighted (c) 2008 by Dearborn Group Technology, and is intended
//                  solely for the use of our DPA customers or potential DPA customers.  No portion,
//                  including "snippets" of this code are to be used, investigated, or copied in any 
//                  shape, form, or fashion, by anyone who is, or may in the future be, in competition 
//                  with Dearborn Group Technology in any way.  The code shall also not be modified in
//                  a way that it is capable of being used with any adapter outside of one from Dearborn 
//                  Group Technology.
//
// ----------------------------------------------------------------------------------------------------
//  Revision  Date       Notes
//     1.0    04-16-08   Original Version.
//     2.1    08-04-08   Added CAN and IESCAN protocols, added write to file option.
//     2.1    10-20-08   Added J1850 protocol and logging capability.
// 
// ----------------------------------------------------------------------------------------------------

#define PROGRAM_ID "Dearborn Group Technology - RP1210 Example Souce Code - Version 2.0 - 10/20/2008"

//-----------------------------------------------------------------------------------------------------
// C Include Files
//-----------------------------------------------------------------------------------------------------

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#include<windows.h>
#include<winuser.h>
#include<signal.h>
#include<conio.h>
#include<time.h>

//-----------------------------------------------------------------------------------------------------
// RP1210B   RP1210_SendCommand Defines (From RP1210B Document)
//-----------------------------------------------------------------------------------------------------

#define RP1210_Reset_Device                            0
#define RP1210_Set_All_Filters_States_to_Pass          3
#define RP1210_Set_Message_Filtering_For_J1939         4
#define RP1210_Set_Message_Filtering_For_CAN           5
#define RP1210_Set_Message_Filtering_For_J1708         7
#define RP1210_Set_Message_Filtering_For_J1850         8
#define RP1210_Set_Message_Filtering_For_ISO15765      9
#define RP1210_Generic_Driver_Command                 14
#define RP1210_Set_J1708_Mode                         15
#define RP1210_Echo_Transmitted_Messages              16
#define RP1210_Set_All_Filters_States_to_Discard      17
#define RP1210_Set_Message_Receive                    18
#define RP1210_Protect_J1939_Address                  19
#define RP1210_Set_Broadcast_For_J1708                20
#define RP1210_Set_Broadcast_For_CAN                  21
#define RP1210_Set_Broadcast_For_J1939                22
#define RP1210_Set_Broadcast_For_J1850                23
#define RP1210_Set_J1708_Filter_Type                  24
#define RP1210_Set_J1939_Filter_Type                  25
#define RP1210_Set_CAN_Filter_Type                    26
#define RP1210_Set_J1939_Interpacket_Time             27
#define RP1210_SetMaxErrorMsgSize                     28
#define RP1210_Disallow_Further_Connections           29
#define RP1210_Set_J1850_Filter_Type                  30
#define RP1210_Release_J1939_Address                  31
#define RP1210_Set_ISO15765_Filter_Type               32
#define RP1210_Set_Broadcast_For_ISO15765             33
#define RP1210_Set_ISO15765_Flow_Control              34
#define RP1210_Clear_ISO15765_Flow_Control            35
#define RP1210_Set_ISO15765_Link_Type                 36
#define RP1210_Set_J1939_Baud                         37
#define RP1210_Set_ISO15765_Baud                      38
#define RP1210_Set_BlockTimeout                      215
#define RP1210_Set_J1708_Baud                        305

//-----------------------------------------------------------------------------------------------------
// RP1210B Constants - Check RP1210B document for any updates.
//-----------------------------------------------------------------------------------------------------

#define BIT0                                           1  // Bit 0 - Used for masking bits
#define BIT1                                           2  // Bit 1 - Used for masking bits
#define BIT2                                           4  // Bit 2 - Used for masking bits
#define BIT3                                           8  // Bit 3 - Used for masking bits
#define BIT4                                          16  // Bit 4 - Used for masking bits
#define BIT5                                          32  // Bit 5 - Used for masking bits
#define BIT6                                          64  // Bit 6 - Used for masking bits
#define BIT7                                         128  // Bit 7 - Used for masking bits

#define CONNECTED                                      1  // Connection state = Connected 
#define NOT_CONNECTED                                 -1  // Connection state = Disconnected
 
#define FILTER_PASS_NONE                               0  // Filter state = DISCARD ALL MESSAGES
#define FILTER_PASS_SOME                               1  // Filter state = PASS SOME (some filters)
#define FILTER_PASS_ALL                                2  // Filter state = PASS ALL

#define NULL_WINDOW                                    0  // Windows 3.1 is no longer supported.

#define BLOCKING_IO                                    1  // For Blocking calls to send/read.
#define NON_BLOCKING_IO                                0  // For Non-Blocking calls to send/read.
#define BLOCK_INFINITE                                 0  // For Non-Blocking calls to send/read.

#define BLOCK_UNTIL_DONE                               0  // J1939 Address claim, wait until done
#define RETURN_BEFORE_COMPLETION                       2  // J1939 Address claim, don't wait

#define CONVERTED_MODE                                 1  // J1708 RP1210Mode="Converted"
#define RAW_MODE                                       0  // J1708 RP1210Mode="Raw"

#define MAX_J1708_MESSAGE_LENGTH                     508  // Maximum size of J1708 message (+1)
#define MAX_J1939_MESSAGE_LENGTH                    1796  // Maximum size of J1939 message (+1)
#define MAX_ISO15765_MESSAGE_LENGTH                 4108  // Maximum size of ISO15765 message (+1)

#define ECHO_OFF                                    0x00  // EchoMode
#define ECHO_ON                                     0x01  // EchoMode

#define RECEIVE_ON                                  0x01  // Set Message Receive
#define RECEIVE_OFF                                 0x00  // Set Message Receive

#define ADD_LIST                                    0x01  // Add a message to the list.
#define VIEW_B_LIST                                 0x02  // View an entry in the list.
#define DESTROY_LIST                                0x03  // Remove all entries in the list.
#define REMOVE_ENTRY                                0x04  // Remove a specific entry from the list.
#define LIST_LENGTH                                 0x05  // Returns number of items in list.

#define FILTER_PGN                            0x00000001  // Setting of J1939 filters
#define FILTER_PRIORITY                       0x00000002  // Setting of J1939 filters
#define FILTER_SOURCE                         0x00000004  // Setting of J1939 filters
#define FILTER_DESTINATION                    0x00000008  // Setting of J1939 filters
#define FILTER_INCLUSIVE                            0x00  // FilterMode
#define FILTER_EXCLUSIVE                            0x01  // FilterMode

#define SILENT_J1939_CLAIM                          0x00  // Claim J1939 Address
#define PASS_J1939_CLAIM_MESSAGES                   0x01  // Claim J1939 Address

#define CHANGE_BAUD_NOW                             0x00  // Change Baud
#define MSG_FIRST_CHANGE_BAUD                       0x01  // Change Baud
#define RP1210_BAUD_9600                            0x00  // Change Baud
#define RP1210_BAUD_19200                           0x01  // Change Baud
#define RP1210_BAUD_38400                           0x02  // Change Baud
#define RP1210_BAUD_57600                           0x03  // Change Baud
#define RP1210_BAUD_125k                            0x04  // Change Baud
#define RP1210_BAUD_250k                            0x05  // Change Baud
#define RP1210_BAUD_500k                            0x06  // Change Baud
#define RP1210_BAUD_1000k                           0x07  // Change Baud

#define STANDARD_CAN                                0x00  // Filters
#define EXTENDED_CAN                                0x01  // Filters
#define STANDARD_CAN_ISO15765_EXTENDED              0x02  // 11-bit with ISO15765 extended address
#define EXTENDED_CAN_ISO15765_EXTENDED              0x03  // 29-bit with ISO15765 extended address
#define STANDARD_MIXED_CAN_ISO15765                 0x04  // 11-bit identifier with mixed addressing
#define ISO15765_ACTUAL_MESSAGE                     0x00  // ISO15765 ReadMessage - type of data
#define ISO15765_CONFIRM                            0x01  // ISO15765 ReadMessage - type of data
#define ISO15765_FF_INDICATION                      0x02  // ISO15765 ReadMessage - type of data

#define LINKTYPE_GENERIC_CAN                        0x00  // Set_ISO15765_Link_Type argument
#define LINKTYPE_J1939_ISO15765_2_ANNEX_A           0x01  // Set_ISO15765_Link_Type argument
#define LINKTYPE_J1939_ISO15765_3                   0x02  // Set_ISO15765_Link_Type argument

#ifndef TRUE
#define TRUE                                        0x01
#define FALSE                                       0x00
#endif

//-----------------------------------------------------------------------------------------------------
// RP1210B Return Definitions
//-----------------------------------------------------------------------------------------------------

#define NO_ERRORS                                      0
#define ERR_DLL_NOT_INITIALIZED                      128
#define ERR_INVALID_CLIENT_ID                        129
#define ERR_CLIENT_ALREADY_CONNECTED                 130
#define ERR_CLIENT_AREA_FULL                         131
#define ERR_FREE_MEMORY                              132
#define ERR_NOT_ENOUGH_MEMORY                        133
#define ERR_INVALID_DEVICE                           134
#define ERR_DEVICE_IN_USE                            135
#define ERR_INVALID_PROTOCOL                         136
#define ERR_TX_QUEUE_FULL                            137
#define ERR_TX_QUEUE_CORRUPT                         138
#define ERR_RX_QUEUE_FULL                            139
#define ERR_RX_QUEUE_CORRUPT                         140
#define ERR_MESSAGE_TOO_LONG                         141
#define ERR_HARDWARE_NOT_RESPONDING                  142
#define ERR_COMMAND_NOT_SUPPORTED                    143
#define ERR_INVALID_COMMAND                          144
#define ERR_TXMESSAGE_STATUS                         145
#define ERR_ADDRESS_CLAIM_FAILED                     146
#define ERR_CANNOT_SET_PRIORITY                      147
#define ERR_CLIENT_DISCONNECTED                      148
#define ERR_CONNECT_NOT_ALLOWED                      149
#define ERR_CHANGE_MODE_FAILED                       150
#define ERR_BUS_OFF                                  151
#define ERR_COULD_NOT_TX_ADDRESS_CLAIMED             152
#define ERR_ADDRESS_LOST                             153
#define ERR_CODE_NOT_FOUND                           154
#define ERR_BLOCK_NOT_ALLOWED                        155
#define ERR_MULTIPLE_CLIENTS_CONNECTED               156
#define ERR_ADDRESS_NEVER_CLAIMED                    157
#define ERR_WINDOW_HANDLE_REQUIRED                   158
#define ERR_MESSAGE_NOT_SENT                         159
#define ERR_MAX_NOTIFY_EXCEEDED                      160
#define ERR_MAX_FILTERS_EXCEEDED                     161
#define ERR_HARDWARE_STATUS_CHANGE                   162
#define ERR_INI_FILE_NOT_IN_WIN_DIR                  202
#define ERR_INI_SECTION_NOT_FOUND                    204
#define ERR_INI_KEY_NOT_FOUND                        205
#define ERR_INVALID_KEY_STRING                       206
#define ERR_DEVICE_NOT_SUPPORTED                     207
#define ERR_INVALID_PORT_PARAM                       208
#define ERR_COMMAND_TIMED_OUT                        213
#define ERR_OS_NOT_SUPPORTED                         220
#define ERR_COMMAND_QUEUE_IS_FULL                    222
#define ERR_CANNOT_SET_CAN_BAUDRATE                  224
#define ERR_CANNOT_CLAIM_BROADCAST_ADDRESS           225
#define ERR_OUT_OF_ADDRESS_RESOURCES                 226
#define ERR_ADDRESS_RELEASE_FAILED                   227
#define ERR_COMM_DEVICE_IN_USE                       230
#define ERR_DATA_LINK_CONFLICT                       441
#define ERR_ADAPTER_NOT_RESPONDING                   453
#define ERR_CAN_BAUD_SET_NONSTANDARD                 454
#define ERR_MULTIPLE_CONNECTIONS_NOT_ALLOWED_NOW     455
#define ERR_J1708_BAUD_SET_NONSTANDARD               456
#define ERR_J1939_BAUD_SET_NONSTANDARD               457
#define ERR_ISO15765_BAUD_SET_NONSTANDARD            458

//-----------------------------------------------------------------------------------------------------
// Used to help in unpacking Motorola format numbers.
//-----------------------------------------------------------------------------------------------------

#define HIWORD_OF_INT4( x )   ( ( x >> 16 ) & 0xFFFF )
#define LOWORD_OF_INT4( x )   ( x & 0x0000FFFF )
#define HIBYTE_OF_WORD( x )   ( ( x >> 8 ) & 0xFF ) 
#define LOBYTE_OF_WORD( x )   ( x & 0x00FF ) 
#define HINIBBLE_OF_CHAR( x ) ( ( x & 0xF0 ) >> 4 )
#define LONIBBLE_OF_CHAR( x ) (   x & 0x0F ) 
#define BYTE0_OF_INT4( x )    LOBYTE_OF_WORD( LOWORD_OF_INT4( x ) )
#define BYTE1_OF_INT4( x )    HIBYTE_OF_WORD( LOWORD_OF_INT4( x ) )
#define BYTE2_OF_INT4( x )    LOBYTE_OF_WORD( HIWORD_OF_INT4( x ) )
#define BYTE3_OF_INT4( x )    HIBYTE_OF_WORD( HIWORD_OF_INT4( x ) )

//-----------------------------------------------------------------------------------------------------
// RP1210B Defined Function Prototypes
//-----------------------------------------------------------------------------------------------------

#define DLLEXPORT __declspec(dllexport)

#ifdef __cplusplus
extern "C" {
#endif
short DLLEXPORT WINAPI RP1210_ClientConnect           (  
                                                       HWND   hwndClient,
                                                       short  nDeviceId,
                                                       char  *fpchProtocol,
                                                       long   lSendBuffer,
                                                       long   lReceiveBuffer,
                                                       short  nIsAppPacketizingIncomingMsgs 
                                                       );

short DLLEXPORT WINAPI RP1210_ClientDisconnect        (
                                                       short  nClientID 
                                                       );

short DLLEXPORT WINAPI RP1210_SendMessage             (  
                                                       short  nClientID,
                                                       char  *fpchClientMessage,
                                                       short  nMessageSize,
                                                       short  nNotifyStatusOnTx,
                                                       short  nBlockOnSend 
                                                       );

short DLLEXPORT WINAPI RP1210_ReadMessage             (  
                                                       short  nClientID,
                                                       char  *fpchAPIMessage,
                                                       short  nBufferSize,
                                                       short  nBlockOnSend 
                                                       );

short DLLEXPORT WINAPI RP1210_SendCommand             (  
                                                       short  nCommandNumber,
                                                       short  nClientID,
                                                       char  *fpchClientCommand,
                                                       short  nMessageSize 
                                                       );

void  DLLEXPORT WINAPI RP1210_ReadVersion             (  
                                                       char  *fpchDLLMajorVersion,
                                                       char  *fpchDLLMinorVersion,
                                                       char  *fpchAPIMajorVersion,
                                                       char  *fpchAPIMinorVersion 
                                                       );

short  DLLEXPORT WINAPI RP1210_ReadDetailedVersion    (  
                                                       short  nClientID,
                                                       char  *fpchAPIVersionInfo,
                                                       char  *fpchDLLVersionInfo,
                                                       char  *fpchFWVersionInfo 
                                                       );

short DLLEXPORT WINAPI RP1210_GetHardwareStatus       (  
                                                       short  nClientID,
                                                       char  *fpchClientInfo,
                                                       short  nInfoSize,
                                                       short  nBlockOnRequest
                                                       );
#ifdef RP1210B
short DLLEXPORT WINAPI RP1210_GetErrorMsg             ( 
                                                       short  err_code,
                                                       char  *fpchMessage,
                                                       short  nClientID 
                                                       );
#else
short DLLEXPORT WINAPI RP1210_GetErrorMsg             ( 
                                                       short  err_code,
                                                       char  *fpchMessage
                                                       );
#endif

short DLLEXPORT WINAPI RP1210_GetLastErrorMsg         (  
                                                       short  err_code,
                                                       int   *SubErrorCode,
                                                       char  *fpchMessage 
                                                       );
#ifdef __cplusplus
}
#endif

//-----------------------------------------------------------------------------------------------------
// Local #define Definitions
//-----------------------------------------------------------------------------------------------------

#define J1939_GLOBAL_ADDRESS                          255
#define J1939_OFFBOARD_DIAGNOSTICS_TOOL_1             249
#define J1587_OFFBOARD_DIAGNOSTICS_TOOL_1             172
 
//-----------------------------------------------------------------------------------------------------
// RP1210B Function Type Definitions
//-----------------------------------------------------------------------------------------------------

typedef short (WINAPI *fxRP1210_ClientConnect)       ( HWND, short, char *, long, long, short );
typedef short (WINAPI *fxRP1210_ClientDisconnect)    ( short );
typedef short (WINAPI *fxRP1210_SendMessage)         ( short, char*, short, short, short );
typedef short (WINAPI *fxRP1210_ReadMessage)         ( short, char*, short, short );
typedef short (WINAPI *fxRP1210_SendCommand)         ( short, short, char*, short );
typedef short (WINAPI *fxRP1210_ReadVersion)         ( char*, char*, char*, char* );
typedef short (WINAPI *fxRP1210_ReadDetailedVersion) ( short, char*, char*, char* );
typedef short (WINAPI *fxRP1210_GetHardwareStatus)   ( short, char*, short, short );
typedef short (WINAPI *fxRP1210_GetLastErrorMsg)     ( short, int *, char*, short );
#ifdef RP1210B
typedef short (WINAPI *fxRP1210_GetErrorMsg)         ( short, char*, short);
#else
typedef short (WINAPI *fxRP1210_GetErrorMsg)         ( short, char* );
#endif

//-----------------------------------------------------------------------------------------------------
// GLOBALVAR  RP1210 Function Pointers using Function Type Definitions
//-----------------------------------------------------------------------------------------------------

fxRP1210_ClientConnect           pRP1210_ClientConnect           = NULL;
fxRP1210_ClientDisconnect        pRP1210_ClientDisconnect        = NULL;
fxRP1210_ReadMessage             pRP1210_ReadMessage             = NULL;
fxRP1210_SendMessage             pRP1210_SendMessage             = NULL;
fxRP1210_SendCommand             pRP1210_SendCommand             = NULL;
fxRP1210_ReadVersion             pRP1210_ReadVersion             = NULL;
fxRP1210_ReadDetailedVersion     pRP1210_ReadDetailedVersion     = NULL;
fxRP1210_GetErrorMsg             pRP1210_GetErrorMsg             = NULL;
fxRP1210_GetLastErrorMsg         pRP1210_GetLastErrorMsg         = NULL;
fxRP1210_GetHardwareStatus       pRP1210_GetHardwareStatus       = NULL;

//-----------------------------------------------------------------------------------------------------
// GLOBALVAR Global variables dealing with DLL, Client, Device, Protocol
//-----------------------------------------------------------------------------------------------------

HINSTANCE                        hRP1210DLL                              = NULL;
int                              iAdapter                                = -1;
int                              iUserDevice                             = -1;
int                              iProtocol                               = -1;
short                            nClientID                               = -1;
char                             szDLLName[81]                           = ""; 
char                             szProtocolName[81]                      = ""; 
unsigned char                    ucTxRxBuffer[6000]                      = "";

//-----------------------------------------------------------------------------------------------------
// GLOBALVAR Global variables dealing with J1939
//-----------------------------------------------------------------------------------------------------

unsigned char                    ucJ1939Address                  =  J1939_OFFBOARD_DIAGNOSTICS_TOOL_1;

//-----------------------------------------------------------------------------------------------------
// GLOBALVAR Global variables dealing with J1708
//-----------------------------------------------------------------------------------------------------

unsigned char                    ucJ1587Address                  =  J1587_OFFBOARD_DIAGNOSTICS_TOOL_1;

//-----------------------------------------------------------------------------------------------------
// RP1210-related functions
//-----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void PrintRP1210Error              ( char *szInfo, short nErrorCode, FILE *fp1, FILE *fp2 );
/*PROCEDURE*/ void LoadRP1210LibraryAndFunctions ( char *szDLLName );
/*PROCEDURE*/ void SetAllFilterStatesToPass      ( short nClientID, FILE *fp1, FILE *fp2 );

//-----------------------------------------------------------------------------------------------------
// Unpacking of character strings representing integers, and then converting them into integers
//-----------------------------------------------------------------------------------------------------

/*FUNCTION*/  int  UnPackTwoByteIntegerLSB       ( unsigned char *ucIntString );
/*FUNCTION*/  int  UnPackTwoByteIntegerMSB       ( unsigned char *ucIntString );
/*FUNCTION*/  int  UnPackThreeByteIntegerLSB     ( unsigned char *ucIntString );
/*FUNCTION*/  int  UnPackThreeByteIntegerMSB     ( unsigned char *ucIntString );
/*FUNCTION*/  int  UnPackFourByteIntegerLSB      ( unsigned char *ucIntString );
/*FUNCTION*/  int  UnPackFourByteIntegerMSB      ( unsigned char *ucIntString );

//-----------------------------------------------------------------------------------------------------
// J1939-related functions
//-----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1939_Message         ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_J1939_Message           ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_Sent_J1939_Message      (
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength 
                                                  );
/*PROCEDURE*/ void SendJ1939PeriodicMessages     ( 
                                                  short          nClientID,
                                                  FILE          *fp1,
                                                  FILE          *fp2
                                                  );
/*PROCEDURE*/ void RequestPGN                    ( 
                                                  short          nClientID, 
                                                  int            iRequestedPGN, 
                                                  FILE          *fp1,
                                                  FILE          *fp2,
                                                  unsigned char  ucSourceAddress, 
                                                  unsigned char  AddressRequestedFrom 
                                                  );
/*FUNCTION*/  char *EngineStarterMode            ( 
                                                  unsigned char  Mode 
                                                  );
/*FUNCTION*/  char *EngineRetarderAndTorqueModes ( 
                                                  unsigned char  Mode 
                                                  );
/*PROCEDURE*/ void ClaimMyJ1939Address           ( 
                                                  FILE          *fp1,
                                                  FILE          *fp2
                                                  );

//-----------------------------------------------------------------------------------------------------
// CAN-related functions
//-----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_CAN_Message           ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_CAN_Message             ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_Sent_CAN_Message        ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength 
                                                  );
/*PROCEDURE*/ void SendCANMessage                ( 
                                                  short          nClientID, 
                                                  short          nExtendedCAN, 
                                                  int            iCANID, 
                                                  FILE          *fp1,
                                                  FILE          *fp2,
                                                  unsigned char *ucMessage, 
                                                  short          nDataLen 
                                                  );
/*PROCEDURE*/ void SendCANPeriodicMessages       ( 
                                                  short          nClientID ,
                                                  FILE          *fp1,
                                                  FILE          *fp2
                                                  );
/*PROCEDURE*/ void SendIESCANPeriodicMessages    ( 
                                                  short          nClientID ,
                                                  FILE          *fp1,
                                                  FILE          *fp2
                                                  );

//-----------------------------------------------------------------------------------------------------
// J1708/J1587-related functions
//-----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1708_Message         ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nJ1708RawMode, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_J1708_Message           ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nJ1708RawMode, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_Sent_J1708_Message      ( 
                                                  short          nClientID, 
                                                  FILE          *fp, 
                                                  FILE          *fp1, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nJ1708RawMode 
                                                  );
/*PROCEDURE*/ void RequestPID                    ( 
                                                  short          nClientID, 
                                                  unsigned char  ucRequestedPID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char  ucSourceAddress 
                                                  );
/*PROCEDURE*/ void SendJ1708PeriodicMessages     ( 
                                                  short          nClientID,
                                                  FILE          *fp1,
                                                  FILE          *fp2 
                                                  );


//-----------------------------------------------------------------------------------------------------
// J1850-related functions
//-----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1850_Message         ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_J1850_Message           ( 
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength, 
                                                  short          nEchoTXMsgs 
                                                  );
/*PROCEDURE*/ void Print_Sent_J1850_Message      (
                                                  short          nClientID, 
                                                  FILE          *fp1, 
                                                  FILE          *fp2, 
                                                  unsigned char *ucMessage, 
                                                  short          nLength 
                                                  );
/*PROCEDURE*/ void SendJ1850PeriodicMessages     ( 
                                                  short          nClientID,
                                                  FILE          *fp1,
                                                  FILE          *fp2
                                                  );
/*PROCEDURE*/ void SendJ1850Message              (    
                                                  short          nClientID,
                                                  FILE          *fp1,
                                                  FILE          *fp2,
                                                  unsigned char *ucMessage, 
                                                  short          nLength 
                                                  );
//-----------------------------------------------------------------------------------------------------
// Function:      main
// 
// Purpose:       Main program entry point.
//
//-----------------------------------------------------------------------------------------------------

/*FUNCTION*/ int main( void )
{
  //
  // Initialize all function variables to a known state.
  //

  short           nRetVal                                 = -1;
  char            szTemp[1000]                            = ""; 

  time_t          tLastTimeRequestsSent                   = -1;
  time_t          tCurrentTime                            = -1;
  time_t          Timer                                   = 0;
  struct tm      *TimeRecord                              = NULL;

  short           bFirst                                  = TRUE;
  FILE           *fpDiskFile                              = NULL;

  //
  // Initialize all array variables to a known state of NULL.
  //

  memset( szTemp,         0x00, sizeof( szTemp         ));
  memset( ucTxRxBuffer,   0x00, sizeof( ucTxRxBuffer   ));
  memset( szDLLName,      0x00, sizeof( szDLLName      ));
  memset( szProtocolName, 0x00, sizeof( szProtocolName ));

  //
  // Print program information.
  //

  printf("-------------------------------------------------------------------------------------\n");
  printf("%s\n", PROGRAM_ID                                                                       );
  printf("-------------------------------------------------------------------------------------\n");

  //
  // Have the user select the adapter they want to use and set szDLLName.
  //

  do 
    {
      printf("                                                                     \n");
      printf("---------------------------------------------------------------------\n");
      printf(" Select Adapter To Use                                               \n");
      printf("---------------------------------------------------------------------\n");
      printf("                                                                     \n");
      printf(" 1 = DG DPA 4+ And Prior DPAs (DLL Name = DG121032.DLL)              \n");
      printf(" 2 = DG DPA 5                 (DLL Name = DG1210B.DLL)               \n");
      printf("                                                                     \n");
      printf("->  ");

      iAdapter = atoi( gets( szTemp ) );

      if ( iAdapter == 1 ) 
        {
          strcpy( szDLLName, "dg121032.dll" );
        }
      else if ( iAdapter == 2 ) 
        {
          strcpy( szDLLName, "dg1210b.dll"  );
        }
      else
        {
        }
      
    } while( iAdapter < 1 || iAdapter > 2 );
  
  //
  // Have the user select the protocol they want to use and set szProtocolName.
  //

  do 
    {
      printf("                                                                     \n");
      printf("---------------------------------------------------------------------\n");
      printf(" Selection Protocol To Use                                           \n");
      printf("---------------------------------------------------------------------\n");
      printf("                                                                     \n");
      printf(" 1 = J1708                                                           \n");
      printf(" 2 = J1939                                                           \n");
      printf(" 3 = CAN                                                             \n");
      printf(" 4 = IESCAN (GMLAN/CAN@500k/J2284)                                   \n");
      printf(" 5 = J1850  (J1850 VPW @10.4k)                                       \n");
      printf("                                                                     \n");
      printf("->  ");

      iProtocol = atoi( gets( szTemp ) );

      if ( iProtocol == 1 )  
        {
          strcpy( szProtocolName, "J1708" );
        }
      else if( iProtocol == 2 )  
        {
          strcpy( szProtocolName, "J1939" );
        }
      else if( iProtocol == 3 )  
        {
          strcpy( szProtocolName, "CAN" );
        }
      else if( iProtocol == 4 )  
        {
          strcpy( szProtocolName, "IESCAN" );
        }
      else if( iProtocol == 5 )  
        {
          strcpy( szProtocolName, "J1850" );
        }
      else
        {
        }
      
    } while( iProtocol < 1 || iProtocol > 5 );

  //
  // Have the user select the device number and set nUserDevice (make 150 the default)
  //

  printf("                                                                     \n");
  printf("                                                                      \n");
  printf("--------------------------------------------------------------------- \n");
  printf(" Select Device To Use                                                 \n");
  printf("--------------------------------------------------------------------- \n");
  printf("                                                                      \n");
  printf(" 150 =  DG DPA 4 or DPA 5  Using USB   (default)                      \n");
  printf(" 101 =  DG DPA II/II+/III+ Using COM1                                 \n");
  printf("                                                                      \n");
  printf(" These are the most common Dearborn Group devices.  Other device      \n");
  printf(" numbers exist in the DG121032.INI or DG1210B.INI files that can be   \n");
  printf(" found in the C:\\Windows directory.                                  \n");
  printf("                                                                      \n");
  printf("->  ");
  
  iUserDevice = atoi( gets( szTemp ) );
  
  if( iUserDevice == 0 )
    {
      iUserDevice = 150;
    }
  
  //
  // Ask user if they want to write to a disk file.
  //

  printf("                                                                      \n");
  printf("--------------------------------------------------------------------- \n");
  printf(" Recording of data to a disk file.                                    \n");
  printf("--------------------------------------------------------------------- \n");
  printf("                                                                      \n");
  printf(" Would you like to write the output of this program to a disk file?   \n");
  printf("                                                                      \n");
  printf("(y/n) or (1/0) ->  ");

  gets( szTemp );

  if( (toupper( szTemp[0] ) == 'Y') || (szTemp[0] == '1') )
    {
      printf("                                                  \n");
      printf(" Enter the name of the file to record/append to ->  ");
      
      gets( szTemp );

      if( NULL == (fpDiskFile = fopen( szTemp, "a" ) ) )
        {
          printf("Unable to open file [%s] for appending.  Exiting program.\n", szTemp );
          exit( 1 );
        }
    }
  else
    {
      fpDiskFile = NULL;
    }

  //
  // Load the DLL in szDLLName and set all function pointer variables (i.e. pRP1210_ClientConnect)
  //

  LoadRP1210LibraryAndFunctions(szDLLName);

  //
  // Get the current time to show we have connected or not connected successfully.
  //

  time( &Timer );

  //
  // Set nClientID by attempting to connect to nUserDevice using szProtocolName.
  //

  nClientID = pRP1210_ClientConnect(NULL_WINDOW, (short) iUserDevice, szProtocolName,0,0,0);
  
  if ( ( nClientID >= 0 ) && ( nClientID <= 127 ) )
    {
      fprintf(stdout,"\n");
      fprintf(stdout,"SUCCESS - RP1210_ClientConnect(NULL_WINDOW,UserDevice=[%d],ProtocolName=[%s],0,0,0);\n", iUserDevice, szProtocolName );
      fprintf(stdout,"Time Connected = %s", ctime( &Timer ) );
      fprintf(stdout,"\n");

      if( fpDiskFile != NULL )
        {
          fprintf(fpDiskFile,"\n");
          fprintf(fpDiskFile,"SUCCESS - RP1210_ClientConnect(NULL_WINDOW,UserDevice=[%d],ProtocolName=[%s],0,0,0);\n", iUserDevice, szProtocolName );
          fprintf(fpDiskFile,"Time Connected = %s", ctime( &Timer ) );
          fprintf(fpDiskFile,"\n");
        }
    }
  else
    {
      fprintf(stdout,"\n");
      fprintf(stdout,"FAILURE - RP1210_ClientConnect(NULL_WINDOW,UserDevice=[%d],ProtocolName=[%s],0,0,0);\n", iUserDevice, szProtocolName );
      fprintf(stdout,"Time Connect Attempted = %s", ctime( &Timer ) );
      fprintf(stdout,"\n");

      if( fpDiskFile != NULL )
        {
          fprintf(fpDiskFile,"\n");
          fprintf(fpDiskFile,"FAILURE - RP1210_ClientConnect(NULL_WINDOW,UserDevice=[%d],ProtocolName=[%s],0,0,0);\n", iUserDevice, szProtocolName );
          fprintf(fpDiskFile,"Time Connect Attempted = %s", ctime( &Timer ) );
          fprintf(fpDiskFile,"\n");
        }

      PrintRP1210Error( "RP1210_ClientConnect", nClientID, stdout, fpDiskFile );

      fclose( fpDiskFile );
        
      FreeLibrary( hRP1210DLL );

      exit(1);
    }
  
  //
  // Set all filter states to pass so that messages will flow.
  //

  SetAllFilterStatesToPass( nClientID, stdout, fpDiskFile );

  //
  // If we are on the J1939 network, we must claim our address before we can send.
  //

  if( 0 == strcmp( szProtocolName, "J1939" ) )
    {
      ClaimMyJ1939Address(stdout, fpDiskFile);
    }
  
  //
  // While the user does not press a key, send request messages every 5 seconds and print messages found
  // on the chosen data bus.
  //

  printf("---------------------------------------------------------------------\n");
  printf(" Ready to begin.  Press \"Enter\" to start, any key exits program.   \n");
  printf("            Press \"Control-S\" to freeze the screen.                \n");
  printf("---------------------------------------------------------------------\n");
  printf(">");
  gets(szTemp);

  //
  // Get the current time in tLastTimeRequestsSent.  Use this variable to help send 
  // request messages every 5 seconds.
  //

  time( &tLastTimeRequestsSent );
  
  while ( TRUE )
    {
      //
      // If user presses a key, break from the loop, cleanup and exit the program.
      //

      if( _kbhit() )
        break;

      //
      // Clear the transmit/receive message buffer of any previous information.
      //

      memset( ucTxRxBuffer, 0x00, sizeof( ucTxRxBuffer ) );
      
      //
      // Look and see if a message is on the data bus in NON_BLOCKING mode.  
      //     0 = No message on bus.
      //   < 0 = Error condition.
      //   > 0 = Message found on bus.
      //

      nRetVal = pRP1210_ReadMessage( nClientID, (char*) &ucTxRxBuffer[0], sizeof(ucTxRxBuffer), NON_BLOCKING_IO );
      
      if( nRetVal == 0 )
        {
          // No message found on bus, do nothing.
        }
      else if ( nRetVal > 0  ) 
        {
          if( 0 == strcmp( szProtocolName, "J1939" ) )
            {
              Process_J1939_Message( nClientID, stdout, fpDiskFile, ucTxRxBuffer, nRetVal, ECHO_OFF );
            }
          else if( 0 == strcmp( szProtocolName, "J1708" ) )
            {
              Process_J1708_Message( nClientID, stdout, fpDiskFile, ucTxRxBuffer, nRetVal, CONVERTED_MODE, ECHO_OFF );
            }
          else if( 0 == strcmp( szProtocolName, "CAN" ) )
            {
              Process_CAN_Message( nClientID, stdout, fpDiskFile, ucTxRxBuffer, nRetVal, ECHO_OFF );
            }
          else if( 0 == strcmp( szProtocolName, "IESCAN" ) )
            {
              Process_CAN_Message( nClientID, stdout, fpDiskFile, ucTxRxBuffer, nRetVal, ECHO_OFF );
            }
          else if( 0 == strcmp( szProtocolName, "J1850" ) )
            {
              Process_J1850_Message( nClientID, stdout, fpDiskFile, ucTxRxBuffer, nRetVal, ECHO_OFF );
            }
          else
            {
            }
        }
      else if ( nRetVal < 0 ) 
        {
          PrintRP1210Error( "RP1210_ReadMessage", (short) -nRetVal, stdout, fpDiskFile );
        }

      //
      // If the first time through, or if it has been more than 15 seconds, send out PGN or PID request messages.
      //

      time( &tCurrentTime );

      if(  (tCurrentTime > ( tLastTimeRequestsSent + 5 ) ) || ( bFirst == TRUE ) )
        {
          bFirst = FALSE;

          tLastTimeRequestsSent = tCurrentTime;

          if( 0 == strcmp( szProtocolName, "J1939" ) )
            {
              SendJ1939PeriodicMessages( nClientID, stdout, fpDiskFile );
            }
          else if( 0 == strcmp( szProtocolName, "J1708" ) )
            {
              SendJ1708PeriodicMessages( nClientID, stdout, fpDiskFile );
            }
          else if( 0 == strcmp( szProtocolName, "CAN" ) )
            {
              SendCANPeriodicMessages( nClientID, stdout, fpDiskFile );
            }
          else if( 0 == strcmp( szProtocolName, "IESCAN" ) )
            {
              SendCANPeriodicMessages( nClientID, stdout, fpDiskFile );
            }
          else if( 0 == strcmp( szProtocolName, "J1850" ) )
            {
              SendJ1850PeriodicMessages( nClientID, stdout, fpDiskFile );
            }
        }
      
      //
      // Always a good idea to yield the processor to other processes.
      // 
      Sleep(10);
    
    }// Main "while" loop.

  fprintf(stdout,"\n");

  if( fpDiskFile != NULL )
    fprintf(fpDiskFile,"\n");

  //
  // Disconnect from the data bus.
  // 

  if ( pRP1210_ClientDisconnect( nClientID ) != 0 ) 
    {
      PrintRP1210Error( "RP1210_ClientDisconnect", (short) nRetVal, stdout, fpDiskFile );
    }
  
  //
  // Free the RP1210 library that we used, and close the log file.
  // 

  FreeLibrary( hRP1210DLL );

  if( fpDiskFile != NULL )
    fclose( fpDiskFile );

  //  
  // Return SUCCESS to the operating system.
  //

  return 0;
}

// ----------------------------------------------------------------------
//  Function:  UnPackTwoByteIntegerLSB
// 
//  Purpose:   Unpack a two-byte integer that has been sent least 
//             significant byte first.  All J1939 variables are sent 
//             in this fashion.
// 
// ----------------------------------------------------------------------

/*FUNCTION*/  int UnPackTwoByteIntegerLSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = ( ucIntString[1] << 8 ) | ( ucIntString[0] );
  
  return iValue;
}

// ----------------------------------------------------------------------
//  Function:  UnPackTwoByteIntegerMSB
// 
//  Purpose:   Unpack a two-byte integer that has been sent most
//             significant byte first.  An 11-bit CANID comes from the
//             RP1210 adapter in this manner.
// 
// ----------------------------------------------------------------------

/*FUNCTION*/  int UnPackTwoByteIntegerMSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = ( ucIntString[0] << 8 ) | ( ucIntString[1] );
  
  return iValue;
}

// ----------------------------------------------------------------------
//  Function:  UnPackThreeByteIntegerLSB
// 
//  Purpose:   Unpack a three-byte integer that has been sent least 
//             significant byte first.  All J1939 variables are sent 
//             in this fashion.
// 
// ----------------------------------------------------------------------

/*FUNCTION*/  int UnPackThreeByteIntegerLSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = (ucIntString[2] << 16) | ( ucIntString[1] << 8 ) | ( ucIntString[0] );
  
  return iValue;
}

// ----------------------------------------------------------------------
//  Function:  UnPackThreeByteIntegerMSB
// 
//  Purpose:   Unpack a three-byte integer that has been sent most
//             significant byte first.  This function is currently 
//             unused.
// 
// ----------------------------------------------------------------------

/*FUNCTION*/  int UnPackThreeByteIntegerMSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = (ucIntString[0] << 16) | ( ucIntString[1] << 8 ) | ( ucIntString[2] );
  
  return iValue;
}

// ----------------------------------------------------------------------
//  Function:  UnPackFourByteIntegerLSB
// 
//  Purpose:   Unpack a four-byte integer that has been sent least 
//             significant byte first.  All J1939 variables are sent 
//             in this fashion.
// 
// ----------------------------------------------------------------------

/*FUNCTION*/  int UnPackFourByteIntegerLSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = (ucIntString[3] << 24) | (ucIntString[2] << 16) | ( ucIntString[1] << 8 ) | ( ucIntString[0] );
  
  return iValue;
}

// ----------------------------------------------------------------------------------------------------
//  Function:  UnPackFourByteIntegerMSB
// 
//  Purpose:   Unpack a four-byte integer that has been sent most significant
//             byte first.  This is used by RP1210 for message timestamps and
//             29-bit CANIDs come from the RP1210 API in this manner.
// 
// ----------------------------------------------------------------------------------------------------

/*FUNCTION*/  int UnPackFourByteIntegerMSB( unsigned char *ucIntString )
{
  int iValue = 0;
  
  iValue = (ucIntString[0] << 24 )|(ucIntString[1] << 16 )|(ucIntString[2] << 8  )|(ucIntString[3] );
  
  return iValue;
}

// ----------------------------------------------------------------------------------------------------
//  Procedure:     LoadRP1210LibraryAndFunctions
// 
//  Description:   This performs the loading of an RP1210 DLL and points
//                 the global function pointers to the RP1210 functions inside of
//                 the DLL.
// 
// *----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void LoadRP1210LibraryAndFunctions( char *szDLLName )
{
  
  FILE *fp1 = stderr;

  if ( ( hRP1210DLL = LoadLibrary( szDLLName ) ) == NULL )   
    {
      fprintf(fp1,"Error:  LoadLibrary( %s ) failed! \n", szDLLName );
      exit(1);
    }
  
  pRP1210_ClientConnect = (fxRP1210_ClientConnect)(
                                                   GetProcAddress( 
                                                                  hRP1210DLL, 
                                                                  "RP1210_ClientConnect" 
                                                                  )
                                                   );
  
  if ( pRP1210_ClientConnect == NULL ) 
    {
      pRP1210_ClientConnect = (fxRP1210_ClientConnect)(
                                                       GetProcAddress( 
                                                                      hRP1210DLL, 
                                                                      "_RP1210_ClientConnect@24" 
                                                                      )
                                                       );
      
      if ( pRP1210_ClientConnect == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_ClientConnect" );
          exit(1);
        }
    }
  
  pRP1210_ClientDisconnect = (fxRP1210_ClientDisconnect)(
                                                         GetProcAddress( 
                                                                        hRP1210DLL, 
                                                                        "RP1210_ClientDisconnect" 
                                                                        )
                                                         );
  
  if ( pRP1210_ClientDisconnect == NULL ) 
    {
      pRP1210_ClientDisconnect = (fxRP1210_ClientDisconnect)(
                                                             GetProcAddress( 
                                                                            hRP1210DLL, 
                                                                            "_RP1210_ClientDisconnect@4" 
                                                                            )
                                                             );
      
      if ( pRP1210_ClientDisconnect == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_ClientDisconnect" );
          exit(1);
        }
    }
  
  pRP1210_ReadMessage = (fxRP1210_ReadMessage)(
                                               GetProcAddress( 
                                                              hRP1210DLL, 
                                                              "RP1210_ReadMessage" 
                                                              )
                                               );
  
  if ( pRP1210_ReadMessage == NULL ) 
    {
      pRP1210_ReadMessage = (fxRP1210_ReadMessage)(
                                                   GetProcAddress( 
                                                                  hRP1210DLL, 
                                                                  "_RP1210_ReadMessage@16" 
                                                                  )
                                                   );
      
      if ( pRP1210_ReadMessage == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_ReadMessage" );
          exit(1);
        }
    }
  
  pRP1210_SendMessage = (fxRP1210_SendMessage)(
                                               GetProcAddress( 
                                                              hRP1210DLL, 
                                                              "RP1210_SendMessage" 
                                                              )
                                               );
  
  if ( pRP1210_SendMessage == NULL ) 
    {
      pRP1210_SendMessage = (fxRP1210_SendMessage)(
                                                   GetProcAddress( 
                                                                  hRP1210DLL, 
                                                                  "_RP1210_SendMessage@20" 
                                                                  )
                                                   );
      
      if ( pRP1210_SendMessage == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_SendMessage" );
          exit(1);
        }
    }
  
  pRP1210_SendCommand = (fxRP1210_SendCommand)(
                                               GetProcAddress( 
                                                              hRP1210DLL, 
                                                              "RP1210_SendCommand" 
                                                              )
                                               );
  
  if ( pRP1210_SendCommand == NULL ) 
    {
      pRP1210_SendCommand = (fxRP1210_SendCommand)(
                                                   GetProcAddress( 
                                                                  hRP1210DLL, 
                                                                  "_RP1210_SendCommand@16" 
                                                                  )
                                                   );
      
      if ( pRP1210_SendCommand == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_SendCommand" );
          exit(1);
        }
    }
  
  pRP1210_ReadVersion = (fxRP1210_ReadVersion)(
                                               GetProcAddress( 
                                                              hRP1210DLL, 
                                                              "RP1210_ReadVersion" 
                                                              )
                                               );
  
  if ( pRP1210_ReadVersion == NULL ) 
    {
      pRP1210_ReadVersion = (fxRP1210_ReadVersion)(
                                                   GetProcAddress( 
                                                                  hRP1210DLL, 
                                                                  "_RP1210_ReadVersion@16" 
                                                                  )
                                                   );
      
      if ( pRP1210_ReadVersion == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_ReadVersion" );
          exit(1);
        }
    }
  
  
  
  pRP1210_ReadDetailedVersion = (fxRP1210_ReadDetailedVersion)(
                                                               GetProcAddress( 
                                                                              hRP1210DLL, 
                                                                              "RP1210_ReadDetailedVersion" 
                                                                              )
                                                               );
  
  if ( pRP1210_ReadDetailedVersion == NULL ) 
    {
      pRP1210_ReadDetailedVersion = (fxRP1210_ReadDetailedVersion)(
                                                                   GetProcAddress( 
                                                                                  hRP1210DLL, 
                                                                                  "_RP1210_ReadDetailedVersion@16" 
                                                                                  )
                                                                   );
      
      if ( pRP1210_ReadDetailedVersion == NULL ) 
        {
          fprintf(fp1,"Warning:  Could not find procedure \"%s\" in DLL (RP1210A Only?) !\n", "RP1210_ReadDetailedVersion" );
        }
    }
  
  pRP1210_GetHardwareStatus = (fxRP1210_GetHardwareStatus)(
                                                           GetProcAddress( 
                                                                          hRP1210DLL, 
                                                                          "RP1210_GetHardwareStatus" 
                                                                          )
                                                           );
  
  if ( pRP1210_GetHardwareStatus == NULL ) 
    {
      pRP1210_GetHardwareStatus = (fxRP1210_GetHardwareStatus)(
                                                               GetProcAddress( 
                                                                              hRP1210DLL, 
                                                                              "_RP1210_GetHardwareStatus@16" 
                                                                              )
                                                               );
      
      if ( pRP1210_GetHardwareStatus == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_GetHardwareStatus" );
          exit(1);
        }
    }
  
  pRP1210_GetErrorMsg = (fxRP1210_GetErrorMsg)(
                                               GetProcAddress( 
                                                              hRP1210DLL, 
                                                              "RP1210_GetErrorMsg" 
                                                              )
                                               );
  
  if ( pRP1210_GetErrorMsg == NULL ) 
    {
      pRP1210_GetErrorMsg= (fxRP1210_GetErrorMsg)(
                                                  GetProcAddress( 
                                                                 hRP1210DLL, 
                                                                 "_RP1210_GetErrorMsg@16" 
                                                                 )
                                                  );
      
      if ( pRP1210_GetErrorMsg == NULL ) 
        {
          fprintf(fp1,"Error:  Could not find procedure \"%s\" in DLL!\n", "RP1210_GetErrorMsg" );
          exit(1);
        }
    }
  
  
  pRP1210_GetLastErrorMsg = (fxRP1210_GetLastErrorMsg)(
                                                       GetProcAddress( 
                                                                      hRP1210DLL, 
                                                                      "RP1210_GetLastErrorMsg" 
                                                                      )
                                                       );
  
  if ( pRP1210_GetLastErrorMsg == NULL ) 
    {
      pRP1210_GetLastErrorMsg= (fxRP1210_GetLastErrorMsg)(
                                                          GetProcAddress( 
                                                                         hRP1210DLL, 
                                                                         "_RP1210_GetLastErrorMsg@20" 
                                                                         )
                                                          );
      if ( pRP1210_GetLastErrorMsg == NULL ) 
        {
          fprintf(fp1,"\nWarning:  Could not find procedure \"%s\" in DLL.  Must be an RP1210A, not RP1210B API?\n\n",  "RP1210_GetLastErrorMsg" );
        }
    }
  
}

// ----------------------------------------------------------------------------------------------------
//  Procedure:     PrintRP1210Error
// 
//  Description:   Call this function when an error occurs.  It will 
//                 print the #define for the error (if found) along with 
//                 the text returned from the RP1210_GetErrorMsg() function.
//                 Procedure prints to fp1 and optionally fp if not NULL.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void PrintRP1210Error( char *szInfo, short nErrorCode, FILE *fp1, FILE *fp2 )
{
  int   i = 0;
  short nFound = FALSE;
  char  szErrorText[5000];
  char  szTemp[100];
  
  typedef struct _RP1210ErrorTable
  {
    char  *szErrorMessage;
    short  nErrorCode;
  } sRP1210ErrorTable;

  sRP1210ErrorTable aRP1210ErrorTable[] = 
  {
    {"NO_ERRORS"                                        ,  0},
    {"ERR_DLL_NOT_INITIALIZED"                          ,128},
    {"ERR_INVALID_CLIENT_ID"                            ,129},
    {"ERR_CLIENT_ALREADY_CONNECTED"                     ,130},
    {"ERR_CLIENT_AREA_FULL"                             ,131},
    {"ERR_FREE_MEMORY"                                  ,132},
    {"ERR_NOT_ENOUGH_MEMORY"                            ,133},
    {"ERR_INVALID_DEVICE"                               ,134},
    {"ERR_DEVICE_IN_USE"                                ,135},
    {"ERR_INVALID_PROTOCOL"                             ,136},
    {"ERR_TX_QUEUE_FULL"                                ,137},
    {"ERR_TX_QUEUE_CORRUPT"                             ,138},
    {"ERR_RX_QUEUE_FULL"                                ,139},
    {"ERR_RX_QUEUE_CORRUPT"                             ,140},
    {"ERR_MESSAGE_TOO_LONG"                             ,141},
    {"ERR_HARDWARE_NOT_RESPONDING"                      ,142},
    {"ERR_COMMAND_NOT_SUPPORTED"                        ,143},
    {"ERR_INVALID_COMMAND"                              ,144},
    {"ERR_TXMESSAGE_STATUS"                             ,145},
    {"ERR_ADDRESS_CLAIM_FAILED"                         ,146},
    {"ERR_CANNOT_SET_PRIORITY"                          ,147},
    {"ERR_CLIENT_DISCONNECTED"                          ,148},
    {"ERR_CONNECT_NOT_ALLOWED"                          ,149},
    {"ERR_CHANGE_MODE_FAILED"                           ,150},
    {"ERR_BUS_OFF"                                      ,151},
    {"ERR_COULD_NOT_TX_ADDRESS_CLAIMED"                 ,152},
    {"ERR_ADDRESS_LOST"                                 ,153},
    {"ERR_CODE_NOT_FOUND"                               ,154},
    {"ERR_BLOCK_NOT_ALLOWED"                            ,155},
    {"ERR_MULTIPLE_CLIENTS_CONNECTED"                   ,156},
    {"ERR_ADDRESS_NEVER_CLAIMED"                        ,157},
    {"ERR_WINDOW_HANDLE_REQUIRED"                       ,158},
    {"ERR_MESSAGE_NOT_SENT"                             ,159},
    {"ERR_MAX_NOTIFY_EXCEEDED"                          ,160},
    {"ERR_MAX_FILTERS_EXCEEDED"                         ,161},
    {"ERR_HARDWARE_STATUS_CHANGE"                       ,162},
    {"ERR_INI_FILE_NOT_IN_WIN_DIR"                      ,202},
    {"ERR_INI_SECTION_NOT_FOUND"                        ,204},
    {"ERR_INI_KEY_NOT_FOUND"                            ,205},
    {"ERR_INVALID_KEY_STRING"                           ,206},
    {"ERR_DEVICE_NOT_SUPPORTED"                         ,207},
    {"ERR_INVALID_PORT_PARAM"                           ,208},
    {"ERR_COMMAND_TIMED_OUT"                            ,213},
    {"ERR_OS_NOT_SUPPORTED"                             ,220},
    {"ERR_COMMAND_QUEUE_IS_FULL"                        ,222},
    {"ERR_CANNOT_SET_CAN_BAUDRATE"                      ,224},
    {"ERR_CANNOT_CLAIM_BROADCAST_ADDRESS"               ,225},
    {"ERR_OUT_OF_ADDRESS_RESOURCES"                     ,226},
    {"ERR_ADDRESS_RELEASE_FAILED"                       ,227},
    {"ERR_COMM_DEVICE_IN_USE"                           ,230},
    {"ERR_DATA_LINK_CONFLICT"                           ,441},
    {"ERR_ADAPTER_NOT_RESPONDING"                       ,453},
    {"ERR_CAN_BAUD_SET_NONSTANDARD"                     ,454},
    {"ERR_MULTIPLE_CONNECTIONS_NOT_ALLOWED_NOW"         ,455},
    {"ERR_J1708_BAUD_SET_NONSTANDARD"                   ,456},
    {"ERR_J1939_BAUD_SET_NONSTANDARD"                   ,457},
    {"ERR_ISO15765_BAUD_SET_NONSTANDARD"                ,458},
    {NULL,999}
  };
  
  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  memset( szErrorText, 0x00, sizeof( szErrorText ) );
  memset( szTemp,      0x00, sizeof( szTemp      ) );

  pRP1210_GetErrorMsg( nErrorCode, szTemp );

  fprintf(fp1, "%s: %s\n", szInfo, szTemp );

  if( fp2 != NULL ) 
    fprintf(fp2, "%s: %s\n", szInfo, szTemp );
  
  for(i=0,nFound=FALSE; aRP1210ErrorTable[i].nErrorCode != 999;i++ )
    {
      if( aRP1210ErrorTable[i].nErrorCode == nErrorCode )
        {
          fprintf(fp1, "%-45.45s %3d \"%s\"\n", aRP1210ErrorTable[i].szErrorMessage, nErrorCode, szTemp );

          if( fp2 != NULL )
            fprintf(fp2, "%-45.45s %3d \"%s\"\n", aRP1210ErrorTable[i].szErrorMessage, nErrorCode, szTemp );

          nFound = TRUE;
          break;
        }
    }
  
  if( FALSE == nFound ) 
    {
      fprintf(fp1, "Error code undefined for error [%d].  Error Text = \"%s\"\n", nErrorCode, szTemp );

      if( fp2 != NULL )
        fprintf(fp2, "Error code undefined for error [%d].  Error Text = \"%s\"\n", nErrorCode, szTemp );
    }
}

// ----------------------------------------------------------------------------------------------------
//  Name:          SetAllFilterStatesToPass
// 
//  Description:   This procedure sets the filter states to PASS ALL.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SetAllFilterStatesToPass ( short nClientID, FILE *fp1, FILE *fp2 )
{
  short         nRetVal;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
  
  nRetVal = pRP1210_SendCommand(RP1210_Set_All_Filters_States_to_Pass,nClientID,(char *) NULL,0 );
  
  if( nRetVal != 0 )
    {
      PrintRP1210Error( "RP1210_Set_All_Filters_States_to_Pass", nRetVal, fp1, fp2 );
    }
  else
    {
      fprintf(fp1, "\nRP1210_Set_All_Filters_States_to_Pass - SUCCESS\n\n" );
      
      if( fp2 != NULL )
        fprintf(fp2, "\nRP1210_Set_All_Filters_States_to_Pass - SUCCESS\n\n" );
    }      
}

/*PROCEDURE*/ void ClaimMyJ1939Address( FILE *fp1, FILE *fp2 )
{
  short nRetVal = 0;

  // J1939 "NAME" for this sample source code application (see J1939/81)
  //    Self Configurable       =   0 = NO
  //    Industry Group          =   0 = GLOBAL
  //    Vehicle System          =   0 = Non-Specific
  //    Vehicle System Instance =   0 = First Diagnostic PC
  //    Reserved                =   0 = Must be zero
  //    Function                = 129 = Offboard Service Tool
  //    Function Instance       =   0 = First Offboard Service Tool
  //    Manufacturer Code       =  11 = Dearborn Group Technology
  //    Manufacturer Identity   =   0 = Dearborn Group Sample Source Code

  const unsigned char ucJ1939Name[8] = { 0x00, 0x00, 0x60, 0x01, 0x00, 0x81, 0x00, 0x00 };

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  memset( ucTxRxBuffer, 0x00, sizeof( ucTxRxBuffer ) );

  ucTxRxBuffer[0] = ucJ1939Address;
  ucTxRxBuffer[1] = ucJ1939Name[0];
  ucTxRxBuffer[2] = ucJ1939Name[1];
  ucTxRxBuffer[3] = ucJ1939Name[2];
  ucTxRxBuffer[4] = ucJ1939Name[3];
  ucTxRxBuffer[5] = ucJ1939Name[4];
  ucTxRxBuffer[6] = ucJ1939Name[5];
  ucTxRxBuffer[7] = ucJ1939Name[6];
  ucTxRxBuffer[8] = ucJ1939Name[7];
  ucTxRxBuffer[9] = BLOCK_UNTIL_DONE;
  
  nRetVal = pRP1210_SendCommand(RP1210_Protect_J1939_Address, nClientID, (char *)&ucTxRxBuffer[0], 10 );
  
  if( nRetVal == 0 )
    {
      fprintf(fp1, "RP1210_SendCommand(\"Protect_J1939_Address\") OK \n" );

      if( fp2 != NULL )
        fprintf(fp2, "RP1210_SendCommand(\"Protect_J1939_Address\") OK \n" );
    }
  else
    {
      PrintRP1210Error(  "Protect_J1939_Address", nRetVal, fp1, fp2 );
    }
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_J1708_Message
// 
//  Description:   This function prints a message that has been received
//                 on the J1708 data bus.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_J1708_Message (
                                        short          nClientID, 
                                        FILE          *fp1, 
                                        FILE          *fp2, 
                                        unsigned char *ucMessage, 
                                        short          nLength, 
                                        short          nJ1708RawMode, 
                                        short          nEchoTXMsgs 
                                        )
{
  int    i = 0, j = 0;
  int    iTimeStamp         = 0;
  short  nEchoByteIndex     = 0;
  short  nMIDIndex          = 0;
  short  nPIDIndex          = 0;
  short  nDataBytesIndex    = 0;
  short  nNumDataBytes      = 0;
  
  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  //
  // Bytes 0-3 are the Timestamp
  //
  iTimeStamp  = UnPackFourByteIntegerMSB( &ucMessage[0] );
  
  //
  //  If ECHO_MODE is turned on (NOT the default)
  //     TS @ [0-3], EchoByte @ [4], MID @ [5], PID @ [6], Data @ [7-(nLength-7)]
  //
  if( nEchoTXMsgs == ECHO_ON )
    {
      nEchoByteIndex     = 4;
      nMIDIndex          = 5;
      nPIDIndex          = 6;
      nDataBytesIndex    = 7;
      nNumDataBytes      = nLength - 7;
      
      fprintf(fp1,"Rx J1708 TS=[%10d] Chan=[%2d] EB=[%d] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
              iTimeStamp, nClientID, ucMessage[nEchoByteIndex], ucMessage[nMIDIndex],ucMessage[nMIDIndex],
              ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );

      if( fp2 != NULL )
        fprintf(fp2,"Rx J1708 TS=[%10d] Chan=[%2d] EB=[%d] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
                iTimeStamp, nClientID, ucMessage[nEchoByteIndex], ucMessage[nMIDIndex],ucMessage[nMIDIndex],
                ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );

    }
  //
  //  If ECHO_MODE is turned off (default)
  //     TS @ [0-3], MID @ [4], PID @ [5], Data @ [6-(nLength-6)]
  //
  if( nEchoTXMsgs == ECHO_OFF )
    {
      nMIDIndex          = 4;
      nPIDIndex          = 5;
      nDataBytesIndex    = 6;
      nNumDataBytes      = nLength - 6; 
      
      fprintf(fp1,"Rx J1708 TS=[%10d] Chan=[%2d] EB=[off] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
              iTimeStamp, nClientID, ucMessage[nMIDIndex],ucMessage[nMIDIndex],
              ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );

      if( fp2 != NULL )
        fprintf(fp2,"Rx J1708 TS=[%10d] Chan=[%2d] EB=[off] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
                iTimeStamp, nClientID, ucMessage[nMIDIndex],ucMessage[nMIDIndex],
                ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );
    }
  
  
  
  // If we are in RAW mode, we will not print the checksum as part of the data, but by itself

  if( nJ1708RawMode == RAW_MODE )
    {
      nNumDataBytes--;
    }

  fprintf(fp1,"\n\tDATA-HEX");

  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for( j=0,i=nDataBytesIndex; j < nNumDataBytes; j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }
  
  // If we are in RAW mode, there will be an extra byte which is the checksum

  if( nJ1708RawMode == RAW_MODE )
    {
      fprintf(fp1,"CSUM[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"CSUM[%02X]", ucMessage[i]);
    }
  
  fprintf(fp1,"\n");

  if( fp2 != NULL )
    fprintf(fp2,"\n");
  
  
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_Sent_J1708_Message
// 
//  Description:   This function prints a message that has been sent
//                 on the J1708 data bus.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_Sent_J1708_Message( 
                                            short          nClientID, 
                                            FILE          *fp1, 
                                            FILE          *fp2, 
                                            unsigned char *ucMessage, 
                                            short          nLength, 
                                            short          nJ1708RawMode 
                                            )
{
  int    i = 0, j = 0;
  short  nPriorityIndex     = 0;
  short  nMIDIndex          = 0;
  short  nPIDIndex          = 0;
  short  nDataBytesIndex    = 0;
  short  nNumDataBytes      = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
  
  //
  //     PRI @ [0], MID @ [1], PID @ [2], Data @ [3-(nLength-3)]
  //
  nPriorityIndex     = 0;
  nMIDIndex          = 1;
  nPIDIndex          = 2;
  nDataBytesIndex    = 3;
  nNumDataBytes      = nLength - 3; 

  fprintf(fp1,"Tx J1708 Pri=[%1d] Chan=[%2d] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
          ucMessage[nPriorityIndex], nClientID, ucMessage[nMIDIndex],ucMessage[nMIDIndex],
          ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );

  if( fp2 != NULL )
    fprintf(fp2,"Tx J1708 Pri=[%1d] Chan=[%2d] MID=[%3d|0x%02X] PID=[%3d|0x%02X] DL=[%4d]", 
            ucMessage[nPriorityIndex], nClientID, ucMessage[nMIDIndex],ucMessage[nMIDIndex],
            ucMessage[nPIDIndex],ucMessage[nPIDIndex], nNumDataBytes );
  
  // If we are in RAW mode, we will not print the checksum as part of the data, but by itself

  if( nJ1708RawMode == RAW_MODE )
    {
      nNumDataBytes--;  
    }

  fprintf(fp1,"\n\tDATA-HEX");
  
  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for(j=0, i = nDataBytesIndex; j < nNumDataBytes; j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }
  
  // If we are in RAW mode, there will be an extra byte which is the checksum

  if( nJ1708RawMode == RAW_MODE )
    {
      fprintf(fp1,"CSUM[0x%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"CSUM[0x%02X]", ucMessage[i]);

    }
  
  fprintf(fp1,"\n");

  if( fp2 != NULL )
    fprintf(fp2,"\n");
  
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_J1939_Message
// 
//  Description:   This function prints a message that has been received
//                 on the J1939 data bus.
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_J1939_Message( 
                                       short          nClientID, 
                                       FILE          *fp1, 
                                       FILE          *fp2, 
                                       unsigned char *ucMessage, 
                                       short          nLength, 
                                       short          nEchoTXMsgs 
                                       )
{
  int   i = 0, j = 0;
  int   iTimeStamp                   = 0;
  int   iPGN                         = 0;
  int   iPF                          = 0;
  int   iPS                          = 0;
  char *szPS                         = 0;
  char *bam                          = 0;
  short nPriority                    = 0;
  short nPGNIndex                    = 0;
  short nEchoByteIndex               = 0;
  short nHowPriorityIndex            = 0;
  short nSourceAddressIndex          = 0;
  short nDestinationAddressIndex     = 0;
  short nDataBytesIndex              = 0;
  short nNumDataBytes                = 0;
  char  szEchoByte[10]               = "";


  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  //
  // Initialize all array variables.  
  //
  
  memset( szEchoByte, 0x00, sizeof( szEchoByte ) );
  
  //
  // Bytes 0-3 are the Timestamp
  //
  iTimeStamp  = UnPackFourByteIntegerMSB( &ucMessage[0] );
  
  //
  //  If ECHO_MODE is turned on (NOT the default)
  //     TS @ [0-3], EchoByte @ [4], PGN @ [5-7], How/Pri @ [8], SRC @ [9], DEST @ [10], Data @ [11-(nLength-11)]
  //
  if( nEchoTXMsgs == TRUE )
    {
      nEchoByteIndex           = 4;
      nPGNIndex                = 5;
      nHowPriorityIndex        = 8;
      nSourceAddressIndex      = 9;
      nDestinationAddressIndex = 10;
      nDataBytesIndex          = 11;
      nNumDataBytes            = nLength - 11;
    }
  //
  //  If ECHO_MODE is turned off (default)
  //     TS @ [0-3], PGN @ [4-6], How/Pri @ [7], SRC @ [8], DEST @ [9], Data @ [10-(nLength-10)]
  //
  if( nEchoTXMsgs == FALSE )
    {
      nPGNIndex                = 4;
      nHowPriorityIndex        = 7;
      nSourceAddressIndex      = 8;
      nDestinationAddressIndex = 9;
      nDataBytesIndex          = 10;
      nNumDataBytes            = nLength - 10;
    }
  
  iPGN =  UnPackThreeByteIntegerLSB( &ucMessage[nPGNIndex] );
  
  if( nNumDataBytes <= 8 )
    bam = "N/A";
  else 
    {
      if( (ucMessage[nHowPriorityIndex] & 0x80) == 0x80 )
        bam = "BAM";
      else
        bam = "RTS";
    }
  
  nPriority = ucMessage[nHowPriorityIndex] & 7;
      
  iPF  = BYTE1_OF_INT4( iPGN );
  iPS  = BYTE0_OF_INT4( iPGN );
  
  if( (iPF >=   0) && (iPF <= 239) )
    szPS = "PDU1-DS";
  if( (iPF >= 240) && (iPF <= 255) )
    szPS = "PDU2-GE";
  
  
  if( nEchoTXMsgs == TRUE )
    {
      sprintf( szEchoByte,"%3d", ucMessage[nEchoByteIndex] );
    }
  else if( nEchoTXMsgs == FALSE )
    {
      strcpy( szEchoByte, "off" );
    }

  fprintf(fp1,"Rx J1939 TS=[%10d] Chan=[%2d] EB=[%s] PGN=[%5d|0x%04X] PF=[0x%02X|%3d] PS=[%s|0x%02X|%3d] HOW=[%3s] P=[%1d] SRC=[%3d] DST=[%3d] DL=[%4d]", 
          iTimeStamp, nClientID, szEchoByte, iPGN, iPGN, iPF, iPF, szPS, iPS, iPS, bam, nPriority, ucMessage[nSourceAddressIndex],
          ucMessage[nDestinationAddressIndex], nNumDataBytes );

  if( fp2 != NULL )
    fprintf(fp2,"Rx J1939 TS=[%10d] Chan=[%2d] EB=[%s] PGN=[%5d|0x%04X] PF=[0x%02X|%3d] PS=[%s|0x%02X|%3d] HOW=[%3s] P=[%1d] SRC=[%3d] DST=[%3d] DL=[%4d]", 
            iTimeStamp, nClientID, szEchoByte, iPGN, iPGN, iPF, iPF, szPS, iPS, iPS, bam, nPriority, ucMessage[nSourceAddressIndex],
            ucMessage[nDestinationAddressIndex], nNumDataBytes );

  fprintf(fp1,"\n\tDATA-HEX");

  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for(j=0, i = nDataBytesIndex; j < nNumDataBytes;j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);

    }
  
  fprintf(fp1, "\n");
  
  if( fp2 != NULL )
    fprintf(fp2, "\n");

}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_Sent_J1939_Message
// 
//  Description:   This function prints a message that has been sent
//                 on the J1939 data bus.
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_Sent_J1939_Message(
                                            short          nClientID,
                                            FILE          *fp1,
                                            FILE          *fp2,
                                            unsigned char *ucMessage, 
                                            short          nLength 
                                            )
{
  int   i = 0, j = 0;
  int   iPGN                         = 0;
  int   iPF                          = 0;
  int   iPS                          = 0;
  char *szPS                         = 0;
  char *bam                          = 0;
  short nPriority                    = 0;
  short nPGNIndex                    = 0;
  short nHowPriorityIndex            = 0;
  short nSourceAddressIndex          = 0;
  short nDestinationAddressIndex     = 0;
  short nDataBytesIndex              = 0;
  short nNumDataBytes                = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  //
  // PGN @ [0-2], How/Pri @ [3], SRC @ [4], DEST @ [5], Data @ [6-(nLength-6)]
  //
  nPGNIndex                = 0;
  nHowPriorityIndex        = 3;
  nSourceAddressIndex      = 4;
  nDestinationAddressIndex = 5;
  nDataBytesIndex          = 6;

  iPGN =  UnPackThreeByteIntegerLSB( &ucMessage[nPGNIndex] );

  nNumDataBytes = nLength - 6;
  
  if( nNumDataBytes <= 8 )
    bam = "N/A";
  else 
    {
      if( (ucMessage[nHowPriorityIndex] & 0x80) == 0x80 )
        bam = "BAM";
      else
        bam = "RTS";
    }
  
  nPriority = ucMessage[nHowPriorityIndex] & 7;

  iPF  = BYTE1_OF_INT4( iPGN );
  iPS  = BYTE0_OF_INT4( iPGN );
  
  if( (iPF >=   0) && (iPF <= 239) )
    szPS = "PDU1-DS";
  if( (iPF >= 240) && (iPF <= 255) )
    szPS = "PDU2-GE";
  
  fprintf(fp1,"Tx J1939 Chan=[%2d] PGN=[%5d|0x%04X] PF=[0x%02X|%3d] PS=[%s|0x%02X|%3d] HOW=[%3s] P=[%1d] SRC=[%3d] DST=[%3d] DL=[%4d]", 
          nClientID, iPGN, iPGN, iPF, iPF, szPS, iPS, iPS, bam, nPriority, ucMessage[nSourceAddressIndex],
          ucMessage[nDestinationAddressIndex], nNumDataBytes );

  if( fp2 != NULL )
    fprintf(fp2,"Tx J1939 Chan=[%2d] PGN=[%5d|0x%04X] PF=[0x%02X|%3d] PS=[%s|0x%02X|%3d] HOW=[%3s] P=[%1d] SRC=[%3d] DST=[%3d] DL=[%4d]", 
            nClientID, iPGN, iPGN, iPF, iPF, szPS, iPS, iPS, bam, nPriority, ucMessage[nSourceAddressIndex],
            ucMessage[nDestinationAddressIndex], nNumDataBytes );
  
  
  
  fprintf(fp1,"\n\tDATA-HEX");

  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for(j=0, i = nDataBytesIndex; j < nNumDataBytes; j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);

    }
  
  fprintf(fp1,"\n");

  if( fp2 != NULL )
    fprintf(fp2,"\n");
}

// ----------------------------------------------------------------------------------------------------
//  Name:          CANIDToPGN
// 
//  Description:   This function is used to create a PGN from a 29-bit CANID.  The "print CAN" routines
//                 uses this to print the PGN number just in case the CANID was on a J1939 data link
//                 and not on a "generic" CAN bus.
//
// ----------------------------------------------------------------------------------------------------

/*FUNCTION*/ int CANIDToPGN( int iCANID )
{
  int iPGN, iPF;

  iPGN = ( iCANID & 0x03FFFF00 ) >> 8;
 
  iPF  = ( iPGN & 0x0000FF00) >> 8;

  if ( iPF < 240 )
    iPGN = iPGN >> 8;

  return iPGN;
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_CAN_Message
// 
//  Description:   This procedure prints both 11 and 29-bit CAN messages seen on a data bus.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_CAN_Message( 
                                     short          nClientID, 
                                     FILE          *fp1, 
                                     FILE          *fp2, 
                                     unsigned char *ucMessage, 
                                     short          nLength, 
                                     short          nEchoTXMsgs 
                                     )
{
  int   i = 0, j = 0;
  int   iTimeStamp               = 0;
  char  *cantype                 = 0;
  short nMessageTypeIndex        = 0;
  short nCANIDIndex              = 0;
  short nHeaderStuff             = 0;
  short nDataBytesIndex          = 0;
  short nNumDataBytes            = 0;
  int   iCANID                   = 0;
  int   iPGN                     = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
   
  iTimeStamp  = UnPackFourByteIntegerMSB( &ucMessage[0] );
  
  if( nEchoTXMsgs == FALSE )
    {
      nMessageTypeIndex        = 4;
      nCANIDIndex              = 5;
      nHeaderStuff             = 5;
    }
  else
    {
      nMessageTypeIndex        = 5;
      nCANIDIndex              = 6;
      nHeaderStuff             = 6;
    }
  
  if( ucMessage[nMessageTypeIndex] == 0 )
    {
      nDataBytesIndex = nCANIDIndex + 2;
      cantype = "STD";
      nHeaderStuff             += 2;
      
      iCANID = UnPackTwoByteIntegerMSB( &ucMessage[nCANIDIndex] );

      iPGN = 0xFFFFFFFF;
    }
  else
    {
      nDataBytesIndex = nCANIDIndex + 4;
      cantype = "EXT";
      nHeaderStuff             += 4;
      
      iCANID =UnPackFourByteIntegerMSB( &ucMessage[nCANIDIndex] );

      iPGN = CANIDToPGN( iCANID );
    }

  nNumDataBytes = nLength - nHeaderStuff;
  
  if( nEchoTXMsgs == FALSE )
    {
      fprintf(fp1,"Rx CAN TS=[%7d] Chan=[%2d] EchoByte=[off] CAN-Type=[%s] CANID=[%7d|0x%X] PGN=[0x%05X] DataLen=[%d]", 
              iTimeStamp, 
              nClientID,
              cantype,
              iCANID,iCANID,iPGN,
              nNumDataBytes );

      if( fp2 != NULL )
        fprintf(fp2,"Rx CAN TS=[%7d] Chan=[%2d] EchoByte=[off] CAN-Type=[%s] CANID=[%7d|0x%X] PGN=[0x%05X] DataLen=[%d]", 
                iTimeStamp, 
                nClientID,
                cantype,
                iCANID,iCANID,iPGN,
                nNumDataBytes );
    }
  else
    {
      fprintf(fp1,"Rx CAN TS=[%7d] Chan=[%2d] EchoByte=[%d] CAN-Type=[%s] CANID=[%7d|0x%X] PGN=[0x%05X] DataLen=[%d]", 
              iTimeStamp, 
              nClientID,
              ucMessage[4],
              cantype,
              iCANID,iCANID,iPGN,
              nNumDataBytes );

      if( fp2 != NULL )
        fprintf(fp2,"Rx CAN TS=[%7d] Chan=[%2d] EchoByte=[%d] CAN-Type=[%s] CANID=[%7d|0x%X] PGN=[0x%05X] DataLen=[%d]", 
                iTimeStamp, 
                nClientID,
                ucMessage[4],
                cantype,
                iCANID,iCANID,iPGN,
                nNumDataBytes );
    }
  
  
  
  fprintf(fp1,"\n\tDATA-HEX");

  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for(j=0, i = nDataBytesIndex; j < nNumDataBytes; j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }
  
  fprintf(fp1, "\n");

  if( fp2 != NULL )
    fprintf(fp2, "\n");
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Print_Sent_CAN_Message
// 
//  Description:   This procedure prints both 11 and 29-bit CAN messages that have been seen on a data bus.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_Sent_CAN_Message(
                                          short          nClientID, 
                                          FILE          *fp1, 
                                          FILE          *fp2, 
                                          unsigned char *ucMessage, 
                                          short          nLength 
                                          )
{
  int   i = 0, j = 0;
  char  *cantype               = 0;
  short  nDataBytesIndex       = 0;
  short  nNumDataBytes         = 0;
  int    iCANID                = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
  
  if( ucMessage[0] == 0 )
    {
      cantype = "STD";
      nDataBytesIndex = 3;
      nNumDataBytes = nLength - 3;
      
      iCANID = ( ucMessage[1] << 8 ) | (ucMessage[2]);
    }
  else
    {
      cantype = "EXT";
      nDataBytesIndex = 5;
      nNumDataBytes = nLength - 5;
      
      iCANID =(ucMessage[1] << 24) | ( ucMessage[2] << 16 ) | (ucMessage[3] << 8 ) | ( ucMessage[4] );
    }
  
  fprintf(fp1,"Tx CAN Chan=[%2d] CAN-Type=[%s] CANID=[%7d|0x%X] DataLen=[%d]", 
          nClientID,
          cantype,
          iCANID,iCANID,
          nNumDataBytes );

  if( fp2 != NULL )
    fprintf(fp2,"Tx CAN Chan=[%2d] CAN-Type=[%s] CANID=[%7d|0x%X] DataLen=[%d]", 
            nClientID,
            cantype,
            iCANID,iCANID,
            nNumDataBytes );
  
  fprintf(fp1,"\n\tDATA-HEX");

  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");
  
  for(j=0, i = nDataBytesIndex; j < nNumDataBytes;j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }

  fprintf(fp1,"\n");

  if( fp2 != NULL )
    fprintf(fp2,"\n");
  
  
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Process_J1708_Message
// 
//  Description:   This function takes a J1708 message, prints it and then
//                 sees if the PID is XXX.  If it is, then the function 
//                 processes the message and prints the parameters
//                 in text form.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1708_Message( 
                                         short          nClientID, 
                                         FILE          *fp1, 
                                         FILE          *fp2, 
                                         unsigned char *ucMessage, 
                                         short          nLength, 
                                         short          nJ1708RawMode, 
                                         short          nEchoTXMsgs 
                                         )
{
  unsigned char     MID             = 0;
  unsigned char     PID             = 0;
  double            fEngineSpeed    = 0.0;
  double            fBarometricPressure = 0.0;
  short             nEchoByteIndex  = 0;
  short             nMIDIndex       = 0;
  short             nPIDIndex       = 0;
  short             nDataBytesIndex = 0;
  int               i               = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  //
  //  If ECHO_MODE is turned on (NOT the default)
  //     TS @ [0-3], EchoByte @ [4], MID @ [5], PID @ [6], Data @ [7-(nLength-7)]
  //
  if( nEchoTXMsgs == TRUE )
    {
      nEchoByteIndex     = 4;
      nMIDIndex          = 5;
      nPIDIndex          = 6;
      nDataBytesIndex    = 7;
    }
  //
  //  If ECHO_MODE is turned off (default)
  //     TS @ [0-3], MID @ [4], PID @ [5], Data @ [6-(nLength-6)]
  //
  if( nEchoTXMsgs == FALSE )
    {
      nMIDIndex          = 4;
      nPIDIndex          = 5;
      nDataBytesIndex    = 6;
    }

  Print_J1708_Message( nClientID, fp1, fp2, ucMessage, nLength, nJ1708RawMode, nEchoTXMsgs );

  MID = ucMessage[nMIDIndex];
  PID = ucMessage[nPIDIndex];

  i = nDataBytesIndex;

  switch( PID )
    {
      //
      // PID 190 - Engine Speed
      //
    case 190:

      fEngineSpeed = (UnPackTwoByteIntegerLSB( &ucMessage[i] ) * 0.25 ) + 0;

      fprintf(fp1,"\n");
      fprintf(fp1,"J1708 PID 190 (Engine Speed) Source Address (Message From)            = [%10d]   \n", MID            );
      fprintf(fp1,"J1708 PID 190 (Engine Speed) EngineSpeed                              = [%10.2f] \n", fEngineSpeed   );
      fprintf(fp1,"\n");

      if( fp2 != NULL )
        {
          fprintf(fp2,"\n");
          fprintf(fp2,"J1708 PID 190 (Engine Speed) Source Address (Message From)            = [%10d]   \n", MID            );
          fprintf(fp2,"J1708 PID 190 (Engine Speed) EngineSpeed                              = [%10.2f] \n", fEngineSpeed   );
          fprintf(fp2,"\n");
        }

      break;


      //
      // PID 48 - Barometric Pressure
      //
    case 48:

      fBarometricPressure = ( ucMessage[i]  * 0.087 ) + 0;

      fprintf(fp1,"\n");
      fprintf(fp1,"J1708 PID 48 (BAR Press) Source Address (Message From)            = [%10d]   \n", MID                   );
      fprintf(fp1,"J1708 PID 48 (BAR Press) Barometric Pressure                      = [%10.2f] \n", fBarometricPressure   );
      fprintf(fp1,"\n");

      if( fp2 != NULL )
        {
          fprintf(fp2,"\n");
          fprintf(fp1,"J1708 PID 48 (BAR Press) Source Address (Message From)            = [%10d]   \n", MID                   );
          fprintf(fp1,"J1708 PID 48 (BAR Press) Barometric Pressure                      = [%10.2f] \n", fBarometricPressure   );
          fprintf(fp2,"\n");
        }

      break;


      //
      // PID 108 - Barometric Pressure
      //
    case 108:

      fBarometricPressure = ( ucMessage[i]  * 0.0625 ) + 0;

      fprintf(fp1,"\n");
      fprintf(fp1,"J1708 PID 108 (BAR Press) Source Address (Message From)            = [%10d]   \n", MID                   );
      fprintf(fp1,"J1708 PID 108 (BAR Press) Barometric Pressure                      = [%10.2f] \n", fBarometricPressure   );
      fprintf(fp1,"\n");

      if( fp2 != NULL )
        {
          fprintf(fp2,"\n");
          fprintf(fp1,"J1708 PID 108 (BAR Press) Source Address (Message From)            = [%10d]   \n", MID                   );
          fprintf(fp1,"J1708 PID 108 (BAR Press) Barometric Pressure                      = [%10.2f] \n", fBarometricPressure   );
          fprintf(fp2,"\n");
        }

      break;


    default:
      break;

    }
}

// ----------------------------------------------------------------------------------------------------
//  Name:          EngineRetarderAndTorqueModes - J1939 SPN 899
// ----------------------------------------------------------------------------------------------------

/*FUNCTION*/  char *EngineRetarderAndTorqueModes( unsigned char Mode )
{
  switch( Mode )
    {
    case  0: return( "Low Idle Governor/No Request (default mode)");
    case  1: return( "Accelerator Pedal/Operator selection");
    case  2: return( "Cruise Control");
    case  3: return( "PTO Governor");
    case  4: return( "Road Speed Governor");
    case  5: return( "ASR Control");
    case  6: return( "Transmission Control");
    case  7: return( "ABS Control");
    case  8: return( "Torque Limiting");
    case  9: return( "High Speed Governor");
    case 10: return( "Braking System");
    case 11: return( "Remote Accelerator");
    case 12: return( "Service Procedure");
    case 13: return( "Not Defined");
    case 14: return( "Other");
    case 15: return( "Not Available");
    }
  return "Unknown Value";
}

// ----------------------------------------------------------------------------------------------------
//  Name:          EngineStarterMode - J1939 SPN 1675
// ----------------------------------------------------------------------------------------------------

/*FUNCTION*/  char *EngineStarterMode ( unsigned char Mode )
{
  switch( Mode )
    {
    case  0:  return "Start Not Requested";
    case  1:  return "Starter Active, Gear Not Engaged";
    case  2:  return "Starter Active, Gear Engaged";
    case  3:  return "Start Finished; Starter Not Active After Having Been Actively Engaged (After 50ms Goes to 0)";
    case  4:  return "Starter Inhibited Due to Engine Already Running";
    case  5:  return "Starter Inhibited Due to Engine Not Ready for Start (Preheating)";
    case  6:  return "Starter Inhibited Due to Driveline Engaged or Other Transmission Inhibit";
    case  7:  return "Starter Inhibited Due to Active Immobilizer";
    case  8:  return "Starter Inhibited Due to Starter Over-Temp";
    case  9:  return "Reserved";
    case 10:  return "Reserved";
    case 11:  return "Reserved";
    case 12:  return "Starter Inhibited - Reason Unknown";
    case 13:  return "Error (Legacy Implementation Only, Use 14)";
    case 14:  return "Error";
    case 15:  return "Not Available";
    }
  return "Unknown Value";
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Process_J1939_Message
// 
//  Description:   This function takes a J1939 message, prints it and then
//                 sees if the PGN is EEC1.  If it is EEC1, then the 
//                 function processes the message and prints the parameters
//                 in text form.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1939_Message( 
                                         short          nClientID, 
                                         FILE          *fp1, 
                                         FILE          *fp2, 
                                         unsigned char *ucMessage, 
                                         short          nLength, 
                                         short          nEchoTXMsgs 
                                         )
{
  int               i                                        = 0;
  int               iPGN                                     = 0;
  short             nEchoByteIndex                           = 0;
  short             nPGNIndex                                = 0;
  short             nHowPriorityIndex                        = 0;
  short             nSourceAddressIndex                      = 0;
  short             nDestinationAddressIndex                 = 0;
  short             nDataBytesIndex                          = 0;
  unsigned char     ucTmp                                    = 0;

  // PGN 61444 EEC1 Parameters
  
  char              szEngineTorqueMode[200]                  = "";
  double            fActualEnginePercentTorqueHighResolution = -1;
  double            fDriversDemandEnginePercentTorque        = -1;
  double            fActualEnginePercentTorque               = -1;
  double            fEngineSpeed                             = -1;
  unsigned char     ucSourceAddressOfEngineControl           = 0xFF;
  char              szEngineStarterMode[200]                 = "";
  double            fEngineDemandPercentTorque               = -1;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  //
  //  If ECHO_MODE is turned on (NOT the default)
  //     TS @ [0-3], EchoByte @ [4], PGN @ [5-7], How/Pri @ [8], SRC @ [9], DEST @ [10], Data @ [11-(nLength-11)]
  //
  if( nEchoTXMsgs == TRUE )
    {
      nEchoByteIndex           = 4;
      nPGNIndex                = 5;
      nHowPriorityIndex        = 8;
      nSourceAddressIndex      = 9;
      nDestinationAddressIndex = 10;
      nDataBytesIndex          = 11;
    }

  //
  //  If ECHO_MODE is turned off (default)
  //     TS @ [0-3], PGN @ [4-6], How/Pri @ [7], SRC @ [8], DEST @ [9], Data @ [10-(nLength-10)]
  //
  if( nEchoTXMsgs == FALSE )
    {
      nPGNIndex                = 4;
      nHowPriorityIndex        = 7;
      nSourceAddressIndex      = 8;
      nDestinationAddressIndex = 9;
      nDataBytesIndex          = 10;
    }

  Print_J1939_Message( nClientID, fp1, fp2, ucMessage, nLength, nEchoTXMsgs );

  iPGN =  UnPackThreeByteIntegerLSB( &ucMessage[nPGNIndex] );
  
  i = nDataBytesIndex;
  
  switch( iPGN )
    {
      //
      // EEC1 - Electronic Engine #1, 61444
      //
    case 61444:
      
      memset( szEngineTorqueMode,  0x00, sizeof( szEngineTorqueMode  ) );
      memset( szEngineStarterMode, 0x00, sizeof( szEngineStarterMode ) );

      fActualEnginePercentTorqueHighResolution = -1;
      fDriversDemandEnginePercentTorque        = -1;
      fActualEnginePercentTorque               = -1;
      fEngineSpeed                             = -1;
      ucSourceAddressOfEngineControl           = 0xFF;
      fEngineDemandPercentTorque               = -1;
      
      // SPN 899

      ucTmp = ucMessage[i] & 0x0F;
      
      strcpy( szEngineTorqueMode, EngineRetarderAndTorqueModes( ucTmp ) ); 
      
      // SPN 4154

      ucTmp = ( ( ucMessage[i] & 0xF0 ) >> 4  );
      
      if( ( ucTmp & 0x08 ) == 0x08 )
        fActualEnginePercentTorqueHighResolution = -1;
      else
        {
          fActualEnginePercentTorqueHighResolution = ( ucTmp *  0.125 ) + 0;
        }
      
      // SPN 512

      fDriversDemandEnginePercentTorque = ( ucMessage[i+1] * 1 ) + (-125);  

      // SPN 513

      fActualEnginePercentTorque        = ( ucMessage[i+2] * 1 ) + (-125);  
      
      // SPN 190

      fEngineSpeed =  (double) (UnPackTwoByteIntegerLSB( &ucMessage[i+3] ) * 0.125) + 0;
      
      // SPN 1483

      ucSourceAddressOfEngineControl = ucMessage[i+5];
      
      // SPN 1675

      ucTmp = ucMessage[i+6] & 0x0F;
      
      strcpy( szEngineStarterMode, EngineStarterMode(ucTmp) );

      // SPN 2432
      
      fEngineDemandPercentTorque = ( ucMessage[i+7] * 1 ) + (-125);
      
      fprintf(fp1,"\n");
      fprintf(fp1,"J1939 EEC1 (PGN 61444)          Source Address (Message From)            = [%10d]   \n",(int) ucMessage[nSourceAddressIndex]      );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 899 ) EngineTorqueMode                         = [%s]     \n",szEngineTorqueMode                        );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 4154) ActualEnginePercentTorqueHighResolution  = [%10.2f] \n",fActualEnginePercentTorqueHighResolution  );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 512 ) DriversDemandEnginePercentTorque         = [%10.2f] \n",fDriversDemandEnginePercentTorque         );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 513 ) ActualEnginePercentTorque                = [%10.2f] \n",fActualEnginePercentTorque                );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 190 ) EngineSpeed                              = [%10.2f] \n",fEngineSpeed                              );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 1483) SourceAddressOfEngineControl             = [%10d]   \n",ucSourceAddressOfEngineControl            );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 1675) EngineStarterMode                        = [%s]     \n",szEngineStarterMode                       );
      fprintf(fp1,"J1939 EEC1 (PGN 61444/SPN 2432) EngineDemandPercentTorque                = [%10.2f] \n",fEngineDemandPercentTorque                );
      fprintf(fp1,"\n");

      if( fp2 != NULL )
        {
          fprintf(fp2,"\n");
          fprintf(fp2,"J1939 EEC1 (PGN 61444)          Source Address (Message From)            = [%10d]   \n",(int) ucMessage[nSourceAddressIndex]      );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 899 ) EngineTorqueMode                         = [%s]     \n",szEngineTorqueMode                        );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 4154) ActualEnginePercentTorqueHighResolution  = [%10.2f] \n",fActualEnginePercentTorqueHighResolution  );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 512 ) DriversDemandEnginePercentTorque         = [%10.2f] \n",fDriversDemandEnginePercentTorque         );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 513 ) ActualEnginePercentTorque                = [%10.2f] \n",fActualEnginePercentTorque                );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 190 ) EngineSpeed                              = [%10.2f] \n",fEngineSpeed                              );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 1483) SourceAddressOfEngineControl             = [%10d]   \n",ucSourceAddressOfEngineControl            );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 1675) EngineStarterMode                        = [%s]     \n",szEngineStarterMode                       );
          fprintf(fp2,"J1939 EEC1 (PGN 61444/SPN 2432) EngineDemandPercentTorque                = [%10.2f] \n",fEngineDemandPercentTorque                );
          fprintf(fp2,"\n");
        }
      
      break;

    default:
      break;

    }//switch
}

// ----------------------------------------------------------------------------------------------------
//  Name:          Process_J1850_Message
// 
//  Description:   This function takes a J1850 message, prints it out but does nothing more with the 
//                 message since J1850 is mostly proprietary.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_J1850_Message( 
                                         short          nClientID, 
                                         FILE          *fp1, 
                                         FILE          *fp2, 
                                         unsigned char *ucMessage, 
                                         short          nLength, 
                                         short          nEchoTXMsgs 
                                         )
{
  short             nDataBytesIndex                          = 0;
  short             nNumDataBytesIndex                       = 0;
  short             nHeaderByteIndex                         = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  if( nEchoTXMsgs == FALSE )
    {
      nNumDataBytesIndex = 4;
      nHeaderByteIndex   = 6;
      nDataBytesIndex    = 9;
    }
  else
    {
      nNumDataBytesIndex = 5;
      nHeaderByteIndex   = 7;
      nDataBytesIndex    = 10;
    }

  Print_J1850_Message( nClientID, fp1, fp2, ucMessage, nLength, nEchoTXMsgs );

}

// ----------------------------------------------------------------------------------------------------
//  Name:          Process_CAN_Message
// 
//  Description:   This function takes a J1939 message, prints it and then
//                 sees if the PGN is EEC1.  If it is EEC1, then the 
//                 function processes the message and prints the parameters
//                 in text form.
// 
// ----------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Process_CAN_Message( 
                                       short          nClientID, 
                                       FILE          *fp1, 
                                       FILE          *fp2, 
                                       unsigned char *ucMessage, 
                                       short          nLength, 
                                       short          nEchoTXMsgs 
                                       )
{
  int               i                                        = 0;
  int               iTimeStamp                               = 0;
  int               iCANID                                   = 0;
  short             nDataBytesIndex                          = 0;
  short             nMessageTypeIndex                        = 0;
  short             nCANIDIndex                              = 0;
  short             nHeaderStuff                             = 0;
  char             *cantype                                  = 0;
  short             nNumDataBytes                            = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  iTimeStamp  = UnPackFourByteIntegerMSB( &ucMessage[0] );
  
  if( nEchoTXMsgs == FALSE )
    {
      nMessageTypeIndex        = 4;
      nCANIDIndex              = 5;
      nHeaderStuff             = 5;
    }
  else
    {
      nMessageTypeIndex        = 5;
      nCANIDIndex              = 6;
      nHeaderStuff             = 6;
    }
  
  if( ucMessage[nMessageTypeIndex] == 0 )
    {
      nDataBytesIndex = nCANIDIndex + 2;
      cantype = "STD";
      nHeaderStuff             += 2;
      
      iCANID = UnPackTwoByteIntegerMSB( &ucMessage[nCANIDIndex] );
      
    }
  else
    {
      nDataBytesIndex = nCANIDIndex + 4;
      cantype = "EXT";
      nHeaderStuff             += 4;
      
      iCANID =UnPackFourByteIntegerMSB( &ucMessage[nCANIDIndex] );
    }
  
  nNumDataBytes = nLength - nHeaderStuff;
  
  Print_CAN_Message( nClientID, fp1, fp2, ucMessage, nLength, nEchoTXMsgs );

  i = nDataBytesIndex;
  
  switch( iCANID )
    {

    default:
      break;

    }//switch
}

//------------------------------------------------------------------------------------------------------------------
// Function:      RequestPGN
// 
// Purpose:       Request "AddressRequestedFrom" send PGN "iRequestedPGN".
//
//------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void RequestPGN( 
                              short          nClientID, 
                              int            iRequestedPGN, 
                              FILE          *fp1,
                              FILE          *fp2,
                              unsigned char  ucSourceAddress, 
                              unsigned char  AddressRequestedFrom 
                              )
{
  int             iPGN                = -1;
  short           nRetVal             = -1;
  short           nDataBytes          = -1;
  unsigned char   ucTxRxBuffer[10]    = "";

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  memset( &ucTxRxBuffer[0], 0x00, sizeof( ucTxRxBuffer ) );
  
  iPGN            = 59904;                         // Request PGN
  ucTxRxBuffer[0] = BYTE0_OF_INT4(iPGN);           // LSB First
  ucTxRxBuffer[1] = BYTE1_OF_INT4(iPGN);
  ucTxRxBuffer[2] = BYTE2_OF_INT4(iPGN);           
  ucTxRxBuffer[3] = 0;                             // 0 = RTS/CTS, 1 = BAM
  ucTxRxBuffer[3] <<= 7;                           // Shift RTS/CTS/BAM to highest bit
  ucTxRxBuffer[3] |= 6;                            // OR in the priority
  ucTxRxBuffer[4] = ucSourceAddress;               // Source Address (us)
  ucTxRxBuffer[5] = AddressRequestedFrom;          // Who we want to respond
  ucTxRxBuffer[6] = BYTE0_OF_INT4(iRequestedPGN);  // LSB First
  ucTxRxBuffer[7] = BYTE1_OF_INT4(iRequestedPGN);
  ucTxRxBuffer[8] = BYTE2_OF_INT4(iRequestedPGN);  
  
  nDataBytes = 9;

  nRetVal = pRP1210_SendMessage(nClientID, (char *)&ucTxRxBuffer[0], nDataBytes, NULL_WINDOW, BLOCKING_IO);
  
  if( nRetVal != 0 )
    {
      PrintRP1210Error( "RP1210_SendMessage", nRetVal, fp1, fp2 );
    }
  else
    {
      Print_Sent_J1939_Message( nClientID, fp1, fp2, ucTxRxBuffer, nDataBytes );
    }
}

//----------------------------------------------------------------------------------------------------------------------
// Function:      RequestPID
// 
// Purpose:       Request "AddressRequestedFrom" send PGN "iRequestedPGN".
//
//---------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void RequestPID( 
                              short          nClientID, 
                              unsigned char  ucRequestedPID, 
                              FILE          *fp1, 
                              FILE          *fp2, 
                              unsigned char  ucSourceAddress 
                              )
{
  short           nRetVal             = -1;
  short           nDataBytes          = -1;
  unsigned char   ucTxRxBuffer[10]    = "";

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
  
  memset( &ucTxRxBuffer[0], 0x00, sizeof( ucTxRxBuffer ) );
  
  ucTxRxBuffer[0] = 8;                // Lowest Priority
  ucTxRxBuffer[1] = ucSourceAddress;  // Source Address
  ucTxRxBuffer[2] = 0x00;             // Request PID
  ucTxRxBuffer[3] = ucRequestedPID;   // Requested PID
  nDataBytes      = 4;

  nRetVal = pRP1210_SendMessage(nClientID, (char *) &ucTxRxBuffer[0], nDataBytes, NULL_WINDOW, BLOCKING_IO );
  
  if( nRetVal != 0 )
    {
      PrintRP1210Error( "RP1210_SendMessage", nRetVal, fp1, fp2 );
    }
  else
    {
      Print_Sent_J1708_Message( nClientID, fp1, fp2, ucTxRxBuffer, nDataBytes, CONVERTED_MODE );
    }
}

//--------------------------------------------------------------------------------------------------------------------
// Function:      SendJ1939PeriodicMessages
// 
// Purpose:       Function that can be used to periodically request different PGNs.
//
//--------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendJ1939PeriodicMessages( short nClientID, FILE *fp1, FILE *fp2 )
{
  RequestPGN( nClientID, 65260, fp1, fp2, ucJ1939Address, J1939_GLOBAL_ADDRESS ); // VIN requested from "all" addreses
}

//--------------------------------------------------------------------------------------------------------------------
// Function:      SendJ1850PeriodicMessages
// 
// Purpose:       Function that can be used to periodically request different PGNs.
//
//--------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendJ1850PeriodicMessages( short nClientID, FILE *fp1, FILE *fp2 )
{
  unsigned char ucData[8] = {0,0,0,0,0,0,0,0};

  // Request of MODE 1 PID 0 -> First 32 supported PIDS for OBDII

  ucData[0] = 0x68;  // Header Byte 0 
  ucData[1] = 0x6A;  // Header Byte 1
  ucData[2] = 0xF1;  // TESTER_SRC_ADDRESS
  ucData[3] = 0x01;  // MODE 1
  ucData[4] = 0x00;  // PID 0

  SendJ1850Message( nClientID, fp1, fp2, ucData, 5 );

}

//--------------------------------------------------------------------------------------------------------------------
// Function:      SendJ1708PeriodicMessages
// 
// Purpose:       Function that can be used to periodically request different PIDs.
//
//--------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendJ1708PeriodicMessages( short nClientID, FILE *fp1, FILE *fp2  )
{
  RequestPID( nClientID, 237, fp1, fp2, ucJ1587Address  ); // VIN request
  RequestPID( nClientID, 234, fp1, fp2, ucJ1587Address  ); // Software Info request
  RequestPID( nClientID, 243, fp1, fp2, ucJ1587Address  ); // Make, Model, Serno request
  RequestPID( nClientID, 194, fp1, fp2, ucJ1587Address  ); // Faults request

}

//--------------------------------------------------------------------------------------------------------------------
// Function:      SendCANPeriodicMessages
// 
// Purpose:       Function that can be used to periodically send CAN messages.
//
//--------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendCANPeriodicMessages( short nClientID, FILE *fp1, FILE *fp2 )
{
  unsigned char ucData[8] = {0,0,0,0,0,0,0,0};

  ucData[0] = 0xC5;
  ucData[1] = 0xFD;
  ucData[2] = 0x00;

  SendCANMessage( nClientID, TRUE, 0x18EA00F9, fp1, fp2, ucData, 3  ); // CAN Message requesting J1939 PGN 0x00FDC5 (ECUID)
}

//--------------------------------------------------------------------------------------------------------------------
// Function:      SendIESCANPeriodicMessages
// 
// Purpose:       Function that can be used to periodically send GMLAN @500k CAN messages.
//                This function merely calls SendCANPeriodicMessages, but could easily be
//                modified for GMLAN CAN identifiers.
//
//--------------------------------------------------------------------------------------------------------------------
 
/*PROCEDURE*/ void SendIESCANPeriodicMessages( short nClientID, FILE *fp1, FILE *fp2 )
{
  SendCANPeriodicMessages( nClientID, fp1, fp2 );
}


//------------------------------------------------------------------------------------------------------------------
// Function:      SendCANMessage
// 
// Purpose:       Function to send a CAN message.
//
//------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendCANMessage( 
                                  short          nClientID, 
                                  short          nExtendedCAN, 
                                  int            iCANID, 
                                  FILE          *fp1, 
                                  FILE          *fp2,
                                  unsigned char *ucMessage, 
                                  short          nDataLen 
                                  )
{
  short           nRetVal             = -1;
  int             i                   = 0;
  short           nDataBytes          = -1;
  unsigned char   ucTxRxBuffer[10]    = "";

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  memset( &ucTxRxBuffer[0], 0x00, sizeof( ucTxRxBuffer ) );
  
  if( nExtendedCAN )
    {
      ucTxRxBuffer[0] = 0x01;                   // 1 = 29-bit
      ucTxRxBuffer[1] = BYTE3_OF_INT4(iCANID);  // iCANID MSB
      ucTxRxBuffer[2] = BYTE2_OF_INT4(iCANID);  // iCANID 3B
      ucTxRxBuffer[3] = BYTE1_OF_INT4(iCANID);  // iCANID 2B
      ucTxRxBuffer[4] = BYTE0_OF_INT4(iCANID);  // iCANID LSB
           
      for(i=0;i<nDataLen;i++)
        {
          ucTxRxBuffer[5+i] = ucMessage[i];
        }

      nDataBytes = 5 + i;
    }
  else
    {
      ucTxRxBuffer[0] = 0x00;                   // 0 = 11-bit
      ucTxRxBuffer[1] = BYTE1_OF_INT4(iCANID);  // iCANID MSB
      ucTxRxBuffer[2] = BYTE0_OF_INT4(iCANID);  // iCANID LSB
           
      for(i=0;i<nDataLen;i++)
        {
          ucTxRxBuffer[3+i] = ucMessage[i];
        }

      nDataBytes = 3 + i;
    }

  nRetVal = pRP1210_SendMessage(nClientID, (char *)&ucTxRxBuffer[0], nDataBytes, NULL_WINDOW, BLOCKING_IO);
  
  if( nRetVal != 0 )
    {
      PrintRP1210Error( "RP1210_SendMessage", nRetVal, fp1, fp2 );
    }
  else
    {
      Print_Sent_CAN_Message( nClientID, fp1, fp2, ucTxRxBuffer, nDataBytes );
    }
}

//------------------------------------------------------------------------------------------------------------------
// Function:      SendJ1850Message
// 
// Purpose:       Function to send a J1850 message.
//
//------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void SendJ1850Message( 
                                  short          nClientID, 
                                  FILE          *fp1, 
                                  FILE          *fp2,
                                  unsigned char *ucMessage, 
                                  short          nDataLen 
                                  )
{
  short           nRetVal             = -1;
  int             i                   = 0;
  short           nDataBytes          = -1;
  unsigned char   ucTxRxBuffer[10]    = "";

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  memset( &ucTxRxBuffer[0], 0x00, sizeof( ucTxRxBuffer ) );
  
  nRetVal = pRP1210_SendMessage(nClientID, (char *)&ucTxRxBuffer[0], nDataBytes, NULL_WINDOW, BLOCKING_IO);
  
  if( nRetVal != 0 )
    {
      PrintRP1210Error( "RP1210_SendMessage", nRetVal, fp1, fp2 );
    }
  else
    {
      Print_Sent_J1850_Message( nClientID, fp1, fp2, ucTxRxBuffer, nDataBytes );
    }
}

//------------------------------------------------------------------------------------------------------------------
// Function:      Print_J1850_Message
// 
// Purpose:       Function to print a J1850  message.
//
//------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_J1850_Message( short nClientID, FILE *fp1, FILE *fp2, unsigned char *ucMessage, short nLength, short nEchoTXMsgs  )
{
  int    i = 0, j = 0, k = 0;
  int    iTimeStamp = 0;
  short  nNumDataBytesIndex = 0;
  short  nHeaderByteIndex   = 0;
  short  nDataBytesIndex    = 0;
  short  nNumDataBytes      = 0;
  
  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;

  iTimeStamp = UnPackFourByteIntegerMSB( &ucMessage[0]);
  
  if( nEchoTXMsgs == FALSE )
    {
      nNumDataBytesIndex = 4;
      nHeaderByteIndex   = 6;
      nDataBytesIndex    = 9;
    }
  else
    {
      nNumDataBytesIndex = 5;
      nHeaderByteIndex   = 7;
      nDataBytesIndex    = 10;
    }

  nNumDataBytes = UnPackTwoByteIntegerMSB( &ucMessage[nNumDataBytesIndex] );
  
  fprintf(fp1,"Rx J1850 TS=[%7d] Chan=[%2d] EchoByte=[off] MsgHeader=[%0X][%0X][%0X] DataLen=[%d]", 
          iTimeStamp, 
          nClientID,
          ucMessage[nHeaderByteIndex], ucMessage[nHeaderByteIndex+1], ucMessage[nHeaderByteIndex+2],
          nNumDataBytes );
  
  if( fp2 != NULL )
    fprintf(fp2,"Rx J1850 TS=[%7d] Chan=[%2d] EchoByte=[off] MsgHeader=[%0X][%0X][%0X] DataLen=[%d]", 
            iTimeStamp, 
            nClientID,
            ucMessage[nHeaderByteIndex], ucMessage[nHeaderByteIndex+1], ucMessage[nHeaderByteIndex+2],
            nNumDataBytes );
  
  fprintf(fp1,"\n\tDATA-HEX");
  
  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");

  for(j=0,i = nDataBytesIndex; j < nNumDataBytes; j++, i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }
  
  fprintf(fp1,"\n");
  
  if( fp2 != NULL )
     fprintf(fp2,"\n");
  
}

//------------------------------------------------------------------------------------------------------------------
// Function:      Print_Sent_J1850_Message
// 
// Purpose:       Function to print a J1850 message that has been sent.
//
//------------------------------------------------------------------------------------------------------------------

/*PROCEDURE*/ void Print_Sent_J1850_Message( short nClientID, FILE *fp1, FILE *fp2, unsigned char *ucMessage, short nLength  )
{
  int   i = 0, j = 0;
  int   iTimeStamp    = 0;
  short nNumDataBytes = 0;

  // Have to print "somewhere", stdout is just as good of a place as any

  if( fp1 == NULL )
    fp1 = stdout;
  
  fprintf(fp1,"Tx J1850 Chan=[%2d] MsgHeader=[%0X][%0X][%0X] DataLen=[%d]", 
          nClientID, 
          ucMessage[0], ucMessage[1], ucMessage[2], 
          nNumDataBytes );

  if( fp2 != NULL )
    fprintf(fp2,"Tx J1850 Chan=[%2d] MsgHeader=[%0X][%0X][%0X] DataLen=[%d]", 
            nClientID, 
            ucMessage[0], ucMessage[1], ucMessage[2], 
            nNumDataBytes );

  fprintf(fp1,"\n\tDATA-HEX");
  
  if( fp2 != NULL )
    fprintf(fp2,"\n\tDATA-HEX");

  for(j=0,i=3;j<nNumDataBytes;j++,i++)
    {
      fprintf(fp1,"[%02X]", ucMessage[i]);

      if( fp2 != NULL )
        fprintf(fp2,"[%02X]", ucMessage[i]);
    }
  
  fprintf(fp1,"\n");

  if( fp2 != NULL )
    fprintf(fp2,"\n");
}
