我正在努力重建/改进以前开发的C#/ .NET项目。 串行通信器无法将字节128-255转换为专用字符串。我尝试根据在该项目的工作版本中看到的字符应用Unicode编码。应用编码后,"消息无法发送异常"被扔了。任何关于解决/探究这个问题的指针都非常感激。 感谢。
public virtual string WriteData(string s, int expectedResponseLength)
{
lock (messageLock)
{
resetEvent.Reset();
}
message = string.Format("WriteData: {0}\r\nMessageType={1}\r\n", s, MessageType.Outgoing);
expectedLength = expectedResponseLength;
if (!comPort.IsOpen)
comPort.Open();
byte[] bs = new byte[s.Length];
for (int i = 0; i < s.Length; ++i)
bs[i] = (byte)s[i];
for (int i = 0; i < 3; i++)
{
response = string.Empty;
comPort.Write(bs, 0, bs.Length);
if (resetEvent.WaitOne(5000))
{
return response;
}
}
throw new Exception("Message failed to send.");
}
public virtual bool OpenPort()
{
try
{
if (comPort.IsOpen)
comPort.Close();
comPort.Encoding = new UnicodeEncoding();
comPort.DtrEnable = false;
comPort.RtsEnable = false;
comPort.ReceivedBytesThreshold = 1;
comPort.ReadTimeout = -1;
comPort.ReadBufferSize = 1024;
comPort.BaudRate = int.Parse(BaudRate);
comPort.DataBits = int.Parse(DataBits);
comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), StopBits);
comPort.PortName = PortName;
comPort.Open();
message = string.Concat("OpenPort: ", MessageType.Normal, ", Port opened at ", DateTime.Now);
return true;
}
catch (Exception ex)
{
message = "OpenPort: " + MessageType.Error + ex.Message;
return false;
}
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string RcvByte = comPort.ReadExisting();
// Encode the string.
byte[] encodedBytes = Encoding.Unicode.GetBytes(RcvByte);
// Decode bytes back to string.
RcvByte = Encoding.Unicode.GetString(encodedBytes);
response += RcvByte;
lock (messageLock)
{
if (response.Length >= expectedLength)
{
resetEvent.Set();
}
}
}
答案 0 :(得分:0)
首先,您需要知道其他设备(您通过串口通信的设备)所需的代码页。理论上它可能是UTF-8(Unicode),但对于较旧的设备,它很可能是一些单字节代码页。例如,对于西欧Windows代码页,它可能是Windows-1252。因此,根据其他设备,您可能需要将comPort.Encoding = new UnicodeEncoding();
中的OpenPort
更改为以下内容:
comPort.Encoding = new Encoding.GetEncoding(1252);
然后,由于您似乎只与字符串交换(即通信协议中没有二进制数据),请使用SerialPort.Write(String)
重载而不是SerialPort.Write(Byte[], Int32, Int32)
。如果您正确设置了Encoding
属性,前者将负责代码页转换。这意味着您需要删除所有bs
数组内容并只需调用
comPort.Write(s);
在WriteData
方法中。
顺便说一下,如果输入字符串中出现非ASCII字符,则bs
填充代码不正确。您最好使用Encoding.GetBytes
来获取它:byte[] bs = Encoding.Unicode.GetBytes(s);
在SerialPort_DataReceived
方法中ReadExisting()
将响应读作string
,可以使用(再次,如果您已正确设置Encoding
属性)。因此,无需使用GetBytes
/ GetString
重新转换,只需设置response
:
response += comPort.ReadExisting();
您还需要修复代码中的同步。您必须使用lock ...
访问response
变量,因为它是从两个线程读取和写入的。我还试图确保不同时调用WriteData
(未在下面的示例中实现)。
因此,我建议按如下方式重写串口读写方法:
public virtual string WriteData(string s, int expectedResponseLength)
{
resetEvent.Reset(); // no need to lock this
expectedLength = expectedResponseLength;
if (!comPort.IsOpen)
comPort.Open();
for (int i = 0; i < 3; i++)
{
lock (messageLock)
{
response = string.Empty;
}
comPort.Write(s);
if (resetEvent.WaitOne(5000))
lock (messageLock)
{
return response;
}
}
throw new Exception("Message failed to send.");
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string received = comPort.ReadExisting();
lock (messageLock)
{
response += received;
if (response.Length >= expectedLength)
{
resetEvent.Set();
}
}
}
这仍然可能无法保证WriteData
返回一些响应而不是抛出"Message failed to send."
异常。在协议违规或编码问题的情况下,设备仍然可能不在给定超时内提供请求长度的响应。您需要跟踪ReadExisting()
返回的数据以跟踪问题。