MachineJP类:
第1部分:串口初始化,串口数据读写
using System; System.Collections.Generic; System.IO.Ports; System.Linq; System.Text; System.Threading; System.Threading.Tasks; System.Windows.Forms; MachineJPDll.Models; MachineJPDll.Utils; namespace MachineJPDll { /// <summary> /// 售货机接口(骏鹏接口) </summary> public partial class MachineJP { #region 变量 <summary> 串口资源 </summary> private SerialPort m_SerialPort = null; 待发送给串口的命令列表 private List<Cmd> m_CommandList = new List<Cmd>(); 等待ACK_RPT或NAK_RPT的PC端向VMC端发送的消息列表 private List<MT> m_WaitResultMTList = new List<MT> 从串口接收的数据集合(数据已通过验证) private ReceiveDataCollection m_ReceiveDataCollection = new ReceiveDataCollection(); #endregion #region 构造函数与析构函数 售货机接口(骏鹏接口) public MachineJP() { } ~MachineJP() { if (m_SerialPort != ) { m_SerialPort.Close(); m_SerialPort.dispose(); m_SerialPort = ; } } #region 读取串口数据 读取串口数据 </summary> <returns>从串口读取的数据</returns> private byte[] ReadPort() { //读取串口数据 DateTime dt = DateTime.Now; while (m_SerialPort.BytesToRead < 2) { Thread.Sleep(1); if (DateTime.Now.Subtract(dt).TotalMilliseconds > 1500) 超时 { return new byte[0]; } } List<byte> recList = new List<byte>(); byte[] recData = [m_SerialPort.BytesToRead]; m_SerialPort.Read(recData,,recData.Length); recList.AddRange(recData); int length = recData[1] + 2; 报文数据总长度 while (recList.Count < length) { if (m_SerialPort.BytesToRead > ) { recData = [m_SerialPort.BytesToRead]; m_SerialPort.Read(recData,recData.Length); recList.AddRange(recData); } Thread.Sleep(); } return recList.ToArray(); } #region 向串口发送数据 向串口发送数据 <param name="cmd">待发送的命令</param> <param name="SN">序列号</param> void WritePort(Cmd cmd, SN) { 发送数据 List<byte> sendData = cmd.Data; sendData[1] = ()sendData.Count; sendData[2] = SN; byte[] checkCode = CommonUtil.CalCheckCode(sendData,sendData.Count); sendData.AddRange(checkCode); if (cmd.Mt != ) { m_WaitResultMTList.Add(cmd.Mt); } m_SerialPort.Write(sendData.ToArray(),sendData.Count); LogHelper.Log(LogMsgType.Info,true#region 发送ACK消息 发送ACK消息 void SendACK( SN) { List<byte> sendData = byte>() { 0xE5,0x00,1)">0x40,1)">0x80 }; WritePort( Cmd(sendData),SN); } #region Init 初始化 初始化 <param name="com">串口号(例:COM1)void Init(string com) { if (m_SerialPort == ) { m_SerialPort = new SerialPort(com,1)">9600,Parity.None,1)">81024; m_SerialPort.WriteBufferSize = ; m_SerialPort.DataReceived += SerialDataReceivedEventHandler(serialPort_DataReceived); } if (!m_SerialPort.IsOpen) { m_SerialPort.open(); } GET_SETUP(); CONTROL_IND(0x13,1)">byte[] { 0x00 }); 初始化完成标志 GET_STATUS(); SetDecimalPlaces(2); 设置小数点位数 } #region Close 关闭连接 关闭连接 void Close() { m_SerialPort.Close(); } #region 接收串口数据 接收串口数据 <param name="type">消息类型<param name="subtype">消息子类型byte[] Receive(byte type,1)"> subtype) { m_ReceiveDataCollection.Get(type,subtype); } byte[] WaitReceive( subtype) { DateTime time =while () { byte[] receiveData =if (receiveData != null) receiveData; if (DateTime.Now.Subtract(time).TotalMinutes > 3) ; Thread.Sleep(50); } } type) { DateTime time = m_ReceiveDataCollection.Get(type); ); } } #region 判断消息是否发送成功 判断消息是否发送成功 bool SendSuccess(3) { false; } byte[] ack =byte[] nak =if (ack != ; if (nak != #endregion } }
第2部分:接收串口数据,并响应货机,向货机发送数据
/* * VMC->PC数据的接收,货机事件的分发 */ MachineJPDll { #region serialPort_DataReceived 数据接收事件的方法 void serialPort_DataReceived(object obj,SerialDataReceivedEventArgs args) { ReadPort(); if (CommonUtil.ValidReceiveData(receiveData)) 只处理验证正确的数据,不正确的数据抛弃不处理 { LogHelper.Log(LogMsgType.Info,receiveData); byte SN = CommonUtil.GetSN(receiveData); MT mt = MT(receiveData); #region 轮询(POLL) if (mt.Type == 0x03if (m_CommandList.Count > ) { WritePort(m_CommandList[],SN); m_CommandList.RemoveAt(); } else { 发送ACK消息 SendACK(SN); } } #endregion #region 发送ACK消息 if (CommonUtil.NeedACK(receiveData)) { SendACK(SN); } #region VMC系统参数 0x05) { m_ReceiveDataCollection.Add(mt.Type,mt.Subtype,receiveData); } #region ACK_RPT或NAK_RPT 0x01 ACK_RPT || mt.Type == 0x02) NAK_RPT if (m_WaitResultMTList.Count > ) { m_ReceiveDataCollection.Add(m_WaitResultMTList[0].Type,m_WaitResultMTList[].Subtype,receiveData); m_WaitResultMTList.RemoveAt(); } } #region INFO_RPT 数据报告 0x11#region 纸币器信息 if (mt.Subtype == 16) { m_ReceiveDataCollection.Add(mt.Type,receiveData); } #endregion #region 硬币器信息 17#region 用户投币余额 } #region vendOUT_RPT 出货报告 0x08#region STATUS_RPT VMC整机状态报告 0x0D#region SALEPRICE_IND 设置当前商品售价 0x8E#region PAYIN_RPT VMC发送现金投币报告给PC 0x06#region PAYOUT_RPT 出币报告 0x07#region COST_RPT VMC扣款报告 0x10#region ACTION_RPT 售货机行为报告 0x0B#region HUODAO_RPT VMC货道报告 0x0E } else 接收到的数据没有验证通过 { LogHelper.LogException(LogMsgType.Error,1)">false,1)">"数据异常" } }
第3部分:货机状态、投币、出货等接口
MachineJPDll.Enums; * PC->VMC数据的发送(并非直接发送,只是添加到发送列表) #region GET_SETUP PC通知VMC发送VMC_SETUP VmcSetup GET_SETUP() { List<0x90 }; m_CommandList.Add( Cmd(sendData)); byte[] receiveData = WaitReceive(); VmcSetup(receiveData); } ; } #region CONTROL_IND PC控制售货机完成对应的动作 PC控制VMC bool CONTROL_IND(byte subtype,1)">[] value) { List<0x41,1)">0x85 }; sendData.Add(subtype); if (value != null && value.Length > ) { sendData.AddRange(value); } m_CommandList.Add(new Cmd(sendData,1)"> MT(sendData))); return SendSuccess(#region 设置小数点位数 设置小数点位数 用于PC 通知VMC,双方的金额数据比例系数关系,PC 每次启动时,都会给 VMC 下发一次type=18 的消息,VMC 需要自己永久保存该数据,直到被PC 再 次更新。 取值范围:0、1、2 分别表示以 元、 角 、分 为单位 bool SetDecimalPlaces(int data) { return CONTROL_IND(18,1)">byte[] { ()data }); } #region GET_STATUS PC通知VMC发送STATUS_RPT PC通知VMC发送STATUS_RPT StatusRpt GET_STATUS() { List<0x86 StatusRpt(receiveData); } #region GET_INFO PC通知VMC发送INFO_RPT PC通知VMC发送INFO_RPT byte[] GET_INFO( subtype) { List<0x8C }; sendData.Add(subtype); m_CommandList.Add(return WaitReceive(); } #region vendOUT_IND 出货 PC出货指示 <param name="device">售货机的箱体号 例如柜1 为 0x01 以此类推<param name="method">method =1:VMC 通过商品ID 指示出货,如果商品ID 不存在,回复NAK_RPT method =2:VMC 通过货道ID 指示VMC 出货,如果货道ID 不存在,回复NAK_RPT<param name="sp_id_hd_id">sp_id:通过商品ID 指示VMC 出货 hd_id:通过货道ID 指示VMC 出货如果type=0,cost 代表本次出货扣款金额 如果TYPE 不为0,则COST 必须为0<param name="cost">cost 代表本次出货扣款金额public vendoutRpt vendOUT_IND(byte device,1)">byte method,1)">byte sp_id_hd_id,1)"> cost) { List<0x83 }; sendData.AddRange([] { device,method,sp_id_hd_id,type }); sendData.AddRange(CommonUtil.Int2ByteArray(cost,1)">)); m_CommandList.Add(if (SendSuccess(0x83,1)">0x00)) { ); vendoutRpt(receiveData); } } #region HUODAO_SET_IND 设置货道商品数量 PC通知VMC,当前货道对应商品的数量等信息 表示箱柜号<param name="huodao">zyxxxxxx “z”固定填0 “y”固定填0 “xxxxxx”,表示商品余量,如果商品余量大于63,则统一为63bool HUODAO_SET_IND(int> huodao) { List<0x8F }; sendData.Add(device); for (int i = 0; i < huodao.Count; i++if (huodao[i] > 63) { huodao[i] = ; } } sendData.AddRange(huodao.ConvertAll<byte>(a => ()a)); m_CommandList.Add(0x8F,1)">#region SALEPRICE_IND 设置当前商品售价 PC通知VMC,当前商品售价 表示设置单价的方式;Type = 0:为按商品ID 发送单价,可以变长发送,商品种类最大不超过80 个;Type = 1: 为按货道号发送,固定发送80 个货道的单价信息<param name="sp_price">商品售价bool SALEPRICE_IND( sp_price) { List< }; sendData.Add(device); sendData.Add(type); sendData.AddRange(sp_price.ConvertAll<0x8E,1)">#region PAYOUT_IND PC指示VMC出币 PC指示VMC出币 出币设备<param name="value">本次出币总金额出币类型 无需理解type 的含义,只需要在出币完成后的PAYOUT_RPT 中将该type 值回传给PC 即可public PayoutRpt PAYOUT_IND(PayoutType device,1)">int value,1)"> type) { List<0x89 }; sendData.Add(()device); sendData.AddRange(CommonUtil.Int2ByteArray(value,1)">)); sendData.Add(type); m_CommandList.Add(0x89,1)"> PayoutRpt(receiveData); } } #region COST_IND PC扣款指示 PC扣款指示 device=0,从用户投币总额中扣款;优先从用户非暂存金额中扣除(纸币尽量滞后压钞),参见《现金扣款顺序》扣款金额VMC 不用理解type 的含义,只需上报对应的COST_RPT 时回传即可public CostRpt COST_IND(0x8B }; sendData.Add(device); sendData.AddRange(CommonUtil.Int2ByteArray(value,1)">0x8B,1)"> CostRpt(receiveData); } } #region GET_HUODAO PC通知VMC上报HUODAO_RPT PC通知VMC上报HUODAO_RPT 箱柜号public HuoDaoRpt GET_HUODAO( device) { List<0x8A }; sendData.Add(device); m_CommandList.Add( HuoDaoRpt(receiveData); } #region SET_HUODAO PC发送配置货道信息 PC发送配置货道信息 <param name="Cabinet"><param name="hd_no">货道逻辑编号,十进制<param name="Hd_id">商品ID号<param name="Hd_count">货道剩余量<param name="Hd_price">货道单价<param name="Reserved">保留字段VMC忽略此字段,PC最好将此字段置为0bool SET_HUODAO(byte Cabinet,1)">int hd_no,1)">int Hd_id,1)">int Hd_count,1)">int Hd_price,1)">int Reserved = ) { List<0x93 }; sendData.Add(Cabinet); sendData.Add(()hd_no); sendData.Add(()Hd_id); sendData.Add(()Hd_count); sendData.AddRange(CommonUtil.Int2ByteArray(Hd_price,1)">)); sendData.AddRange(CommonUtil.Int2ByteArray(Reserved,1)">0x93,1)">#region 开启纸硬币器 现金收银模组(纸币器、硬币器)开关 <param name="open">true:开,false:关bool CtrlCoinPaper(bool open) { (open) { 2,1)"> }); } { }); } } #region 找零 找零 与手工拨弄物理找零开关相同 MakeChange() { 6,1)"> }); } #region 获取硬币器信息 获取硬币器信息 InfoRpt_17 GetCoinInfo() { new InfoRpt_17(GET_INFO()); } #region 获取纸币器信息 获取纸币器信息 InfoRpt_16 GetPaperInfo() { new InfoRpt_16(GET_INFO(#region 获取现金投币报告 获取现金投币报告 PayinRpt GetPayinRpt() { byte[] receiveData = Receive(0x06,1)"> PayinRpt(receiveData); } #region 获取用户投币余额 获取用户投币余额 InfoRpt_3 GetRemaiderAmount() { 0x11,1)">0x003 InfoRpt_3(receiveData); } } }
ReceiveDataCollection类和ReceiveData类:
System.Text; MachineJPDll.Models { 从串口接收到的数据(数据已通过验证) ReceiveData { 从串口接收到的数据(数据已通过验证) byte[] Data { get; set; } 添加到集合ReceiveDataCollection的时间 public DateTime AddTime { 消息类型 byte Type { 消息子类型 byte Subtype { ; } <param name="data">从串口接收到的数据(数据已通过验证)<param name="addTime">添加到集合ReceiveDataCollection的时间public ReceiveData([] data,DateTime addTime) { this.Type = type; this.Subtype = subtype; this.Data = data; this.AddTime = addTime; } } }
从串口接收到的数据集合(数据已通过验证) ReceiveDataCollection { 从串口接收到的数据集合(数据已通过验证) private List<ReceiveData> m_ReceiveDataList = new List<ReceiveData> 数据过期时间 int m_Timeout = ; static object _lock = (); 添加到集合 void Add([] data) { lock (_lock) { ReceiveData receiveData = ReceiveData(type,subtype,data,DateTime.Now); m_ReceiveDataList.Add(receiveData); int i = m_ReceiveDataList.Count - 1; i >= 0; i--if (DateTime.Now.Subtract(m_ReceiveDataList[i].AddTime).TotalMinutes > m_Timeout) { m_ReceiveDataList.RemoveAt(i); } } } } 从集合中获取串口接收到的数据(数据已通过验证) byte[] Get(0; i < m_ReceiveDataList.Count; i++if (m_ReceiveDataList[i].Type == type && m_ReceiveDataList[i].Subtype == subtype) { receiveData = m_ReceiveDataList[i]; m_ReceiveDataList.RemoveAt(i); receiveData.Data; } } ; } } type) { if (m_ReceiveDataList[i].Type == type) { receiveData =; } } } }
Models:
StatusRpt类:
MachineJPDll; VMC状态报告 StatusRpt { 从串口读取的通过验证的数据 [] m_data; VMC状态报告 从串口读取的通过验证的数据public StatusRpt([] data) { m_data = data; } override ToString() { StringBuilder sb = StringBuilder(); sb.AppendFormat(出货检测设备状态:{0}\r\n纸币器状态:{0}\r\n硬币器状态:{0}\r\nVMC状态:{0}\r\n展示位状态:{0} {1} {2} {3}\r\n",pos_st[0].ToString(),1)">1].ToString(),1)">2].ToString(),1)">].ToString()); sb.AppendFormat(机器中可用的找零量总金额(包括硬币和纸币):{0}\r\n货仓1货仓2货仓3货仓4温度:{0} {1} {2} {3}\r\n货仓状态设置值:{0} {1} {2} {3}\r\n].ToString()); if (this.自动退币 == 255) { sb.AppendFormat(自动退币时间:永不自动退币\r\n); } { sb.AppendFormat(自动退币时间:{0}\r\n自动退币.ToString()); } sb.AppendFormat(找零余量(1#--6#):{0} {1} {2} {3} {4} {5}\r\nthis.找零余量1,1)">this.找零余量2,1)">this.找零余量3,1)">this.找零余量4,1)">this.找零余量5,1)">this.找零余量6); sb.ToString(); } 出货检测设备状态 CheckSt check_st { getbyte val = m_data[5]; return (CheckSt)CommonUtil.GetFromByte(val,1)">0,1)"> 纸币器状态 DeviceSt bv_st { return (DeviceSt)CommonUtil.GetFromByte(val,1)"> 硬币器状态 DeviceSt cc_st { 4,1)"> VMC状态 VmcSt vmc_st { return (VmcSt)CommonUtil.GetFromByte(val,1)"> 展示位状态 public List<DeviceSt> pos_st { { List<DeviceSt> deviceStList = new List<DeviceSt>(); 60; i < 4; i++) { DeviceSt deviceSt = (DeviceSt)CommonUtil.GetFromByte(val,i * ); deviceStList.Add(deviceSt); } deviceStList; } } 机器中,可用的找零量总金额(包括硬币和纸币) change { return CommonUtil.ByteArray2Int(m_data,1)">7,1)"> 货仓1 温度,8 位有符号数,该温度通过货仓内传感器获取,单位:℃ TemSub tem1 { new TemSub(m_data[9]); } } 货仓2 温度,8 位有符号数,该温度通过货仓内传感器获取,单位:℃ TemSub tem2 { 10 货仓3 温度,8 位有符号数,该温度通过货仓内传感器获取,单位:℃ TemSub tem3 { 11 货仓4 温度,8 位有符号数,该温度通过货仓内传感器获取,单位:℃ TemSub tem4 { 12 货仓状态设置值,共支持4 个货仓 public List<TemSt> tem_st { { List<TemSt> temStList = new List<TemSt>(); ) { TemSt temSt = (TemSt)CommonUtil.GetFromByte(m_data[13],1)">); temStList.Add(temSt); } temStList; } } 自动退币时间。 0:表示商品出货后,立即自动退币 255:表示永不自动退币 1-254:表示商品出货后,自动退币时间(单位:秒) 自动退币 { return m_data[14]; } } 找零余量“找零量1#”…“找零量6#”,分别对应硬币器信息INFO_RPT.type=17 的“找零 1#”…“找零6#”中每种钱币的找零数量; * 找零量最大为255,超过255 时上报为255; * 找零量单位为“个”,代表可找零硬币的个数。 找零余量1 { 15 找零余量2 { 找零余量3 { 找零余量4 { 18 找零余量5 { 19 找零余量6 { 20]; } } } }
GitHub地址:https://github.com/0611163/Machine.git
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。