一个C#写的接收电子称数据并解析出重量数据的类。

之前单位购买了两架电子称,同时单独购买了配套的软件。该软件是用Delphi 7写就的,界面老旧就不说了,关键是功能太弱了,基本只能简单地记录一下称重的数据,打印的标签效果亦只能是简单的文字打印,基本上无法使用。想着之前曾经有用POS指令控制串口打印机的经验,应该不是很难,就自己写了一个,以解决零散分包标签打印和装箱标签打印的需要。

数据格式说明

共接触到三种电子称数据格式,如数据格式不在此列,则使用例如串口调试助手之类的工具接收电子称数据自行分析并作相应调整即可。

辅助类

电子称状态变更通告事件类,注意此辅助助类原处于不同的命名空间,请自行修改或合并到相同的命名空间。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace CommonLib
{/// <summary>/// 电子称状态变更通告事件类定义/// </summary>public class ScaleStateChangeEventArgs:EventArgs{public int ScaleID { get; private set; }public float? Weight { get; private set; }public bool Disconnected { get; private set; }     public bool PortOpenError { get; private set; }/// <summary>/// 电子称状态变更通告事件/// </summary>/// <param name="scaleID">电子称标号,对应数据库中的ID</param>/// <param name="weight">实时屏显重量</param>/// <param name="disconnected">连接已丢失</param>/// <param name="portOpenError">端口(串口)打开错误</param>public ScaleStateChangeEventArgs(int scaleID, float? weight, bool disconnected, bool portOpenError){this.ScaleID = scaleID;this.Weight = weight;this.Disconnected = disconnected;this.PortOpenError = portOpenError;}}
}

电子称参数类,作为主类构造函数的参数,用于封装电子称的主要参数:串口及数据格式的参数。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace DataReceiver
{/// <summary>/// 电子称参数类/// </summary>public class ScaleParam{public int ScaleID { get; set; }public string Name { get; set; }public string COMPortName { get; set; }public int BaudRate { get; set; }public int DataBits { get; set; }public string StopBits { get; set; }public string Parity { get; set; }public int CheckingInterval { get; set; }public int DataLength { get; set; }public int BeginByte { get; set; }public int EndByte { get; set; }public int ScaleDataBeginLoc { get; set; }public int ScaleDataLength { get; set; }/// <summary>/// 有些电子称发送的数据格式是没有小数点分隔符例如:耀华 Xk3190/// </summary>public bool HasDecimalSeparatorFlag { get; set; }/// <summary>/// 没有小数点的数中整数部分长度/// </summary>public short IntegerPortionLength { get; set; }}
}

数据接收处理类

比较简单,请参考注释即可。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using CommonLib;
using DataReceiver;namespace DataReceiver
{/// <summary>/// 电子称数据接收类 Receiver/// </summary>public class Receiver{/// <summary>/// 电子称状态变更通告事件/// </summary>public event EventHandler<ScaleStateChangeEventArgs> ScaleStateChange;/// <summary>/// 电子称参数/// </summary>private ScaleParam scale;/// <summary>/// 连接类型:串口/// </summary>private SerialPort comport;/// <summary>/// (从电子称)最后一次收到的正确的字节数据/// </summary>private byte[] lastReceivedBytes = new byte[1];/// <summary>/// (从电子称)最后一次收到的正确的重量/// </summary>private float? lastReceivedWeight;/// <summary>/// 电子称发送的有效数据长度/// </summary>int DataLength = -1;/// <summary>/// 从串口读取的数据长度,为保证接到的数据包含有效数据,/// 读取的数据长度为:DataLength * 2/// 此处或可再优化,实际使用正常,故未作深究/// </summary>int bytesToRead = -1;/// <summary>/// 电子称连接丢失标志/// </summary>private bool lostConnectionFlag = false;/// <summary>/// 接收到错误的数据标志/// 通常,电子称开机时、波特率不匹配时数据格式会有问题/// </summary>private bool errorDataReceivedFlag = false;/// <summary>/// 定时读取串口数据的定时器/// </summary>private Timer DataChecker;/// <summary>/// /// </summary>//private GlobalData globalData = GlobalData.Instance;public Receiver() { }/// <summary>/// 自定义构造器/// </summary>/// <param name="targetScale">电子称参数</param>public Receiver(ScaleParam targetScale) :base(){scale = targetScale;DataLength = scale.DataLength;bytesToRead = DataLength * 2;Initializer();DataChecker = new Timer(DataProcessor, null, scale.CheckingInterval, scale.CheckingInterval);}public void Reset(){DataChecker.Change(Timeout.Infinite, Timeout.Infinite); // Stop comport.Close();Initializer();DataChecker.Change(scale.CheckingInterval, scale.CheckingInterval); // Start}public void ClosePort(){comport.Close();}/// <summary>/// 初始化招收器/// 设置标志、初始化接收数组、打开串口等/// </summary>public void Initializer(){lastReceivedBytes = new byte[DataLength];lostConnectionFlag = false;errorDataReceivedFlag = false;bool error = false;comport = new SerialPort();if (comport.IsOpen) comport.Close();else{comport.PortName = scale.COMPortName;comport.BaudRate = scale.BaudRate; comport.DataBits = 8; comport.StopBits = StopBits.One; comport.Parity = Parity.None; comport.DtrEnable = true;comport.RtsEnable = true;try{comport.Open();}catch (UnauthorizedAccessException) { error = true; }catch (IOException) { error = true; }catch (ArgumentException) { error = true; }if (error){ScaleStateChange?.Invoke(this, new ScaleStateChangeEventArgs(scale.ScaleID, null, true, true));}else{}}}/// <summary>/// 电子称数据处理例程/// </summary>/// <param name="state"></param>private void DataProcessor(object state){// 如果串口未打开或缓冲区接收到的数据长度少于 bytesToRead 的定义值if (!comport.IsOpen || comport.BytesToRead < bytesToRead){if (!lostConnectionFlag){lostConnectionFlag = true;ScaleStateChange?.Invoke(this, new ScaleStateChangeEventArgs(scale.ScaleID, null, true, false));}// 直接返回以直至收到足够的数据return;}// 已接收到足够的数据,无须继续接收(因为电子称的数据是连继重复发送的)DataChecker.Change(Timeout.Infinite, Timeout.Infinite); // Stop          byte[] buffer = new byte[bytesToRead];// 从缓冲区读长度为 bytesToRead 的数据comport.Read(buffer, 0, bytesToRead);         // 定位有效数据首字节出现的位置int begingOffset = -1;for (int i= 0; i < buffer.Count(); i++){if (buffer[i] == scale.BeginByte) // {begingOffset = i;break;}}try{byte[] data = new byte[DataLength];// 复制完整数据至字节缓存数组Buffer.BlockCopy(buffer, begingOffset, data, 0, DataLength);// 调用数据处理例程ProcessingScaleData(data);}catch (Exception ex){//Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>> Exception: " + ex.Message);}// 清空串口接收缓冲区以保证数据实时性comport.DiscardInBuffer(); //Start over// 重新启动数据接收定时器DataChecker.Change(scale.CheckingInterval, scale.CheckingInterval); // Start         }/// <summary>/// 电子称数据处理例程/// </summary>/// <param name="receivedBytes">电子称发送的字节数据</param>private void ProcessingScaleData(byte[] receivedBytes){// 只有和最后一次收到的数据不同时才需要处理if (!ByteArrayCompare(lastReceivedBytes, receivedBytes)) {ProcessingData(receivedBytes);}// 收到相同数据时检查电子称的串口连接是否断开// 如果曾经断开过连接,屏幕会显示 ERROR// 此时需重置标志并更新显示else if (lostConnectionFlag){lostConnectionFlag = false;ProcessingData(receivedBytes);}}/// <summary>/// 更新 lastReceivedBytes 变更数据/// 并调用数据解析例程/// </summary>/// <param name="receivedBytes">电子称发送的字节数据</param>private void ProcessingData(byte[] receivedBytes){lastReceivedBytes = receivedBytes;// 字节数据转换为字符串数据并传给解析例程ParseData(Encoding.ASCII.GetString(receivedBytes));}string tmpWeightData = string.Empty;string weightData = string.Empty;float weightResult;/// <summary>/// 电子称数据解析例程/// 支持3种电子称数据格式,请参考图示/// 可根据需要扩充/// </summary>/// <param name="data">电子称发送过来的字符串数据</param>private void ParseData(string data){try{weightData = data.Substring(scale.ScaleDataBeginLoc, scale.ScaleDataLength);if (!scale.HasDecimalSeparatorFlag){tmpWeightData = weightData;weightData = tmpWeightData.Substring(0, scale.IntegerPortionLength) + "." +tmpWeightData.Substring(scale.IntegerPortionLength, scale.ScaleDataLength-scale.IntegerPortionLength);}if (float.TryParse(weightData, out weightResult)){errorDataReceivedFlag = false;lastReceivedWeight = weightResult;ScaleStateChange?.Invoke(this, new ScaleStateChangeEventArgs(scale.ScaleID, weightResult, false, false));}else if (!errorDataReceivedFlag){errorDataReceivedFlag = true;ScaleStateChange?.Invoke(this, new ScaleStateChangeEventArgs(scale.ScaleID, lastReceivedWeight, false, false));lastReceivedBytes = new byte[1];}}catch (Exception ex){ScaleStateChange?.Invoke(this, new ScaleStateChangeEventArgs(scale.ScaleID, null, false, false));lastReceivedBytes = new byte[1];}}[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]static extern int memcmp(byte[] b1, byte[] b2, long count);/// <summary>/// 字节数组比较例程/// </summary>/// <param name="b1">字节数组1</param>/// <param name="b2">字节数组2</param>/// <returns>相同则返回True;否则False</returns>static bool ByteArrayCompare(byte[] b1, byte[] b2){// Validate buffers are the same length.// This also ensures that the count does not exceed the length of either buffer.  return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;}/// <summary>/// TXT日志记录例程/// </summary>/// <param name="data">待写入日志文件中的字符串数据</param>private void WriteFile(string data){FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "Logger.txt", FileMode.Append);StreamWriter sw = new StreamWriter(fs);sw.Write(data);sw.Flush();sw.Close();fs.Close();}} // class ends
}

实际使用效果,供参考

程序界面使用了 MahApps.Metro.Controls,具体请参考 https://mahapps.com/docs/controls/metrowindow
数字屏显组件为 Seven-segment LED control for .NET,来自于 Dmitry Brant(http://dmitrybrant.com)

下载链接

https://download.csdn.net/download/Sunny_Clickman/67631373

说明:
仅保留屏显部分;
比较简单,只有部分简单的注释;
仅演示耀华数据的接收(屏显中的KG字体为:DS-Digital,没有包含在其中)

一个简单实用的电子称数据接收/解析类(C#)(附简单使用实例)相关推荐

  1. anychart java实例_结合AnyChart做报表:一个生成AnyChart图形XML数据的工具类

    今天头有点痛,所以不能详细地写了,先把代码贴上来,等身体状况稍微好一点,再继续完善. 1.(主角)一个使用XML模板生成Anychart XML数据的工具类 /** * */ package com. ...

  2. 使用GitLab或者Github简单实用地将数据导入Colab的方法

    Google Colab为我们提供了免费的GPU和TPU计算资源.对于暂时没有自己的本地GPU显卡资源深度学习科研人员而言真是个福利. 这篇博客主要要解决的问题是:如何简单方便实用地将外部训练数据导入 ...

  3. struts的json数据接收解析

    json格式 /*** 类描述:接收json数据修改延期时间** @ClassName BankManageMent* @Description TODO* @Author 张 * @Date * @ ...

  4. 一个java处理JSON格式数据的通用类(三)

    /** *//**       * 从json数组中解析出java Integer型对象数组       * @param jsonString       * @return      */     ...

  5. 一个java处理JSON格式数据的通用类(五)

    本来上面转载的文章已经很全面了,不过还是再补充一下吧 /***************************************************************** *  * @ ...

  6. 一个java处理JSON格式数据的通用类(四)

    public class DateJsonValueProcessor implements JsonValueProcessor ...{          public static final ...

  7. 简单实用读取字符串信息的c++类

    2019独角兽企业重金招聘Python工程师标准>>> c++代码 class CStringParsing{ public:CStringParsing(const char* s ...

  8. 解析一个xml文件并将数据存入数据库

    解析xml文件并将文件存入数据库 一.使用dom4j对xml文件进行解析 1.首先要有一个xml文件 2.需求 3.创建Books实例类 4.开始解析xml文件 5.测试代码: 6.结果: 二.进行数 ...

  9. 应用终端和服务器之间的交互 简单,基于UM220的北斗数据接收处理及简单应用

    索菲 摘 要:文章以北斗2号和GPS双系统接收模块UM220为硬件平台,利用嵌入式C#二次开发BD2/GPS-OEM板的方法,开发出了能同时运行于PC和Windows CE的跨平台程序,实现了PDA与 ...

最新文章

  1. php方行图片裁剪为圆形,如何将图片裁成圆形、矩形……各种形状?
  2. 吴恩达机器学习笔记31-梯度检验(Gradient Checking)
  3. 伪指令PAGE和TITLE说明
  4. 实训九(游戏退出按钮的实现)
  5. 求最小连续和(洛谷P1614题题解,Java语言描述)
  6. 95-34-020-Context-ChannelHandlerContext
  7. selenium处理动态加载数据
  8. java 串行化_Java中的串行化
  9. LR快捷键 之 图库模式
  10. 关于tc作用在入口(ingress)和出口(egress)效果的调研
  11. spark history server内存不足服务自动挂掉
  12. 读 《人性的弱点》 有感
  13. Flex 下 mm.cfg 文件配置详解
  14. WPF_图画(二)Drawing抽象类
  15. JS特效-星空太空效果-极简
  16. Python生成信息熵决策树
  17. 疯狂的程序员(绝影)
  18. Rextrofit中网络请求空字段传递或过滤
  19. window的休眠、睡眠、快速启动
  20. Stream入门到实践

热门文章

  1. mvc直接在html页面预览pdf,Asp.net MVC 实现在线预览word、excel、ppt、pdf文件
  2. java毕业设计芮城县十全十美火锅店点餐系统源码+lw文档+mybatis+系统+mysql数据库+调试
  3. linux bios设置界面,BIOS怎么设置 史上最详细的bios设置图解教程
  4. 灰色关联度分析法(GRA)_python
  5. 传奇创世孙大宁:2016年H5将井喷,新传播途径是突破口
  6. 多因子模型水平测试题试答(因子部分)
  7. [益智]:3个女儿的年龄
  8. O2OA框架使用笔记
  9. 美通企业日报 | 牛文文称明年创业要弯腰干脏活累活;高校百英里接力赛北大清华同济前三...
  10. Pro10丨枢轴点反转策略