【背景】前段時(shí)間接到一個(gè)需求待秃,需要做一個(gè)化工企業(yè)環(huán)保數(shù)據(jù)(COD嘹吨、VOCs)采集系統(tǒng)遗遵,這里主要是數(shù)據(jù)采集部分萍恕,數(shù)據(jù)來源是分析儀設(shè)備逸嘀。
【實(shí)現(xiàn)方案】使用dtu做數(shù)據(jù)透傳车要,我這里使用的是有人dtu,具體可以自行百度崭倘,然后用C#編寫程序接受采集數(shù)據(jù)翼岁,以下是簡略的拓?fù)鋱D:
【代碼實(shí)現(xiàn)】部分代碼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.發(fā)送指令
private void SerialPortSend(String text, SerialPort serialPort, int spIndex)
{
appLog.LogMessage("發(fā)送指令: " + text, this.dataNames[spIndex]);
if (!serialPort.IsOpen)
{
MessageBox.Show("串口未打開,無法發(fā)送數(shù)據(jù)!");
return;
}
try
{
Encoding encoding = Encoding.GetEncoding("GB18030");
byte[] bytes = strToToHexByte(text);
serialPort.Write(bytes, 0, bytes.Length);
}
catch
{
appLog.LogMessage("發(fā)送失敗!", this.dataNames[spIndex]);
}
}
3.接收數(shù)據(jù)
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];
}
//讀取返回?cái)?shù)據(jù)
while (serialPort.BytesToRead == 0)
{
Thread.Sleep(10);
}
Thread.Sleep(100); //100毫秒內(nèi)數(shù)據(jù)接收完畢类垫,可根據(jù)實(shí)際情況調(diào)整
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.數(shù)據(jù)處理(解析,以怡文-hj212協(xié)議為例)
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協(xié)議
/// </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("獲取數(shù)據(jù)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("獲取數(shù)據(jù)有誤ieeeStr:" + ieeeStr, "ERROR");
return null;
}
//CRC校驗(yàn)
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校驗(yàn)不通過琅坡,數(shù)據(jù)有誤ieeeStr:" + ieeeStr, "ERROR");
return null;
}
float[] datas = new float[len];
try
{
string dataR = ieeeStr.Substring(6, 8);
UInt32 u = Convert.ToUInt32(dataR, 16);//字符串轉(zhuǎn)16進(jìn)制32位無符號(hào)整數(shù)
datas[0] = BitConverter.ToSingle(BitConverter.GetBytes(u), 0);//IEEE754 字節(jié)轉(zhuǎn)換float
}
catch (Exception e)
{
appLog.LogMessage("轉(zhuǎn)換異常:" + e.ToString(), "ERROR");
}
return datas;
}
5.byteToHexStr
/// <summary>
/// 字節(jié)數(shù)組轉(zhuǎn)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校驗(yàn) CRC.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace OPCCollect
{
/// <summary>
/// CRC校驗(yàn)
/// </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>
/// 字符串轉(zhuǎn)16進(jìn)制字符數(shù)組
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static byte[] StringToHexByte(string str)
{
return StringToHexByte(str, false);
}
/// <summary>
/// 字符串轉(zhuǎn)16進(jìn)制字符數(shù)組
/// </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(" ", "");
//若字符個(gè)數(shù)為奇數(shù)悉患,補(bǔ)一個(gè)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
}
}
【參考資料】相關(guān)地址
在線進(jìn)制轉(zhuǎn)換以及ieee754轉(zhuǎn)換:地址
在線CRC校驗(yàn):地址