文章出處

    最近寫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 + @"\bin\", "Machine", "COM1");
    return MachineFactory.Create(@"D:\售藥機代碼\Reception\Machine\bin\Debug\", "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

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()