【背景】前段时间接到一个需求,需要做一个化工企业环保数据(COD、VOCs)采集系统,这里主要是数据采集部分,数据来源是分析仪设备。
【实现方案】使用dtu做数据透传,我这里使用的是有人dtu,具体可以自行百度,然后用C#编写程序接受采集数据,以下是简略的拓扑图:
网络拓扑图
【代码实现】部分代码C#
1.打开串口:
private void openSerialPort(SerialPort serialPort, String portName)
{
try
{
int baudRate = 9600;
serialPort.Encoding = Encoding.GetEncoding("GB18030");
serialPort.PortName = portName;
serialPort.BaudRate = baudRate;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.DataBits = 8;
serialPort.DataReceived += SerialPort_DataReceived;
serialPort.Open();
MessageBox.Show("打开串口" + portName + "成功!");
appLog.LogMessage("open serial port " + portName + " successful");
}
catch
{
MessageBox.Show("打开串口" + portName + "失败!");
appLog.LogMessage("open serial port " + portName + " faild");
}
}
2.发送指令
private void SerialPortSend(String text, SerialPort serialPort, int spIndex)
{
appLog.LogMessage("发送指令: " + text, this.dataNames[spIndex]);
if (!serialPort.IsOpen)
{
MessageBox.Show("串口未打开,无法发送数据!");
return;
}
try
{
Encoding encoding = Encoding.GetEncoding("GB18030");
byte[] bytes = strToToHexByte(text);
serialPort.Write(bytes, 0, bytes.Length);
}
catch
{
appLog.LogMessage("发送失败!", this.dataNames[spIndex]);
}
}
3.接收数据
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
int spIndex = Array.IndexOf(serialPorts, serialPort);
if (!serialPort.IsOpen)
{
appLog.LogMessage("ERROR", "串口未打开!");
appLog.LogMessage("serial port " + serialPort.PortName + " is not open");
return;
}
Control.CheckForIllegalCrossThreadCalls = false;
lastValuesMutex.WaitOne();
try
{
int startValNum = 0;
for (int i = 0; i < spIndex; i++)
{
startValNum += portDataNums[i];
}
//读取返回数据
while (serialPort.BytesToRead == 0)
{
Thread.Sleep(10);
}
Thread.Sleep(100); //100毫秒内数据接收完毕,可根据实际情况调整
byte[] recData = new byte[serialPort.BytesToRead];
serialPort.Read(recData, 0, recData.Length);
float[] datas = protocolAnalyse.getDataByProtocol(recData, portDataNums[spIndex], protocols[spIndex], spIndex, this.anaNames[spIndex]);
if (null == datas || datas.Length == 0)
{
lastValuesMutex.ReleaseMutex();
return;
}
for (int k = 0; k < datas.Length; k++)
{
lastValues[startValNum + k] = datas[k];
}
}
catch (Exception ex)
{
appLog.LogMessage(ex.ToString(), "ERROR");
timerCollect.Enabled = true;
lastValuesMutex.ReleaseMutex();
return;
}
// ...
lastValuesMutex.ReleaseMutex();
}
4.数据处理(解析,以怡文-hj212协议为例)
public float[] getDataByProtocol(byte[] recData, int len, int type, int spIndex, string anaName)
{
int startValNum = 0;
for (int i = 0; i < spIndex; i++)
{
startValNum += portDataNum[i];
}
float[] datas = new float[len];
switch ((PROTOCOTYPE)type)
{
case PROTOCOTYPE.PROTOCOL_USR_YW_HJ212:
datas = getDataByYWHj212IEEE754(recData, len, startValNum);
break;
// case ...
default:
break;
}
return datas;
}
/// <summary>
/// 怡文 - hj212 - ieee754协议
/// </summary>
/// <param name="recData"></param>
/// <param name="len"></param>
/// <param name="startValNum"></param>
/// <returns></returns>
public float[] getDataByYWHj212IEEE754(byte[] recData, int len, int startValNum)
{
String ieeeStr = byteToHexStr(recData);
appLog.LogMessage("获取数据ieeeStr:" + ieeeStr, dataNames[startValNum]);
if (ieeeStr.Length == 20)//www.usr.cn(7777772E7573722E636E)
{
return null;
}
ieeeStr = ieeeStr.Replace("7777772E7573722E636E", "");
ieeeStr = ieeeStr.Replace(" ", "");
//01031042C60F043D8F07E50007001C000C0000071B
ieeeStr = ieeeStr.Replace("010303E80002447B", "");
if (ieeeStr.Length % 42 != 0)
{
appLog.LogMessage("获取数据有误ieeeStr:" + ieeeStr, "ERROR");
return null;
}
//CRC校验
string dataCrc = ieeeStr.Substring(ieeeStr.Length - 4);
string crcStr = CRC.ToModbusCRC16(ieeeStr.Substring(0, ieeeStr.Length - 4), true);
if (!dataCrc.Equals(crcStr))
{
appLog.LogMessage("CRC校验不通过,数据有误ieeeStr:" + ieeeStr, "ERROR");
return null;
}
float[] datas = new float[len];
try
{
string dataR = ieeeStr.Substring(6, 8);
UInt32 u = Convert.ToUInt32(dataR, 16);//字符串转16进制32位无符号整数
datas[0] = BitConverter.ToSingle(BitConverter.GetBytes(u), 0);//IEEE754 字节转换float
}
catch (Exception e)
{
appLog.LogMessage("转换异常:" + e.ToString(), "ERROR");
}
return datas;
}
5.byteToHexStr
/// <summary>
/// 字节数组转hex字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
returnStr += bytes[i].ToString("X2");
}
}
return returnStr;
}
6.CRC校验 CRC.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace OPCCollect
{
/// <summary>
/// CRC校验
/// </summary>
public class CRC
{
#region CRC16
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len > 0)
{
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ (data[i]));
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
byte lo = (byte)(crc & 0x00FF); //低位置
return new byte[] { hi, lo };
}
return new byte[] { 0, 0 };
}
#endregion
#region ToCRC16
public static string ToCRC16(string content)
{
return ToCRC16(content, Encoding.UTF8);
}
public static string ToCRC16(string content, bool isReverse)
{
return ToCRC16(content, Encoding.UTF8, isReverse);
}
public static string ToCRC16(string content, Encoding encoding)
{
return ByteToString(CRC16(encoding.GetBytes(content)), true);
}
public static string ToCRC16(string content, Encoding encoding, bool isReverse)
{
return ByteToString(CRC16(encoding.GetBytes(content)), isReverse);
}
public static string ToCRC16(byte[] data)
{
return ByteToString(CRC16(data), true);
}
public static string ToCRC16(byte[] data, bool isReverse)
{
return ByteToString(CRC16(data), isReverse);
}
#endregion
#region ToModbusCRC16
public static string ToModbusCRC16(string s)
{
return ToModbusCRC16(s, true);
}
public static string ToModbusCRC16(string s, bool isReverse)
{
return ByteToString(CRC16(StringToHexByte(s)), isReverse);
}
public static string ToModbusCRC16(byte[] data)
{
return ToModbusCRC16(data, true);
}
public static string ToModbusCRC16(byte[] data, bool isReverse)
{
return ByteToString(CRC16(data), isReverse);
}
#endregion
#region ByteToString
public static string ByteToString(byte[] arr, bool isReverse)
{
try
{
byte hi = arr[0], lo = arr[1];
return Convert.ToString(isReverse ? hi + lo * 0x100 : hi * 0x100 + lo, 16).ToUpper().PadLeft(4, '0');
}
catch (Exception ex) { throw (ex); }
}
public static string ByteToString(byte[] arr)
{
try
{
return ByteToString(arr, true);
}
catch (Exception ex) { throw (ex); }
}
#endregion
#region StringToHexString
public static string StringToHexString(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
s.Append(c.ToString("X4"));
}
return s.ToString();
}
#endregion
#region StringToHexByte
private static string ConvertChinese(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
if (c <= 0 || c >= 127)
{
s.Append(c.ToString("X4"));
}
else
{
s.Append((char)c);
}
}
return s.ToString();
}
private static string FilterChinese(string str)
{
StringBuilder s = new StringBuilder();
foreach (short c in str.ToCharArray())
{
if (c > 0 && c < 127)
{
s.Append((char)c);
}
}
return s.ToString();
}
/// <summary>
/// 字符串转16进制字符数组
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static byte[] StringToHexByte(string str)
{
return StringToHexByte(str, false);
}
/// <summary>
/// 字符串转16进制字符数组
/// </summary>
/// <param name="str"></param>
/// <param name="isFilterChinese">是否过滤掉中文字符</param>
/// <returns></returns>
public static byte[] StringToHexByte(string str, bool isFilterChinese)
{
string hex = isFilterChinese ? FilterChinese(str) : ConvertChinese(str);
//清除所有空格
hex = hex.Replace(" ", "");
//若字符个数为奇数,补一个0
hex += hex.Length % 2 != 0 ? "0" : "";
byte[] result = new byte[hex.Length / 2];
for (int i = 0, c = result.Length; i < c; i++)
{
result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return result;
}
#endregion
}
}
【参考资料】相关地址
在线进制转换以及ieee754转换:地址
在线CRC校验:地址
网友评论