
// lm_MBslaveDlg.cpp : implementation file
//

#include "stdafx.h"
#include "lm_MBslave.h"
#include "lm_MBslaveDlg.h"
#include <ws2tcpip.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#define NB_CONNECTION    5

char caParity[]={'N','O','E'};
int quit=0;
#define THREAD_STOP					TEXT("THREAD_STOP")
HANDLE hThread = NULL;
HANDLE hStopEvent = NULL;

// Clm_MBslaveDlg dialog


DWORD WINAPI TCPSlaveProc(LPVOID pParam)
{
	int master_socket;
	int server_socket = -1;
	int rc;
	int ret;
	fd_set refset;
	fd_set rdset;
	TIMEVAL	aTime;
	aTime.tv_sec = 1;
	aTime.tv_usec = 0;
	uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];  

	Clm_MBslaveDlg *dlgpt=(Clm_MBslaveDlg *)pParam;
	
	server_socket = modbus_tcp_listen(dlgpt->ctx, NB_CONNECTION);
	if (server_socket == -1) {
		printf("Unable to listen TCP connection\n");
		modbus_free(dlgpt->ctx);
		return -1;
	}
		
	//AfxMessageBox(_T("TCP Create Success!"));
	printf("TCP Create Success!\r\n");
		
		FD_ZERO(&refset);
		/* Add the server socket */
		FD_SET(server_socket, &refset);

	int fdmax = server_socket;

	for (;;) {
		if(quit==1)
		{
			dlgpt->m_check = FALSE;
			break;
		}
		//printf("=");
		rdset = refset;
#if 0
		if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) {
	//            perror("Server select() failure.");
			printf("Server select() failure \r\n");
	        //close_sigint(1);
		}
#endif
		ret=select(0,  &rdset, NULL, NULL, &aTime) ;

		if(ret<0)
		{
			printf("Server select() failure \r\n");
			Sleep(1);
			break;
			//continue;
		}
		/* Run through the existing connections looking for data to be read */
		for (master_socket = 0; master_socket <= fdmax; master_socket++) {
			//printf(".");
			if(quit==1)
			{
				dlgpt->m_check = FALSE;
				break;
			}

			if (!FD_ISSET(master_socket, &rdset)) {
				continue;
			}

			if (master_socket == server_socket) {
				/* A client is asking a new connection */
				socklen_t addrlen;
				struct sockaddr_in clientaddr;
				int newfd;

				/* Handle new connections */
				addrlen = sizeof(clientaddr);
				memset(&clientaddr, 0, sizeof(clientaddr));
				newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen);
				if (newfd == -1) {
//						  perror("Server accept() error");
					printf("accept failed!\r\n");
					
				} else {
					FD_SET(newfd, &refset);

					if (newfd > fdmax) {
						/* Keep track of the maximum */
						fdmax = newfd;
					}
					printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd);
				}
			} else {
				//printf("_");
				modbus_set_socket(dlgpt->ctx, master_socket);
				rc = modbus_receive(dlgpt->ctx, query);
				if (rc > 0) {
					modbus_reply(dlgpt->ctx, query, rc, dlgpt->mb_mapping);
				} else if (rc == -1) {
					/* This example server in ended on connection closing or any errors. */
					printf("Connection closed on socket %d\n", master_socket);
					//close(master_socket);
					closesocket(master_socket);

					/* Remove from reference set */
					FD_CLR(master_socket, &refset);

					if (master_socket == fdmax) {
						fdmax--;
					}
				}
			}
			Sleep(1);
		}					
	} //for(;;)
	printf("quit thread\r\n");
	return 0;
}

DWORD WINAPI RTUSlaveProc(LPVOID pParam)
{
	uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; 
	Clm_MBslaveDlg *dlgpt=(Clm_MBslaveDlg *)pParam;

	//modbus_set_slave(ctx, 1); //Set slave ID 
	if (modbus_connect(dlgpt->ctx) == -1) { //Establish a connection
		printf("Connection failed:%d %s\n", errno, modbus_strerror(errno));
		modbus_free(dlgpt->ctx); //Free libmodbus context
		dlgpt->m_check2 = FALSE;
	}
	else
	{						
		dlgpt->m_check2 = TRUE;
//		AfxMessageBox(_T("RTU Create Success!"));
		printf("RTU Slave Create Success!\n");
		while(1){        					      
			int rc = modbus_receive(dlgpt->ctx, query);  
			if (rc > 0) {            
				modbus_reply(dlgpt->ctx, query, rc, dlgpt->mb_mapping);  
			} 
			else if (rc  == -1) {            /* Connection closed by the client or error */    
				dlgpt->m_check2 = FALSE;
				printf("Message receive error!\r\n");
				break;        
			}    
			if(quit==1)
			{
				dlgpt->m_check2 = FALSE;
				break;
			}
			else
			{
				Sleep(1);
				dlgpt->UpdateData(FALSE);
			}
		}
	}
	printf("quit thread\r\n");
	return 0;
}

Clm_MBslaveDlg::Clm_MBslaveDlg(CWnd* pParent /*=NULL*/)
	: CDialog(Clm_MBslaveDlg::IDD, pParent)
{
	m_com = 0;
	m_databit = 0;
	m_parity = 0;
	m_baud = 0;
	m_stopbit = 0;
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void Clm_MBslaveDlg::DoDataExchange(CDataExchange* pDX)
{
	DDX_Text(pDX, IDC_COM, m_com);
	DDX_Text(pDX, IDC_DataBit, m_databit);
	DDX_Text(pDX, IDC_Parity, m_parity);
	DDX_Text(pDX, IDC_Baud, m_baud);
	DDX_Text(pDX, IDC_StopBit, m_stopbit);
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(Clm_MBslaveDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BUTTON1, &Clm_MBslaveDlg::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &Clm_MBslaveDlg::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_MBTCPRADIO, &Clm_MBslaveDlg::OnBnClickedMbtcpradio)
	ON_BN_CLICKED(IDC_MBRTURADIO, &Clm_MBslaveDlg::OnBnClickedMbrturadio)
	ON_WM_TIMER()
END_MESSAGE_MAP()


// Clm_MBslaveDlg message handlers

BOOL Clm_MBslaveDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

		SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	CenterWindow(GetDesktopWindow());	// center to the screen

	m_com = 2;
	m_baud = 115200;
	m_parity = 0;
	m_databit = 8;
	m_stopbit = 1;

	m_check = FALSE;	//For Modbus TCP
	m_check2 = FALSE;	//For Modbus RTU
	m_which = TRUE;	//Modbus RTU
	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog

	// TODO: Add extra initialization here	((CEdit*)GetDlgItem(IDC_COM))->EnableWindow(1);
	((CButton*)GetDlgItem(IDC_MBRTURADIO))->SetCheck(1);
	((CEdit*)GetDlgItem(IDC_Baud))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_Parity))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_DataBit))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_StopBit))->EnableWindow(1);

	for(int i=0;i<10;i++)
		SetDlgItemInt(IDC_EDIT1+i,0,1);
	
	UpdateData(FALSE);
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void Clm_MBslaveDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR Clm_MBslaveDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void Clm_MBslaveDlg::OnBnClickedButton1()
{
	unsigned long ultimeout=0;
	UpdateData();


	if(m_which){ //rtu
		char tmp[64];

		sprintf(tmp,"COM%d",m_com);
		//printf("%s! \n",tmp);
		ctx = modbus_new_rtu(tmp, m_baud, caParity[m_parity], m_databit, m_stopbit); //Create a Modbus RTU context	
		//ultimeout=m_rtutime*1000; //ms --> us
	}
	else{ //tcp
		//int length=m_ip.GetLength();
		//char str[128];
		//memset(str, 0, 128);
		//WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK|WC_DEFAULTCHAR,m_ip.GetBuffer(length),length+1, str,128,0,0);

		//ctx = modbus_new_tcp(str, m_port); //Create a Modbus TCP context
		ctx = modbus_new_tcp("0", 502);			
		//ultimeout=m_tcptime*1000; //ms --> us

	}

	if(ctx!=NULL){
		mb_mapping = modbus_mapping_new(100, 100, 100, 100);
		if (mb_mapping == NULL) 
		{        
			printf("Failed to allocate the mapping: %s\n", modbus_strerror(errno));        
			modbus_free(ctx);        
			return ;    
		}
		else
		{
			modbus_set_slave(ctx, 1); //Set slave ID 
			quit=0;
			//modbus_set_response_timeout(ctx,0,ultimeout); // set timeout for response			
			if(m_which) //rtu
			{
				DWORD dwThreadID;
				hThread = CreateThread(NULL, 0, RTUSlaveProc, (LPVOID)this, 0, &dwThreadID);
				if(hThread==NULL)
					printf("Failed to create RTU slave\r\n");

			}
			else //tcp
			{				
				DWORD dwThreadID;
				//hStopEvent = CreateEvent(NULL, TRUE, FALSE, THREAD_STOP);
				hThread = CreateThread(NULL, 0, TCPSlaveProc, (LPVOID)this, 0, &dwThreadID);
				if(hThread==NULL)
					printf("Failed to create TCP slave\r\n");			

			}
			SetTimer(1,2000,NULL);
		}
	}
	else{
		AfxMessageBox(_T("Create Failure!"));
		m_check = FALSE;
	}

	UpdateData(FALSE);
}

void Clm_MBslaveDlg::OnBnClickedButton2()
{
	// TODO: Add your control notification handler code here
	quit=1;
	KillTimer(1);
	Sleep(1500);
	if(m_which) //rtu
		;
	else //tcp
	{
		m_check=FALSE;
		CloseHandle(hThread);
		hThread=NULL;
	}
}

void Clm_MBslaveDlg::OnBnClickedMbtcpradio()
{
	((CButton*)GetDlgItem(IDC_MBTCPRADIO))->SetCheck(1);
	((CButton*)GetDlgItem(IDC_MBRTURADIO))->SetCheck(0);
	((CEdit*)GetDlgItem(IDC_COM))->EnableWindow(0);
	((CEdit*)GetDlgItem(IDC_Baud))->EnableWindow(0);
	((CEdit*)GetDlgItem(IDC_Parity))->EnableWindow(0);
	((CEdit*)GetDlgItem(IDC_DataBit))->EnableWindow(0);
	((CEdit*)GetDlgItem(IDC_StopBit))->EnableWindow(0);
	m_which=FALSE;
}

void Clm_MBslaveDlg::OnBnClickedMbrturadio()
{
	((CButton*)GetDlgItem(IDC_MBRTURADIO))->SetCheck(1);
	((CButton*)GetDlgItem(IDC_MBTCPRADIO))->SetCheck(0);
	((CEdit*)GetDlgItem(IDC_COM))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_Baud))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_Parity))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_DataBit))->EnableWindow(1);
	((CEdit*)GetDlgItem(IDC_StopBit))->EnableWindow(1);
	m_which=TRUE;
}

void Clm_MBslaveDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	int i;
	//printf("%d %d %d %d %d\r\n",mb_mapping->tab_bits[0],mb_mapping->tab_bits[1],mb_mapping->tab_bits[2],mb_mapping->tab_bits[3],mb_mapping->tab_bits[4]);		

	for(i=0;i<5;i++)
	{
		if(mb_mapping->tab_bits[i]) //show coil (0xxxx) for UI
			((CButton*)GetDlgItem(IDC_CHECK1+i))->SetCheck(1);
		else
			((CButton*)GetDlgItem(IDC_CHECK1+i))->SetCheck(0);

		//show holding register (4xxxx) for UI
		SetDlgItemInt(IDC_EDIT6+i,mb_mapping->tab_registers[i],0); //0:unsigned
		
		/*if(mb_mapping->tab_input_bits[i]) //check input (1xxxx)
			((CButton*)GetDlgItem(IDC_CHECK6+i))->SetCheck(1);
		else
			((CButton*)GetDlgItem(IDC_CHECK6+i))->SetCheck(0);*/
	}
	UpdateData(TRUE);

	CDialog::OnTimer(nIDEvent);
}
