using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;

using Client.Properties;
using System.Text.RegularExpressions;
using System.Threading;

namespace Client
{
    public partial class Frmmain : Form
    {
        private TcpClient tclient = null;
        private NetworkStream ns1;
        private NetworkStream ns2;
        DateTime conntime = DateTime.Now;

        public Frmmain()
        {
            InitializeComponent();
        }

        private void Frmmain_Load(object sender, EventArgs e)
        {
            Settings.Default.Reload();
            ip1.Text = Settings.Default["ip"].ToString();
            txtport.Text = Settings.Default["port"].ToString();
            txtaddr.Text = Settings.Default["addr"].ToString();
            txtDONum.Text = Settings.Default["donum"].ToString();
            txtDINum.Text = Settings.Default["dinum"].ToString();
            txtInterval.Text = Settings.Default.timeInverse.ToString();
        }

        private void Frmmain_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                timer1.Enabled = false;
                tclient.Close();
            }
            catch (Exception)
            {

            }


            Settings.Default["ip"] = ip1.Text;
            Settings.Default["port"] = txtport.Text;
            Settings.Default["addr"] = txtaddr.Text;
            Settings.Default["donum"] = txtDONum.Text;
            Settings.Default["dinum"] = txtDINum.Text;
            Settings.Default.timeInverse = Convert.ToInt32(txtInterval.Text);

            Settings.Default.Save();
        }
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                //tclient = (TcpClient)ar.AsyncState;
                if (tclient.Connected == true)
                {
                    ns1 = tclient.GetStream();
                    ns2 = tclient.GetStream();

                    //因为要访问ui资源，所以需要使用invoke方式同步ui。
                    this.Invoke((EventHandler)(delegate
                    {
                        //追加的形式添加到文本框末端，并滚动到最后。
                        txtBoxRcv.AppendText("\r\n连接成功\r\n");
                        btnopen.Text = "连接成功";
                    }));
                }
                else
                    connectClient();
            }
            catch (Exception)
            {
                connectClient();
            }
        }

        private void connectClient()
        {
            try
            {
                if (tclient == null)
                {
                    conntime = DateTime.Now;
                    tclient = new TcpClient();
                    tclient.BeginConnect(ip1.Text, Convert.ToInt32(txtport.Text), new AsyncCallback(ConnectCallback), tclient);
                    //因为要访问ui资源，所以需要使用invoke方式同步ui。
                    this.Invoke((EventHandler)(delegate
                    {
                        //追加的形式添加到文本框末端，并滚动到最后。
                        txtBoxRcv.AppendText(string.Format("\r\n重新连接中：{0}：{1}\r\n", ip1.Text, Convert.ToInt32(txtport.Text)));
                    }));
                }
            }
            catch (Exception ex)
            {
                tclient = null;
                DebugInfo(ex.Message, null, 0);
            }
        }

        private byte[] RcvData()
        {
            byte[] info = new byte[2048 + 10];
            int len = 0;
            int retrycnt = 0;
            int timeout = 1000;

            Thread.Sleep(10);
            while (timeout > 0)
            {
                timeout -= 20;
                ns2.ReadTimeout = 5;
                try
                {
                    byte[] rcv = new byte[2048 + 10];
                    int rdlen = ns2.Read(rcv, 0, 2048);
                    for (int i = 0; i < rdlen; i++)
                    {
                        if (len < 2048) info[len++] = rcv[i];
                    }

                    if (rdlen > 0) retrycnt = 0;
                }
                catch (Exception)
                {

                }
                if (len > 0)
                {
                    retrycnt++;
                    if (retrycnt > 3) timeout = 0;
                }
            }
            if (len < 5) return null;
            byte[] rst = new byte[len];
            for (int i = 0; i < len; i++)
                rst[i] = info[i];
            return rst;
        }
        private byte[] sendinfo(byte[] info)
        {
            if (tclient == null)
            {
                connectClient();
                return null;
            }

            if (tclient.Connected == false)
            {
                if (conntime.AddMilliseconds(5 * Convert.ToInt32(txtInterval.Text)) < DateTime.Now)
                {
                    tclient.Close();
                    tclient = null;
                }
                return null;
            }
            tclient.SendTimeout = 300;
            tclient.ReceiveTimeout = 300;
            try
            {

                try
                {
                    ns1.WriteTimeout = 3 + info.Length;
                    ns1.Write(info, 0, info.Length);
                    DebugInfo("发送", info);
                }
                catch (Exception)
                {
                    tclient = null;
                    return null;
                }


                byte[] data = RcvData();
                if (data == null) return null;
                DebugInfo("接收", data);
                return analysisRcv(data, data.Length);
            }
            catch (Exception)
            {

            }
            return null;
        }

        private void btnopen_Click(object sender, EventArgs e)
        {
            if (tclient != null) tclient.Close();
            tclient = null;
            connectClient();
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            txtBoxRcv.Clear();
        }

        private void txtDONum_TextChanged(object sender, EventArgs e)
        {
            int donum = Convert.ToInt16(txtDONum.Text);
            if (donum > 32)
            { txtDONum.Text = "32"; return; }

            Button[] btns = {
                btnDO1 , btnDO2 , btnDO3 , btnDO4 , btnDO5, btnDO6 , btnDO7 , btnDO8 , btnDO9, btnDO10,
                btnDO11, btnDO12, btnDO13, btnDO14, btnDO15, btnDO16, btnDO17, btnDO18, btnDO19, btnDO20,
                btnDO21, btnDO22, btnDO23, btnDO24, btnDO25, btnDO26, btnDO27, btnDO28, btnDO29, btnDO30,
                btnDO31, btnDO32
            };

            for (int i = 0; i < donum; i++)
                btns[i].Visible = true;
            for (int i = donum; i < 32; i++)
                btns[i].Visible = false;
        }

        private void txtDINum_TextChanged(object sender, EventArgs e)
        {
            int dinum = Convert.ToInt16(txtDINum.Text);
            if (dinum > 32)
            { txtDINum.Text = "32"; return; }

            Button[] btns = {
                btnDI1, btnDI2, btnDI3, btnDI4, btnDI5, btnDI6, btnDI7, btnDI8, btnDI9, btnDI10,
                btnDI11, btnDI12, btnDI13, btnDI14, btnDI15, btnDI16, btnDI17, btnDI18, btnDI19,btnDI20,
                btnDI21, btnDI22, btnDI23, btnDI24, btnDI25, btnDI26, btnDI27, btnDI28, btnDI29,btnDI30,
                btnDI31, btnDI32
            };

            for (int i = 0; i < dinum; i++)
                btns[i].Visible = true;
            for (int i = dinum; i < 32; i++)
                btns[i].Visible = false;
        }

        private void OpenDO(int io)
        {
            Button[] btns = {
                btnDO1 , btnDO2 , btnDO3 , btnDO4 , btnDO5, btnDO6 , btnDO7 , btnDO8 , btnDO9, btnDO10,
                btnDO11, btnDO12, btnDO13, btnDO14, btnDO15, btnDO16, btnDO17, btnDO18, btnDO19, btnDO20,
                btnDO21, btnDO22, btnDO23, btnDO24, btnDO25, btnDO26, btnDO27, btnDO28, btnDO29, btnDO30,
                btnDO31, btnDO32
            };
            byte[] info = CModbusDll.WriteDO(Convert.ToInt16(txtaddr.Text), io - 1, true);
            byte[] rst = sendinfo(info);
            if (rst != null)
            {
                btns[io - 1].ImageIndex = (rst[0] == 0) ? 0 : 1;
            }
        }
        private void CloseDO(int io)
        {
            Button[] btns = {
                btnDO1 , btnDO2 , btnDO3 , btnDO4 , btnDO5, btnDO6 , btnDO7 , btnDO8 , btnDO9, btnDO10,
                btnDO11, btnDO12, btnDO13, btnDO14, btnDO15, btnDO16, btnDO17, btnDO18, btnDO19, btnDO20,
                btnDO21, btnDO22, btnDO23, btnDO24, btnDO25, btnDO26, btnDO27, btnDO28, btnDO29, btnDO30,
                btnDO31, btnDO32
            };
            byte[] info = CModbusDll.WriteDO(Convert.ToInt16(txtaddr.Text), io - 1, false);
            byte[] rst = sendinfo(info);
            if (rst != null)
            {
                btns[io - 1].ImageIndex = (rst[0] == 0) ? 0 : 1;
            }
        }

        private void btnDO_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            Button btn = (sender as Button);
            bool isopen = (btn.ImageIndex == 0) ? false : true;

            int io = Convert.ToInt32(btn.Tag);
            if (isopen) CloseDO(io);
            else OpenDO(io);
        }
        private void btnCloseAll_Click(object sender, EventArgs e)
        {
            ClearLedMode();
            byte[] info = CModbusDll.WriteAllDO(Convert.ToInt16(txtaddr.Text), Convert.ToInt16(txtDONum.Text), false);
            byte[] rst = sendinfo(info);
            if (rst != null)
            {
                byte[] doinfo = new byte[4];
                doinfo[0] = 0x00;
                doinfo[1] = 0x00;
                doinfo[2] = 0x00;
                doinfo[3] = 0x00;
                ShowDO(doinfo);
            }
        }

        private void btnOpenAll_Click(object sender, EventArgs e)
        {
            ClearLedMode();
            byte[] info = CModbusDll.WriteAllDO(Convert.ToInt16(txtaddr.Text), Convert.ToInt16(txtDONum.Text), true);
            byte[] rst = sendinfo(info);
            if (rst != null)
            {
                byte[] doinfo = new byte[4];
                doinfo[0] = 0xff;
                doinfo[1] = 0xff;
                doinfo[2] = 0xff;
                doinfo[3] = 0xff;
                ShowDO(doinfo);
            }
        }

        private void btnReadDO_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            int donum = Convert.ToInt16(txtDONum.Text);
            byte[] info = CModbusDll.ReadDO(Convert.ToInt16(txtaddr.Text), Convert.ToInt16(txtDONum.Text));
            byte[] rst = sendinfo(info);
            ShowDO(rst);
        }

        private void ShowDO(byte[] rst)
        {
            if (rst == null) return;
            Button[] btns = {
                btnDO1 , btnDO2 , btnDO3 , btnDO4 , btnDO5, btnDO6 , btnDO7 , btnDO8 , btnDO9, btnDO10,
                btnDO11, btnDO12, btnDO13, btnDO14, btnDO15, btnDO16, btnDO17, btnDO18, btnDO19, btnDO20,
                btnDO21, btnDO22, btnDO23, btnDO24, btnDO25, btnDO26, btnDO27, btnDO28, btnDO29, btnDO30,
                btnDO31, btnDO32
                };

            for (int j = 0; j < rst.Length & j < 4; j++)
            {
                byte status = rst[j];
                for (int i = 0; i < 8; i++)
                {
                    if ((status & (1 << i)) == 0x00)
                        btns[j * 8 + i].ImageIndex = 0;
                    else
                        btns[j * 8 + i].ImageIndex = 1;
                }
            }

        }
        private void ShowDI(byte[] rst)
        {
            if (rst == null) return;
            Button[] btns = {
                btnDI1, btnDI2, btnDI3, btnDI4, btnDI5, btnDI6, btnDI7, btnDI8, btnDI9, btnDI10,
                btnDI11, btnDI12, btnDI13, btnDI14, btnDI15, btnDI16, btnDI17, btnDI18, btnDI19,btnDI20,
                btnDI21, btnDI22, btnDI23, btnDI24, btnDI25, btnDI26, btnDI27, btnDI28, btnDI29,btnDI30,
                btnDI31, btnDI32
                };

            for (int j = 0; j < rst.Length & j < 4; j++)
            {
                byte status = rst[j];
                for (int i = 0; i < 8; i++)
                {
                    if ((status & (1 << i)) == 0x00)
                        btns[j * 8 + i].ImageIndex = 0;
                    else
                        btns[j * 8 + i].ImageIndex = 1;
                }
            }
        }

        private void ReadDODIAddr()
        {
            byte[] info = CModbusDll.ReadAIInfo(Convert.ToInt16(txtaddr.Text), 1000, 6);
            byte[] rst = sendinfo(info);

            if (rst == null) return;
            if (rst.Length != 12) return;
            ushort baseaddr = 0;
            ushort equipid = 0;
            byte[] dostatus = new byte[4];
            byte[] distatus = new byte[4];

            baseaddr = rst[0];
            baseaddr <<= 8;
            baseaddr |= rst[1];

            equipid = rst[2];
            equipid <<= 8;
            equipid |= rst[3];

            for (int i = 0; i < 4; i++)
            {
                dostatus[i] = rst[4 + i];
                distatus[i] = rst[8 + i];
            }
            txtID.Text = equipid.ToString();
            txtbaseaddr.Text = baseaddr.ToString();
            ShowDO(dostatus);
            ShowDI(distatus);
        }

        private void btnReadDI_Click(object sender, EventArgs e)
        {
            byte[] info = CModbusDll.ReadDI(Convert.ToInt16(txtaddr.Text), Convert.ToInt16(txtDINum.Text));
            byte[] rst = sendinfo(info);
            ShowDI(rst);
        }

        private byte[] analysisRcv(byte[] src, int len)
        {
            if (len < 6) return null;
            if (src[0] != Convert.ToInt16(txtaddr.Text)) return null;

            switch (src[1])
            {
                case 0x01:
                    if (CMBRTU.CalculateCrc(src, src[2] + 5) == 0x00)
                    {
                        byte[] dst = new byte[src[2]];
                        for (int i = 0; i < src[2]; i++)
                            dst[i] = src[3 + i];
                        return dst;
                    }
                    break;
                case 0x02:
                    if (CMBRTU.CalculateCrc(src, src[2] + 5) == 0x00)
                    {
                        byte[] dst = new byte[src[2]];
                        for (int i = 0; i < src[2]; i++)
                            dst[i] = src[3 + i];
                        return dst;
                    }
                    break;
                case 0x04:
                    if (CMBRTU.CalculateCrc(src, src[2] + 5) == 0x00)
                    {
                        byte[] dst = new byte[src[2]];
                        for (int i = 0; i < src[2]; i++)
                            dst[i] = src[3 + i];
                        return dst;
                    }
                    break;
                case 0x05:
                    if (CMBRTU.CalculateCrc(src, 8) == 0x00)
                    {
                        byte[] dst = new byte[1];
                        dst[0] = src[4];
                        return dst;
                    }
                    break;
                case 0x0f:
                    if (CMBRTU.CalculateCrc(src, 8) == 0x00)
                    {
                        byte[] dst = new byte[1];
                        dst[0] = 1;
                        return dst;
                    }
                    break;
                case 0x06:
                    if (CMBRTU.CalculateCrc(src, 8) == 0x00)
                    {
                        byte[] dst = new byte[4];
                        dst[0] = src[2];
                        dst[1] = src[3];
                        dst[2] = src[4];
                        dst[3] = src[5];
                        return dst;
                    }
                    break;
                case 0x10:
                    if (CMBRTU.CalculateCrc(src, 8) == 0x00)
                    {
                        byte[] dst = new byte[4];
                        dst[0] = src[2];
                        dst[1] = src[3];
                        dst[2] = src[4];
                        dst[3] = src[5];
                        return dst;
                    }
                    break;
            }
            return null;
        }


        private void DebugInfo(string infotxt, byte[] info, int len = 0)
        {
            string debuginfo;
            StringBuilder builder = new StringBuilder();
            if (info != null)
            {
                if (len == 0) len = info.Length;
                //判断是否是显示为16禁止
                //依次的拼接出16进制字符串
                for (int i = 0; i < len; i++)
                {
                    builder.Append(info[i].ToString("X2") + " ");
                }
            }
            debuginfo = string.Format("{0}:{1}\r\n", infotxt, builder.ToString());
            builder.Clear();

            //追加的形式添加到文本框末端，并滚动到最后。


            //因为要访问ui资源，所以需要使用invoke方式同步ui。
            this.Invoke((EventHandler)(delegate
            {
                txtBoxRcv.AppendText(debuginfo);
                if (txtBoxRcv.TextLength > 1000) txtBoxRcv.Clear();
            }));
        }
        private void tmrrecv_Tick(object sender, EventArgs e)
        {
            if (tclient.Connected == false) return;
            if (tclient.Available <= 0) return;

            byte[] data = new byte[2048];
            int len = ns2.Read(data, 0, 2048);

        }

        #region 正常模式 流水灯模式  跑马灯模式
        int ledmode = 0;//
        int ioindex = 0;
        private void btnLed1_Click(object sender, EventArgs e)
        {
            timer1.Interval = Convert.ToInt32(txtInterval.Text);

            if (timer1.Interval < 10) timer1.Interval = 100;
            timer1.Enabled = true;
            btnLed1.ImageIndex = 1;
            btnLed2.ImageIndex = 0;
            btnDIRead.ImageIndex = 0;

            ledmode = 1;
            ioindex = 0;
        }

        private void btnLed2_Click(object sender, EventArgs e)
        {
            timer1.Interval = 1 + Convert.ToInt32(txtInterval.Text);
            if (timer1.Interval < 10) timer1.Interval = 100;
            timer1.Enabled = true;
            btnLed1.ImageIndex = 0;
            btnLed2.ImageIndex = 1;
            btnDIRead.ImageIndex = 0;

            ledmode = 2;
            ioindex = 0;
        }
        private void btnDIRead_Click(object sender, EventArgs e)
        {
            timer1.Interval = 1 + Convert.ToInt32(txtInterval.Text);
            if (timer1.Interval < 10) timer1.Interval = 100;
            timer1.Enabled = true;
            btnLed1.ImageIndex = 0;
            btnLed2.ImageIndex = 0;
            btnDIRead.ImageIndex = 1;

            ledmode = 3;
            ioindex = 0;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            int donum = Convert.ToInt16(txtDONum.Text);

            switch (ledmode)
            {
                case 0:
                    timer1.Enabled = false;
                    break;
                case 1:
                    if (ioindex >= 2 * donum) ioindex = 0;
                    if (ioindex < donum) OpenDO(1 + ioindex);
                    else if (ioindex < 2 * donum) CloseDO(1 + ioindex - donum);
                    ioindex++;
                    break;
                case 2:
                    if (ioindex >= 2 * donum) ioindex = 0;
                    if ((ioindex & 0x01) == 0x00) OpenDO(1 + (ioindex >> 1));
                    else CloseDO(1 + (ioindex >> 1));
                    ioindex++;
                    break;
                case 3:
                    break;
                default:
                    ledmode = 0;
                    break;
            }
            ReadDODIAddr();
        }
        private void ClearLedMode()
        {
            timer1.Enabled = false;
            ledmode = 0;
            btnLed1.ImageIndex = 0;
            btnLed2.ImageIndex = 0;
            btnDIRead.ImageIndex = 0;
        }


        #endregion

        private void txtInterval_TextChanged(object sender, EventArgs e)
        {

        }

        private void btnReadAI_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            int ainum = Convert.ToInt16(txtAINum.Text);
            byte[] info = CModbusDll.ReadAIInfo(Convert.ToInt16(txtaddr.Text), 0, ainum);
            byte[] rst = sendinfo(info);
            ShowAI(rst);
        }

        private void ShowAI(byte[] rst)
        {
            StringBuilder str = new StringBuilder();
            str.Append("\r\n读取AI：");
            for(int i=0;i<rst.Length;i+=2)
            {
                short ai = rst[i];
                ai <<= 8;
                ai += rst[i+1];
                str.Append(ai+",");
            }
            str.Append(".\r\n");
            txtBoxRcv.AppendText(str.ToString());
        }

        private void txtwao1_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            byte[] info = CModbusDll.WriteAOInfo(Convert.ToInt16(txtaddr.Text), 0, Convert.ToInt16(txtao1.Text));
            byte[] rst = sendinfo(info);
            ShowAO(rst);
        }

        private void txtwao2_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            byte[] info = CModbusDll.WriteAOInfo(Convert.ToInt16(txtaddr.Text), 1, Convert.ToInt16(txtao2.Text));
            byte[] rst = sendinfo(info);
            ShowAO(rst);
        }

        private void txtwao12_Click(object sender, EventArgs e)
        {
            ClearLedMode();

            short[] ao = new short[2];
            ao[0] = Convert.ToInt16(txtao1.Text);
            ao[1] = Convert.ToInt16(txtao2.Text);
            byte[] info = CModbusDll.WriteAOInfo(Convert.ToInt16(txtaddr.Text), 0, 2, ao);
            byte[] rst = sendinfo(info);
            ShowAO(rst);
        }

        private void ShowAO(byte[] rst)
        {            
            short regstart = 0;
            short regnum = 0;
            regstart = rst[0]; regstart <<= 8; regstart += rst[1];
            regnum = rst[2]; regnum <<= 8; regnum += rst[3];
            txtBoxRcv.AppendText(string.Format("\r\n写入AO成功：地址{0}  数量{1}\r\n",regstart,regnum));
        }
    }
}