查询流,串行输入 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/1
或 read/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
来实现不同的目标。
我以简单的方式设置了一个 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/1
或 read/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
来实现不同的目标。