用户 GUI 活动如何破坏我的串行端口输入
我正在使用来自“大牌”之一的第 3 方串行端口组件(是的,我已要求他们提供支持,但存在时区差异,我需要快速解决此问题)。该组件已经存在多年,我没有理由相信问题出在它身上(硬件软件也是如此)。
硬件规范规定,如果我将某个字符串写入串行端口,以回车符终止,然后读取,它将回复一个特定格式的 8 个字符串,再次以回车符终止。
正确执行此操作并根据读取的内容更新 GUI 的代码可以运行数小时。
但是,当 GUI 上有任何用户活动时,我会从串行端口读取垃圾数据。
我第一次注意到它是在单击导致模式表单打开然后关闭表单的按钮时。
然而,当我简单地拖动 TStringGrid 的滚动条时,我也会看到它。
这是代码。有什么建议吗?
更新:该组件是线程化的,并且供应商同意此处的海报 - 串行端口是异步设备。我更改了代码以编写数据请求并处理组件的 OnCharReceived() 事件处理程序中收到的每个字符。塔恩斯克感谢所有的帮助。
function TForm1.ReadChannelValueFromSerialPort(
device_number : String; channel_number : String) : Real;
const SLEEP_TIME = 50; // ms
NUM_READ_ATTEMPTS = 10;
var serialPortInput : String;
read_attempt_counter : Integer;
messageString : String;
begin
WriteToSerialPort('#' + device_number + channel_number + #13);
serialPortInput := '';
read_attempt_counter := 0;
while Length(serialPortInput) = 0 do
begin
try
Application.ProcessMessages();
serialPortInput := serialPortInput + SerialPort.ReadText();
except
on E: Exception do
begin
messageString := 'Can''t read from serial port' ;
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
end;
Inc(read_attempt_counter);
if (read_attempt_counter = NUM_READ_ATTEMPTS)
and (Length(serialPortInput) = 0) then
begin
messageString := 'Can''t read from serial port after trying ' +
IntToStr(NUM_READ_ATTEMPTS) + ' times in ' +
FloatToStr((SLEEP_TIME * NUM_READ_ATTEMPTS) / 1000) + ' seconds';
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
if (Length(serialPortInput) = 0) then
Sleep(SLEEP_TIME);
end;
if Copy(serialPortInput, 1, 1) <> '>' then
begin
DebugBreak();
MessageDlg('Invalid value read from serial port "' +
serialPortInput + '"', mtError, [mbOK], 0);
Halt(0);
end;
// drop the3 leading >
serialPortInput := Copy(serialPortInput, 2, Length(serialPortInput) - 1);
serialPortInput := TrimRight(serialPortInput); // just in case
Result := StrToFloat(serialPortInput);
end; // ReadChannelValueFromSerialPort();
I am using a 3rd party serial port component from one of "the big names" (yes, I have asked them for support, but there is a time zone difference and I need to fix this really quickly). The component has been around for years and I have no reason to believe that the problem lies with it (ditto the harwdware).
The h/w spec says that if I write a certain string to the seial port, terminated by carriage return, and then read, it will reply with a spcifically formatted 8 character string, again terminated by carriage return.
The code can run for hours doing this correctly and updating the GUI based on what it reads.
However, when there is any user activity on the GUI, I read junk from the serial port.
I first noticed it when clicking a button which caused a modal form to open and then closing the form.
However, I also see it when simply dragging the scollbar of a TStringGrid.
Here is the code. Any advice?
Update: the component is threaded and the suppliers agree with the posters here - a serial port is an asynchrnonous device. I have changed the code to write a request for data and handle each receied character in the component's OnCharReceived() event handler. Tahnsk for all of the help.
function TForm1.ReadChannelValueFromSerialPort(
device_number : String; channel_number : String) : Real;
const SLEEP_TIME = 50; // ms
NUM_READ_ATTEMPTS = 10;
var serialPortInput : String;
read_attempt_counter : Integer;
messageString : String;
begin
WriteToSerialPort('#' + device_number + channel_number + #13);
serialPortInput := '';
read_attempt_counter := 0;
while Length(serialPortInput) = 0 do
begin
try
Application.ProcessMessages();
serialPortInput := serialPortInput + SerialPort.ReadText();
except
on E: Exception do
begin
messageString := 'Can''t read from serial port' ;
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
end;
Inc(read_attempt_counter);
if (read_attempt_counter = NUM_READ_ATTEMPTS)
and (Length(serialPortInput) = 0) then
begin
messageString := 'Can''t read from serial port after trying ' +
IntToStr(NUM_READ_ATTEMPTS) + ' times in ' +
FloatToStr((SLEEP_TIME * NUM_READ_ATTEMPTS) / 1000) + ' seconds';
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
if (Length(serialPortInput) = 0) then
Sleep(SLEEP_TIME);
end;
if Copy(serialPortInput, 1, 1) <> '>' then
begin
DebugBreak();
MessageDlg('Invalid value read from serial port "' +
serialPortInput + '"', mtError, [mbOK], 0);
Halt(0);
end;
// drop the3 leading >
serialPortInput := Copy(serialPortInput, 2, Length(serialPortInput) - 1);
serialPortInput := TrimRight(serialPortInput); // just in case
Result := StrToFloat(serialPortInput);
end; // ReadChannelValueFromSerialPort();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果不知道您实际使用的是哪个组件,或者它的内部功能,则很难说。但是用户活动会通过主消息队列,并且您的代码正在手动抽取新消息的队列,因此这可能与您的问题有关。您允许在写入和读取之间处理用户活动。您是否尝试过将串行端口逻辑移至单独的工作线程中?
Without knowing which component you are actually using, or what it does internally, it is hard to say. But user activity goes through the main message queue, and your code is manually pumping the queue for new messages, so that is likely related to your problem. You are allowing user activity to be processed in between your writes and reads. Have you tried moving the serial port logic into a separate worker thread?
串行通信通常涉及发送/接收缓冲区以及流量控制。由于您的实施,正常流程显然被中断。
我建议您将访问端口的代码迁移到后台线程中,并在适当的时候对客户端 GUI 进行某种同步回调。 (一种选择是简单的 Synchronize() 调用)
Serial comm usually involves send/receive buffers along with flow control. The normal flow is apparently being interrupted due to your implementation.
I suggest you migrate the code accessing the port into a background thread and have some sort of synchronized callback to the client GUI when appropriate. (one option being the simple Synchronize() call)
它记得几年前我也遇到过同样的问题:点击鼠标中断了与 PLC 的串行通信(严重!)。
不知道我们到底是如何解决这个问题的,但我认为这与 USB 鼠标的组合、串行电缆的接地和/或电子/磁屏蔽不当、串行电缆太长、扭曲的 USB/串行电缆或类似的东西(是的,研发工作场所有点混乱......)
所以也许你可以尝试更改和/或检查一些硬件?
It remembers me having the same problem some years ago: clicking the mouse interrupted the serial communication with a PLC (serious!).
Don't know exactly how we fixed that, but I think it had something to do with the combination of an USB mouse, improper grounding and/or electronic/magnetic shielding of serial cable, too long serial cable, twisted USB/serial cable or something like that (yes it was a bit messy at the R&D workfloor...)
So maybe you can try changing and/or checking some hardware?