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 FrmRAMOperation : Form
    {
        //（1）定义使用的局部变量
        private frm_main fmain;  //主页面变量
        private UInt32 addr;
        public EMUART emuart = new EMUART();
        private UInt32 addrSt = 0;     //可读的起始地址

        //握手帧
        // 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 string lastSelect;  //保存上一次下拉框的文本
        public FrmRAMOperation()
        {
            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窗体变量
            cB_Format.SelectedText = "十六进制";  //格式选择框
            lastSelect = "十六进制";
        }

        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)如果接收到设备返回信息
            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", "");

                //设置设备信息                   
                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 = "重新连接";    //连接按钮文本
                btn_RAMRead.Enabled = true;   //读取RAM按钮
                //发送获取地址命令  发送错误地址  得到设备返回的地址信息
                Byte[] GetRAM = { (byte)17, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };  //发送命令帧，获取某一RAM地址的数据
                emuart.DataReceivedEvent += new EMUART.DataReceived(recvData);     //增加代理
                emuart.bufferClear();
                emuart.send(GetRAM);     
            }
            else goto btn_uartcheck_Click_EXIT3;  //接收到错误返回

        //(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 = "协议信息：";    //底部右侧提示区
            btn_RAMRead.Enabled = false;  //读取RAM按钮
            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 = "协议信息：";   //底部右侧提示区
            btn_RAMRead.Enabled = false;  //读取RAM按钮
            goto btn_uartcheck_Click_EXIT;

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


        //======================================================================
        //函数名称：recvData
        //函数返回：无
        //参数说明：处理串口发来的数据
        //功能概要：串口接收到数据时触发该处理函数
        //======================================================================
        public void recvData(byte[] data)
        {
            int i,j,k;
            byte[] vals=new byte[16];
            byte[] temp = null;
            string s1,s2;      
            //如果接收到RAM发来的数据
            if(data.Length==0) goto recv_Exit1;  //没有接收到串口返回消息
            //如果接收到串口返回消息
            temp = new byte[1];
            Array.Copy(data, 0, temp, 0, 1);
            //RAM访问没有越界，显示系统发送来的数据
            if(temp[0]==1)
            {
                temp = new byte[128];
                Array.Copy(data, 1, temp, 0, 128);
                txt_recv.Text = "";
                s1 = "";
                s2 = "";
                //一行显示16字节的数据
                for (i = 0; i < 128; i += 16)
                {
                    Array.Copy(temp,i,vals,0,16);   
                    s1 = s1 + Convert.ToString(addr + i, 16) + "：";
                    for (k = 0; k < 16; k++) s1 = s1 + BitConverter.ToString(vals, k, 1) + " ";//输出16进制数据
                    s1 = s1 + " ";
                    for (k = 0; k < 16; k++)                         
                    {
                        s2= ""+(char)vals[k];  //输出其字符含义
                        if (s2.Trim().Length == 0 || s2.Trim().Length>1) s2 = ".";
                        if (vals[k] < 32 || vals[k] > 127) s2 = ".";
                        s1 = s1 + s2;
                    }
                    s1=s1+ "\r\n\r\n";
                    txt_recv.Text = s1;
                    txt_recv.Refresh();
                }
            }
            //RAM访问越界,显示可访问的地址
            else if (temp[0] == 0)
            {
                temp = new byte[8];
                Array.Copy(data, 1, temp, 0, 8);
                //获取起始与结束地址
                addrSt = (UInt32)(temp[0] + temp[1] * Math.Pow(2, 8) + temp[2] * Math.Pow(2, 16) + temp[3] * Math.Pow(2, 24));
                //根据地址格式显示可访问范围
                if (rB_Ab.Checked == true)
                {
                    label_addr.Text = "允许访问的起始地址为：0x" + Convert.ToString(addrSt, 16) ;
                    txtShow1("\r\n\r\n允许访问的起始地址为：0x" + Convert.ToString(addrSt, 16) + "，");
                }                    
                else
                {
                    label_addr.Text = "允许访问的相对地址范围：0x0~0x" + Convert.ToString(addrSt, 16);
                    txtShow1("\r\n\r\n允许访问的相对地址范围：0x0~0x" + Convert.ToString(addrSt, 16) + "，");
                }
                txtShow1("请于合法地址范围内进行访问。");
                txt_recv.Text = "请于合法地址范围内进行访问";
            }

        //退出程序    
        recv_Exit:
            emuart.DataReceivedEvent -= new EMUART.DataReceived(recvData);     //消除代理
            return;

        //设备未返回数据
        recv_Exit1:
            FrmUpdateInf_Load(null, null);
            txt_recv.Text = "设备未返回数据，请确认是否与设备断开连接"; //下方Uart接收区
            goto recv_Exit;  


        }

   
        //======================================================================
        //函数名称：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;
        }

        //======================================================================
        //函数名称：btn_RamRead_Click
        //函数返回：无
        //参数说明：无
        //功能概要：读取输入地址之后的32字节数据
        //======================================================================
        private void btn_RamRead_Click(object sender, EventArgs e)
        {
            //发送命令帧，获取某一RAM地址的数据 17绝地址获取、18相对地址获取
            Byte[] GetRAM={(byte)17,(byte)0x20,(byte)0,(byte)0,(byte)0};
            string PATTERN2 = @"^[0-1]+$";   //正则表达式  纯数字
            string PATTERN10 = @"^[0-9]+$";   //正则表达式  纯数字
            string PATTERN16 = @"^[A-Fa-f0-9]+$";  //正则表达式  十六进制

            //根据选择相对或绝对地址的形式来决定发送命令
            if (rB_Off.Checked == true)
            {
                GetRAM[0] = (byte)18;  //相对
            }
            else
                GetRAM[0] = (byte)17;  //绝对
            //如果输入文本长度小于10且为十进制组合
            if (cB_Format.Text == "十进制" && text_AddrForRead.Text.Length <= 10
                && text_AddrForRead.Text.Length >0 
                &&System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN10)
                && Convert.ToUInt64(text_AddrForRead.Text) <= 4294967295)
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text);
                GetRAM[1] = (byte)(addr / Math.Pow(2, 24));
                GetRAM[2] = (byte)(addr / Math.Pow(2, 16));
                GetRAM[3] = (byte)(addr / Math.Pow(2, 8));
                GetRAM[4] = (byte)addr;
                emuart.DataReceivedEvent += new EMUART.DataReceived(recvData);     //增加代理
                emuart.bufferClear();
                emuart.send(GetRAM);     

            }
            //如果输入文本长度小于8且为十六进制组合
            else if (/*cB_Format.Text=="十六进制"&&*/ text_AddrForRead.Text.Length <= 8
                && text_AddrForRead.Text.Length > 0 
                &&System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN16))
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text, 16);
                GetRAM[1] = (byte)(addr / Math.Pow(2, 24));
                GetRAM[2] = (byte)(addr / Math.Pow(2, 16));
                GetRAM[3] = (byte)(addr / Math.Pow(2, 8));
                GetRAM[4] = (byte)addr;
                emuart.DataReceivedEvent += new EMUART.DataReceived(recvData);     //增加代理
                emuart.bufferClear();
                emuart.send(GetRAM);                   
            }
            //如果输入文本长度小于32且为二进制组合
            else if (cB_Format.Text == "二进制" && text_AddrForRead.Text.Length <= 32
                && text_AddrForRead.Text.Length > 0 
                &&System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN2))
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text,2);
                GetRAM[1] = (byte)(addr / Math.Pow(2, 24));
                GetRAM[2] = (byte)(addr / Math.Pow(2, 16));
                GetRAM[3] = (byte)(addr / Math.Pow(2, 8));
                GetRAM[4] = (byte)addr;
                emuart.DataReceivedEvent += new EMUART.DataReceived(recvData);     //增加代理
                emuart.bufferClear();
                emuart.send(GetRAM);
            }
            else           
                MessageBox.Show("输入错误，请输入正确地址格式");
                 
        }


        //======================================================================
        //函数名称：btn_clear_Click
        //函数返回：无
        //参数说明：无
        //功能概要：清空Uart接收区域
        //======================================================================
        private void btn_clear_Click(object sender, EventArgs e)
        {
            txt_recv.Text = string.Empty;
        }


        //======================================================================
        //函数名称：rB_Off_CheckedChanged
        //函数返回：无
        //参数说明：无
        //功能概要：选择相对地址方式触发事件
        //======================================================================
        private void rB_Off_CheckedChanged(object sender, EventArgs e)
        {
            if (rB_Off.Checked == true)
            {

                rB_Ab.Checked = false;
            }
            else
                rB_Ab.Checked = true;
        }

        //======================================================================
        //函数名称：rB_Ab_CheckedChanged
        //函数返回：无
        //参数说明：无
        //功能概要：选择绝对地址方式触发事件
        //======================================================================
        private void rB_Ab_CheckedChanged(object sender, EventArgs e)
        {
            if (rB_Ab.Checked == true)
            {

                rB_Off.Checked = false;
            }
            else
                rB_Off.Checked = true;
        }

        //======================================================================
        //函数名称：cB_Format_SelectedIndexChanged
        //函数返回：无
        //参数说明：无
        //功能概要：地址格式选择下拉框触发事件
        //======================================================================
        private void cB_Format_SelectedIndexChanged(object sender, EventArgs e)
        {
            string PATTERN2 = @"^[0-1]+$";   //正则表达式  纯数字
            string PATTERN10 = @"^[0-9]+$";   //正则表达式  纯数字
            string PATTERN16 = @"^[A-Fa-f0-9]+$";  //正则表达式  十六进制
            bool convEnable = false;  //是否允许进制转换 默认不允许

            if (lastSelect == "十进制" && text_AddrForRead.Text.Length <= 10
                && text_AddrForRead.Text.Length > 0
                && System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN10)
                && Convert.ToUInt32(text_AddrForRead.Text) <= 4294967295)
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text);
                convEnable = true;  //允许进制转换
            }
            //如果输入文本长度小于8且为十六进制组合
            else if (lastSelect == "十六进制" && text_AddrForRead.Text.Length <= 8
                && text_AddrForRead.Text.Length > 0
                && System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN16))
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text, 16);
                convEnable = true;  //允许进制转换
            }
            //如果输入文本长度小于32且为二进制组合
            else if (lastSelect == "二进制" && text_AddrForRead.Text.Length <= 32
                && text_AddrForRead.Text.Length > 0
                && System.Text.RegularExpressions.Regex.IsMatch(text_AddrForRead.Text, PATTERN2))
            {
                addr = Convert.ToUInt32(text_AddrForRead.Text, 2);
                convEnable = true;  //允许进制转换
            }
            else
            {
                text_AddrForRead.Text = "";
            }
                
            //如果允许进制转换
            if (convEnable)
            {
                if(cB_Format.Text=="十进制") 
                {
                    label1.Text = "";
                    text_AddrForRead.Text = Convert.ToString(addr, 10);
                }
                else if(cB_Format.Text=="十六进制")
                {
                    label1.Text = "0X";
                    text_AddrForRead.Text = Convert.ToString(addr, 16);
                }

                else if (cB_Format.Text == "二进制")
                {
                    label1.Text = "0b";
                    text_AddrForRead.Text = Convert.ToString(addr, 2);
                }
                   
            }
            lastSelect = cB_Format.Text;//保存上一次下拉框文本
        }
        //======================================================================
        //函数名称：txtShow1
        //函数返回：无
        //参数说明：str:在终端执行信息提示框追加显示的内容
        //功能概要：整体更新过程中，在终端执行信息提示框textBox1中追加提示
        //          信息，并显示最新信息      
        //======================================================================
        private void txtShow1(string str)
        {
            //右侧更新提示区
            if (this.textBox1.Lines.Length >= 55) this.textBox1.Text = String.Empty; //长度过长则清空内容
            this.textBox1.Text += str;
            this.textBox1.Refresh();
            this.textBox1.SelectionStart = this.textBox1.Text.Length;  //光标指向最后一位
            this.textBox1.ScrollToCaret();   //移动到光标处

        }


        //======================================================================
        //函数名称：btn_Write_Click
        //函数返回：无
        //参数说明：无
        //功能概要：写入存储区 按键事件  将写入内容的数据写入到写入地址处      
        //======================================================================
        private void btn_Write_Click(object sender, EventArgs e)
        {
            UInt32 WAddr;
            string PATTERN16 = @"^[A-Fa-f0-9]+$";  //正则表达式  十六进制
            byte[] WData = null;
            byte[] byteArray = null;
            string s1 = null;

            WAddr = 0xF800;
            //判断输入地址格式是否正确  长度和是否符合十六进制格式
            if (tB_WAddr.Text.Length <= 8
                && tB_WAddr.Text.Length > 0
                && System.Text.RegularExpressions.Regex.IsMatch(tB_WAddr.Text, PATTERN16)) WAddr = Convert.ToUInt32(tB_WAddr.Text,16)*16;
            //如果输入地址格式不对，则退出函数 
            else goto btn_Write_Click_EXIT1;
            //除去字符串中的干扰字符
            s1 = tB_WContent.Text.Trim().Replace("0X0 ", "00").Replace("0x0,", "00").Replace("0x0;", "00").Replace("0X", "").Replace("0x", "").Replace(";", "").Replace(",", "").Replace(" ", "");
            if (!(System.Text.RegularExpressions.Regex.IsMatch(s1, PATTERN16))) goto btn_Write_Click_EXIT2; //如果输入内容不合法,退出函数
           //组帧并进行发送
            byteArray = strToToHexByte(s1);
            WData = new byte[6+byteArray.Length];
            WData[0] = 19;
            WData[1] = (byte)byteArray.Length;
            WData[2] = (byte)(WAddr / Math.Pow(2, 24));
            WData[3] = (byte)(WAddr / Math.Pow(2, 16));
            WData[4] = (byte)(WAddr / Math.Pow(2, 8));
            WData[5] = (byte) WAddr;
            Array.Copy(byteArray, 0, WData, 6, byteArray.Length);
            emuart.bufferClear();
            emuart.send(WData);
                             
            //退出函数
        btn_Write_Click_EXIT:
            return;

        //输入地址格式不正确
        btn_Write_Click_EXIT1:
            MessageBox.Show("输入地址格式不对,请重新输入");
            goto btn_Write_Click_EXIT;

        //写入内容格式不正确
        btn_Write_Click_EXIT2:
            MessageBox.Show("写入内容格式不对,请检查并重新输入");
            goto btn_Write_Click_EXIT;

        }


        //======================================================================
        //函数名称：strToToHexByte
        //函数返回：byte 返回转换的字节数组
        //参数说明：需转换的字符串内容
        //功能概要：将字符串内容转换为16进制数组      
        //======================================================================
        private static byte[] strToToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

    }
}
