最近写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[];
while (fs.Read(byteArray, , byteArray.Length) > )
{
ms.Write(byteArray, , 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、变量与构造函数

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

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, , sendData.Length); //读取返回数据
DateTime dt = DateTime.Now;
while (serialPort.BytesToRead == )
{
Thread.Sleep(); if (DateTime.Now.Subtract(dt).TotalMilliseconds > ) //如果5秒后仍然无数据返回,则视为超时
{
throw new Exception("主版无响应");
}
}
Thread.Sleep();
byte[] recData = new byte[serialPort.BytesToRead];
serialPort.Read(recData, , recData.Length); //关闭连接
if (serialPort.IsOpen) serialPort.Close(); return recData;
}
}

优化版:

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

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 >=
&& recData[] == 0x01
&& recData[] == 0x02
&& recData[] == 0x00
&& CommonUtil.ValidCheckCode(recData))
{
switch (recData[])
{
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 创建售货机接口
/// <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 > )
{
msg = "联机超时";
return false;
}
Thread.Sleep();
} 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

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>

C#串口通信—向串口发送数据,同步接收返回数据的更多相关文章

  1. 背水一战 Windows 10 (101) - 应用间通信: 通过协议打开指定的 app 并传递数据以及获取返回数据, 将本 app 沙盒内的文件共享给其他 app 使用

    [源码下载] 背水一战 Windows 10 (101) - 应用间通信: 通过协议打开指定的 app 并传递数据以及获取返回数据, 将本 app 沙盒内的文件共享给其他 app 使用 作者:weba ...

  2. 基于NIO的同步非阻塞编程完整案例,客户端发送请求,服务端获取数据并返回给客户端数据,客户端获取返回数据

    这块还是挺复杂的,挺难理解,但是多练几遍,多看看研究研究其实也就那样,就是一个Selector轮询的过程,这里想要双向通信,客户端和服务端都需要一个Selector,并一直轮询, 直接贴代码: Ser ...

  3. java URL实现调用其他系统发送报文并获取返回数据

    模拟本系统通过Url方式发送报文到目标服务器,并获取返回数据:(实现类) import java.io.BufferedOutputStream; import java.io.BufferedRea ...

  4. WebRequest发送请求并接收返回值

    public string getXmlStr(string hphmcode)         {            string Url = "http://localhost:80 ...

  5. Ajax的GET,POST方法传输数据和接收返回数据

    //首先创建一个Ajax对象 function ajaxFunction(){ var xmlHttp; try{ // Firefox, Opera 8.0+, Safari xmlHttp=new ...

  6. .NET发送请求(get/post/http/https),携带json数据,接收json数据

    C#发送https请求有一点要注意: ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateVa ...

  7. jQuery表单 Ajax向PHP服务端发送文件请求并返回数据

    ImageAjaxUpLoad.htm <!DOCTYPE html> <head> <meta charset='utf-8'> <title>< ...

  8. php用fsockopen实现post提交数据并获得返回数据

    /** * 函数介绍: 用于post方式提交数据 * 输入参数: 完整url, 数据 * 返回值 : 接口返回值 */ function post_it($url, $data = '', $time ...

  9. webservice一片:其中在外线呼叫数据,查看返回数据

    经Android数据被访问,返回的数据(json格公式,object数据类型:strJson) 业务需求:经webservice调用外部暴露数据并返回json数据序列化.阅读到数据库表:[SQ_Eve ...

随机推荐

  1. 统计第一个空字符前面的字符长度(java实现)

    举例来说:char buf[] = {'a','b','c','d','e','f','\0','x','y','z'}当输入N=10或20,期待输出是6:当输入N=3或5,期待输出是3或5. pac ...

  2. LuaAlchemy API 介绍

    The AS3 Sugar provides a Lua-like way to access AS3 class and instance creation, property getter/set ...

  3. 我心中的核心组件(可插拔的AOP)~第五回 消息组件

    回到目录 之所以把发消息拿出来,完全是因为微软的orchard项目,在这个项目里,将公用的与领域无关的功能模块进行抽象,形成了一个个的组件,这些组件通过引用和注入的方式进行工作,感觉对于应用程序的扩展 ...

  4. MVVM架构~Knockoutjs系列之对象与对象组合

    返回目录 在面向对象的程序设计里,对象是核心,一切皆为对象,对象与对象之间的关系可以表现为继承和组合,而在Knockoutjs或者JS里,也存在着对象的概念,今天主要说一下JS里的对象及对象的组合. ...

  5. 第1讲 Redis部署与基本操作

    目录 一.简介 二.安装 1.默认安装位置 2.指定安装位置 3.安装的可执行文件的作用 三.启动与关闭 四.配置文件 五.Redis的数据类型 1. 共计5种类型 2. String(子串类型) 3 ...

  6. salesforce 零基础学习(二十一)workflow Q&A

    有一篇内容专门写了workflow,后来用到的时候心生疑问,不知道小伙伴有没有和我想法一样的,workflow具体内容原来已经说过,不在过多叙述,只说一下运行条件. 那就是:当满足运行条件时,执行相关 ...

  7. spring之BeanFactory

    spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后 ...

  8. Leetcode 4 Median of Two Sorted Arrays 二分查找(二分答案+二分下标)

    貌似是去年阿里巴巴c++的笔试题,没有什么创新直接照搬的... 题意就是找出两个排序数组的中间数,其实就是找出两个排序数组的第k个数. 二分答案,先二分出一个数,再用二分算出这个数在两个排序数组排序第 ...

  9. Java EE开发平台随手记2——Mybatis扩展1

    今天来记录一下对Mybatis的扩展,版本是3.3.0,是和Spring集成使用,mybatis-spring集成包的版本是1.2.3,如果使用maven,如下配置: <properties&g ...

  10. react-native —— 在Windows下搭建React Native Android开发环境

    在Windows下搭建React Native Android开发环境 前段时间在开发者头条收藏了 @天地之灵_邓鋆 分享的<在Windows下搭建React Native Android开发环 ...