查询流,串行输入 Swi prolog

Querying Streams, serial input Swi prolog

我以简单的方式设置了一个 arduino,它读取电位器并将输出写入串行连接。

void setup() {
 // initialize serial communication
 Serial.begin(9600);
}

void loop() {
 // read the value of A0, divide by 4 and
 // send it as a byte over the serial connection
 Serial.write(analogRead(A0) / 4);
 delay(20);
}

我想要一个在后台监视此流的序言程序。我希望它记录某些事件,但也希望我可以随时查询流。

我真的不知道从哪里开始设置它。我如何实际读取一个流,然后如何有两个 process/threads (不知道如何称呼它们)。一个进程监视流而第二个进程运行顶层以便我可以查询动态数据库?

我想是这样的:

:-dynamic currentvalue/1.

startthread1 :-
 loop.

loop :- read_serial(X),
    check(X),
    retractall(currentvalue(_)),
    assertz(currentvalue(X)),
    loop.

check(X):-
  X>100,
  get_time(Time),
  write_file(Time,X).
check(_).

*我省略了write_file

的def

我想我会查询:

?- thread_create(startthread1, Id, [alias(serial_mon)]).

然后我仍然可以使用顶层来查询“currentvalue/1”?

这行得通吗?我如何定义 read_serial/1。 ?另外,我将如何停止我命名为 serial_mon 的线程?

更新

我已将循环代码更改为以下内容:

void loop() {
 // read the value of A0, divide by 4 and
 // send it as a byte over the serial connection
 Serial.print(analogRead(A0) / 4);
 Serial.print('.');
 delay(100);
}

如果我查看串行监视器,我现在得到如下输出:

205.205.205.205. etc

我认为我应该可以使用 read/2 阅读?

如果我的序言代码很简单:

go(File):-
 open(File,read,Stream,[]),
 read(Stream,X),
 writeln(X),
 close(Stream).

我查询:

?-go('/dev/ttyACM0').

我没有输出。我做错了什么?

你的基本思路是可以的。但是,您需要考虑几个非常重要的点。

从流中读取

这是比较容易的部分:关于"how do I actually read a stream",有几个选项,它们是您在单线程应用程序中已经知道的选项。最干净的方法是使用 Ulrich Neumerkel 的 library(pio),并简单地编写一个透明地从流中读取的 DCG。另一个不错的选择是简单地使用 read/1read/2 从流中读取 Prolog term。当然,后一种选择需要传感器实际发出可以由这些谓词自动处理的 Prolog 术语,而前一种选择更灵活。

您还可以使用 网络套接字 并设置一个简单的基于 Web 的通信界面。例如,查看 JSON libraries 以获得简单且可移植的格式。总而言之,不乏处理此问题的选项。

就我个人而言,我建议使用 Prolog 语法 进行此类交换,因为当数据用 Prolog 编写时,您可以更方便地处理数据。例如,如果您将日志文件编写为一系列 Prolog 事实,那么您可以轻松地查阅它们并 运行 查询它们以生成 报告。任何其他格式只会使这更复杂。

同步

第二个问题要微妙得多,它涉及线程之间的竞争条件。例如,在您显示的代码中,有一个关键时间点根本没有数据

loop :-
    read_serial(X),
    check(X),
    retractall(currentvalue(_)),
    currentvalue/1 is not available at this point in time!
    assertz(currentvalue(X)),
    loop.

如果获取线程在这个未定义的关键时刻查询currentvalue/1,它可能意外失败。

对于 SWI-Prolog,请仔细阅读有关 thread synchronization 的章节以避免此类问题。

比如我们可以实现为:

loop :-
    read_serial(X),
    check(X),
    with_mutex(synch, (retractall(currentvalue(_)),
                       assertz(currentvalue(X)))),
    loop.

在查询线程中,您可以使用 with_mutex(synch, currentvalue(X)) 获取 当前值。这确保它不会在旧值已被删除但新值尚未断言的关键时间点发生。

请注意,顶层和任何其他线程都可以查询这些谓词。要生成更多线程,只需再次使用 thread_create/3 来实现不同的目标。