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
{
    /// 
    /// 售货机工厂类
    /// 
    public class MachineFactory
    {
        /// 
        /// 货机接口缓存
        /// 
        private static Dictionary dicMachine = new Dictionary();
        /// 
        /// 锁变量
        /// 
        public static object _lock = new object();

        /// 
        /// 创建售货机类
        /// 
        /// DLL物理路径
        /// DLL名称(不含扩展名),命名空间必须为DLL名称加“Dll”后缀,类名必须和DLL名称相同
        ///  串口名称,如:COM1
        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];
        }
    }
}

 

二、Machine类

    1、变量与构造函数

/// 
/// 串口资源
/// 
private SerialPort serialPort = null;

public Machine(string com)
{
    serialPort = new SerialPort(com, 9600, Parity.None, 8, StopBits.One);
    serialPort.ReadBufferSize = 1024;
    serialPort.WriteBufferSize = 1024;
}

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

/// 
/// 向串口发送数据,读取返回数据
/// 
/// 发送的数据
/// 返回的数据
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;
    }
}

优化版:

/// 
/// 向串口发送数据,读取返回数据
/// 
/// 发送的数据
/// 返回的数据
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 recList = new List();
        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();
    }
}

3、发送联机指令:

/// 
/// 联机
/// 
/// 传出错误信息
/// 联机是否成功
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("货机返回的数据格式不正确");
    }
}

三、如何使用

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

#region 创建售货机接口
/// 
/// 创建售货机接口
/// 
private IMachine CreateMachine()
{
    //return MachineFactory.Create(Request.PhysicalApplicationPath + @"\bin\", "Machine", "COM1");
    return MachineFactory.Create(@"D:\售药机代码\Reception\Machine\bin\Debug\", "Machine", "COM1");
}
#endregion

#region 联机
/// 
/// 联机
/// 
/// 错误信息
/// 联机是否成功
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 单次联机
/// 
/// 单次联机
/// 
public ActionResult Conn()
{
    string msg = null;
    Dictionary dic = null;

    try
    {
        IMachine machine = CreateMachine();

        if (machine.Connect(out msg)) //联机成功
        {
            dic = new Dictionary();
            dic["ok"] = true;
            dic["msg"] = "成功";
            return Content(JsonConvert.SerializeObject(dic));
        }
        else
        {
            dic = new Dictionary();
            dic["ok"] = false;
            dic["msg"] = "联机失败:" + msg;
            return Content(JsonConvert.SerializeObject(dic));
        }
    }
    catch (Exception ex)
    {
        dic = new Dictionary();
        dic["ok"] = false;
        dic["msg"] = "错误:" + ex.Message;
        return Content(JsonConvert.SerializeObject(dic));
    }
}
#endregion

#region 联机并使能硬纸币器
/// 
/// 联机并使能硬纸币器
/// 
public ActionResult ConnectEnable()
{
    string msg = null;
    Dictionary dic = null;

    try
    {
        IMachine machine = CreateMachine();

        if (Connect(out msg) && machine.CoinEnable(out msg) && machine.PaperMoneyEnable(out msg)) //联机并使能硬纸币器成功
        {
            dic = new Dictionary();
            dic["ok"] = true;
            dic["msg"] = "成功";
            return Content(JsonConvert.SerializeObject(dic));
        }
        else
        {
            dic = new Dictionary();
            dic["ok"] = false;
            dic["msg"] = "硬币器使能失败:" + msg;
            return Content(JsonConvert.SerializeObject(dic));
        }
    }
    catch (Exception ex)
    {
        dic = new Dictionary();
        dic["ok"] = false;
        dic["msg"] = "错误:" + ex.Message;
        return Content(JsonConvert.SerializeObject(dic));
    }
}
#endregion

2、前台代码:

@{
    ViewBag.Title = "货机接口测试";
    Layout = null;
}




    @ViewBag.Title
    
    


    
 

你可能感兴趣的:(C#遗忘系列,c#,开发语言)