3. Jython脚本类型

有三种不同的“类型”- CORECONFIGUSER -可以编写的Jython脚本。因为它们的用途不同,所以将它们放在不同的目录中,甚至对这些目录拥有不同的权限是很有帮助的。每种“类型”的脚本都可以有任意数量的目录,因此光束线除了可以使用自己的配置文件夹之外,还可以使用来自共享库的配置脚本。设置这些路径在中定义 配置 下面的部分。下面给出的位置只是提供指导的示例。

核心脚本
  • 位置: ${{gda.root}}/uk.ac.gda.core/scripts

  • 类型: CORE

这些是由所有光束线共享的通用脚本。它们不打算由用户或光束线工作人员编辑。脚本应该放在 gdascripts 文件夹清楚地显示模块不是具有GDA顶级目录的Java类。

史诗剧本
  • 位置: ${{gda.root}}/uk.ac.gda.epics/scripts

  • 类型: CORE

这些脚本的类型与 Core Scripts 如上所述。它们是与EPICS相关的脚本,需要EPICS IOC才能成功运行。

光束线配置脚本
  • 位置: ${{gda.config}}/scripts

  • 类型: CONFIG

这些是特定于光束线的脚本。这些将是用户不希望编辑的数据收集或分析实用程序。它们可能是由光束线工作人员撰写的。

localStation.py 通常位于此目录中;它由 JythonServer 在启动服务器以定制该光束线的Jython环境时创建。

用户脚本
  • 位置:由Spring IOC XML配置为GDA定义的任何文件夹 command_server .

  • 类型: USER

这些是用户为他们的实验编写或编辑的脚本。这是一个与其他类型的脚本不同的文件夹,因为该目录可能希望在实验结束时清空。

这方面的一个例子 USER 类型文件夹为 example-config/users/scriptsexample-config 项目。

3.1. 配置

脚本项目文件夹的路径是通过Bean定义的,这些Bean在服务器的Spring IOC配置中作为Jython Server对象的参数给出。每个项目都定义为 gda.jython.ScriptProject 对象,其中包括包含脚本的文件夹的路径、项目名称及其类型 (USERCONFIGCORE )。项目名称出现在Project Explorer视图的RCP用户界面中。

项目对象应该由 gda.jython.ScriptPaths 豆子。项目定义为有序列表:当Jython run 命令后,命令服务器将按照它们在Spring配置中列出的顺序在文件夹中搜索脚本文件。这个 ScriptPaths 对象还应配置到启动脚本的完整路径(通常名为 localStation.py ),它在命令服务器启动时运行。

最后, ScriptPaths Bean本身应该由 gda.jython.JythonServer 实例,该实例必须命名为 command_server 。这是GDA中Jython环境的控制器对象。将所有这些放在一起,最终得到的Spring配置将如以下摘录自示例配置的 server.xml 文件::

<bean id="command_server" class="gda.jython.JythonServer">
  <property name="jythonScriptPaths">
    <bean class="gda.jython.ScriptPaths">
      <property name="projects">
        <list>
          <bean class="gda.jython.ScriptProject">
            <property name="path" value="${gda.config}/users/scripts" />
            <property name="name" value="Scripts: User" />
            <property name="type" value="USER" />
          </bean>
          <bean class="gda.jython.ScriptProject">
            <property name="path" value="${gda.config}/scripts" />
            <property name="name" value="Scripts: Config" />
            <property name="type" value="CONFIG" />
          </bean>
          <bean class="gda.jython.ScriptProject">
            <property name="path" value="${gda.root}/uk.ac.gda.core/scripts" />
            <property name="name" value="Scripts: Core" />
            <property name="type" value="CORE" />
          </bean>
        </list>
      </property>
      <property name="startupScript" value="${gda.config}/scripts/localStation.py" />
    </bean>
  </property>
</bean>

3.2. 将脚本队列添加到配置

在服务器上添加以下Bean:

<bean id="commandQueue" class = "gda.commandqueue.CommandQueue">
</bean>

<bean id="commandQueueProcessor"
        class = "gda.commandqueue.FindableProcessorQueue">
        <property name="queue" ref="commandQueue"/>
        <property name="startImmediately" value="true"/>
        <property name="logFilePath" value="${gda.var}/commandQueueProcessor.log"/>
</bean>

<bean class="uk.ac.gda.remoting.server.GdaRmiServiceExporter">
        <property name="serviceName" value="gda/commandQueueProcessor" />
        <property name="service" ref="commandQueueProcessor" />
        <property name="serviceInterface"
                value="gda.commandqueue.IFindableQueueProcessor" />
</bean>

在客户端上添加以下bean:

<bean id="commandQueueProcessor"
        class="uk.ac.gda.remoting.client.GdaRmiProxyFactoryBean">
        <property name="serviceUrl"
                value="rmi://<server-host-name>/gda/commandQueueProcessor" />
        <property name="serviceInterface"
                value="gda.commandqueue.IFindableQueueProcessor" />
        <property name="refreshStubOnConnectFailure" value="true" />
</bean>
<bean class="gda.rcp.util.OSGIServiceRegister">
        <property name="class" value="gda.commandqueue.Processor" />
        <property name="service" ref="commandQueueProcessor" />
</bean>
<bean class="gda.rcp.util.OSGIServiceRegister">
        <property name="class" value="gda.commandqueue.Queue" />
        <property name="service" ref="commandQueueProcessor" />
</bean>

需要很长时间的脚本应该通知用户进度,并定期允许脚本暂停。这两个操作可以通过以下形式的代码来完成::

from gda.commandqueue import JythonScriptProgressProvider
JythonScriptProgressProvider.sendProgress( percent, msg)

其中Percent是完成百分比(整数),msg是要显示的字符串。

本课程介绍了如何将脚本从RCP GUI提交到队列 uk.ac.gda.client.actions.QueueScriptSelectionActionDelegate

要从Jython终端向队列提交脚本,请输入命令:

finder.find("commandQueue").addToTail(
        JythonScriptFileCommandProvider(<path to script>))

3.3. 链接脚本和GUI

在Jython脚本而不是Java代码中保存特定于光束线的实验逻辑是有益的,因为Jython可以由GDA开发人员和其他光束线工作人员在运行时编辑。这些Jython脚本可能会使用主扫描机制来收集数据,但是可能有扫描之外的工作要执行,比如准备样本环境或运行一些光束线对齐逻辑。

将此逻辑存储为Jython脚本可以简化GDA安装中通常最复杂且随时间变化的部分的开发和维护。但是,向用户报告这些脚本的进度通常很有用。

为了实现来自脚本的通信,使用分布式对象,该对象充当特定脚本和对该脚本的工作感兴趣的GUI部分之间的中间人。ScriptController类是一个服务器端对象,它将消息从脚本散布到客户端的iWatch类。然后,GUI可以将进度报告回用户。

示例配置:

<bean id="MyScriptObserver" class="gda.jython.scriptcontroller.ScriptControllerBase"/>

然后,客户端Java类将实现IObservable接口,将自己注册为该对象的观察者,并通过update(object,object)方法接收事件。脚本将通过从查找器检索此对象并通过ScriptController的update()方法发送消息来发送这些消息::

controller = Finder.find("MyScriptObserver")
controller.update(None,ScriptProgressEvent("I have got to this point in the script"))

尽管IWatch/IObservable接口允许将任何可序列化的对象传递到GUI,但是当使用特定的事件对象时,通信会更加清晰。在gda.jython.scriptControler.event包中有一些。如果足够通用,应该在那里添加新的事件类型,以便为系统提供某种形式的标准化。

例如,脚本启动的扫描可以使用ScanCreationEvent类将其唯一ID广播给脚本的观察者::

myscan = ConcurrentScan(args)
scan_id = myscan.getName()
controller.update(None,ScriptProgressEvent("Starting scan..."))
controller.update(None,ScanCreationEvent(scan_id)
myscan.runScan()
controller.update(None,ScanFinishEvent(scan_id,ScanFinishEvent.FinishType.OK))

从客户端启动脚本的一种简单方法是使用InterfaceProvider。ICommandRunner runner=InterfaceProvider.getCommandRunner();runner.runScript(new File(“script/to/run.py”,“source”);