using cn.edu.suda.sumcu.iot;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

 namespace RemoteUpdate
{
    public partial class FrmUpdateInf : Form
    {
        //（1）定义使用的局部变量
        private frm_main fmain;  //主页面变量
        private System.IO.Ports.SerialPort uartPort;
        public EMUART emuart = new EMUART();
        private SynchronizationContext syn = null;

        //握手帧
        // LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局,字符串转换成ANSI字符串，Pack表示1字节对齐
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct shakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
        }
        private shakeData shakedata;
        private string uecomType;   //通信模组类型
        private string mcuType;     //MCU类型
        private uint uStartAds;     //User程序起始地址
        private uint uCodeSize;     //User程序总代码大小
        private uint replaceNum;    //替换更新最大字节
        private uint reserveNum;    //保留更新最大字节（不等于0意味着有User程序）
        public FrmUpdateInf()
        {
            InitializeComponent();
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:FrmUpdateInf:窗体                                            
        /// 事    件:Load                                                   
        /// 功    能:执行加载窗体程序,自动连接软件更新界面所连接的串口                    
        /// 函数调用:无                                                     
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        /// <remarks></remarks>                                             
        ///-----------------------------------------------------------------
        private void FrmUpdateInf_Load(object sender, EventArgs e)
        {
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
            //（1）本窗体由frmMain创建，所以本窗体的父窗体为frmMain
            fmain = (frm_main)this.MdiParent;  //得到frmMain窗体变量

        }

        private void btn_uecheck_Click(object sender, EventArgs e)
        {
            //（1）变量声明
            int ret;            //返回值
            string com = "";    //串口信息
            string sTemp = "";
            byte[] recv = null;//保存串口接收信息
            byte[] shake = { (byte)10, (byte)'s', (byte)'h', (byte)'a', (byte)'k', (byte)'e' }; //与终端握手帧数据

            //（2）清除一些可能余留信息
            this.lbl_uart.Text = "";       //右上角显示区
            fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...";     //底部提示
            this.Refresh();                     //刷新显示      

            //（3）重新遍历串口，寻找终端
            if (emuart._Uartport != null) emuart._Uartport.Close();
            Thread.Sleep(10);   //等待串口关闭
            emuart = new EMUART();    //每次“重新连接”重新实例化
            fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...正在查找匹配设备...";     //底部提示
            ret = emuart.findDevice(out com, 115200);  //寻找emuart设备
            this.lbl_uart.Text = com;       //右上角显示区
            fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...尝试与匹配设备建立通信...";     //底部提示

            //（4）根据寻找串口的返回值确定执行下列分支
            //（4.1）如果连接终端的emuart失败，退出函数
            if (ret == 1) goto btn_uartcheck_Click_EXIT1;
            //（4.2）找到串口，没有找到UE，退出函数
            else if (ret == 2) goto btn_uartcheck_Click_EXIT2;
            //（4.3)如果找到串口与UE,向设备发送握手帧
            fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...向该设备发送握手信息，准备接收设备返回消息...";     //底部提示
            emuart.bufferClear();   //清除接收数组缓冲区
            emuart.send(shake, out recv, 200, 3); //获得设备的信息在recv中
            fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...正在处理设备返回信息...";     //底部提示
            if (recv == null) goto btn_uartcheck_Click_EXIT3;  //没有接收到设备返回消息
            //（4.4)如果接收到设备返回信息
            //（4.4.1）如果终端的更新程序版本为VA.10之后的版本
            if (recv.Length == Marshal.SizeOf(typeof(shakeData)))
            {
                fmain.lbl_mainstatus.Text = "运行状态：成功连接到GEC设备";     //底部提示
                //byte数组转结构体
                shakedata = (shakeData)bytesToStruct(recv, typeof(shakeData));
                //获取握手帧数据
                uecomType = Encoding.Default.GetString(shakedata.uecomType).Replace("\0", "");
                mcuType = Encoding.Default.GetString(shakedata.mcuType).Replace("\0", "");
                uStartAds = shakedata.uStartAds;
                uCodeSize = shakedata.uCodeSize;
                replaceNum = shakedata.replaceNum;
                reserveNum = shakedata.reserveNum;
                //设置设备信息                   
                sTemp = com + "：" + uecomType + " " + mcuType;  //设备信息
                //状态提示
                this.lbl_uart.Text = sTemp;     //右上角显示区
                fmain.lbl_mainstatus.Text = "运行状态：" + sTemp;   //底部提示
                fmain.lbl_protocal.Text = "协议：串口";    //底部中间协议类型
                fmain.lbl_protocalinfo.Text = "协议信息：端口" + com + ",波特率115200";   //底部右侧协议信息
                btn_uecheck.Text = "重新连接";    //连接按钮文本
                this.btn_start.Enabled = true;
            }
            //（4.4.2）如果终端的更新程序版本为VA.10及其以前的版本
            else if (System.Text.Encoding.Default.GetString(recv).Contains("shake:GEC-"))
            {
                sTemp = "MKL36Z64" + "(" + com + ")";//设备信息
                lbl_uart.Text = "成功连接" + sTemp;       //右上角显示区
                fmain.lbl_mainstatus.Text = "运行状态：正在连接GEC...成功连接" + sTemp;   //底部提示
                lbl_uart.Text = "找到终端设备" + sTemp;   //右上角显示区
                fmain.lbl_protocal.Text = "协议：串口";    //底部中间协议类型
                fmain.lbl_protocalinfo.Text = "协议信息：端口" + com + ",波特率115200";   //底部右侧协议信息
                btn_uecheck.Text = "重新连接";    //连接按钮文本
                this.btn_start.Enabled = true;
                fmain.lbl_mainstatus.Text = "过程提示:串口接收界面开启成功";   //底部提示

            }
            else goto btn_uartcheck_Click_EXIT3;  //接收到错误返回
                
            //PublicVar.uartPort = uartPort;//将uartPort传递给串口测试菜单

        //(5)退出区
        //(5.1)退出函数
        btn_uartcheck_Click_EXIT:
            return;

        //(5.2)不存在可用串口
        btn_uartcheck_Click_EXIT1:
            lbl_uart.Text = "运行状态：当前不存在可用串口";  //右上角提示区
            fmain.lbl_mainstatus.Text = "运行状态：当前不存在可用串口";    //底部提示区
            fmain.lbl_protocal.Text = "协议：";    //底部右侧提示区
            fmain.lbl_protocalinfo.Text = "协议信息：";    //底部右侧提示区
            goto btn_uartcheck_Click_EXIT;

        //(5.3)存在串口，但不存在emuar设备
        btn_uartcheck_Click_EXIT2:
            emuart.terminate(115200); //发送数据给终端设备，让终端设备清空其数据缓冲区
            btn_uecheck.Text = "重新连接";  //连接按键文本
            lbl_uart.Text = "运行状态：已连接串口" + com + "但未找到设备";   //右上角提示区
            fmain.lbl_mainstatus.Text = "运行状态：已连接串口" + com + "但未找到设备";    //底部提示区
            fmain.lbl_protocal.Text = "协议：";    //底部右侧提示区
            fmain.lbl_protocalinfo.Text = "协议信息：";   //底部右侧提示区
            goto btn_uartcheck_Click_EXIT;

        //(5.4)没有正确接收到设备返回消息
        btn_uartcheck_Click_EXIT3:
            emuart.terminate(115200); //发送数据给终端设备，让终端设备清空其数据缓冲区
            btn_uecheck.Text = "重新连接";   //连接按钮文本
            lbl_uart.Text = "找到GEC在" + com + "但握手失败，请再次单击[重新连接]按钮";   //右上角提示区
            goto btn_uartcheck_Click_EXIT;
        }

        public void recv(byte[] data)
        {
            byte[] temp = null;
            //（1）将获取到的数据加载到文本框中
            if (data.Length > 0)
            {
                temp = new byte[30];
                Array.Copy(data, 0, temp, 0, 30);
                this.text_equipName.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[20];
                Array.Copy(data, 30, temp, 0, 20);
                this.text_equipID.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[20];
                Array.Copy(data, 50, temp, 0, 20);
                this.text_equipType.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[30];
                Array.Copy(data, 70, temp, 0, 30);
                this.text_vendor.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[4];
                Array.Copy(data, 100, temp, 0, 4);
                this.text_softVer.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[8];
                Array.Copy(data, 104, temp, 0, 8);
                long ldata = BitConverter.ToInt64(temp, 0);
                this.text_productTime.Text = ConvertStringToDateTime(ldata.ToString()).ToString();
                temp = new byte[20];
                Array.Copy(data, 112, temp, 0, 20);
                this.text_userName.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[11];
                Array.Copy(data, 132, temp, 0, 11);
                this.text_phone.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[15];
                Array.Copy(data, 143, temp, 0, 15);
                this.text_serverIP.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[5];
                Array.Copy(data, 158, temp, 0, 5);
                this.text_serverPort.Text = Encoding.Default.GetString((byte[])temp);
                temp = new byte[4];
                Array.Copy(data, 163, temp, 0, 4);
                this.text_sendFrequencySec.Text = BitConverter.ToInt32(temp, 0).ToString();
            }
            emuart.DataReceivedEvent -= new EMUART.DataReceived(recv);     //消除代理   
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:FrmUpdateInf:窗体                                            
        /// 事    件:Click                                                  
        /// 功    能:获取Flash数据                    
        /// 函数调用:无                                                     
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        /// <remarks></remarks>                                             
        ///-----------------------------------------------------------------
        private void btn_start_Click(object sender, EventArgs e)
        {
            emuart.DataReceivedEvent += new EMUART.DataReceived(recv);  //开启接收代理
            if (emuart.send(new byte[] { 14 }))
            {
                this.btn_certify.Enabled = true;
                this.btn_clear.Enabled = true;
            }

        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:FrmUpdateInf:窗体                                            
        /// 事    件:Click                                                  
        /// 功    能:更改数据                    
        /// 函数调用:无                                                     
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        /// <remarks></remarks>                                             
        ///-----------------------------------------------------------------
        private void button1_Click(object sender, EventArgs e)
        {
            //(1)获取IP并且判断其格式是否符合规范
            string ip = text_serverIP.Text.ToString();
            ip = ip.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (!JudgeIPFormat(ip)||ip=="")
                goto button1_Click_exit;
            //(2)获取端口号并且判断其格式是否符合规范
            string port = text_serverPort.Text.ToString();
            port = port.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (Convert.ToInt32(port) > 65535 || port=="")
            {
                MessageBox.Show("不符合端口格式，请重新输入端口号");
                goto button1_Click_exit;
            }
            //(3)获取手机号码并且判断其格式是否符合规范
            string phone = text_phone.Text.ToString();
            phone = phone.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (phone.Length != 11|| phone=="" )
            {
                MessageBox.Show("不符合电话号码格式，请重新输入手机号");
                goto button1_Click_exit;
            }
            //(4)获取发送间隔并且判断其格式是否符合规范
            string sendFrequence = this.text_sendFrequencySec.Text;
            sendFrequence = sendFrequence.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (Convert.ToInt32(sendFrequence) <= 0||sendFrequence=="")
            {
                MessageBox.Show("发送频率不能小于等于0，请重新输入发送频率");
                goto button1_Click_exit;
            }
            //(5)获取用户名并且判断其格式是否符合规范
            string userName = this.text_userName.Text;
            userName = userName.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
            if (userName.Length > 20 || userName == "")
            {
                MessageBox.Show("用户名长度须小于20位且不能为空");
            } 
            //（6）发送数据
            string sData = userName + "," + phone + "," + ip + "," + port + "," + Convert.ToInt32(sendFrequence);
            byte[] temp = Encoding.Default.GetBytes(sData);
            byte[] bSendData = new byte[temp.Length + 1];
            bSendData[0] = 16;
            Array.Copy(temp, 0, bSendData, 1, temp.Length);
            emuart.send(bSendData);

        button1_Click_exit:
            return;
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                                                                       
        /// 功    能:清空Flash信息                  
        /// 函数调用:无                                                     
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        /// <remarks></remarks>                                             
        ///-----------------------------------------------------------------
        private void btn_clear_Click(object sender, EventArgs e)
        {
            text_equipID.Text = "";
            text_equipName.Text = "";
            text_equipType.Text = "";
            text_phone.Text = "";
            text_productTime.Text = "";
            text_serverIP.Text = "";
            text_serverPort.Text = "";
            text_userName.Text = "";
            text_vendor.Text = "";
            text_softVer.Text = "";
            text_sendFrequencySec.Text = "";

            this.btn_certify.Enabled = false;

        }

        ///-----------------------------------------------------------------
        /// <summary>                                                                                                       
        /// 功    能:将时间戳转换成时间                   
        /// 函数调用:无                                                     
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        /// <remarks></remarks>                                             
        ///-----------------------------------------------------------------
        private DateTime ConvertStringToDateTime(string timeStamp)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = long.Parse(timeStamp + "0000000");
            TimeSpan toNow = new TimeSpan(lTime);
            return dtStart.Add(toNow);
        }



        /// <summary>
        /// 判断输入的ip地址是否正确，返回TRUE or FALSE
        /// </summary>
        /// <param name="strJudgeString">等待判断的字符串</param>
        /// <returns>TRUE OR FALSE</returns>
        private bool JudgeIPFormat(string strJudgeString)
        {
            bool blnTest = false;
            bool _Result = true;

            Regex regex = new Regex("^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$");
            blnTest = regex.IsMatch(strJudgeString);
            if (blnTest == true)
            {
                string[] strTemp = strJudgeString.Split(new char[] { '.' }); // textBox1.Text.Split(new char[] { ‘.’ });
                int nDotCount = strTemp.Length - 1; //字符串中.的数量，若.的数量小于3，则是非法的ip地址
                if (3 == nDotCount)//判断字符串中.的数量
                {
                    for (int i = 0; i < strTemp.Length; i++)
                    {
                        if (Convert.ToInt32(strTemp[i]) > 255)
                        { //大于255则提示，不符合IP格式
                            MessageBox.Show("不符合IP格式");
                            _Result = false;
                            //txtbox_ServerIP.Text = "";
                        }
                    }
                }
                else
                {
                    MessageBox.Show("不符合IP格式");
                    _Result = false;
                }
            }
            else
            {
                //输入非数字则提示，不符合IP格式
                MessageBox.Show("不符合IP格式");
                _Result = false;
                // txtbox_ServerIP.Text = "";
            }
            return _Result;
        }

        //======================================================================
        //函数名称：bytesToStruct
        //函数返回：byte数组转换为对应的结构体
        //参数说明：bytes:字节数组;type:结构体类型
        //功能概要：将byte字节数组数据转换为对应的结构体数据
        //======================================================================
        private object bytesToStruct(byte[] bytes, Type type)
        {
            //（1）变量声明
            int size;
            object obj;
            IntPtr structPtr;

            size = Marshal.SizeOf(type);
            //（2）判断字节长度
            if (size > bytes.Length) return null;
            //（3）分配结构体内存空间
            structPtr = Marshal.AllocHGlobal(size);
            //（4）将byte数组拷贝到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //（5）将内存空间转换为目标结构体
            obj = Marshal.PtrToStructure(structPtr, type);
            //（6）释放内存空间
            Marshal.FreeHGlobal(structPtr);
            return obj;
        }
    }
}
