• 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
  • 相关阅读:
    查看mysql版本的四种方法及常用命令
    newInstance和new的区别(good)
    Citrix 服务器虚拟化之六 Xenserver虚拟机创建与快照
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 前缀表达式
    Java实现 蓝桥杯 算法训练 前缀表达式
  • 原文地址:https://www.cnblogs.com/s0611163/p/4229842.html
Copyright © 2020-2023  润新知