2

背景

为了从逻辑控制器捕获数据,我使用screen作为终端仿真器并通过KeySpan USA-19HS USB Serial Adapter连接我的 MacBook 。我创建了以下 bash 脚本,以便我可以键入talk2controller <filename>其中文件名是数据文件的名称。

#!/bin/bash
if [ -z "$1" ]; then
    echo Please provide the filename to save the logfile
    exit
fi
LOGFILE=$1
echo "logfile $1" > screenrc        # Set the logfile filename
echo "logfile flush 1" >> screenrc  # Wait 1 sec before flushing buffer to filesystem
screen -L -c screenrc /dev/tty.KeySerial1 19200

我更改了日志文件的文件名,并将日志文件缓冲区刷新到文件系统之前的等待时间从默认的 10 秒更改为 1 秒。我将这些命令保存到screenrc. 然后我用以下方法调用屏幕:

  1. -L— 启用日志记录
  2. -c screenrc— 覆盖默认配置文件
  3. /dev/tty.KeySerial1 19200— 使用 19200 的波特率与串行端口通信

我记录的每个测试大约需要 3-6 分钟,并且包含速度、加速度和位置信息。我会根据加速度知道测试是有效的。目前,我正在等到测试结束后再运行 Python matplotlib脚本来绘制速度、加速度和位置,以查看测试是否有效,然后再进行下一个测试。

为了节省时间,我宁愿在测试进行到一半时绘制数据,而数据仍在捕获中。

问题

在我看来,在捕获更多数据的同时绘制数据有两种选择:

  • 选项 1:使用屏幕记录数据并让 Python matplotlib 脚本读取部分日志文件。
    • 问题 1:如果 Python 脚本读取日志文件,而 screen 仍在向其中写入数据,会有什么顾虑?
  • 选项 2:从使用 screen 切换到使用pySerial。但是,在测试期间绘制数据的优先级低于在测试期间简单地捕获数据。我无法承受代码绘图部分中的异常导致数据记录失败。这就是 screen 的优点——它只是转储数据而不尝试做任何其他事情。
    • 问题 2:如果我要切换到 pySerial,我是否可以运行两个线程来减少代码的绘图部分不影响数据捕获代码的机会?这能给我带来什么吗?

问题3:有没有更好的选择,我没有想到?

4

3 回答 3

3

选项 1 和 2 都可以,但是哦,天哪,以一切美好的名义,避免为此使用线程!您最终会遇到两全其美的情况:锁定问题,并且图形线程中的异常无论如何都会杀死整个程序(包括日志记录线程)。正如其他人提到的,为此使用两个单独的进程很好。 screen为此目的选择的工具有点奇怪,就像在 python 中手动编写代码一样。我只是将talk2controller 脚本重写为这个简单的脚本:

stty -F /dev/tty.KeySerial1 19200 raw
cat </dev/tty.KeySerial1 >logfile

>>logfile(如果您希望脚本的每次运行都附加到文件中,而不是从头开始重写,您也可以使用它。)

另一个问题是关于是否可以让程序从文件中读取,只要其他人正在写入它。这个问题的一个更具体的版本是:如果在您尝试阅读日志时一行日志写了一半怎么办?

答案是:你可以这样做,但你是对的,你不能保证在你阅读的时候一行不会写一半。(如果您编写自己的替换,cat或者您实际上可以通过始终使用而不是或screen写入文件来做出此保证。)os.read()sys.stdout.write()print

但是,无论如何都不需要这种保证。您只需要在阅读文件时小心,您就不会遇到问题。本质上,不完整的行只是不以\n换行符结尾的行。因此:

for line in open('logfile'):
    if not line.endswith('\n'): break
    ...handle valid line...

由于\n字符是日志每一行写入的最后一件事,因此您可以肯定地知道,如果您读取了一个\n字符,那么它之前的所有内容都被正确写入。

于 2010-09-17T00:26:32.857 回答
1

我认为选项 1是完全可行的,因为您可以轻松地让 Python 在只读管道中“跟踪”日志文件,这样在screen仍在写入时不会对其造成伤害。在跟踪文件时,您可以在日志文件中检测到新的日志事件时执行指定的操作。

如果你很好奇并想看一些工作代码,我的一个个人项目利用了这个功能。该项目称为thrasher-logdrop,胆量是logdrop.py。基本流程是:

  • 尾随文件do_tail()
  • 监视日志事件tail_lines()
  • 对事件执行操作handle_line()
于 2010-09-14T18:24:49.780 回答
1

我会说选项2是要走的路。当您收到输入的每个字节时,您可以完全控制您对输入的处理方式。你可以有一个非常简单的 Python 脚本,它在读取数据时将数据写入磁盘。您的绘图代码可以在由fork()第一个创建的完全独立的进程中运行。要从一个到另一个获取数据,您可以(a)让第一个进程也写入一个socketpair()或其他 IPC 机制;或 (b) 将输出文件对象配置为行缓冲 - 使其在写入每一整行后显式同步 - 并在第二个进程中监视它的新内容。

选项 1 的问题是您无法控制screen的缓冲行为。您可以监视其日志文件中的新内容,但您的日志代码需要准备好同时处理不完整的行和大块数据。根据确切的缓冲行为,您甚至可能在screen进程退出之前根本看不到任何数据!

于 2010-09-14T18:56:07.377 回答