using DotNet.Utilities;
using ITLDG;
using ITLDG.DataCheck;
using ITLDG.SerialPortExtend;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SerialPortForward
{
    public partial class FrmMain : Form
    {
        private Dictionary<string, byte[]> dicCache = new Dictionary<string, byte[]>();
        private string lastSendHex = "";
        string iniFile = Application.StartupPath + "\\config.ini";
        string pluginDir = Application.StartupPath + "\\plugins\\";
        List<IPlugin> listPlugins = new List<IPlugin>();
        List<Plugin> listCheckPlugins = new List<Plugin>();
        string CheckToolPath = "";
        int tipIndex = -1;
        string[] tips = new string[] { "本软件主要作用是转发两个串口的数据,搭配虚拟串口更好用", "校验位如果不是增加在末尾,请在HEX中填写一个错误的校验占位", "定时发送时间设置不要过小,否则可能会引起卡顿" };
        //插件索引,-1为不使用插件
        int pluginIndex = -1;
        /// <summary>
        /// 记录数据
        /// </summary>
        bool RecordData = false;
        /// <summary>
        /// 自动应答
        /// </summary>
        bool AutoAnswer = false;
        public FrmMain()
        {
            InitializeComponent();
        }
        SerialPortInfo com1 = new SerialPortInfo(), com2 = new SerialPortInfo(), timerCom;
        byte[] timerSendBytes = new byte[0];
        string timerSendToName = "";
        string com1Name = "", com2Name = "";
        bool com1Forward = true, com2Forward = true;
        PluginCommon pluginCommon;

        void Com2_DataReceived(object sender, byte[] data)
        {
            DataReceivedHandle(com2Name, false, com2Forward, com2, com1, data);
        }

        void Com1_DataReceived(object sender, byte[] data)
        {
            DataReceivedHandle(com1Name, true, com1Forward, com1, com2, data);
        }
        /// <summary>
        /// 串口收到数据
        /// </summary>
        /// <param name="name">串口名称</param>
        /// <param name="isCom1">是否是串口1</param>
        /// <param name="openForward">是否勾选了转发</param>
        /// <param name="spReceive">收到消息的串口</param>
        /// <param name="spSend">准备转发的串口</param>
        /// <param name="data">收到的数据</param>
        void DataReceivedHandle(string name, bool isCom1, bool openForward, SerialPortInfo spReceive, SerialPortInfo spSend, byte[] data)
        {
            Task.Run(() =>
            {
                AddLog(data, name, isCom1, openForward, spReceive, spSend);
            });
        }

        Regex regGetComName = new Regex("^(COM(\\d+))", RegexOptions.Multiline);
        public string GetCom(string ComName)
        {
            //strs.Add(x.COM.PadRight(5, ' ') + " - " 
            Match match = regGetComName.Match(ComName);
            if (match.Success)
            {
                return match.Groups[1].Value;
            }
            return ComName;
        }

        private void btnMoreSerialOption_Click(object sender, EventArgs e)
        {
            if (!com1.IsOpen && !com2.IsOpen)
            {
                FrmMoreSerial fms = new FrmMoreSerial(ref com1, ref com2);
                if (fms.ShowDialog() == DialogResult.OK)
                {
                    SaveSerialOption();
                    ReadSerialOption();
                }
            }
            else
            {
                MessageBox.Show("请先停止串口转发再进行操作", "请先停止", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
        void SaveOption()
        {
            INIFileHelper ini = new INIFileHelper(iniFile);
            ini.IniWriteValue("Option", "Plugin", cmbPlugins.Text);
            ini.IniWriteValue("Option", "SendTo", cmbSendTo.SelectedIndex.ToString());
            ini.IniWriteValue("Option", "SendHex", txtSendHex.Text);
            ini.IniWriteValue("Option", "Timer", nudSend.Value.ToString());
            ini.IniWriteValue("Option", "CheckPlugin", cmbCheck.Text.ToString());
            ini.IniWriteValue("Option", "CheckStart", nudCheckStart.Value.ToString());
            ini.IniWriteValue("Option", "CheckEnd", nudCheckEnd.Value.ToString());
            ini.IniWriteValue("Option", "CheckToolPath", CheckToolPath);
            ini.IniWriteValue("Option", "RecordData", RecordData.ToString());
            ini.IniWriteValue("Option", "AutoAnswer", AutoAnswer.ToString());

            ini.IniWriteValue("Log", "Enable", serialLog1.LogEnable.ToString());
            ini.IniWriteValue("Log", "AutoScroll", serialLog1.LogAutoScroll.ToString());
            ini.IniWriteValue("Log", "LogType", ((int)serialLog1.SerialLogType).ToString());

        }
        void ReadOption()
        {
            INIFileHelper ini = new INIFileHelper(iniFile);
            string plugin = ini.IniReadValue("Option", "Plugin", "");
            cmbSendTo.SelectedIndex = Convert.ToInt32(ini.IniReadValue("Option", "SendTo", "0"));
            txtSendHex.Text = ini.IniReadValue("Option", "SendHex", "");
            for (int i = 0; i < cmbPlugins.Items.Count; i++)
            {
                if (cmbPlugins.Items[i].ToString() == plugin)
                {
                    cmbPlugins.SelectedIndex = i;
                    break;
                }
            }
            if (cmbPlugins.SelectedIndex < 0 && cmbPlugins.Items.Count > 0) { cmbPlugins.SelectedIndex = 0; }

            CheckToolPath = ini.IniReadValue("Option", "CheckToolPath", "");
            nudSend.Value = Convert.ToInt32(ini.IniReadValue("Option", "Timer", "1000"));
            nudCheckStart.Value = Convert.ToInt32(ini.IniReadValue("Option", "CheckStart", "1"));
            nudCheckEnd.Value = Convert.ToInt32(ini.IniReadValue("Option", "CheckEnd", "0"));
            string checkPlugin = ini.IniReadValue("Option", "CheckPlugin", "None");
            for (int i = 1; i < cmbCheck.Items.Count; i++)
            {
                if (cmbCheck.Items[i].ToString() == checkPlugin)
                {
                    cmbCheck.SelectedIndex = i;
                    break;
                }
            }

            chkRecordData.Checked = ini.IniReadValue("Option", "RecordData", "False") == "True";
            chkAutoAnswer.Checked = ini.IniReadValue("Option", "AutoAnswer", "False") == "True";

            serialLog1.LogEnable = ini.IniReadValue("Log", "Enable", "True") == "True";
            serialLog1.LogAutoScroll = ini.IniReadValue("Log", "AutoScroll", "True") == "True";
            serialLog1.SerialLogType = (ITLDG.LogType)Convert.ToInt32(ini.IniReadValue("Log", "LogType", "0"));
        }
        void SaveSerialOption()
        {
            INIFileHelper ini = new INIFileHelper(iniFile);
            SerialSave(com1, "Com1", ini);
            SerialSave(com2, "Com2", ini);
            SerialOtherSave(ini);
        }
        void SerialSave(SerialPortInfo sp, string Name, INIFileHelper ini)
        {
            ini.IniWriteValue(Name, "PortName", sp.PortName.ToString());
            ini.IniWriteValue(Name, "Stop", sp.StopBits.ToString());
            ini.IniWriteValue(Name, "Data", sp.DataBits.ToString());
            ini.IniWriteValue(Name, "Parity", sp.Parity.ToString());
            ini.IniWriteValue(Name, "Dtr", sp.DtrEnable.ToString());
            ini.IniWriteValue(Name, "Rts", sp.RtsEnable.ToString());
            ini.IniWriteValue(Name, "BaudRate", sp.BaudRate.ToString());
            ini.IniWriteValue(Name, "Timer", sp.Timer.ToString());
            ini.IniWriteValue(Name, "TimeOut", sp.TimeOut.ToString());
            ini.IniWriteValue(Name, "IP", sp.IP.ToString());
            ini.IniWriteValue(Name, "Port", sp.Port.ToString());
        }
        void SerialOtherSave(INIFileHelper ini)
        {

            ini.IniWriteValue("Com1", "Name", com1Name);
            ini.IniWriteValue("Com2", "Name", com2Name);

            ini.IniWriteValue("Com1", "Forward", com1Forward.ToString());
            ini.IniWriteValue("Com2", "Forward", com2Forward.ToString());
        }

        void SerialOtherRead(INIFileHelper ini)
        {
            txtName1.Text = ini.IniReadValue("Com1", "Name", "");
            txtName2.Text = ini.IniReadValue("Com2", "Name", "");

            chkForward1.Checked = Convert.ToBoolean(ini.IniReadValue("Com1", "Forward", "true"));
            chkForward2.Checked = Convert.ToBoolean(ini.IniReadValue("Com2", "Forward", "true"));
        }
        void ReadSerialOption()
        {
            INIFileHelper ini = new INIFileHelper(iniFile);
            SerialRead(com1, "Com1", cmbBaudRate1, ini);
            SerialRead(com2, "Com2", cmbBaudRate2, ini);
            SerialOtherRead(ini);
        }
        void SerialRead(SerialPortInfo sp, string Name, ComboBox cmb, INIFileHelper ini)
        {
            sp.PortName = ini.IniReadValue(Name, "PortName", sp.PortName.ToString());
            sp.StopBits = (StopBits)Enum.Parse(typeof(StopBits), ini.IniReadValue(Name, "Stop", sp.StopBits.ToString()));
            sp.DataBits = Convert.ToInt32(ini.IniReadValue(Name, "Data", sp.DataBits.ToString()));
            sp.Parity = (Parity)Enum.Parse(typeof(Parity), ini.IniReadValue(Name, "Parity", sp.Parity.ToString()));
            sp.DtrEnable = Convert.ToBoolean(ini.IniReadValue(Name, "Dtr", sp.DtrEnable.ToString()));
            sp.RtsEnable = Convert.ToBoolean(ini.IniReadValue(Name, "Rts", sp.RtsEnable.ToString()));
            sp.BaudRate = Convert.ToInt32(ini.IniReadValue(Name, "BaudRate", "9600"));
            sp.Timer = Convert.ToInt32(ini.IniReadValue(Name, "Timer", "0"));
            sp.TimeOut = Convert.ToInt32(ini.IniReadValue(Name, "TimeOut", "30"));

            sp.IP = IPAddress.Parse(ini.IniReadValue(Name, "IP", "0.0.0.0"));
            sp.Port = Convert.ToInt32(ini.IniReadValue(Name, "Port", "8866"));
            cmb.Text = sp.BaudRate.ToString();
        }
        private void btnReload_Click(object sender, EventArgs e)
        {
            refreshPortList();
        }
        void LoadPlugins()
        {
            pluginCommon = new PluginCommon(com1, com2, PluginWrite);
            string[] files = Directory.GetFiles(Application.StartupPath + "\\Plugins", "*.dll");
            cmbPlugins.Items.Add("不使用");
            foreach (string file in files)
            {
                try
                {
                    Assembly assembly = Assembly.LoadFile(file);
                    Type[] types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        if (type.GetInterface("IPlugin") != null)
                        {
                            IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
                            listPlugins.Add(plugin);
                            cmbPlugins.Items.Add(plugin.Name);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("加载插件失败:" + ex.Message);
                }
            }
        }
        private void FrmMain_Shown(object sender, EventArgs e)
        {
            try
            {
                ReadSerialOption();
                cmbSendTo.Items[0] = com1Name;
                cmbSendTo.Items[1] = com2Name;
            }
            catch (Exception ex)
            {
                Console.WriteLine("读取配置文件失败:" + ex.Message);
            }
            refreshPortList();
            LoadPlugins();
            InitCheckPlugins();
            ReadOption();
            NextTip();
        }

        void InitCheckPlugins()
        {

            listCheckPlugins = Plugin.GePlugins();
            listCheckPlugins = listCheckPlugins.OrderBy(x => x.Name).ToList();
            cmbCheck.Items.Clear();
            cmbCheck.Items.Add("None");
            cmbCheck.Items.AddRange(listCheckPlugins.ToArray());
            cmbCheck.SelectedIndex = 0;
        }

        private void cmbBaudRate2_TextChanged(object sender, EventArgs e)
        {
            ComBaudChange(com2, cmbBaudRate2);
        }

        private void cmbBaudRate1_TextChanged(object sender, EventArgs e)
        {
            ComBaudChange(com1, cmbBaudRate1);
        }

        private void chkForward1_CheckedChanged(object sender, EventArgs e)
        {
            com1Forward = chkForward1.Checked;
        }

        private void chkForward2_CheckedChanged(object sender, EventArgs e)
        {
            com2Forward = chkForward2.Checked;
        }

        private void txtName1_TextChanged(object sender, EventArgs e)
        {
            com1Name = txtName1.Text;
            cmbSendTo.Items[0] = com1Name;
        }

        private void txtName2_TextChanged(object sender, EventArgs e)
        {
            com2Name = txtName2.Text;
            cmbSendTo.Items[1] = com2Name;
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            if (!Directory.Exists(pluginDir))
            {
                Directory.CreateDirectory(pluginDir);
            }
            Text += " V" + Application.ProductVersion;
            com1.DataReceived += Com1_DataReceived;
            com2.DataReceived += Com2_DataReceived;
        }
        void PluginWrite(IPlugin plugin, bool isCom1, byte[] bytes)
        {
            string hex = bytes.GetString_HEX("");
            Color color = Color.DarkGreen;
            if (!isCom1)
            {
                color = Color.DarkBlue;
            }
            serialLog1.AddLog(plugin.Name, color, hex);
            BitAnalysis(bytes);

            SerialPortInfo sp = com1;
            if (!isCom1)
            {
                sp = com2;
            }
            if (sp == null || !sp.IsOpen)
            {
                return;
            }
            sp.Write(bytes, 0, bytes.Length);



        }
        /// <summary>
        /// 串口添加日志并转发
        /// </summary>
        /// <param name="data">收到的数据</param>
        /// <param name="name">串口名称</param>
        /// <param name="isCom1">是否是串口1</param>
        /// <param name="openForward">是否勾选了转发</param>
        /// <param name="spReceive">收到数据的串口</param>
        /// <param name="spSend">将转发到的串口</param>
        /// <param name="isAuto">这条消息是否来自自动回复</param>
        private void AddLog(byte[] data, string name, bool isCom1, bool openForward, SerialPortInfo spReceive, SerialPortInfo spSend, bool isAuto = false)
        {
            byte[] rep = null;
            if (!isAuto && pluginIndex >= 0)
            {
                try
                {
                    rep = listPlugins[pluginIndex].GetBytes(isCom1, name, ref data);
                }
                catch (Exception ex)
                {
                    serialLog1.AddLog(listPlugins[pluginIndex].Name + " 插件处理异常", Color.DarkRed, Encoding.Default.GetBytes(ex.Message));
                }

            }
            string hex = data.GetString_HEX("");
            Color color = Color.DarkBlue;
            if (!isCom1)
            {
                color = Color.DarkGreen;
            }
            serialLog1.AddLog(name, color, hex);
            BitAnalysis(data);
            if (isAuto) { return; }
            if (rep != null && rep.Length > 0)
            {
                BitAnalysis(rep);
                spReceive.Write(rep, 0, rep.Length);
                AddLog(rep, "插件答复", isCom1: !isCom1, openForward: openForward, spSend, spReceive, true);
                return;
            }
            if (isCom1 && AutoAnswer && dicCache.ContainsKey(hex))
            {
                spReceive.Write(dicCache[hex], 0, dicCache[hex].Length);
                BitAnalysis(dicCache[hex]);
                AddLog(dicCache[hex], "自动应答-" + com2Name, isCom1: false, openForward: false, spSend, spReceive, true);
                return;
            }

            //!hex.Contains("0D0D0A") &&


            if (isCom1)
            {
                lastSendHex = hex;
            }
            else if (RecordData && !string.IsNullOrEmpty(lastSendHex))
            {
                if (dicCache.ContainsKey(lastSendHex))
                {
                    dicCache[lastSendHex] = data;
                }
                else
                {
                    dicCache.Add(lastSendHex, data);
                }
                Invoke((MethodInvoker)delegate
                {
                    UpCacheCount();

                });
            }
            if (openForward && spSend.IsOpen)
            {
                spSend.Write(data, 0, data.Length);
            }
        }

        void BitAnalysis(byte[] bytes)
        {
            if (frm != null && !frm.IsDisposed)
            {
                frm.NewData(bytes);
            }
        }
        void RunUI(Action action)
        {
            Invoke((MethodInvoker)delegate
            {
                action();

            });
        }
        void OpenSerial(SerialPortInfo sp, ComboBox cmbCom, ComboBox cmbBaudRate, Button btnCom)
        {

            if (cmbCom.SelectedIndex < 0)
            {
                MessageBox.Show("未获取到串口号,不可以打开空的串口", "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string comNameTemp = GetCom(cmbCom.Text);
            if ((sp == com1 ? com2 : com1).IsOpen && cmbCom1.Text == cmbCom2.Text)
            {
                MessageBox.Show("不可转发两个相同的串口", "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            try
            {
                sp.PortName = comNameTemp;
                sp.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
                sp.Open();
                SaveSerialOption();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            RunUI(() =>
            {
                cmbCom.Enabled = false;
                btnCom.Text = "关闭";
                CheckSendEnable();
            });

        }
        void CloseSerial(SerialPortInfo sp)
        {
            if (sp == com1)
            {
                CloseSerial(com1, cmbCom1, btnCom1);
            }
            else
            {
                CloseSerial(com2, cmbCom2, btnCom2);
            }
        }
        void CloseSerial(SerialPortInfo sp, ComboBox cmbCom, Button btnCom)
        {

            try
            {
                sp.Close();
            }
            catch (Exception)
            {
            }
            RunUI(() =>
            {
                cmbCom.Enabled = true;
                btnCom.Text = "打开";
                CheckSendEnable();
            });

        }
        private void btnCom1_Click(object sender, EventArgs e)
        {
            if (btnCom1.Text == "打开")
            {
                OpenSerial(com1, cmbCom1, cmbBaudRate1, btnCom1);
            }
            else
            {
                CloseSerial(com1, cmbCom1, btnCom1);
            }

        }

        private void btnCom2_Click(object sender, EventArgs e)
        {
            if (btnCom2.Text == "打开")
            {
                OpenSerial(com2, cmbCom2, cmbBaudRate2, btnCom2);
            }
            else
            {
                CloseSerial(com2, cmbCom2, btnCom2);
            }
        }

        void CheckSendEnable()
        {
            bool enable = false;
            if (cmbSendTo.SelectedIndex == 0 && com1.IsOpen)
            {
                enable = true;
            }
            else if (cmbSendTo.SelectedIndex == 1 && com2.IsOpen)
            {
                enable = true;
            }
            btnSend.Enabled = enable;
            if (!enable)
            {
                timerSend.Enabled = false;
                chkTimer.Checked = false;
            }
            chkTimer.Enabled = enable;
        }

        private void serialLog1_Load(object sender, EventArgs e)
        {

        }

        private void timerCom1_Tick(object sender, EventArgs e)
        {
            DataShow(com1Name, true, com1Forward, com1, com2);
        }

        private void timerCom2_Tick(object sender, EventArgs e)
        {
            DataShow(com2Name, false, com2Forward, com2, com1);
        }
        void DataShow(string name, bool isCom1, bool openForward, SerialPortInfo spReceive, SerialPortInfo spSend)
        {
            if (!spReceive.IsOpen)
            {
                return;
            }
            try
            {
                int length = spReceive.BytesToRead;
                if (length == 0)//没数据,退出去
                    return;
                byte[] rev = new byte[length];
                spReceive.Read(rev, 0, length);//读数据
                if (rev.Length == 0)
                    return;

                AddLog(rev, name, isCom1, openForward, spReceive, spSend);
            }
            catch (Exception)
            {
                return;
            }//崩了?
        }


        private void btnLoadCache_Click(object sender, EventArgs e)
        {
            Import();
        }
        private void UpCacheCount()
        {
            lblDataCount.Text = dicCache.Count.ToString();
        }
        private void Import()
        {
            OpenFileDialog ofd = new OpenFileDialog
            {
                Title = "请选择缓存文件",
                Filter = "缓存数据|*.json"
            };
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    dicCache.Clear();
                    string jsonStr = File.ReadAllText(ofd.FileName);
                    Dictionary<string, string> dicCacheTemp = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonStr);
                    foreach (var item in dicCacheTemp)
                    {
                        dicCache.Add(item.Key.Replace(" ", ""), item.Value.Replace(" ", "").GetBytes_HEX());
                    }
                    UpCacheCount();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "加载失败", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
        }

        private void Save()
        {
            SaveFileDialog sfd = new SaveFileDialog
            {
                Title = "选择保存路径",
                Filter = "缓存数据|*.json"
            };
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    Dictionary<string, string> dicJson = new Dictionary<string, string>();
                    foreach (var item in dicCache)
                    {
                        dicJson.Add(item.Key.GetBytes_HEX().GetString_HEX(), item.Value.GetString_HEX());
                    }
                    var settings = new JsonSerializerSettings
                    {
                        Formatting = Formatting.Indented
                    };
                    string jsonStr = JsonConvert.SerializeObject(dicJson, settings);
                    File.WriteAllText(sfd.FileName, jsonStr);
                    MessageBox.Show("保存成功");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            Save();
        }

        private void cmbPlugins_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (pluginIndex >= 0)
            {
                try
                {
                    listPlugins[pluginIndex].UnCheck();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "插件错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }

            }
            pluginIndex = cmbPlugins.SelectedIndex - 1;
            if (pluginIndex >= 0)
            {
                btnPluginOption.Enabled = listPlugins[pluginIndex].HasOption;
                listPlugins[pluginIndex].Checked(pluginCommon);
            }
            else
            {
                btnPluginOption.Enabled = false;
            }

        }

        private void btnClearCache_Click(object sender, EventArgs e)
        {
            dicCache.Clear();
            UpCacheCount();
        }

        private void FrmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            SaveOption();
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (!txtSendHex.IsHex())
            {
                return;
            }
            timerSendToName = cmbSendTo.Text;
            SerialPortInfo sp = com1;
            if (cmbSendTo.SelectedIndex == 1)
            {
                sp = com2;
            }
            byte[] bytes = GetSendBytes();
            DebugSend(sp, bytes);
        }
        byte[] GetSendBytes()
        {
            byte[] bytes = txtSendHex.Text.GetBytes_HEX();
            if (cmbCheck.SelectedIndex <= 0)
            {
                return bytes;
            }
            int start = (int)nudCheckStart.Value - 1;
            int end = (int)nudCheckEnd.Value - 1;
            if (start < 0 || end < 0 || start >= bytes.Length || end >= bytes.Length)
            {
                return bytes;
            }
            int bytesLength = (bytes.Length - end) - start;
            byte[] bytesCheck = new byte[bytesLength];
            Array.Copy(bytes, start, bytesCheck, 0, bytesLength);

            int checkIndex = cmbCheck.SelectedIndex - 1;
            byte[] bytesCheckResult = listCheckPlugins[checkIndex].CheckData(bytesCheck);
            List<byte> listBytes = new List<byte>();
            listBytes.AddRange(bytes);
            if (end == 0)
            {
                listBytes.AddRange(bytesCheckResult);
            }
            else
            {
                end = bytes.Length - end;
                listBytes.RemoveRange(end, bytesCheckResult.Length);
                listBytes.InsertRange(end, bytesCheckResult);
            }

            return listBytes.ToArray();
        }
        private void lblDataCount_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            bool temp = RecordData;
            RecordData = false;
            FrmDatas frmDatas = new FrmDatas(dicCache);
            frmDatas.SaveDatasEvent += FrmDatas_SaveDatasEvent;
            frmDatas.ShowDialog();
            UpCacheCount();
            RecordData = temp;
        }

        private void FrmDatas_SaveDatasEvent(Dictionary<string, byte[]> dic)
        {
            dicCache = dic;
        }

        private void chkTimer_CheckedChanged(object sender, EventArgs e)
        {

            if (chkTimer.Checked && (txtSendHex.Text == "" || !txtSendHex.IsHex()))
            {
                MessageBox.Show("请先输入正确的HEX字符串", "发送消息错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                chkTimer.Checked = false;
                return;
            }

            if (chkTimer.Checked)
            {
                timerSendBytes = GetSendBytes();
                timerSend.Interval = (int)nudSend.Value;
                timerSendToName = cmbSendTo.Text;
            }
            txtSendHex.Enabled = cmbCheck.Enabled = nudCheckStart.Enabled = nudCheckEnd.Enabled = cmbSendTo.Enabled = !chkTimer.Checked;
            timerSend.Enabled = chkTimer.Checked;
        }

        private void cmbSendTo_SelectedIndexChanged(object sender, EventArgs e)
        {
            timerCom = com1;
            if (cmbSendTo.SelectedIndex == 1)
            {
                timerCom = com2;
            }
            CheckSendEnable();
        }

        private void nudSend_ValueChanged(object sender, EventArgs e)
        {
            timerSend.Interval = (int)nudSend.Value;
        }

        private void timerSend_Tick(object sender, EventArgs e)
        {
            DebugSend(timerCom, timerSendBytes);
        }

        private void tsslTip_MouseHover(object sender, EventArgs e)
        {
            timerTip.Enabled = false;
        }

        private void tsslTip_MouseLeave(object sender, EventArgs e)
        {
            timerTip.Enabled = true;
        }

        private void timerTip_Tick(object sender, EventArgs e)
        {
            NextTip();
        }
        void NextTip()
        {
            tipIndex++;
            if (tipIndex >= tips.Length)
            {
                tipIndex = 0;
            }
            tsslTip.Text = tips[tipIndex];
        }

        private void btnCheckTool_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(CheckToolPath) && File.Exists("CheckTool.exe"))
            {
                Process.Start("CheckTool.exe");
                return;
            }
            if (File.Exists(CheckToolPath))
            {
                Process.Start(CheckToolPath);
                return;
            }
            if (MessageBox.Show("工具路径未配置或不存在,是否立即配置?", "错误", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
            {
                return;
            }
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "exe文件|*.exe";
            openFileDialog.Title = "选择校验工具";
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                CheckToolPath = openFileDialog.FileName;
                Process.Start(CheckToolPath);
            }
        }

        private void chkRecordData_CheckedChanged(object sender, EventArgs e)
        {
            RecordData = chkRecordData.Checked;
        }

        private void chkAutoAnswer_CheckedChanged(object sender, EventArgs e)
        {
            AutoAnswer = chkAutoAnswer.Checked;
        }

        private void btnPluginOption_Click(object sender, EventArgs e)
        {
            listPlugins[pluginIndex].Option();
        }
        FrmBitAnalysis frm;
        private void btnBit_Click(object sender, EventArgs e)
        {
            if (frm == null || frm.IsDisposed)
            {
                frm = new FrmBitAnalysis();
            }
            frm.Show();
            frm.Activate();
        }

        void DebugSend(SerialPortInfo sp, byte[] bytes)
        {
            if (sp == null)
            {
                return;
            }
            if (!sp.IsOpen)
            {
                CloseSerial(sp);
                return;
            }
            if (sp == com2)
            {
                lastSendHex = bytes.GetString_HEX("");
            }
            try
            {
                sp.Write(bytes, 0, bytes.Length);
            }
            catch (Exception ex)
            {
                CloseSerial(sp);
                MessageBox.Show(ex.Message, "发送失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            
            serialLog1.AddLog("串口调试-" + timerSendToName, Color.OrangeRed, bytes);
        }
        void ComBaudChange(SerialPortInfo sp, ComboBox cmb)
        {
            if (int.TryParse(cmb.Text, out int baud))
            {
                sp.BaudRate = baud;
            }
        }

        private bool refreshLock = false;
        /// <summary>
        /// 刷新设备列表
        /// </summary>
        private void refreshPortList()
        {
            if (refreshLock)
                return;
            refreshLock = true;
            cmbCom1.Items.Clear();
            cmbCom2.Items.Clear();
            List<string> strs = new List<string>();
            Task.Run(() =>
            {
                SerialPortExtensions.GetPortNames().ForEach(x =>
                {
                    strs.Add(x.COM.PadRight(5, ' ') + " - " + x.Name);
                });
                strs.Add(SerialPortInfo.SERIAL_TCP_SERVER);
                strs.Add(SerialPortInfo.SERIAL_TCP_CLIENT);

                this.Invoke((MethodInvoker)delegate ()
                {
                    for (int i = 0; i < strs.Count; i++)
                    {
                        string name = strs[i];
                        cmbCom1.Items.Add(name);
                        cmbCom2.Items.Add(name);
                        if (name == com1.PortName || name.StartsWith(com1.PortName + " "))
                        {
                            cmbCom1.SelectedIndex = i;
                        }
                        if (name == com2.PortName || name.StartsWith(com2.PortName + " "))
                        {
                            cmbCom2.SelectedIndex = i;
                        }
                    }
                    if (strs.Count >= 1)
                    {
                        cmbCom1.SelectedIndex = cmbCom1.SelectedIndex == -1 ? 0 : cmbCom1.SelectedIndex;
                        cmbCom2.SelectedIndex = cmbCom2.SelectedIndex == -1 ? 0 : cmbCom2.SelectedIndex;
                        btnCom1.Enabled = btnCom2.Enabled = true;
                    }
                    else
                    {
                        btnCom1.Enabled = btnCom2.Enabled = false;
                    }
                    refreshLock = false;

                });

            });
        }




    }
}