bluesky-消息协议
概要
计划是描述数据采集过程的一系列原子操作。每个操作由一个 bluesky.Msg(“消息”)对象表示。计划可以作为简单的消息列表来实现:
In [3]: plan = [Msg('open_run'), Msg('close_run')]
In [4]: RE(plan)
Out[4]: ('6e21a67d-afe5-45f8-b1c6-586aa644e8a8',)
或者作为一次只生成一条消息的生成器:
In [6]: def plan():
...: yield Msg('open_run')
...: yield Msg('close_run')
...:
In [7]: RE(plan())
Out[7]: ('b71e6f0d-60fe-4cd9-8958-44c824d18fd1',)
上述示例是等效的。对于更复杂的用途,第二种方式(生成器)更为强大,因为它可以包含循环、条件判断、自适应逻辑——实际上任何 Python 代码都可以。
但关键在于,计划代码本身绝不能与硬件直接通信。(切勿在计划中使用 epics.caput(...)!)相反,每个操作都由一个描述应执行动作的 Msg 对象表示。
在不接触真实硬件的情况下,可以安全地检查计划以进行错误校验、模拟和可视化。例如,我们可以如下打印计划中的每条消息:
In [8]: plan = [Msg('open_run'), Msg('close_run')]
In [9]: for msg in plan:
...: print(msg)
...:
Msg('open_run', obj=None, args=(), kwargs={}, run=None)
Msg('close_run', obj=None, args=(), kwargs={}, run=None)
一个Msg包含5个作为属性可访问的成员:
- command
- obj
- args
- kwargs
- run
其中 command 必须是受控命令列表中的一项,obj 是要应用该命令的对象(即设备),如果适用的话,args 和 kwargs 是该命令的参数,而 run 是用户定义的运行键。运行键由运行引擎用于将每条消息与一个打开的运行相关联,管理每个打开运行的状态,并将运行数据路由到一组独立的回调函数(参见多运行计划的文档)。
要执行计划, 运行引擎一次一条消息地消费它:
def very_simple_run_engine(plan):
for msg in plan:
# Process the msg.
运行引擎维护着一个注册表,其用于根据 Msg.command 值来分发这些Msg对象。例如,若运行引擎接收到消息 Msg('set', motor, 5),它将:
- 识别出该消息的命令为 'set'。
- 在其命令注册表中查找 'set',发现它映射到 RunEngine._set。
- 将 Msg('set', motor, 5) 传递给其 _set 方法。
- 在 _set 内部,调用 motor.set(5)。(这里才发生与硬件的实际通信。)
- 更新一些将在之后有用地内部缓存。例如,它会跟踪电机可能正在移动的事实,以便在发生错误时安全地停止它。这说明了另一个重要原因:计划必须始终通过生成消息来与硬件交互,绝不能直接与硬件通信。在计划内部调用 epics.caput 会使 RunEngine 无法感知,从而绕过其在意外退出或错误时将设备置于安全状态的机制。
默认情况下,一组标准命令已注册。按照惯例,命令为 'name' 的消息映射到 RunEngine 上名为 _name 的协程方法,如上例中的 'set' -> RunEngine._set。用户可以注册自己的协程来添加自定义命令,尽管这很少需要。
某些命令不涉及与硬件的通信。例如,Msg('sleep', None, 5) 使 RunEngine 休眠 5 秒。None 是不适用于 'sleep' 命令的“对象”(设备)的占位符。正如计划绝不应直接与硬件通信一样,它们也绝不应使用像 time.sleep() 这样的长时间阻塞调用。相反,'sleep' 命令映射到 RunEngine._sleep,与 RunEngine 的事件循环集成,以非阻塞方式休眠,使得 RunEngine 在此期间保持响应——监视用户中断并可能在后台异步采集数据。
其他命令用于控制元数据和输入/输出。例如,Msg('open_run') 和 Msg('close_run') 界定了一次运行的范围。传递给 'open_run' 消息的任何关键字参数都被解释为元数据,并编码到 RunStart 文档中。
以下是内置命令的全面概述:
命令
以下是“内置”命令,其中一些与 RunEngine 实例的状态紧密相关。
create
此命令告知运行引擎应开始收集read的结果以创建一个事件。如果在它们之间没有save或drop的情况下调用两次,则会引发异常(因为一次不能有多个打开的事件)。这非常依赖于运行引擎的内部状态,不应由用户覆盖。此调用向协程返回 None。它忽略消息中除命令外的所有部分。
save
这是与 create 配对的命令,用于打包并触发Event文档被发出。必须在 create 之后调用,否则扫描将失败并引发 IllegalMessageSequence 异常。此命令高度依赖运行引擎的内部状态,不应被篡改。该调用向协程返回 None。除命令本身外,消息的其他部分均被忽略。
read
这触发对此消息中obj调用读取.
msg.obj.read(*msg.args, **msg.kwargs)
在create和save之间被读取的任何内容将被打包到一个事件中.这高度依赖于运行引擎的内部状态,不应被篡改。将 read 返回的字典返回给协程。消息的 args 和 kwargs 部分会传递给 read 方法。
null
这是一个空消息,被运行引擎忽略。它的存在是为了使逻辑代数成立。向协程返回 None。忽略消息中除命令外的所有值。
set
指示一个Mover进行移动。目前,这模仿了类似 EPICS 的即时运动逻辑。
stage 和 unstage
指示运行引擎对对象进行阶段准备/阶段清理。这会调用 obj.stage()/obj.unstage()。预计的消息对象是:
Msg('stage', object)
Msg('unstage', object)
这产生这些调用:
staged_devices = object.stage()
unstaged_devices = object.unstage()
其中 staged_devices/unstaged_devices 是被(取消)阶段准备的 ophyd.Device 列表,而非状态对象。
有人可能会疑惑为什么返回值是一个设备列表,而不是像 set 及类似Msg中那样的状态对象。这个问题曾讨论过一段时间。阶段准备期间执行的操作应涉及配置调整,且应快速完成。阶段准备不应包含耗时的设置调用。
为什么要返回被阶段准备的对象列表?对设备进行阶段准备会导致其组件设备(如果有)也被阶段准备。所有这些子设备连同 [self] 会被添加到一个列表中,并由 Device.stage() 返回,以便计划能够跟踪哪些设备已被阶段准备,示例如下:
devices_staged = yield Msg('stage', device)
为什么计划需要知道这些?这是为了避免意外地尝试对同一设备进行两次阶段准备,例如,对父设备进行阶段准备后,又试图对其子设备也进行阶段准备。避免这种情况非常重要,因为重复阶段准备会引发错误。
trigger
这将调用obj.trigger方法并且缓存返回的状态对象.
sleep
休眠当前事件循环.
wait
阻塞进程,直到每个被触发或设置了关键字参数 group=<GROUP> 的对象完成操作。
预期的消息对象是:
Msg('wait',group=<GROUP>)
此处<GROUP>是任何可哈希键.
wait_for
指示运行引擎等待此 asyncio.Future 对象完成。这允许从外部对运行引擎进行任意控制。
input
处理一个输入. 允许在运行中用户输入.示例:
Msg('input', None)
Msg('input', None, prompt='>')
checkpoint
指示运行引擎创建一个检查点, 因而如果需要我们可以退到这个点
clear_checkpoint
清除一个设置的检查点
rewindable
pause
请求运行引擎暂停
预计的消息对象是:
Msg('pause', defer=False, name=None, callback=None)
kickoff
启动飞行扫描对象.
collect
收集由飞行器缓存的数据并且发出描述符和事件文档. 这调用obj.collect()方法.
complete
告知飞行器:“随时准备停止采集”。
这会调用给定对象的 obj.complete() 方法。飞行器返回一个状态对象。某些飞行器会响应此命令,停止采集并立即返回一个已完成的状态对象。其他飞行器则会完成其既定过程,无论该命令何时发出,都会在其完成时结束。
configure
配置一个对象. 预计的消息对象是:
Msg('configure', object, *args, **kwargs)
这产生这个调用:
object.configure(*args, **kwargs)
subscribe
在运行开始后添加订阅。
与传递给 __call__ 的订阅一样,这些订阅将在运行结束时由 RunEngine 移除。
预期的消息对象为:
Msg(‘subscribe’, None, callback_function, document_name)
其中 document_name 可以是以下之一:{‘start’, ‘descriptor’, ‘event’, ‘stop’, ‘all’}
并且 callback_function 应具有如下签名:f(name, document)
这里的 name 是上述 document_name 选项之一,document 是事件模型中的文档字典之一。
更多信息请参阅 bluesky.run_engine.Dispatcher.subscribe() 的文档字符串。
unsubscribe
在调用过程中移除订阅——适用于多运行调用中某些运行需要订阅而其他运行不需要的场景。
预计的消息对象是:
Msg('unsubscribe', None, TOKEN)
Msg('unsubscribe', token=TOKEN)
此处TOKEN是从RunEngine._subscribe()返回的值.
open_run
指示RunEngine开始一个新"运行", 预计的消息对象是:
Msg('open_run', None, **kwargs)
此处**kwargs是应该进入到RunStart文档的任何其它元数据
close_run
指示运行引擎编写RunStop文档, 预计的消息对象是:
Msg('close_run', None, exit_status=None, reason=None)
如果未提供 exit_stats 和 reason,则使用运行引擎中存储的值。
drop
在不发出已完成的事件文档的情况下, 丢弃一组读数。
此命令用于放弃先前 create 和 read 命令的结果,而不发出事件。它可用于丢弃已知的坏事件(例如没有光束),保持事件文档流的整洁。在执行 drop 之后,可以安全地开始新的 create、read、save 序列。
必须在 create 之后调用,否则扫描将失败并引发 IllegalMessageSequence 异常。
此调用向协程返回 None。除命令本身外,消息的所有其他部分均被忽略。
monitor
监控一个信号。异步发出事件文档。
立即发出一个描述符文档。随后,定义一个闭包,该闭包从独立线程中发出与该描述符关联的事件文档。此过程与主要的事件打包流程(create/read/save)无关。
预期的消息对象为:
Msg('monitor', obj, **kwargs)
Msg('monitor', obj, name='event-stream-name', **kwargs)
此处kwargs被传递到obj.subscribe().
unmonitor
停止监视, 即, 移除发出事件文档的回调. 预计的消息对象是:
Msg('unmonitor', obj)
stop
停止一个设备,预计的消息对象是:
Msg('stop', obj)
这等价于调用obj.stop().
注册自定义的命令
运行引擎可以注册任何新命令. 可以使用以下方法注册它们.
RunEngine.register_command(name, func)
注册新的消息命令.
参数:
- name: str
- func: callable, 这可以是一个函数或方法. 签名为f(msg)
RunEngine.unregister_command(name)
注销一条消息命令. 参数: name: str
RunEngine.commands
对Msg可用的命令列表.
In [41]: RE.commands
Out[41]:
['declare_stream',
'create',
'save',
'drop',
'read',
'locate',
'monitor',
'unmonitor',
'null',
'RE_class',
'stop',
'set',
'trigger',
'sleep',
'wait',
'checkpoint',
'clear_checkpoint',
'rewindable',
'pause',
'_resume_from_suspender',
'_start_suspender',
'prepare',
'collect',
'kickoff',
'complete',
'configure',
'stage',
'unstage',
'subscribe',
'unsubscribe',
'open_run',
'close_run',
'wait_for',
'input',
'install_suspender',
'remove_suspender']
RunEngine.print_command_registry(verbose=False)
这便捷地打印出可用命令的命令注册表.
参数:
- verbose: bool, 可选, 详细打印, 默认是Fasle.
In [45]: print(RE.print_command_registry())
List of available commands
declare_stream : Trigger the run engine to start bundling future obj.describe() calls for
create : Trigger the run engine to start bundling future obj.read() calls for
save : Save the event that is currently being bundled
drop : Drop the event that is currently being bundled
read :
locate :
monitor :
unmonitor :
null :
RE_class :
stop :
set :
trigger :
sleep :
wait : Block progress until every object that was triggered or set
checkpoint : Instruct the RunEngine to create a checkpoint so that we can rewind
clear_checkpoint : Clear a set checkpoint
rewindable : Set rewindable state of RunEngine
pause : Request the run engine to pause
_resume_from_suspender : Request the run engine to resume
_start_suspender :
prepare : Prepare a flyer for a flyscan
collect :
kickoff : Start a flyscan object
complete :
configure : Configure an object
stage : Instruct the RunEngine to stage the object
unstage : Instruct the RunEngine to unstage the object
subscribe :
unsubscribe :
open_run : Instruct the RunEngine to start a new "run"
close_run : Instruct the RunEngine to write the RunStop document
wait_for : Instruct the RunEngine to wait for futures and return the resulting tasks.
input :
install_suspender :
remove_suspender :更多推荐



所有评论(0)