/* MTDEMO00: Modbus/TCP to Modbus/RTU Master/Slave converter 

    Compiler: BC++ 3.1
    Compile mode: large
    Project: user.c
             v7000.c
             ..\LIB\7188EL.Lib
             ..\LIB\TCPIPL.Lib
             ..\LIB\VCOM_NNNN.Lib, with NNNN being the lib file's version.
             ..\LIB\MBT7_NNN.Lib,	Modbus/TCP library for 7188E, 
    								NNN being the lib's version.
    
    Communication:
        Modbus Master <= Modbus/TCP => Ethernet port of 7188EX 
        <==> [Modbus Kernel] <==> COM Ports of 7188EX <==> Modbus/RTU slave device
    
    The Modbus library has 4 main functions 
        1.  One Modbus/TCP slave port
        2.  Multi Modbus/RTU slave port 
            Each COM port can be configured as a Modbus/RTU slave port 
            or other functionality.
        3.  Modbus gateway function
        4.  Share memory
        3.  Programmable Modbus/RTU master function
        4.  
        1.  1 to N port Modbus/TCP to Modbus/RTU converter
            The Ethernet port is used to receive Modbus/Request from Modbus/TCP master.
            Other COM ports are used to link to Modbus/RTU slave devices.
            When gets Modbus/TCP request, the kernel can decide to send request to 
            which COM port and return the response to the Modbus/TCP master.
        2.	X board supports Modbus/TCP protocol
            Modbus/TCP master can send request to specific NetID(station number) to
            access the X board that mount on io expansion bus.
        3.	Supports user-defined function
            The internal registers are open for X board and user-defined process.
            You can use the internal register to store some special information.
        4.  Supports Modbus/RTU master function	
            Put the value to internal register, use function "ModbusMaster2Slave", 
            you can let 7188E be a Modbus/RTU master controller. 
        
    Hardware: 7188EX 
    
    Note: to use MBT7_NNN.Lib, the project is a little different to Xserver demos.
          You must remove vModbus.c from this project.

[25,Oct,2002] by Kevin   

[13,Mar,2005] by Kevin
    Update Xserver library from 3.0.9 to 3.2.1
        Needs to add two functions: PortUserStart(in user.c), 
                                    Port9999Start(in V7000.c). 
[2006,Feb,17] by Kevin
    Use new I/O memory mapping method.                                    
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <alloc.h>

#include "module1.h"
#include "MBTCP.h"
#include "io_scan.h"


int iScanCOMPort=1;	// of Modbus Kernel

//The I/O memory mapping         
unsigned char far iMemory_DI[1000];
unsigned char far iMemory_DO[1000];
          int far iMemory_AI[800];
          int far iMemory_AO[800];
//extern int iBaud9600_Blank;          
void UserCount(void)
{
	// user's timer trigger function
	//
	// In this function cannot use any function that will use the hardware signal "clock",
	// Such as:
	//  1. ClockHigh(),ClockLow(), ClockHighLow(),
	//  2. Any EEPROM functions.
	//  3. Any 5DigitLed functions.
	//  4. Any NVRAM function.
	//  5. Any RTC function.(GetTime(),SetTime(),GetDate(),SetDate())
	//
	// refer to demo9 for example code
}
extern unsigned long ACKDELAY;
void UserInit(void)
{
/*
	In this function, user CAN:
	1. initialize user's program.
	2. set time interval for calling UserCount().
	3. set initial value of I/O or variables for UserLoopFun().
	4. set initial value of I/O or variables for another functions.
	5. change the default TCP PORT 10000/9999/502 to others value. [after vcom3004.lib]
	   Syntax:
    	Port10000=newport_10000;  for calling UserCmd       (user.c)
    	Port9999=newport_9999;    for calling VcomCmd7000   (v7000.c)
    	Port502=newport_502;      for calling VcomCmdModbus (vModbus.c) [after vcom3002.lib]
	    PortUser=newport_User;    for calling VcomCmdUser   (user.c)    [after vcom3005.lib]
	    
		Default port value:
		Port10000=10000;
		Port9999=9999;
		Port502=502;
		PortUser=0;
		If the port value is 0, Xserver will not 
		listen that port. That means the port will 
		be disable.
		
*/	
	int iRet,iPort,i,iValue;	
  extern int _CpuSpeed;   //40=40M, others=80M

	ACKDELAY=200;
	//======= Begin of Modbus Kernel =======

  for(i=0;i<800;i++)
    iMemory_AO[i]=0;
  for(i=0;i<1000;i++)
    iMemory_DO[i]=0;
  
	iRet=InitModbus(iMemory_DI,iMemory_DO,iMemory_AI,iMemory_AO);
	if(iRet==0)
	{
		// Initial Modbus configuration success.
	}
	else
	{
		// Initial Modbus configuration failure.
	}
	/*
	For 40M CPU.
	RX of COM4 uses INT 6, RX of COM0 DMA uses INT6. 
	When plug 87K module, INT6 is necessary for COM0 DMA.
	Then COM4 can not use INT6 any more.
	You have to call EnableMonitoCom4 to use system timer0 and INT9
	to poll the RX of COM4.
	
	And then following functions must be notified
	    1. InstallUserTimerFun  
	        <== use INT9. Cann't be used in the situation.
	            You can call InstallUserTimerFun_ms or InstallUserTimerFun_us to 
	            replace the function.
	        
	    2. InstallUserTimerFun_ms, InstallUserTimerFun_us 
	        <== use system timer0. But Delay also use timer0 and it is already called
	            in I/O scan library.
	            Thus you have to call SetUserTimer to change to timer1 first before
	            calling either of the two functions.
	        
	    3. i8017H_AD_TimerINT, i8017H_AD_TimerINT_Scan
	        <== use system timer0, but I/O scan library doesn't call the two functions.    
	*/
	if(iTotalCOMPort==4)
        if(_CpuSpeed==40)
            EnableMonitorCom4();    
	
	Init_IO_Scan(iMemory_DI,iMemory_DO,iMemory_AI,iMemory_AO);
	//======= End of Modbus Kernel=======
	Port9999=0;	//Disable listening TCP port 9999 to speed up 7188E.

}


int iNet_Status=0,iOffset_Status=0;

void UserLoopFun(void)
{
	//======= Begin of Modbus Kernel =======
	UpdateIOModule();
  if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Slave)
    CheckModbusRTURequest(iScanCOMPort);		// Is any request from Modbus/RTU Master ?
	    
	if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusASCII_Slave)
	    CheckModbusASCIIRequest(iScanCOMPort);		// Is any request from Modbus/ASCII Master ?
	    
	if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Gateway)
	{
		SendModbusRequest(iScanCOMPort);		// Passes request to modbus slave device.
		CheckResponseTimeout(iScanCOMPort);		// If response timeout, sets iModbusAction
												// to IDLE status.
		CheckModbusResponse(iScanCOMPort);		// Is any response from modbus slave device?
		SendModbusResponse(iScanCOMPort);		// Passes response to Modbus/RTU Master.
	}
	iScanCOMPort++;
	if(iScanCOMPort==2)
	    iScanCOMPort=3;

	if(iScanCOMPort>iTotalCOMPort)
		iScanCOMPort=1;
		
    if(iOffset_Status==0)
	    iNet_Status=SMMI_Net_CheckIn();
	  
	if(iNet_Status==0)    
	    iOffset_Status=SMMI_Offset_CheckIn();	
	    
	//printCom1("Memory left %lu bytes\n\r",(unsigned long)coreleft());		
	//======= End of Modbus Kernel =======
	
	//if(iModbusRequest_Fun)
	//{
	//    printCom1("FC%02d ",iModbusRequest_Fun);
	//    iModbusRequest_Fun=0;
	//}
}

int UserCmd(unsigned char *Cmd,unsigned char *Response)
{
	int iRet;
	
	//======= Begin Modbus Kernel =======	
	if(Cmd[0]=='!')
	{
		iRet=Configuration(Cmd,Response);
		if(iRet==0)
			return 1;	/* return ok */
		else 
			return 0;	/* return ERROR */
	}
	//======= End Modbus =======
	
	else 
		return 1;	/* return ok */
}

int VcomUserBinaryCmd(TCPREADDATA *p)
{
 /* VXCOMM.EXE 2.6.12(09/04/2001) or later will support this function.

		TCP PORT 10000, command 23 will call this function.
		user can get the following message:
		p->ReadUartChar : the buffer store the command data(include "23")
		p->Length : the command data length(include the two byte "23")
		p->Socket : the socket number that receive the command, that is when the user function want
								return message to the client, just use the socket to send data.
								use:  VcomSendSocket(p->Socket,pdata,datalength);
 */
	VcomSendSocket(p->Socket,"User-defined command(23)",24);	// return 24 bytes.
	return 1;	/* any value will be accept */
}

int VcomCmdUser(TCPREADDATA *p)  
{ 
	/*
    VCOM3005 (Feb,22,2002) or later will call this function for PortUser.
    
    When packets received by TCP PORT PortUser(user defined) 
    of 7188E/8000E, Xserver will call this function.
    user can get the following message:
    p->ReadUartChar : the buffer store the command data.
                      Maximum length of p->ReadUartChar is 32767 bytes.
    p->Length : the command data length.
    p->Socket : the socket number that receive the command, that is when the user function wants 
                return message to the client, just use the socket to send data.
                usage:  VcomSendSocket(p->Socket,pdata,datalength);
	*/
  
	/* here just send back the command to the client. */
	VcomSendSocket(p->Socket,p->ReadUartChar,p->Length);
 	return 1;	/* any value will be accept */	
}

void PortUserStart(int skt)
{
    /* 
    XS8_3200.Lib Version 3.2.00 (20,Apr,2004) 
    or later version supports this function.
    When a TCP/IP client connects to the 7188E/8000E via the user's defined port(PortUser), 
    the Xserver calls the function once.
    
    You can use function VcomSendSocket to send a message to the client 
    when a connection is established.
    
    For example:
        VcomSendSocket(skt,"Connection is established.",26); //return 26 bytes.
        
        skt: socket number assigned to the TCP/IP client.
    */
    
    skt=skt; //do nothing
}

void UserEnd(void)
{
	//Print("Call UserEnd().\r\n");
	StopUserTimerFun();
} 

void Modbus_Request_Event(char* CommandData,int* CommandLength)
{   
    /*
    Modbus_Request_Event is supported since version 1.6.8 [2007,03,13]. 
    
    char* CommandData: For Modbus/TCP, it includes 6 leading bytes. (needful) 
                       For Modbus/RTU, it includes 6 leading bytes. (needless) 
                       
    int* CommandLength: For Modbus/TCP, it includes 6 leading bytes. 
                        For Modbus/RTU, it includes 6 leading bytes.                      
    */
    /* Example code */
    /*int i;    
    printCom1("FC:%2d  StartAddress:%3d  IOCount:%4d\n\r",iModbusRequest_Fun, iModbusRequest_Addr,iModbusRequest_IOCount);
    printCom1("Modbus Request\n\r    In==>");
	for(i=0;i<*CommandLength;i++)
        printCom1("[%02X] ",CommandData[i]&0xFF);*/
}    

void Modbus_Response_Event(char* ResponseData,int* ResponseLength)
{
    /*
    char* ResponseData: For Modbus/TCP, it includes 6 leading bytes. 
                        For Modbus/RTU, it doesn't include 6 leading bytes
                       
    int* CommandLength: For Modbus/TCP, it includes 6 leading bytes. 
                        For Modbus/RTU, it doesn't include 6 leading bytes 
    */
    //If you want to change the content of the ResponseData, 
    //you have to do 2 steps for Modbus/TCP or Modbus/RTU.
    
    //Step1: Change content (Note:you must know the modbus protocol well)
    //ResponseData[6]=0x19;
    //ResponseData[7]=0x75;
    //ResponseData[8]=0x04;
    //ResponseData[9]=0x01;
    
    //Step2: Update data length
    //*ResponseLength=10;
    //int i;
    //printCom1("\n\r    Out==>");
	//for(i=0;i<*ResponseLength;i++)
    //    printCom1("[%02X] ",ResponseData[i]&0xFF);
    //printCom1("\n\r");  
}
