【发布时间】:2018-01-06 12:24:30
【问题描述】:
您好,我在 C# 中创建了一个程序来接收 ASCII 字符串,该字符串是 XML 标记。
我从我那里接收数据的计算机无法控制并且不接受响应,它大约每 10 分钟在 COM 端口上发送一次数据
我收集并存储这些数据的控制台应用程序并不总是有效,我会说大约 50% 的时间数据丢失,例如数据包或字节丢失,并且 XML 字符串不会读入 XmlDocument
我已经尝试了大约一个星期来使它更稳定,但这是我第一次使用 C#,并且希望有一些帮助来改进它。
代码
class SerialPortProgram : IDisposable
{
// Create the serial port with basic settings
private SerialPort port = new SerialPort("COM1",
115200, Parity.None, 8, StopBits.One);
string sBuffer = null;
string filePath1 = @"C:\Data\data1.xml";
string filePath2 = @"C:\Data\data2.xml";
[STAThread]
static void Main(string[] args)
{
// Instatiate this class
new SerialPortProgram();
}
private SerialPortProgram()
{
Console.WriteLine("Started Data Monitoring:");
//Attach a method to be called when there
//is data waiting in the port's buffer
port.ReadBufferSize = 20971520;
port.ReceivedBytesThreshold = 1;
port.DataReceived += new SerialDataReceivedEventHandler(Port_DataReceived);
//Begin communications
port.Open();
//Enter an application loop to keep this thread alive
Application.Run();
}
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//Show all the incoming data in the port's buffer
SerialPort sp = (SerialPort)sender;
sBuffer += sp.ReadExisting();
if (sBuffer.Length > 26000) // check the file size
{
if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
{
Console.WriteLine("Found: Processing...");
//Thread.Sleep(1000);
ProcessXML();
sBuffer = null;
Console.WriteLine("Done!");
DateTime now = DateTime.Now;
Console.WriteLine(now);
Console.WriteLine("Monitoring...");
}
else
{
Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
}
}
else
{
Console.WriteLine("Receiving Data: " + sBuffer.Length);
}
}
private void ProcessXML()
{
XmlDocument xmlDoc = new XmlDocument();
try
{
xmlDoc.LoadXml("<wrapper>" + sBuffer + "</wrapper>");
int index = 0;
XmlNodeList xnl = xmlDoc.SelectNodes("wrapper/xml");
foreach (XmlNode node in xnl)
{
// Console.WriteLine(index.ToString());
if (index == 0)// xml file 1
{
using (XmlReader r = new XmlNodeReader(node))
{
DataSet ds = new DataSet();
ds.ReadXml(r);
ds.WriteXml(filePath1);
Console.WriteLine("NEW Data1");
ds.Dispose();
var db = new Database();
db.SaveMetersToDatabase(ds);
}
}
else if (index == 1)// xml file 2
{
using (XmlReader r1 = new XmlNodeReader(node))
{
DataSet dst = new DataSet();
dst.ReadXml(r1);
dst.WriteXml(filePath2);
Console.WriteLine("NEW Data2");
dst.Dispose();
}
}
index++;
}
}
catch
{
Console.WriteLine("Error: in data");
try
{
string now = DateTime.Now.ToString("yyyyMMddHHmmss");
System.IO.File.WriteAllText(@"C:\Data\log" + now + ".xml", "<wrapper>" + sBuffer + "</wrapper>");
}
catch
{
Console.WriteLine("Failed to write to log");
}
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing && port != null)
{
port.Dispose();
port = null;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
更新代码:
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//Show all the incoming data in the port's buffer
SerialPort sp = (SerialPort)sender;
sBuffer += sp.ReadExisting();
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
if (sBuffer.Length > 25000) // check the file size
{
if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
{
Console.WriteLine("Found: Processing...");
Task.Run(() =>
{
ProcessXML();
sBuffer = null;
Console.WriteLine("Done!");
DateTime now = DateTime.Now;
Console.WriteLine(now);
Console.WriteLine("Monitoring...");
});
}
else
{
Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
}
}
else
{
Console.WriteLine("Receiving Data: " + sBuffer.Length);
}
}).Start();
}
更新
仍然有这个问题,可能是发送计算机有时不发送所有数据或存在数据包丢失我已经尝试了所有方法我已经使用 Serial Port BaseStream BeginRead 在下面尝试了这个新代码
private SerialPortProgram()
{
Console.WriteLine("Started Data Monitoring:");
//Attach a method to be called when there
try
{
Port.BaudRate = 115200;
Port.DataBits = 8;
Port.Parity = Parity.None;
Port.StopBits = StopBits.One;
Port.Handshake = Handshake.None;
Port.DtrEnable = true;
Port.NewLine = Environment.NewLine;
Port.ReceivedBytesThreshold = 2048;
Port.Open();
byte[] buffer = new byte[35000];
Action StartRead = null;
StartRead = () => {
Port.BaseStream.BeginRead(buffer, 0, buffer.Length, async (IAsyncResult ar) =>
{
try
{
int actualLength = Port.BaseStream.EndRead(ar);
byte[] received = new byte[actualLength];
Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
await Task.Run(() =>
{
sBuffer += Encoding.ASCII.GetString(received);
CheckBuffer();
});
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
StartRead();
}, null);
};
StartRead();
}
catch (Exception ex)
{
Console.WriteLine("Error accessing port." + ex);
Port.Dispose();
Application.Exit();
}
//Enter an application loop to keep this thread alive
Application.Run();
}
private void CheckBuffer()
{
if (sBuffer != null && sBuffer.Length > 26000) // check the file size
{
if (sBuffer.Substring(sBuffer.Length - 6) == "</xml>") // check for end of file
{
new Thread(async () =>
{
Console.WriteLine("Found: Processing...");
await Task.Run(() => ProcessXML());
sBuffer = null;
Console.WriteLine("Done!");
DateTime now = DateTime.Now;
Console.WriteLine(now);
Console.WriteLine("Monitoring...");
}).Start();
}
else
{
Console.WriteLine("Still Receiving Data: " + sBuffer.Length);
}
}
else if (sBuffer != null && sBuffer.Length > 0)
{
Console.WriteLine("Receiving Data: " + sBuffer.Length);
}
}
【问题讨论】:
-
睡觉是为了什么?那只是要求丢失数据
-
是的,我认为它不会影响它,因为它正在保存文件并重新打开它但不再需要已删除
-
通过通信,您希望最大限度地减少回调中的时间,否则您将丢失数据。我建议您使用 循环缓冲区 或类似技术,并将您对所述缓冲区的处理移动到另一个 thread
-
请看我的回答stackoverflow.com/questions/33310396/…,并在使用答案中提到的控件时判断问题是否仍然存在。
-
哦,您应该使用更小的缓冲区,接收所有数据,将其放入队列或某处,然后分析数据(当数据在您身边时)。考虑一个线程仅用于接收数据,这样您就不会遇到任何滞后(记住 - 单一责任)和另一个用于解析它的线程。我一直把它分成两类——接收器和处理器。如果您愿意,我可以向您展示示例实现
标签: c#