using cn.edu.suda.sumcu.iot.data;
using cn.edu.suda.sumcu.iot.json;
using cn.edu.suda.sumcu.iot.util;
using cn.edu.suda.sumcu.iot.wsServices;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using WebSocketSharp.Server;


//Form类主要针对iot应用开发，故放在cn.edu.suda.sumcu.iot空间下的form子空间中
namespace cn.edu.suda.sumcu.iot.form
{
    public partial class FrmRealtimeData : Form
    {
        
        SynchronizationContext m_SyncContext = null;    //定义一个同步上下文变量

        public delegate void updateFrameControl(FrameData frame);

        public delegate void DeviceData(string imsi,byte[] data);
        public event DeviceData DeviceDataEvent;             //数据接收事件   

        private FrmMain frmMain;
        //【1】定义类的成员变量
        public HCICom com = new HCICom();    //用于发送数据的通讯对象
        private long replyTime = 0;           //允许回发时间（秒）
        private const ulong MaxTime = 30;     //最长允许回发时间（秒）
        private Dictionary<string, ulong> activedIMSI = new Dictionary<string, ulong>();   //IMSI号，时间戳
        private Dictionary<string, FrameData> reSendData = new Dictionary<string, FrameData>();//IMSI号，要回发的数据
        //“实时数据”的界面显示
        private TextBox[] dTextbox;
        private Label[] dLabel;
        private Panel[] dPanel;
        //private WebSocketServer wsssv = null;
        public static WebSocketServer wssv = null;
        public static string sqlclearflag = "";
        private static FrmRealtimeData mInstance;

        DateTime start = DateTime.Now;      //系统开始运行时间
        DateTime end;                       //收到最近一帧数据时间
        TimeSpan runningTime;               //系统正常通信时间
        
        /// <summary>
        /// 初始化界面布局
        /// </summary>
        /// <returns></returns>
        public FrmRealtimeData()
        {
            InitializeComponent();
            //chart1.Series[0]["PieLabelStyle"] = "Outside";//将文字移到外侧
            //chart1.Series[0]["PieLineColor"] = "Black";//绘制黑色的连线。
            //List<string> xData = new List<string>() { "Normal", "Error" };
            //List<double> yData = new List<double>() { 1, 0 };
            //chart1.Series[0].Points.DataBindXY(xData, yData);
        }

        /// <summary>
        /// 允许一个线程和另外一个线程进行通讯
        /// </summary>
        /// <param name="frmMain"></param>
        /// 修改注释：【20200912】CRX，详细注释见注①
        public FrmRealtimeData(FrmMain frmMain):this()
        {
            this.frmMain = frmMain;
            m_SyncContext = SynchronizationContext.Current;    //用于跨线程创建标签
        }


        /// <summary>
        /// 创建窗体实例（单例模式）
        /// </summary>
        /// <returns></returns>
        public static FrmRealtimeData getInstance()
        {
            if (mInstance == null || mInstance.IsDisposed)
            {
                mInstance = new FrmRealtimeData(new FrmMain());  //创建一个实时数据窗体实例
            }
            return mInstance;
        }


        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函 数 名：RealtimePage_Load:实时数据页面加载函数                              
        /// 功    能：完成实时数据页面的初始化工作  
        /// 修    改：【20200912】CRX
        /// </summary>                                                      
        ///-----------------------------------------------------------------
        private void FrmRealtime_Load(object sender, EventArgs e)
        { 
            //【20200916】CRX在界面上显示程序开始运行时间
            this.TextStart.Text = start.ToString();
            frmMain.SetToolStripUserOperText("运行状态: 进入 FrmRealtime_Load()...");         //状态条显示
            //【1】设置允许跨线程文本框赋值
            Control.CheckForIllegalCrossThreadCalls = false;
            //【2】创建用来展示数据的标签和文本框并设置“回发“不可用
            createLabel(this.frmMain.g_frmStruct1);  //创建标签和文本框
            BtnSend.Enabled = false;                 //设置“回发”按钮无效
            //【3】在listBox_imsi中显示正在监听终端的imsi
            ListImsi.Items.Clear();                 //清空list_imsi框
            int count = 0;                          //监听IMSI个数变量
            //遍历g_IMSI数组中的每个IMSI号
            foreach (string Imsi in frmMain.g_IMSI) 
            {
                string one_imsi = Imsi.Trim();      //清除空格
                //IMSI号长度为15时，添加到监听IMSI列表中，IMSI号个数加1 
                if (one_imsi.Length == 15)          
                {
                    ListImsi.Items.Add(one_imsi);  
                    count++;                                      
                }
            }
            //提示框显示信息
            LabelImsi.Text = "正在监听IMSI：" + "\n(共" + count.ToString() + "个)";
            TextRecv.Text = "注意：\r\n" + "AHL.xml文件中的IMSI需与需监听的终端UE的"
                                + "IMSI号一致，否则无法收到终端UE发来的数据\r\n";
            //【4】与云平台转发建立连接        
            com.HCIComTarget = frmMain.g_target;     //设置属性（目标地址及端口）
            timer_FrmRealTimeData_1S.Stop();
            //若与将要侦听的imsi号建立连接失败，在界面下方提示错误
            if (com.Init(frmMain.g_IMSI) != 0)       //将要侦听的imsi号数组g_IMSI作为实参
            {
                frmMain.SetToolStripUserOperText("金葫芦提示：本机连接的云平台IP地址及端口"
                    + frmMain.g_target + "不对！"); //状态条
                frmMain.SetToolStripConnectStatusText("侦听面向终端数据的端口失败！");
            }
            ////若与将要侦听的imsi号建立连接成功，在界面下方给出提示信息
            else
            {
                frmMain.SetToolStripUserOperText("金葫芦提示：本机连接云平台" + frmMain.g_target +
                                  "成功...正等待接收数据...");   //状态条 
                frmMain.SetToolStripConnectStatusText("侦听面向终端数据的端口成功！");
            }
            frmMain.SetToolStripUserOperText("工作状态：运行");
            //【5】注册接收数据事件处理程序.
            com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);//【20200527】在注册IoT_recv事件之前，先将该事件注销一次
            com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);
            //【6】开启websocket服务
            //【6.1】为WsServices服务注册回发处理事件并初始化其frmMain
            WsServices.ueReSendEvent += new WsServices.UeReSend(UE_reSend);
            WsServices.frmMain = this.frmMain;
            //【6.2】开启ws服务，并使用AHL.xml文件【2.2】部分设置的地址、端口与目录
            wssv = new WebSocketServer(frmMain.g_wsTarget);             //建立连接
            wssv.AddWebSocketService<WsServices>(frmMain.g_wsDirection);//添加ws服务
            wssv.Start();
            //【20200608】设定凌晨1点重启程序
            //SetTaskAtFixedTime();
            frmMain.SetToolStripUserOperText("运行状态: 退出 FrmRealtime_Load()...");         //状态条显示
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:UE_reSend                                          
        /// 事    件:当有数据需要回发时触发，由WsServices服务触发                                            
        /// 功    能:将要回发的数据存储至reSendData，定时器会在允许时将数据
        ///          回发至终端
        /// 修改注释：【20200912】CRX，详细见注释②
        /// </summary>                                                      
        /// <param name="imsi">待回发的终端IMSI</param>                                   
        /// <param name="frame">待回发的数据</param>                                        
        public void UE_reSend(string imsi,FrameData frame)
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 UE_reSend()...");         //状态条显示
            //清空待发送数据缓冲区
            if (reSendData.ContainsKey(imsi))
                reSendData.Remove(imsi);
            for (int i = 0; i < frame.Parameter.Count; i++)
            { 
                //对文本框中的温度值进行处理，转换成字符类型
                if (frame.Parameter[i].name == "mcuTemp")
                {
                    string temp = dTextbox[i].Text.ToString();
                    temp = temp.Replace(".", "");
                    frame.Parameter[i].value = temp;//读出文本框中的数据               
                }
            }
            //添加要回发的数据
            reSendData.Add(imsi, frame);
            frmMain.SetToolStripUserOperText("运行状态: 退出 UE_reSend()...");         //状态条显示
        }


        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称: Text_update                                         
        /// 功    能:解帧并显示到文本框
        /// </summary>                                                      
        ///-----------------------------------------------------------------
        private void Text_update(ref FrameData tmpFrmStruct, int i, ref int isDataOkFlag, ref int inTheCyc)
        {
            DateTime realTime = DateTime.Now;
            TimeSpan offline = realTime - end;
            //chart1.Series[0]["PieLabelStyle"] = "Outside";//将文字移到外侧
            //chart1.Series[0]["PieLineColor"] = "Black";//绘制黑色的连线。
            //List<string> xData = new List<string>() { "Normal", "Error" };
            //List<double> yData = new List<double>() { runningTime.TotalSeconds, offline.TotalSeconds};
            ////List<double> yData = new List<double>() { 2, 3 };
            //chart1.Series[0].Points.DataBindXY(xData, yData);
            frmMain.SetToolStripUserOperText("运行状态: 进入 Text_update()...");         //状态条显示
            //【20200916】CRX更新系统已运行时间
            end = DateTime.Now;
            this.TextLatest.Text = end.ToString();
            runningTime = end - start;    //获取到的运行时间格式，秒精确到八位小数
            string r = Regex.Replace(runningTime.ToString(), @"\.\d+$", string.Empty);  //用正则表达式去掉秒后面的部分
            runningTime = TimeSpan.Parse(r);
            this.TextRunning.Text = runningTime.ToString();
            for (i = 0; i < tmpFrmStruct.Parameter.Count; i++)
            {
                inTheCyc++;
                //将发送时间转化为标准时间
                if (tmpFrmStruct.Parameter[i].name == "currentTime")
                {
                    //【20200910】CRX显示的时间格式为2020/9/10 15：57：00
                    System.DateTime startTime =
                        new DateTime(1970, 1, 1);      //获取时间基准
                    string sendTime =
                        tmpFrmStruct.Parameter[i].value;  //记录发送时间
                    ulong u = Convert.ToUInt64    //将时间转为ulong格式
                        (sendTime);
                    double d =                    //将u转化为double类型
                        Convert.ToDouble(u);
                    TimeSpan delta = TimeSpan.FromSeconds(d);
                    DateTime curTime =
                        startTime.Add(delta);     //获得发送时间
                    dTextbox[i].Text =
                        curTime.ToString();       //显示发送时间
                }
                else if (tmpFrmStruct.Parameter[i].name == "mcuTemp")
                {
                    string temp = tmpFrmStruct.Parameter[i].value;
                    dTextbox[i].Text = temp.Insert(temp.Length - 1, ".");
                    float mcuWendu = Convert.ToSingle(dTextbox[i].Text);
                    TempControl11.CurValue = mcuWendu;
                    TempControl11.Refresh();
                }
                else if (tmpFrmStruct.Parameter[i].name == "signalPower")
                {
                    dTextbox[i].Text = tmpFrmStruct.Parameter[i].value.ToString();
                    string temp = tmpFrmStruct.Parameter[i].value;
                    //dTextbox[i].Text = temp.Insert(temp.Length - 1, ".");
                    //float mcuWendu = Convert.ToSingle(dTextbox[i].Text);
                    //TempControl11.CurValue = mcuWendu;
                    ucMeter1.Value = Convert.ToDecimal(temp);
                    ucMeter1.Refresh();
                }
                else
                {
                    //其他情况直接将数据显示在文本框中
                    dTextbox[i].Text = tmpFrmStruct.Parameter[i].value.ToString();
                }
            }
            frmMain.SetToolStripUserOperText("运行状态: 退出 Text_update()...");         //状态条显示
        }
        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称：IoT_recv                                       
        /// 输入参数：无                              
        /// 功  能：（1）读出数据并显示，每接收一帧改变一次颜色（红和绿）
        ///         （2）将数据写入数据库
        /// 返  回：无
        /// 说  明：HCIcom类的DataReceived（数据接收）事件的处理程序
        /// </summary>   
        /// 【20200519】
        ///-----------------------------------------------------------------
        [Obsolete]
        public void IoT_recv()
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 IoT_recv()...");         //状态条显示

            string ss = "";                 //接收终端数据帧的文本框
            string sendTime = "";           //用于存储终端UE发送来的时间


            string str = string.Empty;      //用于存储要显示的内容
            byte[] frameData = null;        //用于存储接收帧中的有效数据
            int i,len,rowNum;
            int[] NodeType1 = { 71, 80, 84, 86 };      //节点类型

            int isDataOkFlag;              //判断数据是否正常的标志位
            int flag = 0;                  //
            int inTheCyc;                  //一帧数据子元素个数
            FrameData tmpFrmStruct;

            tmpFrmStruct = null;
            isDataOkFlag = 0;
            inTheCyc = 0;

            com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);    // 先将该事件注销一次

             DateTime dateTimeStart =new DateTime(1970, 1, 1);   //获取时间基准
             //frmMain.SetToolStripUserOperText("运行状态: 进入 HCICom_Recv()...");         //状态条显示
            
            string imsiRecv = null;
            try
            {
                //【1】判断是否有数据可取，若无数据，退出；若有读取到imsiRecv、frameData中
                if (com.Read(ref imsiRecv, ref frameData) == false)
                {
                    goto IoT_recv_Err1;
                }
                //【2】收到数据，对数据进行解析、显示并存储在数据库中
                //【2.1】将本IMSI号加入activedIMSI字典中，若已加入，则更新对应的时间。
                //       并将本IMSI号加入“选择查询的IMSI号”列表中
                if (!activedIMSI.ContainsKey(imsiRecv))
                    activedIMSI.Add(imsiRecv, frmMain.g_TimeSec);
                else
                    activedIMSI[imsiRecv] = frmMain.g_TimeSec;
                //【2.2】数据显示
                //【2.2.1】将接收到的字节数据显示在上方textBox_recv框中（十六进制）
                if (TextRecv.ForeColor == Color.Green)  //改变颜色
                {
                    TextRecv.ForeColor = Color.Red;
                    TextReAsk.ForeColor = Color.Red;
                }
                else
                {
                    TextRecv.ForeColor = Color.Green;
                    TextReAsk.ForeColor = Color.Green;
                }
                len = frameData.Length;                   //获取数据帧的长度
                //for (i = 0; i < len; i++)
                //{
                //    str += frameData[i].ToString("x2");   //转换成16进制
                //    str += "-";                           //加上分隔符“-”
                //}
                //TextRecv.Text = str;            //显示信息        
                //TextRecv.Refresh();             //立即刷新显示
                //frmMain.setToolStripUserOperText("Operating status：" + len + "Bytes are displayed directly, continue to deframe...");
                //【2.3】触发设备配置窗体的数据接收事件
                DeviceDataEvent?.Invoke(imsiRecv, frameData);  //若事件不为空，则使用invoke
                //【2.4】根据接收到的数据重新创建标签
                if (frameData[0] == 'A' || frameData[0] == 'B') goto IoT_recv_exit;  //若收到的为配置命令
                string command = Encoding.Default.GetString(frameData, 0, 2);
                if(this.frmMain.g_commandsFrame.ContainsKey(command))
                {
                    tmpFrmStruct = this.frmMain.g_commandsFrame[command];
                }
                if (command != dTextbox[0].Text && m_SyncContext != null)    //若命令更换，则新建标签           
                {
                    //左边为方法，右边为参数
                    try
                    {
                        m_SyncContext.Post(createLabel, tmpFrmStruct);    //重新创建标签
                    }
                    catch(Exception)
                    { }
                    Thread.Sleep(200);
                }          
                //把接收到的字节数组类型的数据转为结构体的成员变量
                if (tmpFrmStruct.byteToStruct(frameData)== false) goto IoT_recv_exit;
                ////【20200608】文本框显示
                //Text_update(ref tmpFrmStruct, i, ref isDataOkFlag, ref inTheCyc);
                //（2.4) 解析接收到的数据并显示在文本框中
                tmpFrmStruct.byteToStruct(frameData);//把接收到的字节数组类型的数据转为结构体的成员变量
                for (i = 0; i < tmpFrmStruct.Parameter.Count; i++)
                {
                    //将发送时间转化为标准时间
                    if (tmpFrmStruct.Parameter[i].name == "currentTime")
                    {
                        System.DateTime startTime =
                            new DateTime(1970, 1, 1);      //获取时间基准
                        sendTime =
                            tmpFrmStruct.Parameter[i].value;  //记录发送时间
                        ulong u = Convert.ToUInt64    //将时间转为ulong格式
                            (sendTime);
                        double d =                    //将u转化为double类型
                            Convert.ToDouble(u);
                        TimeSpan delta = TimeSpan.FromSeconds(d);
                        DateTime curTime =
                            startTime.Add(delta);     //获得发送时间
                        dTextbox[i].Text =
                            curTime.ToString();       //显示发送时间
                    }
                    else if (tmpFrmStruct.Parameter[i].name == "mcuTemp")
                    {
                        string temp = tmpFrmStruct.Parameter[i].value;
                        dTextbox[i].Text = temp.Insert(temp.Length - 1, ".");
                        ss += tmpFrmStruct.Parameter[i].otherName + "：" + dTextbox[i].Text + "；";
                    }

                    else if (i == tmpFrmStruct.Parameter.Count)
                    {
                        ss += tmpFrmStruct.Parameter[i].otherName + "：" + dTextbox[i].Text;
                    }
                    else
                    {
                        //其他情况直接将数据显示在文本框中
                        dTextbox[i].Text = tmpFrmStruct.Parameter[i].value.ToString();
                        ss += tmpFrmStruct.Parameter[i].otherName + "：" + dTextbox[i].Text + "；";
                    }
                }
                //【20200608】文本框显示
                Text_update(ref tmpFrmStruct, i, ref isDataOkFlag, ref inTheCyc);
                ss = ss.Substring(0, ss.Length - 1);
                TextRecv.Text = ss;            //显示信息
                TextRecv.Refresh();             //立即刷新显示
                //【2.5】数据进库。将接收到的数据写入到数据库的上行表Up中
                frmMain.SetToolStripUserOperText("运行状态: 向数据库写数据, 请等待...");
                string imsis = "";
                foreach (string s in activedIMSI.Keys)
                    imsis += s + "" + ",";
                imsis.Remove(imsis.Length - 1);
                if(inTheCyc == tmpFrmStruct.Parameter.Count)
                {
                    inTheCyc = 0;
                    //【20200527】入库前，二次判断数据是否异常
                    for (i = 27; i < tmpFrmStruct.Parameter.Count; i += 8)
                    {
                        for(int k = 0;k < NodeType1.Length;k++)
                        {
                            if (int.Parse(tmpFrmStruct.Parameter[i].value) == NodeType1[k])
                            {
                                flag++;
                                //取原始数据
                                double data_temp1 = double.Parse(tmpFrmStruct.Parameter[i + 3].value);  //数据
                                double up_temp1 = double.Parse(tmpFrmStruct.Parameter[i + 4].value);    //阈值上限
                                double low_temp1 = double.Parse(tmpFrmStruct.Parameter[i + 5].value);   //阈值下限
                                if ((data_temp1 > up_temp1 * 2.0) || (data_temp1 < low_temp1 * 0.5))
                                {
                                    isDataOkFlag++;
                                }
                                //【20200602】以整形数据再次做判断
                                if ((Convert.ToInt32(data_temp1) > Convert.ToInt32(up_temp1 * 2.0)) || (Convert.ToInt32(data_temp1) < Convert.ToInt32(low_temp1 * 0.5)))
                                {
                                    isDataOkFlag++;
                                }
                            }
                        }
                        
                    }
                    if (flag == 0) isDataOkFlag++;
                    //若数据无异常，则正常操作
                    if(isDataOkFlag == 1)
                    {

                        //数据入库
                        rowNum = frmMain.sQLUp.insertFrame(tmpFrmStruct);
                        //调用报警程序
                        //new myState().Deealjson(tmpFrmStruct);

                        if (rowNum == -1)    //写入上行帧记录表
                        {
                            MessageBox.Show("写入数据库失败！"
                            , "金葫芦友情提示：", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            Application.Restart();   //重启程序
                        }

                        //（2.6）通知所有连接websocket服务的客户端有数据到来
                        //（2.6.1）组成要发送的Json数据
                        JsonCommand jsonCommand = new JsonCommand();
                        jsonCommand.command = "recv";
                        jsonCommand.source = imsiRecv;
                        //jsonCommand.password = tmpFrmStruct.Parameter[23].value + " " + tmpFrmStruct.Parameter[26].value;//password存放数据的gatewayhardaddr以及gatewauaddr
                        jsonCommand.password = "";
                        jsonCommand.value = rowNum.ToString();    //json包的数据等于数据库最后一条数据行号
                        JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
                        javaScriptSerializer.MaxJsonLength = Int32.MaxValue; //取得最大数值
                        string dataString = javaScriptSerializer.Serialize(jsonCommand);
                        //（2.6.2）发送数据至连接ws服务的客户端
                        WebSocketServiceManager bb2 = wssv.WebSocketServices;
                        bb2.Broadcast(dataString);
                        TextReAsk.Refresh();
                        TextReAsk.Text = dataString;
                    }
                }
                //【20200523】WYH,FrmRealtimeData.cs,防止PH阈值多次乘以系数
                tmpFrmStruct = null;
            }
            catch
            {
                frmMain.SetToolStripUserOperText("接收到错误的一帧数据，已被丢弃。");
                goto IoT_recv_exit;
            }
            //【3】启用“回发”按钮，设置允许回发时间并更新历史数据表标签文本框
            BtnSend.Enabled = true;             //启用“回发”按钮
            replyTime = (long)MaxTime;          //设置允许回发时间为MaxTime秒
            frmMain.SetToolStripUserOperText("运行状态: 数据解析完成");//状态条显示
            goto IoT_recv_exit;
            IoT_recv_Err1:
            frmMain.SetToolStripUserOperText("工作状态: 接收的数据不正确"); //状态条显示
        IoT_recv_exit:
            tmpFrmStruct = null;
            flag = 0;
            com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);
            com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);
            frmMain.SetToolStripUserOperText("运行状态: 退出 IoT_recv()..."); //状态条显示
            return;
            
        }



        ///-----------------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称: SetTaskAtFixedTime                                         
        /// 功    能:设定重启，每天重启
        /// </summary>                                                      
        ///------------------------------------------------------------------------
        private void SetTaskAtFixedTime()
        {
            //【20200812】把当前Up表名更改为Up-日期,创建新一个Up空表，与原表结构相同
            if (DateTime.Now.Day % 10 == 0)     //每十天
            {
                frmMain.sQLUp.BackUP_table();
            }
        }


        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:BtnSend(“回发”按钮）                                           
        /// 事    件:Click(单击)                                                  
        /// 功    能:根据文本框里的内容更新结构体g_frmStruct，然后组帧并发送
        /// 修改注释：CRX【20200914】
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        ///-----------------------------------------------------------------
        private void BtnSend_Click(object sender, EventArgs e)
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 BtnSend_Click()...");    //状态条显示
            int i;
            string imsi = "";
            FrameData frame = this.frmMain.g_commandsFrame[dTextbox[0].Text.ToString()];
            frmMain.SetToolStripUserOperText("运行状态: 正在回发数据...");    //状态条显示
            //【1】将文本框中内容更新到结构体frame中
            for (i = 0; i < frame.Parameter.Count; i++)
            {
                if (frame.Parameter[i].name.ToString() == "IMSI")
                    imsi = dTextbox[i].Text.ToString();    //读出要发送的IMSI号
                if (frame.Parameter[i].name.ToString() == "currentTime")
                {
                    System.DateTime startTime =           //获取时间基准
                    TimeZone.CurrentTimeZone.ToLocalTime
                    (new System.DateTime(1970, 1, 1));
                    ulong temp = (ulong)
                        (System.DateTime.Now.AddHours(8) - startTime).TotalSeconds; //计算当前时间与基准时间的差值时间戳
                    frame.Parameter[i].value =
                        temp.ToString();   //更新当前时间与基准时间的差值                            
                }
                else if (frame.Parameter[i].name == "mcuTemp")
                {
                    string temp = dTextbox[i].Text.ToString();
                    temp = temp.Replace(".", "");
                    frame.Parameter[i].value = temp;//读出文本框中的数据               
                }
                else
                {
                    if (frame.Parameter[i].type.ToString() == "byte[]")
                    {
                        frame.Parameter[i].value = "";    //清空该字节数组
                        frame.Parameter[i].value =
                            dTextbox[i].Text.ToString();    //读出文本框中的数据
                    }
                    else
                    {
                        frame.Parameter[i].value =
                            dTextbox[i].Text.ToString();    //读出文本框中的数据
                    }
                }
            }
            //【2】根据结构体frame中内容组帧为字节数组，并存入data中
            byte[] data =      //将结构体frame中的内容放入字节数组data
                frame.structToByte();
            //【3】发送数据
            if (com.Send(imsi, data) != 0)  //若发送数据失败
            {
                MessageBox.Show("与云平台断开连接, 请检查网络！！"
                        , "金葫芦友情提示 (回发按钮)：", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Application.Exit();
            }
            else
            { }
            frmMain.SetToolStripUserOperText("运行状态: 数据回发成功");    //状态条显示
            frmMain.SetToolStripUserOperText("运行状态: 退出 BtnSend_Click()...");    //状态条显示
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:button_clear_Click（“清空”按钮）                                           
        /// 事    件:Click（单击）                                                  
        /// 功    能:清空数据接收文本框textBox_recv                              
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        ///-----------------------------------------------------------------
        private void button_clear_Click(object sender, EventArgs e)
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 button_clear_Click()...");    //状态条显示
            int i;
            TextRecv.Text = "";                    //将接收文本框内容清空
            for (i = 0; i < dTextbox.Count(); i++) //清空所有自动创建的文本框
            {
                dTextbox[i].Text = "";
            }
            TextReAsk.Text = "";
            TempControl11.CurValue = 0;
            TempControl11.Refresh();
            ucMeter1.Value = 0;
            replyTime = -1;            //清空数据后禁用回发
            BtnSend.Enabled = false;
            frmMain.SetToolStripUserOperText("运行状态: 清空接收框成功！");//状态条显示
            frmMain.SetToolStripUserOperText("运行状态: 退出 button_clear_Click()...");    //状态条显示
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:timer_FrmRealtimeData_1S_Tick                                          
        /// 事    件:定时器的Tick事件，每秒运行本例程一次                                                  
        /// 功    能:（1）将数据回发至终端 （2）待回发的数据过多时则清空
        ///          （3）更新“实时数据”的状态条
        /// 
        /// 修改备忘：2018年8月27日，WB
        ///           2018年11月18日，HZX
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        ///-----------------------------------------------------------------

        private void timer_FrmRealtimeData_1S_Tick(object sender, EventArgs e)
        {
            //【1】遍历全局变量activedIMSI（字典），删除其中链接超过MaxTime秒的IMSI号
            //   (因为终端UE超过MaxTime秒断电，不可回发数据），确保activedIMSI字典中
            //   为可回发的IMSI号，若reSendData中的数据可回发至终端则回发，并存入下行
            //   表Down
            for (int i = 0; i < activedIMSI.Count; i++) //遍历可回发的IMSI
            {
                string key = activedIMSI.ElementAt(i).Key;
                //若reSendData中有需要回发该IMSI，则回发
                if (reSendData.ContainsKey(key))
                {
                    com.Send(key, reSendData[key].structToByte());//发送数据
                    frmMain.sQLDown.insertFrame(reSendData[key]);//将该数据存入下行表Down
                    reSendData.Remove(key);//去除已发送的数据
                }
                if ((frmMain.g_TimeSec - activedIMSI.ElementAt(i).Value
                    % ulong.MaxValue > (long)MaxTime))
                {
                    activedIMSI.Remove(key);
                }
            }
            //【2】若待回发的数据超过100条，则删除
            if (reSendData.Count > 100)
                reSendData.Clear();
            //【3】更新“实时”标签页的状态条，显示回发倒计时。
            try
            {
                --replyTime;                                 //允许回发时间减1
                if (replyTime > 0 && activedIMSI.Count > 0)    //若允许回发时间大于0且UE数量大于0
                {
                    //（3.1）更新“实时”标签页的状态条，显示回发倒计时。使能“回发”按钮
                    frmMain.SetToolStripUserOperText("运行状态: 解析完成   " +
                        "   您有 " + replyTime.ToString() + " 秒来回发");
                    BtnSend.Enabled = true;
                }
                else if (replyTime == 0)  //若允许回发时间等于0
                {
                    replyTime = -1;
                    frmMain.SetToolStripUserOperText("运行状态: 现在无法回发, 请等待下一帧数据！");
                    BtnSend.Enabled = false;   //禁用“回发”按钮
                }
                else if (replyTime < 0)   //若允许回发时间小于0
                    replyTime = -1;
            }
            catch (System.Data.SqlClient.SqlException sqlEx)
            {
                //显示错误信息
                MessageBox.Show("数据库操作错误" + sqlEx.ToString()
                    , "金葫芦友情提示 (实时数据 1S 定时器)：", MessageBoxButtons.OK, MessageBoxIcon.Error);
                //退出程序
                this.Dispose();
                Application.Exit();
            }
        }

        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:FrmRealtimeData_FormClosing                                          
        /// 事    件:点击“实时数据”窗口右上角的叉号                                                  
        /// 功    能:停止websocket服务
        /// 修改备忘：2018年8月27日，WB
        /// </summary>                                                      
        /// <param name="sender"></param>                                   
        /// <param name="e"></param>                                        
        ///-----------------------------------------------------------------
        private void FrmRealtimeData_FormClosing(object sender, FormClosingEventArgs e)
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 FrmRealtimeData_FormClosing()...");    //状态条显示
            try
            {
                wssv.RemoveWebSocketService("/wsServices");
                wssv.Stop();
                System.Environment.Exit(0);
                this.Dispose();
                this.Close();
            }
            catch
            {
                System.Environment.Exit(System.Environment.ExitCode);
                this.Dispose();
                this.Close();
                //System.Threading.Thread.Sleep(500);
            }
            frmMain.SetToolStripUserOperText("运行状态: 退出 FrmRealtimeData_FormClosing()...");    //状态条显示
        }

        ///以下为内部函数
        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 对    象:createLabel                                          
        /// 功    能:根据传入的frame1创建标签
        /// 修改备忘：2018年8月27日，WB
        /// </summary>                                                      
        /// <param name="frame1"></param>                                   
        ///-----------------------------------------------------------------
        public void createLabel(object frame1)
        {
            frmMain.SetToolStripUserOperText("运行状态: 进入 createLabel()...");    //状态条显示
            int i;
            FlowPanelRealData.Controls.Clear();   //清除窗体中的控件
            FrameData frame = (FrameData)frame1;
            if (frame.Parameter.Count > 0)
            {
                //重新定义标签与文本框
                dTextbox = new TextBox[frame.Parameter.Count];
                dLabel = new Label[frame.Parameter.Count];
                dPanel = new Panel[frame.Parameter.Count];
                //tabControl1.TabPages[0].BackColor = Color.AliceBlue;
                //设置底色
                FlowPanelRealData.BackColor = Color.AliceBlue;
                //创建标签和文本框，并设置属性和内容
                for (i = 0; i < frame.Parameter.Count; i++)
                {
                    try
                    {
                        if (i % 3 == 0)//第1列
                        {
                            dPanel[i] = new Panel();
                            dPanel[i].Width = 310;
                            dPanel[i].Height = 25;
                            dLabel[i] = new Label();
                            dLabel[i].Width = 120;
                            dLabel[i].Height = 25;
                            dLabel[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dLabel[i].TextAlign =
                                ContentAlignment.MiddleRight;
                            dLabel[i].Text =
                                frame.Parameter[i].otherName;

                            dTextbox[i] = new TextBox();
                            dTextbox[i].Width = 210;
                            dTextbox[i].Height = 25;
                            dTextbox[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dTextbox[i].Left = dPanel[i].Left + 120;
                            dTextbox[i].ForeColor = Color.White;
                        }
                        else if (i % 3 == 1) //第2列
                        {
                            dPanel[i] = new Panel();
                            dPanel[i].Width = 350;
                            dPanel[i].Height = 25;
                            dLabel[i] = new Label();
                            dLabel[i].Width = 120;
                            dLabel[i].Height = 25;
                            dLabel[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dLabel[i].TextAlign =
                                ContentAlignment.MiddleRight;
                            dLabel[i].Text =
                                frame.Parameter[i].otherName;

                            dTextbox[i] = new TextBox();
                            dTextbox[i].Width = 230;
                            dTextbox[i].Height = 25;
                            dTextbox[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dTextbox[i].Left = dPanel[i].Left + 120;
                            dTextbox[i].ForeColor = Color.White;
                        }
                        else //第3列
                        {
                            dPanel[i] = new Panel();
                            dPanel[i].Width = 280;
                            dPanel[i].Height = 25;
                            dLabel[i] = new Label();
                            dLabel[i].Width = 100;
                            dLabel[i].Height = 25;
                            dLabel[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dLabel[i].TextAlign =
                                ContentAlignment.MiddleRight;
                            dLabel[i].Text =
                                frame.Parameter[i].otherName;

                            dTextbox[i] = new TextBox();
                            dTextbox[i].Width = 200;
                            dTextbox[i].Height = 25;
                            dTextbox[i].Font = new System.Drawing.Font
                                ("宋体", 10.8F, FontStyle.Bold);
                            dTextbox[i].Left = dPanel[i].Left + 100;
                            dTextbox[i].ForeColor = Color.White;
                        }

                        //设置不同数据类型的文本框颜色不同
                        switch (frame.Parameter[i].type)
                        {
                            case "uint_8":
                            case "int_8":
                            case "byte":
                            case "sbyte":
                                dTextbox[i].BackColor = Color.Red; break;
                            case "uint_16":
                            case "int_16":
                            case "short":
                            case "ushort":
                                dTextbox[i].BackColor = Color.Orange; break;
                            case "uint_32":
                            case "int_32":
                            case "int":
                            case "uint":
                                dTextbox[i].BackColor = Color.Yellow;
                                dTextbox[i].ForeColor = Color.Black; break;
                            case "uint_64":
                            case "int_64":
                            case "long":
                            case "ulong":
                                dTextbox[i].BackColor = Color.Green; break;
                            case "float":
                                dTextbox[i].BackColor = Color.Cyan; break;
                            case "double":
                                dTextbox[i].BackColor = Color.DarkBlue; break;
                            case "uint_8[]":
                            case "byte[]":
                                dTextbox[i].BackColor = Color.Purple; break;
                            default:
                                if (frame.Parameter[i].type.Contains("byte[")
                                    || frame.Parameter[i].type.Contains("uint_8["))
                                {
                                    dTextbox[i].BackColor = Color.Purple; break;
                                }
                                else
                                {
                                    break;
                                }
                        }
                        if (frame.Parameter[i].wr == "read")
                        {
                            dTextbox[i].BackColor = Color.LightGray;
                            dTextbox[i].Enabled = false;
                        }
                        //设置文本框和标签框等依附的控件
                        dTextbox[i].Parent = dPanel[i];
                        dLabel[i].Parent = dPanel[i];
                        //dPanel[i].Parent = FlowPanelRealData;
                        FlowPanelRealData.Controls.Add(dPanel[i]);
                    }
                    catch
                    {
                        MessageBox.Show("自动创建文本框和标签框失败！");
                    }
                }
            }
            frmMain.SetToolStripUserOperText("运行状态: 退出 createLabel()...");    //状态条显示   
        }
        

        public void refreshControl()
        {
            this.LabelImsi.Refresh();
        }

        /// <summary>
        /// 改变数据接收框内容
        /// </summary>
        /// <param name="message"></param>
        public void showMessage(string message, string flag, string nameSource)
        {
            if (flag.Equals("TextAsk"))
            {

                if (this.TextAsk.InvokeRequired)
                {
                    if (TextAsk.ForeColor == Color.Red)
                    {
                        TextAsk.ForeColor = Color.Green;
                    }
                    else
                    {
                        TextAsk.ForeColor = Color.Red;
                    }
                    this.TextAsk.Invoke(new Action(() =>
                    {
                        GroupboxAsk.Text = "数据来自 [" + nameSource + "]";
                        TextAsk.Text = message;
                    }));
                }
                else
                    this.TextAsk.Text = message;
            }

            if (flag.Equals("TextResponse"))
            {
                if (TextResponse.ForeColor == Color.Red)
                {
                    TextResponse.ForeColor = Color.Green;
                }
                else
                    TextResponse.ForeColor = Color.Red;
                if (this.TextResponse.InvokeRequired)
                {
                    this.TextResponse.Invoke(new Action(() =>
                    {
                        GroupboxResponse.Text = "发送数据到 [" + nameSource + "]";
                        TextResponse.Text = message;
                    }));
                }
                else
                    this.TextResponse.Text = message;
            }
            else return;
        }

        private void ucMeter1_Load(object sender, EventArgs e)
        {

        }
    }
}


/*
 注①
     窗体是由一种称为“UI线程”的特殊类型的线程创建的。一个窗体不可以控制其他窗体里的控件，现在为了能控制主窗体FrmMain里的标签，对标签进行赋
     值，所以要跨线程创建标签。SynchronizationContext.Current可以使我们得到一个当前线程的SynchronizationContext的对象。
 注②
    Dictionary字典类必须包含命名空间System.Collection.Generic。
    Dictionary里面的每一个元素都是一个键值对（由两个元素组成：键和值）。键必须是唯一的，而值不需要唯一的。
    键和值都可以是任何类型（比如：string，int，自定义类型，等等）
    通过一个键读取一个值的时间是接近O(1)
    键值对之间的偏序可以不定义。
注③
    Invoke

    



 */
