zoukankan      html  css  js  c++  java
  • C#串口通信—向串口发送数据,同步接收返回数据

        最近写C#串口通信程序,系统是B/S架构。SerialPort类有一个DataReceived事件,用来接收串口返回的数据,但这种方式在C/S架构下很好用,但B/S就不好处理了。所以写了一个同步模式接收返回数据的方法,不使用DataReceived事件。经过测试,可以正常使用。

        一、MachineFactory类

        为什么使用工厂类:售货机由不止一个厂家提供,接口协议都不一样。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.IO;
    using System.IO.Ports;
    
    namespace IMachineDll
    {
        /// <summary>
        /// 售货机工厂类
        /// </summary>
        public class MachineFactory
        {
            /// <summary>
            /// 货机接口缓存
            /// </summary>
            private static Dictionary<string, IMachine> dicMachine = new Dictionary<string, IMachine>();
            /// <summary>
            /// 锁变量
            /// </summary>
            public static object _lock = new object();
    
            /// <summary>
            /// 创建售货机类
            /// </summary>
            /// <param name="path">DLL物理路径</param>
            /// <param name="dllName">DLL名称(不含扩展名),命名空间必须为DLL名称加“Dll”后缀,类名必须和DLL名称相同</param>
            ///  <param name="com">串口名称,如:COM1</param>
            public static IMachine Create(string path, string dllName, string com)
            {
                if (!dicMachine.ContainsKey(dllName)
                    || dicMachine[dllName] == null)
                {
                    using (FileStream fs = new FileStream(path + dllName + ".dll", FileMode.Open, FileAccess.Read))
                    {
                        using (MemoryStream ms = new MemoryStream())
                        {
                            byte[] byteArray = new byte[4096];
                            while (fs.Read(byteArray, 0, byteArray.Length) > 0)
                            {
                                ms.Write(byteArray, 0, byteArray.Length);
                            }
    
                            Assembly assembly = Assembly.Load(ms.ToArray());
                            dicMachine[dllName] = (IMachine)assembly.CreateInstance(dllName + "Dll." + dllName, false, BindingFlags.Default, null, new object[] { com }, null, null);
                        }
                    }
                }
    
                return dicMachine[dllName];
            }
        }
    }
    View Code

        二、Machine类

        1、变量与构造函数

    /// <summary>
    /// 串口资源
    /// </summary>
    private SerialPort serialPort = null;
    
    public Machine(string com)
    {
        serialPort = new SerialPort(com, 9600, Parity.None, 8, StopBits.One);
        serialPort.ReadBufferSize = 1024;
        serialPort.WriteBufferSize = 1024;
    }
    View Code

        2、向串口发送数据,同步接收返回数据的方法:

    /// <summary>
    /// 向串口发送数据,读取返回数据
    /// </summary>
    /// <param name="sendData">发送的数据</param>
    /// <returns>返回的数据</returns>
    private byte[] ReadPort(byte[] sendData)
    {
        lock (MachineFactory._lock)
        {
            //打开连接
            if (!serialPort.IsOpen) serialPort.Open();
    
            //发送数据
            serialPort.Write(sendData, 0, sendData.Length);
    
            //读取返回数据
            DateTime dt = DateTime.Now;
            while (serialPort.BytesToRead == 0)
            {
                Thread.Sleep(1);
    
                if (DateTime.Now.Subtract(dt).TotalMilliseconds > 5000) //如果5秒后仍然无数据返回,则视为超时
                {
                    throw new Exception("主版无响应");
                }
            }
            Thread.Sleep(50);
            byte[] recData = new byte[serialPort.BytesToRead];
            serialPort.Read(recData, 0, recData.Length);
    
            //关闭连接
            if (serialPort.IsOpen) serialPort.Close();
    
            return recData;
        }
    }
    View Code

    优化版:

    /// <summary>
    /// 向串口发送数据,读取返回数据
    /// </summary>
    /// <param name="sendData">发送的数据</param>
    /// <returns>返回的数据</returns>
    private byte[] ReadPort(byte[] sendData)
    {
        lock (MachineFactory._lock)
        {
            //打开连接
            if (!serialPort.IsOpen) serialPort.Open();
    
            //发送数据
            serialPort.Write(sendData, 0, sendData.Length);
    
            //读取返回数据
            DateTime dt = DateTime.Now;
            while (serialPort.BytesToRead < 2)
            {
                Thread.Sleep(1);
    
                if (DateTime.Now.Subtract(dt).TotalMilliseconds > 5000) //如果5秒后仍然无数据返回,则视为超时
                {
                    throw new Exception("主版无响应");
                }
            }
            List<byte> recList = new List<byte>();
            byte[] recData = new byte[serialPort.BytesToRead];
            serialPort.Read(recData, 0, recData.Length);
            recList.AddRange(recData);
            int length = recData[1] + 3; //报文数据总长度
            while (recList.Count < length)
            {
                if (serialPort.BytesToRead > 0)
                {
                    recData = new byte[serialPort.BytesToRead];
                    serialPort.Read(recData, 0, recData.Length);
                    recList.AddRange(recData);
                }
                Thread.Sleep(1);
            }
    
            //关闭连接
            if (serialPort.IsOpen) serialPort.Close();
    
            return recList.ToArray();
        }
    }
    View Code

        3、发送联机指令:

    /// <summary>
    /// 联机
    /// </summary>
    /// <param name="msg">传出错误信息</param>
    /// <returns>联机是否成功</returns>
    public bool Connect(out string msg)
    {
        byte[] sendData = new byte[] { 0x01, 0x01, 0x00, 0x00 };
        CommonUtil.CalCheckCode(sendData);
        byte[] recData = ReadPort(sendData);
    
        if (recData.Length >= 4
            && recData[0] == 0x01
            && recData[1] == 0x02
            && recData[2] == 0x00
            && CommonUtil.ValidCheckCode(recData))
        {
            switch (recData[3])
            {
                case 0x00:
                    msg = "控制主板正在重启";
                    return false;
                case 0x01:
                    msg = "联机成功";
                    return true;
                case 0x02:
                    msg = "控制主板正在维护";
                    return false;
                case 0x03:
                    msg = "控制主板收到的数据格式不正确";
                    return false;
                default:
                    msg = "未知状态";
                    return false;
            }
        }
        else if (IsRunning(recData, out msg) || !IsConnected(recData, out msg))
        {
            return false;
        }
        else
        {
            throw new Exception("货机返回的数据格式不正确");
        }
    }
    View Code

        三、如何使用

        1、Controller层代码(还不完善,仅测试,真实情况是根据硬件信息,确定调用哪个Dll使用哪个串口):

    #region 创建售货机接口
    /// <summary>
    /// 创建售货机接口
    /// </summary>
    private IMachine CreateMachine()
    {
        //return MachineFactory.Create(Request.PhysicalApplicationPath + @"in", "Machine", "COM1");
        return MachineFactory.Create(@"D:售药机代码ReceptionMachineinDebug", "Machine", "COM1");
    }
    #endregion
    
    #region 联机
    /// <summary>
    /// 联机
    /// </summary>
    /// <param name="msg">错误信息</param>
    /// <returns>联机是否成功</returns>
    private bool Connect(out string msg)
    {
        try
        {
            IMachine machine = CreateMachine();
            DateTime dt1 = DateTime.Now;
            while (!machine.Connect(out msg))  //联机
            {
                if (DateTime.Now.Subtract(dt1).TotalMilliseconds > 20000)
                {
                    msg = "联机超时";
                    return false;
                }
                Thread.Sleep(50);
            }
    
            return true;
        }
        catch (Exception ex)
        {
            msg = ex.Message;
            return false;
        }
    }
    #endregion
    
    #region 单次联机
    /// <summary>
    /// 单次联机
    /// </summary>
    public ActionResult Conn()
    {
        string msg = null;
        Dictionary<string, object> dic = null;
    
        try
        {
            IMachine machine = CreateMachine();
    
            if (machine.Connect(out msg)) //联机成功
            {
                dic = new Dictionary<string, object>();
                dic["ok"] = true;
                dic["msg"] = "成功";
                return Content(JsonConvert.SerializeObject(dic));
            }
            else
            {
                dic = new Dictionary<string, object>();
                dic["ok"] = false;
                dic["msg"] = "联机失败:" + msg;
                return Content(JsonConvert.SerializeObject(dic));
            }
        }
        catch (Exception ex)
        {
            dic = new Dictionary<string, object>();
            dic["ok"] = false;
            dic["msg"] = "错误:" + ex.Message;
            return Content(JsonConvert.SerializeObject(dic));
        }
    }
    #endregion
    
    #region 联机并使能硬纸币器
    /// <summary>
    /// 联机并使能硬纸币器
    /// </summary>
    public ActionResult ConnectEnable()
    {
        string msg = null;
        Dictionary<string, object> dic = null;
    
        try
        {
            IMachine machine = CreateMachine();
    
            if (Connect(out msg) && machine.CoinEnable(out msg) && machine.PaperMoneyEnable(out msg)) //联机并使能硬纸币器成功
            {
                dic = new Dictionary<string, object>();
                dic["ok"] = true;
                dic["msg"] = "成功";
                return Content(JsonConvert.SerializeObject(dic));
            }
            else
            {
                dic = new Dictionary<string, object>();
                dic["ok"] = false;
                dic["msg"] = "硬币器使能失败:" + msg;
                return Content(JsonConvert.SerializeObject(dic));
            }
        }
        catch (Exception ex)
        {
            dic = new Dictionary<string, object>();
            dic["ok"] = false;
            dic["msg"] = "错误:" + ex.Message;
            return Content(JsonConvert.SerializeObject(dic));
        }
    }
    #endregion
    View Code

        2、前台代码:

    @{
        ViewBag.Title = "货机接口测试";
        Layout = null;
    }
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>@ViewBag.Title</title>
        <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
        <script type="text/javascript" src="~/Scripts/LongPolling.js"></script>
    </head>
    <body>
        <div style="padding: 20px;">
            <input type="button" value="联机" onclick="connect()" />
            <div style="font-size: 20px; line-height: 30px;">
                <div style="padding: 20px;">
                    <span id="msg">&nbsp;</span>
                </div>
            </div>
        </div>
    </body>
    </html>
    <script type="text/javascript">
        //联机
        function connect() {
            commonAjax({
                url: "@Url.Content("/MachineInterface/Conn")",
                callback: function (data) {
                    if (data.ok) {
                        var html = "联机成功";
                        $("#msg").html(html);
                    }
                    else {
                        alert(data.msg);
                    }
                }
            });
        }
    </script>
    View Code
  • 相关阅读:
    继承中的虚函数、纯虚函数、普通函数
    struct与class的区别
    boost::filesystem总结
    ASM: Active Shape Models--Their Training and Application
    基础知识:仿射变换、相似变换、等距变换等常见变换
    PDM:Training Models of Shape from Sets of Examples
    常见优化器
    深度学习基础(五)ResNet_Deep Residual Learning for Image Recognition
    深度学习基础(四) Dropout_Improving neural networks by preventing co-adaptation of feature detectors
    ios 各种变量和作用范围
  • 原文地址:https://www.cnblogs.com/s0611163/p/4229842.html
Copyright © 2011-2022 走看看