// ModbusAscii.c
//						for Modbus Ascii (MASTER) protocol.


#define _ModbusAsciiM_C


#include <s_addr.h>
#include <kservice.h>
#include "../struct.h"


#define BlockReadWrite	60			// words.

unsigned char Rxlength=17, CommandStatus=ReadResponse;

unsigned char CheckCodes[4];		// 0, 1 --- PLC Station No., 2, 3 ---- Function code.



// ****************************************************************************************************
// Return Value:
//			RespOK							-> OK
//			Unknow							-> Error
//			OtherError(0x07,02,03,01)		-> Error Code
//			ControllerCheckSumError			-> check sum error
//			NoResponse						-> Time out
// ****************************************************************************************************
unsigned char Receive_Response(HNDL handle, unsigned char *pBuffer, unsigned char *pSize, U32 Timeout)
{
	unsigned char RxChar232;
	unsigned char LastResponse, RxPtr, RespSum, SumBuffer, Index;

	unsigned long StartTimeStamp;

	int i;

	STATUS Stat;
	U32 dwBytesRead;		

	RxPtr = 0;
	LastResponse = Busy;

	StartTimeStamp = sc_getTimeStamp();

					//  Read :	0		1 2				3 4			5 6				----	--			-	-
	while(1)		//			STX		Station_No.		Function	Bytes_counter	Data	LRC16(HL)	CR	LF
 	{				// Write :	0       1 2				3 4			5 6	7 8			9 10 11 12			13 14		15	16
					//			STX		Station_No.		Function	Address(HL)		counter(HL)/Data	LRC16(HL)	CR	LF	
		Stat = COMM_ReadData(handle, &RxChar232, 1, &dwBytesRead);
		
		if ((Stat == COMM_OK) && (dwBytesRead == 1))
		{
			if (LastResponse == Busy)		// 2004.06.17.
			{
				pBuffer[RxPtr++] = RxChar232;
				switch (RxPtr)
				{
				case 1:	// STX
						if (RxChar232 != ':')		RxPtr = 0;
						break;

				case 2: // Station_No.
				case 3: // 
				case 5:	//
						if (RxChar232 != CheckCodes[RxPtr-2])	LastResponse = Unknow;
						break;

				case 4:	// Function
						if (RxChar232=='8' || RxChar232=='9')	
						{
							if (CommandStatus == ReadResponse)
								Rxlength = 11; //15;
							else Rxlength = 11; // 17;
						}
						break;

				default:
						break;
				}

				if (RxPtr >= Rxlength)
				{
					if (pBuffer[Rxlength-2]!=0x0D || pBuffer[Rxlength-1]!=0x0A)
					{
						LastResponse = Unknow;
					}
					else
					{
						for (i = 1 ; i < (Rxlength-2) ; i++)  {
							if (pBuffer[i] > '9')  pBuffer[i] -= 0x37;
							else  pBuffer[i] -= 0x30;
						}
								// LRC
						RespSum = 0;
						for (i = 1 ; i < (Rxlength-4) ; i++)  {
							if (i%2)  RespSum += (pBuffer[i] << 4);	// *16
							else  RespSum += pBuffer[i];
						}
						RespSum = ~RespSum + 1;

						SumBuffer = (pBuffer[Rxlength-4]<<4) + pBuffer[Rxlength-3];
						if (SumBuffer != RespSum)		
							LastResponse = ControllerCheckSumError;
						else
						{
							if (pBuffer[3]==8 || pBuffer[3]==9)
							{
								if (CommandStatus == ReadResponse)
									Index = 6; //10;
								else
									Index = 6; //12;

								if (pBuffer[Index]==1 || pBuffer[Index]==4)
									LastResponse = CommandError;
								else
								if (pBuffer[Index] == 2)
									LastResponse = AddressError;
								else
								if (pBuffer[Index] == 3)
									LastResponse = ValueError;
								else
								if (pBuffer[Index] == 6)
									LastResponse = ControllerBusy;
								else
								if (pBuffer[Index] == 5)
								{
									RxPtr = 0;
								}
								else
									LastResponse = Unknow;
							}
							else
								LastResponse = RespOK;
						}
					}
				} //if (RxPtr >= Rxlength)
			} //if (LastResponse == Busy)	
		} // if ((Stat == COMM_OK) && (dwBytesRead == 1))
 	
		if (LastResponse != Busy)
		{
			if (LastResponse==RespOK || (sc_getTimeStamp() - StartTimeStamp) > Timeout)
			{
				*pSize = RxPtr;
				return LastResponse;
			}
		}
		else
 		if ((sc_getTimeStamp() - StartTimeStamp) > Timeout)
		{
			return NoResponse; // Time Out
		}
	} // while(1)
}

// ========== Modbus Ascii protocol == Word No. =======	PLC Address =================
#define WORD_DEVICE_O	0x00		// 40001~50000 
#define WORD_DEVICE_I	0x01		// 30001~40000

#define BIT_DEVICE_O	0x80		//     1~10000
#define BIT_DEVICE_I	0x81		// 10001~20000

// ==================================================================================
// input  : *CommArg
// output : Address
void GetModbusAddr(COMM_ARGUMENT *CommArg, unsigned short *Address)
{
	unsigned short device_no, offset;
	unsigned char  Device_Addr_Type;
	
	Device_Addr_Type = CommArg->Device_Addr[DEVICE_NAME_BYTE];
	if (Device_Addr_Type>BIT_DEVICE_I
				|| (Device_Addr_Type>WORD_DEVICE_I && Device_Addr_Type<BIT_DEVICE_O))
	{
		*Address = 0xFFFF;
		return;
	}

	if (CommArg->Access_Offset == 0)
	{
		offset = 0;
	}
	else
	{
		switch (CommArg->Access_Type)
		{
		case Bit_Length:
				if (Device_Addr_Type < BIT_DEVICE_O)	// words is error
					offset = 0xFFFF;
				else
					offset = CommArg->Access_Offset;	// bits
				break;
		
		case Byte_Length:
				if (Device_Addr_Type >= BIT_DEVICE_O)
					offset = (CommArg->Access_Offset) << 3;		// bits
				else											// words
					offset = ((CommArg->Access_Offset) >> 1) + ((CommArg->Access_Offset) % 2);
				break;
			
		case Word_Length:
		case LSB_Length:
				if (Device_Addr_Type >= BIT_DEVICE_O)
					offset = (CommArg->Access_Offset) << 4;		// bits
				else											// words
					offset = CommArg->Access_Offset;
				break;
			
		case DWord_Length:
				if (Device_Addr_Type >= BIT_DEVICE_O)
					offset = (CommArg->Access_Offset) << 5;		// bits
				else											// words
					offset = (CommArg->Access_Offset) << 1;
				break;
			
		default:
				offset = 0xFFFF;
				break;
		}
	}			

	if (offset != 0xFFFF)
	{
		if ((CommArg->Device_Addr[DEVICE_NAME_BYTE])>=BIT_DEVICE_O)
			device_no = (unsigned short)(CommArg->Device_Addr[BIT_NO_BYTE] + (CommArg->Device_Addr[BIT_NO_BYTE+1] << 8));
		else
			device_no = (unsigned short)(CommArg->Device_Addr[WORD_NO_BYTE] + (CommArg->Device_Addr[WORD_NO_BYTE+1] << 8));
		
		switch (Device_Addr_Type)
		{
		case WORD_DEVICE_O:
					*Address = (device_no - 40001) + offset;
					break;

		case WORD_DEVICE_I:
					*Address = (device_no - 30001) + offset;
					break;

		case BIT_DEVICE_O:
					*Address = device_no + offset;
					break;

		case BIT_DEVICE_I:
					*Address = (device_no - 10000) + offset;
					break;
		}
	}
	else  // offset == 0xFFFF
	{
		*Address = 0xFFFF;
	}
}

// ==========================================================================================================
void ModbusAddrLabel(COMM_ARGUMENT *LabelcommArg, ERROR_CODE *LabelerrorCode)
{

	LabelerrorCode->StationNo = LabelcommArg->Device_Addr[STATION_NO_BYTE];
	//	" ____, ____ " 
	//	" ____, ____ "
	LabelerrorCode->AddrLabel[0] = LabelerrorCode->AddrLabel[1] = LabelerrorCode->AddrLabel[2] = LabelerrorCode->AddrLabel[3] = ' ';


	if (LabelcommArg->Device_Addr[DEVICE_NAME_BYTE] & 0x80)
	{ //Bit
		LabelerrorCode->AddrNum = (unsigned short)((LabelcommArg->Device_Addr[BIT_NO_BYTE+1]<<8) + 
														LabelcommArg->Device_Addr[BIT_NO_BYTE]);
	}
	else
	{ //Word
		LabelerrorCode->AddrNum = (unsigned short)((LabelcommArg->Device_Addr[WORD_NO_BYTE+1]<<8) + 
														LabelcommArg->Device_Addr[WORD_NO_BYTE]);
	}
}

// ==================================================================================
BOOL read_command(HNDL comPortHandle, COMM_ARGUMENT *commArg, ERROR_CODE *errorCode)
{ 
	unsigned char uSend[17];
	unsigned char uReceive[251];		// 251 = 11 + 60 * 4
	unsigned char readSize, responseStat, RetryCtr=0;

	unsigned short uAddress;

	unsigned int uSum, sentSize, i;
	unsigned int BitsWords, WordsLength, nBctr, DataIndex=0;
	
	BOOL status;
	

	GetModbusAddr(commArg, &uAddress);
	if (uAddress == 0xFFFF)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = AddressError;
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}


//	 0	  1	 2		 3	4		 5 6 7 8		9 10 11 12		13 14	15		16
//	 :																	0x0D	Ox0A
//	STX	Station NO.	Function	[Address(HL)	Counter(HL)		LRC16]	CR		LF
// =============================================================================
//1. package up the command packet
	uSend[0] = 0x3A;	// Heading
	
	uSend[15] = 0x0D;	// End
	uSend[16] = 0x0A;

	// Slave address
	uSend[1] = ((commArg->Device_Addr[STATION_NO_BYTE])>>4) & 0x0F;
	uSend[2] = (commArg->Device_Addr[STATION_NO_BYTE]) & 0x0F;
	
	// Function
	switch (commArg->Access_Type)
	{
	case Bit_Length:
		if (commArg->Device_Addr[DEVICE_NAME_BYTE]  < BIT_DEVICE_O)
		{
			ModbusAddrLabel(commArg, errorCode);
			errorCode->ErrorType = AddressError;
			//errorCode->AddrNum = uAddress;
			return False;
		}

		uSend[3] = 0;
		if (commArg->Device_Addr[DEVICE_NAME_BYTE]  == BIT_DEVICE_O)
			uSend[4] = 1;
		else
			uSend[4] = 2;

		BitsWords = commArg->Access_Length;		// points = bits

		if ((BitsWords%16) != 0)
			WordsLength = (BitsWords >> 4) + 1; // (BitsWords/16)+1 words
		else
			WordsLength = (BitsWords >> 4);		// (BitsWords/16)   words
		break;

	case Byte_Length:
	case Word_Length:
	case LSB_Length:
	case DWord_Length:	
		if (commArg->Device_Addr[DEVICE_NAME_BYTE] >= BIT_DEVICE_O)
		{
			uSend[3] = 0;
			if (commArg->Device_Addr[DEVICE_NAME_BYTE]  == BIT_DEVICE_O)
				uSend[4] = 1;
			else
				uSend[4] = 2;
				
			if (commArg->Access_Type == Byte_Length)
				BitsWords = commArg->Access_Length << 3;  // number of bits (x8)
			else 
			if ((commArg->Access_Type==Word_Length) || (commArg->Access_Type==LSB_Length))
				BitsWords = commArg->Access_Length << 4;  // number of bits (x16)
			else // DWord
				BitsWords = commArg->Access_Length << 5;  // number of bits (x32)

			if (BitsWords % 16)
				WordsLength = (BitsWords >> 4) + 1;
			else
				WordsLength = BitsWords >> 4;
		}
		else
		{
			uSend[3] = 0; 
			if (commArg->Device_Addr[DEVICE_NAME_BYTE]  == WORD_DEVICE_O)	
				uSend[4] = 3;
			else
				uSend[4] = 4;

			if (commArg->Access_Type == Byte_Length)
				BitsWords = ((commArg->Access_Length)/2) + ((commArg->Access_Length)%2);  // number of words
			else 
			if ((commArg->Access_Type == Word_Length) || (commArg->Access_Type == LSB_Length))
				BitsWords = commArg->Access_Length;		   // number of words
			else // DWord
				BitsWords = (commArg->Access_Length) << 1; // number of words

			WordsLength = BitsWords;
		}
		break;

	default:
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = AddressError;
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}
	
	CommandStatus = ReadResponse;
	nBctr = BlockReadWrite;  // 60 words.
	do {
		if (WordsLength > nBctr)		  			// if data length more than block length
			WordsLength -= nBctr;					// divide several frame to read
		else { nBctr = WordsLength, WordsLength = 0; }	// last frame
		
		uSend[5] = (unsigned char)(uAddress >> 12) & 0x0F; 
		uSend[6] = (unsigned char)(uAddress >>  8) & 0x0F;
		uSend[7] = (unsigned char)(uAddress >>  4) & 0x0F;
		uSend[8] = (unsigned char)(uAddress) & 0x0F;

		if (commArg->Device_Addr[DEVICE_NAME_BYTE]>=BIT_DEVICE_O && BitsWords==1)		// 1 Coil
		{
			uSend[9]= uSend[10]= uSend[11]= 0; 
			uSend[12]= 1; 
		}
		else
		{
			if (commArg->Device_Addr[DEVICE_NAME_BYTE] >= BIT_DEVICE_O)
				i = nBctr << 4;				// Coils
			else
				i = nBctr;					// Words

			uSend[9]  = (unsigned char)(i >> 12) & 0x0F; 
			uSend[10] = (unsigned char)(i >>  8) & 0x0F;
			uSend[11] = (unsigned char)(i >>  4) & 0x0F;
			uSend[12] = (unsigned char)(i) & 0x0F;
		}
	
		// LRC
		uSum = 0;
		for (i = 1 ; i < 13 ; i++)  {
			if (i%2)  uSum += (uSend[i] << 4);	// *16
			else  uSum += uSend[i];
		}
		uSum = ~uSum + 1;

		uSend[13] = (uSum>>4) & 0x0F;
		uSend[14] = uSum & 0x0F;

	    for (i = 1 ; i < 15 ; i++)  {
		    if (uSend[i] > 9)  uSend[i] += 0x37;
		    else  uSend[i] += 0x30;
	    }
	    for (i = 0 ; i < 4 ; i++)
		    CheckCodes[i] = uSend[i+1];

		if (commArg->Device_Addr[DEVICE_NAME_BYTE]>=BIT_DEVICE_O && BitsWords==1)
			Rxlength = 13;
		else 
			Rxlength = 11 + (nBctr << 2);	// * 4
		RetryCtr = 0;

// =======================================================================================
ReSendReadCommand:
//2.Send the command package
		if (COMM_WriteData(comPortHandle, uSend, 17, &sentSize, GeneralTimeOutValue) == COMM_OK)
		{
		}
		else
		{
			COMM_Flush(comPortHandle, RECEIVER);
			COMM_Flush(comPortHandle, TRANSMITTER);

			ModbusAddrLabel(commArg, errorCode);
			errorCode->ErrorType = UARTCommunicateFail;
			//errorCode->AddrNum = uAddress;
			return FALSE;
		}

//3. Receive the response
		responseStat = Receive_Response(comPortHandle, uReceive, &readSize, commArg->TimeOut_Time);   // 300);

		switch (responseStat)
 		{
		case RespOK: 
//4. Return the interget Hex value.
			status = TRUE;

			if (commArg->Device_Addr[DEVICE_NAME_BYTE] >= BIT_DEVICE_O) 
			{
				if (BitsWords == 1)
					commArg->Access_Value[0] = (uReceive[8] & 0x01);	
				else
				{
					for (i = 0 ; i < nBctr ; i++)
					{
					// multi bits reading response bug 0131 2005 HsiaoML
						//commArg->Access_Value[DataIndex++] = (uReceive[i*4+6]<<4) + (uReceive[i*4+7]);
						//commArg->Access_Value[DataIndex++] = (uReceive[i*4+8]<<4) + (uReceive[i*4+9]);
						commArg->Access_Value[DataIndex++] = (uReceive[i*4+7]<<4) + (uReceive[i*4+8]);
						commArg->Access_Value[DataIndex++] = (uReceive[i*4+9]<<4) + (uReceive[i*4+10]);
					}
				}
				if (WordsLength > 0)	uAddress += (nBctr << 4);	// bit address
			}	
			else				
			{	// ABCD... Hi->Low
				for (i = 0 ; i < nBctr ; i++)		// words
				{
					commArg->Access_Value[DataIndex++] = (uReceive[i*4+9]<<4) + (uReceive[i*4+10]); // Low
					commArg->Access_Value[DataIndex++] = (uReceive[i*4+7]<<4) + (uReceive[i*4+8]);  // Hi
				}
				if (WordsLength > 0)
				{
					uAddress += nBctr;		// word address
					for (i = 1 ; i < 5 ; i++)  {
						if (uSend[i] > '9')  uSend[i] -= 0x37;
					    else  uSend[i] -= 0x30;
					}
				}
			}
			break;

		case ControllerCheckSumError:
		case Unknow:		
		case NoResponse: 
			if (RetryCtr++ < commArg->Retry)    // 3)
				goto ReSendReadCommand;

			status = FALSE;
			break;

		default: 
			status = FALSE;
			break;
		}
	} while (WordsLength>0 && status==TRUE);

	if (status == FALSE)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = responseStat;
		//errorCode->AddrNum = uAddress;
	}

	if (commArg->Delay_Time != 0)
	{
		sc_sleep(commArg->Delay_Time);
	}

	return status;
}

// ==================================================================================
BOOL force_command(HNDL comPortHandle, COMM_ARGUMENT *commArg, ERROR_CODE *errorCode)
{
	unsigned char uSend[17];
	unsigned char uReceive[17];
	unsigned char readSize, responseStat, RetryCtr=0;

	unsigned short uAddress;

	unsigned int uSum, sentSize, i;

	BOOL status;
	
	
	GetModbusAddr(commArg, &uAddress);
	if (uAddress == 0xFFFF || (commArg->Device_Addr[DEVICE_NAME_BYTE])!=BIT_DEVICE_O)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (AddressError | WriteError);
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}

//	 0	  1	 2		 3	4		5 6	7 8		9 10 11 12	13 14	15		16
//	 :															0x0D	Ox0A
//	STX	Station NO.	Function	Address		Data		LRC16	CR		LF
// =============================================================================
//1. package up the command packet
	uSend[0] = 0x3A;	// Heading
	
	uSend[15] = 0x0D;	// End
	uSend[16] = 0x0A;
	
	uSend[1] = ((commArg->Device_Addr[STATION_NO_BYTE])>>4) & 0x0F;		// Slave address
	uSend[2] = (commArg->Device_Addr[STATION_NO_BYTE]) & 0x0F;
	
	uSend[3] = 0;		// Function
	uSend[4] = 5;

	uSend[5] = (unsigned char)(uAddress >> 12) & 0x0F;	// Starting address
	uSend[6] = (unsigned char)(uAddress >>  8) & 0x0F;
	uSend[7] = (unsigned char)(uAddress >>  4) & 0x0F;
	uSend[8] = (unsigned char)(uAddress) & 0x0F;

	//Force data
	if (commArg->Access_Value[0] != 0) // TURN_ON
	{
		uSend[9]  = 0x0F;
		uSend[10] = 0x0F;
	}
	else // TURN_OFF
	{
		uSend[9]  = 0x00;
		uSend[10] = 0x00;
	}
	uSend[11] = 0x00;
	uSend[12] = 0x00; 

	// LRC
	uSum = 0;
	for (i = 1 ; i < 13 ; i++)  {
		if (i%2)  uSum += (uSend[i] << 4);	// *16
		else  uSum += uSend[i];
	}
	uSum = ~uSum + 1;

	uSend[13] = (unsigned char)(uSum>>4) & 0x0F;
	uSend[14] = (unsigned char)(uSum) & 0x0F;

    for (i = 1 ; i < 15 ; i++)  {
	    if (uSend[i] > 9)  uSend[i] += 0x37;
	    else  uSend[i] += 0x30;
    }
    for (i = 0 ; i < 4 ; i++)
	    CheckCodes[i] = uSend[i+1];

	Rxlength = 17;

	CommandStatus = WriteResponse;
	RetryCtr = 0;

// ======================================================================================
ReSendForceCommand:
//2.Send the command package
	if (COMM_WriteData(comPortHandle, uSend, 17, &sentSize, GeneralTimeOutValue) == COMM_OK)
	{
	}
	else
	{
		COMM_Flush(comPortHandle, RECEIVER);
		COMM_Flush(comPortHandle, TRANSMITTER);

		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (UARTCommunicateFail | WriteError);
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}

//3. Receive the response
	responseStat = Receive_Response(comPortHandle, uReceive, &readSize, commArg->TimeOut_Time);   // 300);

	switch (responseStat)
 	{
	case RespOK: 
		status = TRUE;
		break;

	case ControllerCheckSumError: 
	case Unknow:		
	case NoResponse: 
		if (RetryCtr++ < commArg->Retry)   // 3)
			goto ReSendForceCommand;

		status = FALSE;
		break;
		
	default:
		status = FALSE;
		break;
	}

	if (status == FALSE)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (responseStat | WriteError);
		//errorCode->AddrNum = uAddress;
	}

	if (commArg->Delay_Time != 0)
	{
		sc_sleep(commArg->Delay_Time);
	}

	return status;
}

// ==================================================================================
BOOL write_command(HNDL comPortHandle, COMM_ARGUMENT *commArg, ERROR_CODE *errorCode)
{ 
	unsigned char uSend[259];		// 259 = 19 + 4 * 60
	unsigned char uReceive[17];
	unsigned char readSize, responseStat, RetryCtr=0;

	unsigned short uAddress, i, BitsWords, WordsLength, nBctr;

	unsigned int uSum, sentSize, pLength, DataIndex=0;

	BOOL status;
	

	GetModbusAddr(commArg, &uAddress);
	if (uAddress == 0xFFFF)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (AddressError | WriteError);
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}
	if (commArg->Device_Addr[DEVICE_NAME_BYTE]==BIT_DEVICE_I 
			|| commArg->Device_Addr[DEVICE_NAME_BYTE]==WORD_DEVICE_I)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (AddressError | WriteError);
		//errorCode->AddrNum = uAddress;
		return False;
	}

//	 0	  1	 2		 3	4		 5 6 7 8	9 10 11 12	13 14	15 16 17 18..	--		-		-
//	 :																					0x0D	Ox0A
//	STX	Station NO.	Function	[Address	Counter		Bytes	Data			LRC16	CR		LF]
// =============================================================================
//1. package up the command packet
	uSend[0] = 0x3A;	// Heading
	
	// Slave address
	uSend[1] = ((commArg->Device_Addr[STATION_NO_BYTE])>>4) & 0x0F;
	uSend[2] = (commArg->Device_Addr[STATION_NO_BYTE]) & 0x0F;
	
	// Function
	switch (commArg->Access_Type)
	{
	case Bit_Length:
		if (commArg->Device_Addr[DEVICE_NAME_BYTE]  < BIT_DEVICE_O)
		{
			ModbusAddrLabel(commArg, errorCode);
			errorCode->ErrorType = (AddressError | WriteError);
			//errorCode->AddrNum = uAddress;
			return False;
		}

		uSend[3] = 0;
		uSend[4] = 0x0F;

		BitsWords = commArg->Access_Length;		// points = bits

		if ((BitsWords%16) != 0)
			WordsLength = (BitsWords >> 4) + 1; // (BitsWords/16)+1 words
		else
			WordsLength = (BitsWords >> 4);		// (BitsWords/16)   words
		break;

	case Byte_Length:
	case Word_Length:
	case LSB_Length:
	case DWord_Length:	
		if (commArg->Device_Addr[DEVICE_NAME_BYTE] >= BIT_DEVICE_O)
		{
			uSend[3] = 0;
			uSend[4] = 0x0F;
				
			if (commArg->Access_Type == Byte_Length)
				BitsWords = commArg->Access_Length << 3;  // number of bits (x8)
			else 
			if ((commArg->Access_Type==Word_Length) || (commArg->Access_Type==LSB_Length))
				BitsWords = commArg->Access_Length << 4;  // number of bits (x16)
			else // DWord
				BitsWords = commArg->Access_Length << 5;  // number of bits (x32)

			if (BitsWords % 16)
				WordsLength = (BitsWords >> 4) + 1;
			else
				WordsLength = BitsWords >> 4;
		}
		else
		{
			if (commArg->Access_Type == Byte_Length)
				BitsWords = ((commArg->Access_Length)/2) + ((commArg->Access_Length)%2);  // number of words
			else 
			if ((commArg->Access_Type == Word_Length) || (commArg->Access_Type == LSB_Length))
				BitsWords = commArg->Access_Length;		   // number of words
			else // DWord
				BitsWords = (commArg->Access_Length) << 1; // number of words

			WordsLength = BitsWords;

			uSend[3] = 1; 
			uSend[4] = 0;
		}
		break;

	default:
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (AddressError | WriteError);
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}
	if ((commArg->Device_Addr[DEVICE_NAME_BYTE])>=BIT_DEVICE_O && (BitsWords%16)!=0)	// must words
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (AddressError | WriteError);
		//errorCode->AddrNum = uAddress;
		return FALSE;
	}
	
	CommandStatus = WriteResponse;
	nBctr = BlockReadWrite;  // 60 words.
	do {
		if (WordsLength > nBctr)		  			// if data length more than block length
			WordsLength -= nBctr;					// divide several frame to read
		else { nBctr = WordsLength, WordsLength = 0; }	// last frame


		uSend[5] = (unsigned char)(uAddress >> 12) & 0x0F; 
		uSend[6] = (unsigned char)(uAddress >>  8) & 0x0F;
		uSend[7] = (unsigned char)(uAddress >>  4) & 0x0F;
		uSend[8] = (unsigned char)(uAddress) & 0x0F;

		if (commArg->Device_Addr[DEVICE_NAME_BYTE] >= BIT_DEVICE_O)
			i = nBctr << 4;				// Coils
		else
			i = nBctr;					// Words

		pLength = 9;
		if (uSend[4] != 6)
		{
			uSend[pLength++]  = (unsigned char)(i >> 12) & 0x0F; 
			uSend[pLength++] = (unsigned char)(i >>  8) & 0x0F;
			uSend[pLength++] = (unsigned char)(i >>  4) & 0x0F;
			uSend[pLength++] = (unsigned char)(i) & 0x0F;
	
			i = nBctr << 1;		// Bytes
			uSend[pLength++] = (unsigned char)(i >>  4) & 0x0F;
			uSend[pLength++] = (unsigned char)(i) & 0x0F;
		}

// ================= Writing Data =============================================================
	
		if (commArg->Device_Addr[DEVICE_NAME_BYTE] == WORD_DEVICE_O) //Preset Multi Register (Function code: 0x10) 
		{
			for (i = 0 ; i < nBctr ; i++)
			{
				// Hi Byte
				uSend[pLength++] = (commArg->Access_Value[DataIndex+1]>>4) & 0x000F;
				uSend[pLength++] =  commArg->Access_Value[DataIndex+1] & 0x000F;
	
				// Low byte
				uSend[pLength++] = (commArg->Access_Value[DataIndex]>>4) & 0x000F;
				uSend[pLength++] =  commArg->Access_Value[DataIndex] & 0x000F;
				DataIndex += 2;
			}
		}
		else 
		{
			for (i = 0 ; i < nBctr ; i++)
			{
				uSend[pLength++] = (commArg->Access_Value[DataIndex]>>4) & 0x000F;
				uSend[pLength++] =  commArg->Access_Value[DataIndex++] & 0x000F;

				uSend[pLength++] = (commArg->Access_Value[DataIndex]>>4) & 0x000F;
				uSend[pLength++] =  commArg->Access_Value[DataIndex++] & 0x000F;
			}
		}

		// LRC
		uSum = 0;
		for (i = 1 ; i < pLength ; i++)  {
			if (i%2)  uSum += (uSend[i] << 4);	// *16
			else  uSum += uSend[i];
		}
		uSum = ~uSum + 1;

		uSend[pLength++] = (uSum>>4) & 0x0F;
		uSend[pLength++] = uSum & 0x0F;

	    for (i = 1 ; i < pLength ; i++)  {
		    if (uSend[i] > 9)  uSend[i] += 0x37;
		    else  uSend[i] += 0x30;
	    }
	    for (i = 0 ; i < 4 ; i++)
		    CheckCodes[i] = uSend[i+1];

		uSend[pLength++] = 0x0D;	// End
		uSend[pLength++] = 0x0A;

		Rxlength = 17;
		RetryCtr = 0;

// ======================================================================================
ReSendWriteCommand:
//2.Send the command package
		if (COMM_WriteData(comPortHandle, uSend, pLength, &sentSize, GeneralTimeOutValue) == COMM_OK)
		{
		}
		else
		{
			COMM_Flush(comPortHandle, RECEIVER);
			COMM_Flush(comPortHandle, TRANSMITTER);

			ModbusAddrLabel(commArg, errorCode);
			errorCode->ErrorType = (UARTCommunicateFail | WriteError);
			//errorCode->AddrNum = uAddress;
			return FALSE;
		}

//3. Receive the response
		responseStat = Receive_Response(comPortHandle, uReceive, &readSize, commArg->TimeOut_Time);   // 100);
		switch (responseStat)
 		{
		case RespOK: 
//4. Return the interget Hex value
			status = TRUE;
			if (WordsLength > 0)
			{
				if (commArg->Device_Addr[DEVICE_NAME_BYTE] == BIT_DEVICE_O)
					uAddress += (nBctr << 4);	// bit address
				else
					uAddress += nBctr;			// word address				
			}
			break;
	
		case ControllerCheckSumError: 
		case Unknow:		
		case NoResponse:	// Retry
			if (RetryCtr++ < commArg->Retry)  // 3)
				goto ReSendWriteCommand;

			status = FALSE;
			break;
		
		default: 
			status = FALSE;
			break;
		}
	} while (WordsLength>0 && status==TRUE);	

	if (status == FALSE)
	{
		ModbusAddrLabel(commArg, errorCode);
		errorCode->ErrorType = (responseStat | WriteError);
		//errorCode->AddrNum = uAddress;
	}

	if (commArg->Delay_Time != 0)
	{
		sc_sleep(commArg->Delay_Time);
	}

	return status;
}


