secsgem v0.3.0版本使用说明文档
文章目录
- secsgem概述
- 如何安装
- 1 使用方法
- 1.1、回调函数和事件
- 1.1.1、回调函数
- 1.1.1.1、继承的处理程序
- 1.1.1.2、目标对象
- 1.1.1.3、注册回调函数
- 1.1.2、事件
- 1.1.2.1、继承式处理程序
- 1.1.2.2、目标对象
- 1.1.2.3、注册事件
- 1.2、实施一套 GEM 设备
- 1.2.1、使用你自己的名字
- 1.2.2、添加状态变量
- 1.2.3、添加设备常数
- 1.2.4、添加收集事件
- 1.2.5、添加警报
- 1.2.6、添加远程命令
- 1.3、自定义流和函数
- 1.3.1、自定义数据项
- 1.3.2、 自定义流函数
- 1.3.3、 整合流函数
- 1.4、Secs 函数定义语言
- 1.4.1、数据项
- 1.4.2、 列表
- 1.4.2.1、 固定长度列表
- 1.4.2.2、 打开长度列表
- 1.4.2.3、 嵌套
- 1.4.2.1.1、 在固定长度列表中嵌套开放长度列表
- 1.4.3、 注释
- 1.4.4、 为什么要创建自定义函数定义语言?
- 1.4.5、 为什么不创建一个全新的定义语言呢?
- 1.5、测试
- 2 GEM
- 2.1、处理器
- 2.1.1、事件
- 2.2、GEM 合规性
- 2.2.1、状态模型
- 2.2.2、 设备加工状态
- 2.2.3、 文档
- 2.2.4、 控制 (作员启动)
- 2.2.5、 动态事件报告配置
- 2.2.6、 跟踪数据收集
- 2.2.7、 报警管理
- 2.2.8、 远程控制
- 2.2.9、 设备常量
- 2.2.10、 工艺配方管理
- 2.2.11、 物料移动
- 2.2.12、 设备终端服务
- 2.2.13、 时钟
- 2.2.14、 限制监测
- 2.2.15、 堆叠(或:缓冲)
- 3 SECS
- 3.1、变量
- 3.1.1、数组类型
- 3.1.2、获取数据
- 3.1.3、设置数据
- 3.1.4、编码/解码
- 3.1.5、Array
- 3.1.6、List
- 3.1.7、动态变量
- 3.2、Items
- 3.2.1、 Item types
- 3.2.2、 Creating items
- 3.2.1.1、 From value
- 3.2.1.2、From SML text
- 3.2.1.3、 From protocol text
- 3.2.3、 Getting data
- 3.2.3.1、 Python value
- 3.2.3.2、 SML text
- 3.2.1.3、 Protocol text
- 3.3、Functions
- 3.4、Handler
- 4、HSMS
- 4.1、消息快
- 4.1.1、选择 请求
- 4.1.2、选择响应
- 4.1.3、取消选择请求
- 4.1.4、取消选择响应
- 4.1.5、Linktest 请求
- 4.1.6、Linktest 响应
- 4.1.7、拒绝请求
- 4.1.8、单独请求
- 4.1.9、数据消息
- 4.2、 协议
- 4.2.1、 事件
secsgem概述
secsgem 是一个用于与半导体行业中的主机或设备系统进行通信的 Python 包。
这些用例涵盖了从为实现或功能编写测试、开发环境中的模拟到完整的主机/设备实现等多个方面。该软件包的部分内容可以单独使用,例如 HSMS 可以在没有 SECS-II 的情况下单独使用,或者流和功能可以与不同的网络栈一起使用。
目前尚无支持通过串行端口进行通信的功能(SECS-I、SEMI E04)。只有以太网(HSMS、SEMI E37)是可用的。
HSMS、SECS 和 GEM 均为来自 SEMI 的标准。
如何安装
官方发布版本可通过
Pypi 仓库获取。安装这些版本最简单的方法是使用像 pip 这样的包管理器:
$ pip install secsgem
为了使用当前的开发代码(有时可能会不稳定),请直接使用 Git 代码库:
(注:原文中的“instable”意为“不稳定的”,“git repository”意为“Git 代码库”)
$ pip install git+git://github.com/bparzella/secsgem
1 使用方法
secsgem 可以有多种使用方式。您可以将其用于创建针对设备端和主机端的 GEM 实现。此外,在 SECS-II 和 HSMS 层面上实现也是可行的。还有另一种使用方式,即通过使用 Python 单元测试来测试您的主机或设备实现。
1.1、回调函数和事件
- 回调函数
用于处理来自远程系统的请求,并返回用户定义的结果(例如,已收到警报且需要作出响应)。 - 事件
可以向实现程序通知已发生的情况(例如,已选择 HSMS 连接)。事件不会向远程系统返回任何结果,并且是在后台执行的。
1.1.1、回调函数
回调函数用于向特定实现请求信息。
它们可用于处理传递过来的信息并向对方呈现结果。
对于一个回调函数而言,只能注册一个函数。
该过程会等待回调函数返回计算结果。
因此,回调函数应当尽可能高效地运行。
定义回调函数有三种方式,分别是:在 继承的处理程序 中创建它们;设置一个 目标对象 ;以及 注册回调 。
注册的回调函数会取代目标对象和被覆盖的函数。
1.1.1.1、继承的处理程序
在使用继承类时,可以通过创建具有特定名称的回调成员来实现回调功能:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)def _on_alarm_received(self, handler, ALID, ALCD, ALTX):return ACKC5.ACCEPTED
在这个示例中,当接收到警报(回调名称为 alarm_received)时,会调用 _on_alarm_received
方法。
结果(在本例中为 ACKC5.ACCEPTED
)将传递给发送警报的主机。
该函数的通用表述为:
def _on_<callback_name>(self, handler, <parameters>):return <result>
对于流/函数的回调函数也可以通过遵循特定的命名方式来实现重写:这种方式可以实现对回调函数的覆盖。
def _on_s05f01(self, handler, message):return self.stream_function(5, 2)(ACKC5.ACCEPTED)
请注意,流和函数编号会格式化为在它们仅有一个字符长时带有前导零。
在这种情况下,必须返回回复流/函数。
1.1.1.2、目标对象
这些方法无需在处理程序自身上实现。
还可以使用处理程序的回调成员名称注册另一个对象。
然后会在该对象中搜索 _on_<callback_name>
方法:
class TestClass:def _on_alarm_received(self, handler, ALID, ALCD, ALTX):return ACKC5.ACCEPTEDt = TestClass()handler.callbacks.target = t
1.1.1.3、注册回调函数
回调函数也可以从类之外进行注册:
Callbacks can also be registered from outside a class:
def f_alarm_received(handler, ALID, ALCD, ALTX):return ACKC5.ACCEPTED
要取消注册,请直接清除会员信息即可。
handler.callbacks.alarm_received = None
1.1.2、事件
事件会通知执行者事情已经发生了。
它们是异步触发的,执行结果会被忽略掉。
定义事件有三种方式,分别是:在 继承式处理程序 中创建事件;在处理程序的事件属性中设置 目标对象 ;以及注册事件 [#registering-events] 。
1.1.2.1、继承式处理程序
在使用继承类时,可以通过创建具有特定名称的成员来实现事件:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)def _on_event_communicating(self, connection):pass
在这个示例中,当 HSMS 连接状态发生变化时,会调用 _on_event_communicating
方法。
该函数的通用表述为:
def _on_event_<event_name>(self, <parameters>):pass
若要捕获所有事件,可以重写 _on_event
方法:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)def _on_event(self, *args):pass
1.1.2.2、目标对象
这些方法无需在处理程序自身上实现。
还可以使用处理程序的事件成员名称来注册其他对象。
然后会在该对象中搜索 _on_event_<event_name>
和 _on_event
方法:
class TestClass:def _on_event_communicating(self, connection):passt = TestClass()handler.events.targets += t
该事件处理程序能够处理多个目标对象。
1.1.2.3、注册事件
事件也可以从类外进行注册:
def f_communicating(connection):passhandler.events.communicating += f_communicating
要取消注册,请直接删除会员信息即可。
handler.events.communicating -= f_communicating
1.2、实施一套 GEM 设备
此包可用于创建 GEM 设备实现。
这是通过继承 {py:class}secsgem.gem.equipmenthandler.GemEquipmentHandler
类来完成的:
import secsgem.gem
import codeclass SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)h = SampleEquipment("127.0.0.1", 5000, False, 0, "sampleequipment")
h.enable()code.interact("equipment object is available as variable 'h', press ctrl-d to stop", local=locals())h.disable()
1.2.1、使用你自己的名字
如果您要使用自己的模型名称和版本来回复 S1F14,可以重写 GemHandler 类中的 {py:attr}secsgem.gem.handler.GemHandler.MDLN
和 {py:attr}secsgem.gem.handler.GemHandler.SOFTREV
成员:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.MDLN = "gemequp"self.SOFTREV = "1.0.0"
1.2.2、添加状态变量
可以通过向 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.status_variables
字典中插入一个 {py:class}secsgem.gem.equipmenthandler.StatusVariable
类的实例来添加一个状态变量:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.status_variables.update({10: secsgem.gem.StatusVariable(10, "sample1, numeric SVID, SecsVarU4", "meters", secsgem.secs.variables.U4, False),"SV2": secsgem.gem.StatusVariable("SV2", "sample2, text SVID, SecsVarString", "chars", secsgem.secs.variables.String, False),})self.status_variables[10].value = 123self.status_variables["SV2"].value = "sample sv"
或者,也可以通过将构造函数的 use_callback 参数设置为 True 来使用回调函数获取这些值:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.sv1 = 123self.sv2 = "sample sv"self.status_variables.update({10: secsgem.gem.StatusVariable(10, "sample1, numeric SVID, SecsVarU4", "meters", secsgem.secs.variables.U4, True),"SV2": secsgem.gem.StatusVariable("SV2", "sample2, text SVID, SecsVarString", "chars", secsgem.secs.variables.String, True),})def on_sv_value_request(self, svid, sv):if sv.svid == 10:return sv.value_type(value=self.sv1)elif sv.svid == "SV2":return sv.value_type(value=self.sv2)return []
1.2.3、添加设备常数
可以通过向 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.status_variables
字典中插入一个 {py:class}secsgem.gem.equipmenthandler.EquipmentConstant
类的实例来添加一个设备常量:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.equipment_constants.update({20: secsgem.gem.EquipmentConstant(20, "sample1, numeric ECID, SecsVarU4", 0, 500, 50, "degrees", secsgem.secs.variables.U4, False),"EC2": secsgem.gem.EquipmentConstant("EC2", "sample2, text ECID, SecsVarString", "", "", "", "chars", secsgem.secs.variables.String, False),})self.status_variables[20].value = 321self.status_variables["EC2"].value = "sample ec"
或者,也可以通过将构造函数的 use_callback 参数设置为 True 来使用回调函数来获取和更新这些值:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.ec1 = 321self.ec2 = "sample ec"self.equipment_constants.update({20: secsgem.gem.EquipmentConstant(20, "sample1, numeric ECID, SecsVarU4", 0, 500, 50, "degrees", secsgem.secs.variables.U4, True),"EC2": secsgem.gem.EquipmentConstant("EC2", "sample2, text ECID, SecsVarString", "", "", "", "chars", secsgem.secs.variables.String, True),})def on_ec_value_request(self, ecid, ec):if ec.ecid == 20:return ec.value_type(value=self.ec1)elif ec.ecid == "EC2":return ec.value_type(value=self.ec2)return []def on_ec_value_update(self, ecid, ec, value):if ec.ecid == 20:self.ec1 = valueelif ec.ecid == "EC2":self.ec2 = value
1.2.4、添加收集事件
可以通过向 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.collection_events
字典中插入一个 {py:class}secsgem.gem.equipmenthandler.CollectionEvent
类的实例来添加一个集合事件。
可以通过向 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.data_values
字典中插入一个 {py:class}secsgem.gem.DataValue
类的实例来添加数据值。
在创建 {py:class}secsgem.gem.equipmenthandler.CollectionEvent
实例时,可以传递集合事件的数据值:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.dv1 = 31337self.data_values.update({30: secsgem.gem.DataValue(30, "sample1, numeric DV, SecsVarU4", secsgem.secs.variables.U4, True),})self.collection_events.update({50: secsgem.gem.CollectionEvent(50, "test collection event", [30]),})def on_dv_value_request(self, dvid, dv):if dv.dvid == 30:return dv.value_type(value=self.dv1)return []def trigger_sample_collection_event():self.trigger_collection_events([50])
1.2.5、添加警报
可以通过向 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.alarms
字典中插入一个 {py:class}secsgem.gem.equipmenthandler.Alarm
类的实例来添加警报。
添加警报时必须提供警报的集合事件。
有关示例,请参见上述部分:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.collection_events.update({100025: secsgem.gem.CollectionEvent(100025, "test collection event alarm set", []),200025: secsgem.gem.CollectionEvent(200025, "test collection event alarm clear", []),})self.alarms.update({25: secsgem.gem.Alarm(25, "test alarm", "test text", self.data_items.ALCD.PERSONAL_SAFETY | self.data_items.ALCD.EQUIPMENT_SAFETY, 100025, 200025),})def set_sample_alarm():self.set_alarm(25)def clear_sample_alarm():self.clear_alarm(25)
1.2.6、添加远程命令
可以通过将 {py:class}secsgem.gem.equipmenthandler.RemoteCommand
类的一个实例插入到 {py:attr}secsgem.gem.equipmenthandler.GemEquipmentHandler.remote_commands
字典中来添加远程命令。
添加远程命令时必须提供集合事件和参数。
有关示例,请参见上述部分:
class SampleEquipment(secsgem.gem.GemEquipmentHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.collection_events.update({5001: secsgem.gem.CollectionEvent(5001, "TEST_RCMD complete", []),})self.remote_commands.update({"TEST_RCMD": secsgem.gem.RemoteCommand("TEST_RCMD", "test rcmd", ["TEST_PARAMETER"], 5001),})def on_rcmd_TEST_RCMD(self, TEST_PARAMETER):print "remote command TEST_RCMD received"
1.3、自定义流和函数
1.3.1、自定义数据项
通过重写 {py:class}secsgem.secs.data_items.DataItemBase
类,可以创建一个新的数据项:
class UNITS_New(DataItemBase):__type__ = SecsVarDynamic__allowedtypes__ = [SecsVarArray, SecsVarBoolean, SecsVarU1, SecsVarU2, SecsVarU4, SecsVarU8, SecsVarI1, SecsVarI2, SecsVarI4, SecsVarI8, \SecsVarF4, SecsVarF8, SecsVarString, SecsVarBinary]
In this case the UNITS
field allows all types instead only a string.
1.3.2、 自定义流函数
若要在流函数中整合这个新数据项,那么您就需要继承 {py:class}secsgem.secs.functions.SecsStreamFunction
:
class SecsS01F12_New(secsgem.secs.functions.SecsStreamFunction):_stream = 1_function = 12_data_format = """<L<L<SVID><SVNAME><UNITS_New>>>"""_to_host = True_to_equipment = False_has_reply = False_is_reply_required = False_is_multi_block = True
数据格式是在基于 SML 的定义语言中定义的。
有关该定义语言的更多信息,请参阅 Secs 函数定义语言 。
数据也可以通过一种古老的基于 Python 类型的格式来定义。
这是最初编写这些函数所依据的格式。
该格式仍然有效,但不久之后将会被弃用。
class SecsS01F12_New(secsgem.secs.functions.SecsStreamFunction):_stream = 1_function = 12_data_format = [[SVID,SVNAME,UNITS_New]]_to_host = True_to_equipment = False_has_reply = False_is_reply_required = False_is_multi_block = True
1.3.3、 整合流函数
现在我们要将这个流/功能整合到 {py:class}secsgem.gem.handler.GemHandler
中。
您需要创建一个新的类,该类继承自它,并更新该类的功能列表:
class NewHandler(secsgem.gem.GemHostHandler):def __init__(self, settings: secsgem.common.Settings):super().__init__(settings)self.settings.streams_functions.update(SecsS01F12_New)
You can also add new methods and properties to the class if required.
The streams functions list can also be updated outside of the Handler:
settings = secsgem.hsms.HsmsSettings()
settings.streams_functions.update(SecsS01F12_New)handler = NewHandler(settings)
1.4、Secs 函数定义语言
SFDL 派生自 SML,但它允许定义函数时无需使用混淆、冗余的类型定义。
1.4.1、数据项
数据项是通过在其所在的一组尖括号中给出其名称来定义的。
标准 S6F2 的结构定义
< ACKC6 >
标准 S2F36 的结构定义
< LRACK >
1.4.2、 列表
列表在一组尖括号中的左括号中用 ‘L’ 进行描述。
1.4.2.1、 固定长度列表
具有多个不同数据项的列表映射到 dict 或 object。 这可以通过数据项名称作为 key 来访问。
标准 S5F1 的结构定义
< L< ALCD >< ALID >< ALTX >
>
1.4.2.2、 打开长度列表
打开的列表仅使用一个数据项进行定义。 它们可以保存具有相同数据类型的多个值。
标准 S1F3 的结构定义
< L< SVID >
>
1.4.2.3、 嵌套
列表可以相互嵌套。
标准 S1F22 的结构定义
< L< L< VID >< DVVALNAME >< UNITS >>
>
1.4.2.1.1、 在固定长度列表中嵌套开放长度列表
如果包含单个数据项的打开列表嵌套在固定长度列表(映射到 dict/object)中,则嵌套数据项的名称将用于覆盖父列表的键。
标准 S2F23 的结构定义
< L< TRID >< DSPER >< TOTSMP >< REPGSZ >< L < SVID >>
>
这将使 SVID 列表可用于键/属性 SVID。
如果固定长度的开放列表嵌套在固定长度列表 (oof) 中,则解析名称不会像上一个示例那样工作。 在这种情况下,该项被简单地命名为 “DATA”。 但是可以通过在 L 标记后传递名称来覆盖此名称
标准 S2F33 的结构定义
< L< DATAID >< L REPORTS<L< RPTID >< L< VID >>>>
>
在这种情况下,报表列表将可通过键/属性 REPORTS.
但其他嵌套列表也可以这样命名
标准 S2F23 的结构定义
< L< TRID >< DSPER >< TOTSMP >< REPGSZ >< L SVIDS< SVID >>
>
这将使 SVID 列表可用于键/属性 SVIDS。
标准 S6F8 的结构定义
< L< DATAID >< CEID >< L DS< L< DSID >< L DV< L< DVNAME >< DVVAL >>>>>
>
1.4.3、 注释
注释以 # 开头,以换行符结尾。
< L # Sample list< DATAID > # The data id< CEID > # The collection event id
>
1.4.4、 为什么要创建自定义函数定义语言?
虽然有一种名为 SML 的函数定义语言,但它并不满足定义泛型函数格式本身的要求。 在可视化消息中发送/接收的实际数据时,它会大放异彩。
该规范缺少开放列表(n 个元素)的定义。 此外,规范中未描述为数据项定义多个数据格式。(例如)
1.4.5、 为什么不创建一个全新的定义语言呢?
SML 在半导体行业非常普遍,很多人都接触过它。 因此,最好坚持使用至少与已知格式相似的格式。
1.5、测试
secsgem 可用于对您所实现的 SEMI 标准进行单元测试。
示例:
import unittestimport secsgem.common
import secsgem.gem
import secsgem.hsmsclass TestExampleSecsGem(unittest.TestCase):def setUp(self):settings = secsgem.hsms.Settings(address="127.0.0.1",port=5000,connect_mode=secsgem.hsms.HsmsConnectMode.PASSIVE,device_type=secsgem.common.DeviceType.HOST)self.handler = secsgem.gem.GemHostHandler.hsms(settings)self.handler.enable()self.handler.waitfor_communicating()def tearDown(self):self.handler.disable()def testLinktest(self):result_packet = self.handler.send_linktest_req()self.assertEqual(result_packet.header.s_type.value, 6)self.assertEqual(result_packet.header.device_id, 65535)
见测试用例文件:/testExample.py
2 GEM
SEMI 30
GEM 对设备的某些行为以及如何利用 SECS 消息来实现这些行为进行了定义。
2.1、处理器
{py:class}secsgem.gem.handler.GemHandler
继承了 {py:class}secsgem.secs.handler.SecsHandler
的功能(见 {doc}/secs/handler
)。
为了区分主机和设备处理过程,有两种专门的 {py:class}secsgem.gem.handler.GemHandler
类型:
{py:class}secsgem.gem.hosthandler.GemHostHandler
和 {py:class}secsgem.gem.equipmenthandler.GemEquipmentHandler
。
如果您想要实现主机系统,请使用 GemHostHandler,如果想要实现设备系统,请使用 GemEquipmentHandler。
它会自动完成链路的整个设置与拆除工作。
传入的收集事件和终端消息会自动被接受并传播。
收集事件报告的设置也得到了简化。
它具备发送远程命令和处理进程程序的功能。
该处理程序还实现了并维护了一个通信状态,该状态在标准中有所定义。
>>> settings = secsgem.hsms.Settings(address="127.0.0.1", port=5000, connect_mode=secsgem.hsms.HsmsConnectMode.PASSIVE, device_type=secsgem.common.DeviceType.HOST)
>>> client = secsgem.GemHostHandler(settings)
>>>
>>> client.enable()
>>> client.waitfor_communicating()
True
>>> client.get_process_program_list()
['test1', 'test2']
>>> client.request_process_program('test1')
This is process program test1
>>> client.disable()
等待通信状态也可以异步完成
>>> def on_communicating(event, data):
... print "Communicating"
...
>>> settings = secsgem.hsms.Settings(address="127.0.0.1", port=5000, connect_mode=secsgem.hsms.HsmsConnectMode.PASSIVE, device_type=secsgem.common.DeviceType.HOST)
>>> client = secsgem.GemHostHandler(settings)
>>> client.events.handler_communicating += on_communicating
>>>
>>> client.enable()
Communicating
>>> client.get_process_program_list()
['test1', 'test2']
>>> client.request_process_program('test1')
This is process program test1
>>> client.disable()
也可以使用处理程序发送和接收流/函数:
>>> f = secsgem.secs.functions.SecsS01F01()
>>> client.send_and_waitfor_response(f)
HsmsMessage({'header': HsmsHeader({device_id:0x0000, stream:01, function:02, p_type:0x00, s_type:0x00, system:0x75b78c3e, require_response:False}), 'data': '\x01\x02A\x06EQUIPMA\x06SV n/a'})
2.1.1、事件
GemHandler 定义了一些新的事件,这些事件可以通过使用 {py:class}secsgem.common.EventHandler
来接收:
事件名称 | 描述 |
---|---|
handler_communicating | Connection is setup–连接已设置 |
collection_event_received | Collection event was received–收到 Collection 事件 |
terminal_received | Terminal message was received–已收到终端消息 |
For an example on how to use these events see the code fragment in {doc}/secs/handler
.
2.2、GEM 合规性
GEM 合规性声明
基本 GEM 要求 | 实现 | 符合 GEM 标准t |
---|---|---|
State Models | Yes ✓ | No |
Equipment Processing States | No | No |
Host-Initiated S1,F13/F14 Scenario | Yes ✓ | Yes ✓ |
Event Notification | Yes ✓ | Yes ✓ |
On-Line Identification | Yes ✓ | Yes ✓ |
Error Messages | Yes ✓ | Yes ✓ |
Documentation | Yes ✓ | No |
Control (Operator Initiated) | Yes ✓ | No |
其他功能 | 实现 | 符合 GEM 标准t |
---|---|---|
Establish Communications | Yes ✓ | Yes ✓ |
Dynamic Event Report Configuration | Yes ✓ | No |
Variable Data Collection | Yes ✓ | Yes ✓ |
Trace Data Collection | No | No |
Status Data Collection | Yes ✓ | Yes ✓ |
Alarm Management | Yes ✓ | No |
Remote Control | Yes ✓ | Yes ✓ |
Equipment Constants | Yes ✓ | No |
Process RecipeManagement | No | No |
Material Movement | No | No |
Equipment Terminal Services | Yes ✓ | Yes ✓ |
Clock | No | No |
Limits Monitoring | No | No |
Spooling | No | No |
Control (Host-Initiated) | Yes ✓ | Yes ✓ |
2.2.1、状态模型
- 虽然实现了通信和控制状态模型, 尤其是控制状态模型需要返工。
2.2.2、 设备加工状态
- 尚未实现
2.2.3、 文档
- 文档尚未完成.
2.2.4、 控制 (作员启动)
- 尚未实现 ONLINE LOCAL/REMOTE 的持久性.
- 最终的 UI(或硬件)需要此 UI 所需的按钮 部分.
2.2.5、 动态事件报告配置
报告定义、报告与事件的关联以及启用状态的持久化功能尚未实现。
2.2.6、 跟踪数据收集
- 尚未实现。
2.2.7、 报警管理
“启用/禁用状态和报告定义的持久化功能尚未实现。”
2.2.8、 远程控制
启动和停止远程命令必须实现才能符合 GEM 标准。目前仅提供了虚拟函数。
2.2.9、 设备常量
- 设备常量的持久化尚未实现。
- 将设备状态限制在“安全”范围内尚未实现?
- 设备常量变更收集事件尚未实现。
2.2.10、 工艺配方管理
- 尚未实现。
2.2.11、 物料移动
- 尚未实现。
2.2.12、 设备终端服务
该用户界面(UI)需求无法通过该库来实现。
2.2.13、 时钟
(注:此内容似乎是一个单独的词汇或
- 尚未实现。
2.2.14、 限制监测
- 尚未实现。
2.2.15、 堆叠(或:缓冲)
- 尚未实现。
3 SECS
3.1、变量
SECS 定义了几种用于传输数据的类型。
Data Type | Class | Code |
---|---|---|
List | {py:class}secsgem.secs.variables.Array | L |
List | {py:class}secsgem.secs.variables.List | L |
Binary | {py:class}secsgem.secs.variables.Binary | B |
Boolean | {py:class}secsgem.secs.variables.Boolean | TF |
ASCII | {py:class}secsgem.secs.variables.String | A |
8-Byte integer | {py:class}secsgem.secs.variables.I8 | I8 |
1-Byte integer | {py:class}secsgem.secs.variables.I1 | I1 |
2-Byte integer | {py:class}secsgem.secs.variables.I2 | I2 |
4-Byte integer | {py:class}secsgem.secs.variables.I4 | I4 |
8-Byte float | {py:class}secsgem.secs.variables.F8 | F8 |
4-Byte float | {py:class}secsgem.secs.variables.F4 | F8 |
8-Byte unsigned integer | {py:class}secsgem.secs.variables.U8 | U8 |
1-Byte unsigned integer | {py:class}secsgem.secs.variables.U1 | U1 |
2-Byte unsigned integer | {py:class}secsgem.secs.variables.U2 | U2 |
4-Byte unsigned integer | {py:class}secsgem.secs.variables.U4 | U4 |
Example:
>>> import secsgem.secs
>>> secsgem.secs.variables.String("TESTString")
<A "TESTString">
>>> secsgem.secs.variables.Boolean(True)
<BOOLEAN True >
>>> secsgem.secs.variables.U4(1337)
<U4 1337 >
3.1.1、数组类型
numeric 类型也可以是该类型的数组:
>>> secsgem.secs.variables.U1([1, 2, 3, 4])
<U1 1 2 3 4 >
>>> secsgem.secs.variables.Boolean([True, False, False, True])
<BOOLEAN True False False True >
这个数组的长度可以用 length 参数来固定:
>>> secsgem.secs.variables.U1([1, 2, 3], count=3)
<U1 1 2 3 >
>>> secsgem.secs.variables.U1([1, 2, 3, 4], count=3)
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/home/ext005207/Development/secsgem/secsgem/secs/variables.py", line 1439, in __init__self.set(value)File "/home/ext005207/Development/secsgem/secsgem/secs/variables.py", line 1537, in setraise ValueError("Value longer than {} chars".format(self.count))
ValueError: Value longer than 3 chars>>> secsgem.secs.variables.String("Hello", count=3).get()
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/home/ext005207/Development/secsgem/secsgem/secs/variables.py", line 1220, in __init__self.set(value)File "/home/ext005207/Development/secsgem/secsgem/secs/variables.py", line 1337, in setraise ValueError("Value longer than {} chars ({} chars)".format(self.count, len(value)))
ValueError: Value longer than 3 chars (5 chars)
3.1.2、获取数据
该数据可通过 {py:func}
secsgem.secs.variables.U1.get` 方法进行访问,数组则可通过索引运算符进行访问:
>>> secsgem.secs.variables.U1(1).get()
1
>>> secsgem.secs.variables.U1([1, 2, 3], count=3).get()
[1, 2, 3]
>>> secsgem.secs.variables.U1(1)[0]
1
>>> secsgem.secs.variables.U1([1, 2, 3])[1]
2
3.1.3、设置数据
数据可以通过 {py:func}secsgem.secs.variables.String.set
方法进行设置,数组可以通过索引运算符进行更新:
>>> v=secsgem.secs.variables.U1([1, 2, 3], count=3)
>>> v.set([3, 2, 1])
>>> v
<U1 3 2 1 >
>>> v[0] = 1
>>> v
<U1 1 2 1 >
3.1.4、编码/解码
这些变量类型本身可以使用 {py:func}secsgem.secs.variables.Array.encode
和 {py:func}secsgem.secs.variables.String.decode
方法将数据转换为可与 HSMS 协议进行传输的 ASCII 数据:
>>> v=secsgem.secs.variables.String("Hello")
>>> d=v.encode()
>>> d
'A\x05Hello'
>>> secsgem.common.format_hex(d)
'41:05:48:65:6c:6c:6f'
>>> v.set("NewText")
>>> v
<A "NewText">
>>> v.decode(d)
7
>>> v
<A "Hello">
3.1.5、Array
{py:class}secsgem.secs.variables.Array
是一种用于表示同类型列表的特殊类型。数组中的各项可以通过索引运算符进行访问。
>>> v=secsgem.secs.variables.Array(secsgem.secs.variables.U4)
>>> v.set([1, 2, 3])
>>> v
<L [3]
<U4 1 >
<U4 2 >
<U4 3 >>
>>> v.get()
[1, 2, 3]
>>> v[1]
<U4 2 >
可以使用 {py:func}secsgem.secs.variables.Array.append
方法向数组添加新元素。
3.1.6、List
{py:func}secsgem.secs.variables.List
是一种用于不同类型列表的特殊类型。
列表中的各项可以通过像访问对象属性那样进行访问。
创建有序字典是必需的,因为 Python 的默认字典是随机排序的。排序是必不可少的,因为双方都需要数据保持相同的顺序。
>>> v=secsgem.secs.variables.List([secsgem.secs.data_items.OBJACK, secsgem.secs.data_items.SOFTREV])
>>> v.OBJACK=3
>>> v.SOFTREV="Hallo"
>>> v
<L [2]
<U1 3 >
<A "Hallo">>
>>> v.SOFTREV
<A "Hallo">
>>> secsgem.common.format_hex(v.encode())
'01:02:a5:01:03:41:05:48:61:6c:6c:6f'
3.1.7、动态变量
{py:class}secsgem.secs.variables.Dynamic
类型可变,若指定为特定的一组类型,则可取不同的类型。
>>> v=secsgem.secs.variables.Dynamic([secsgem.secs.variables.String, secsgem.secs.variables.U1])
>>> v.set(secsgem.secs.variables.String("Hello"))
>>> v
<A "Hello">
>>> v.set(secsgem.secs.variables.U1(10))
>>> v
<U1 10 >
>>> v.set(secsgem.secs.variables.U4(10))
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/home/ext005207/Development/secsgem/secsgem/secs/variables.py", line 255, in setraise ValueError("Unsupported type {} for this instance of Dynamic, allowed {}".format(value.__class__.__name__, self.types))
ValueError: Unsupported type U4 for this instance of Dynamic, allowed [<class 'secsgem.secs.variables.String'>, <class 'secsgem.secs.variables.U1'>]
3.2、Items
项目是构成 SECS 消息结构的实体。
就本包而言,列表也被假定为项目。
3.2.1、 Item types
Type | Class | Code |
---|---|---|
List | {py:class}secsgem.secs.items.ItemL | L |
Binary | {py:class}secsgem.secs.items.ItemB | B |
Boolean | {py:class}secsgem.secs.items.ItemBOOLEAN | TF |
ASCII | {py:class}secsgem.secs.items.ItemA | A |
JIS-8 | {py:class}secsgem.secs.items.ItemJ | J |
8-Byte integer | {py:class}secsgem.secs.items.ItemI8 | I8 |
1-Byte integer | {py:class}secsgem.secs.items.ItemI1 | I1 |
2-Byte integer | {py:class}secsgem.secs.items.ItemI2 | I2 |
4-Byte integer | {py:class}secsgem.secs.items.ItemI4 | I4 |
8-Byte float | {py:class}secsgem.secs.items.ItemF8 | F8 |
4-Byte float | {py:class}secsgem.secs.items.ItemF4 | F8 |
8-Byte unsigned integer | {py:class}secsgem.secs.items.ItemU8 | U8 |
1-Byte unsigned integer | {py:class}secsgem.secs.items.ItemU1 | U1 |
2-Byte unsigned integer | {py:class}secsgem.secs.items.ItemU2 | U2 |
4-Byte unsigned integer | {py:class}secsgem.secs.items.ItemU4 | U4 |
3.2.2、 Creating items
3.2.1.1、 From value
可以通过 Item
类的 from_value
方法来创建项目对象。
>>> import secsgem.secs.items
>>>
>>> secsgem.secs.items.Item.from_value(10)
< U1 10 >
>>>
>>> secsgem.secs.items.Item.from_value([["Hallo", b"Welt"], 10, 2.5])
< L [3]< L [2]< A "Hallo">< B 0x57 0x65 0x6c 0x74 >>< U1 10 >< F4 2.5 >
>
这将自动为传递的 Python 值选择相应的项类型。
但可以通过传入一个项对象而非 Python 值来对其进行覆盖。
>>> secsgem.secs.items.Item.from_value([secsgem.secs.items.ItemU4(10)])
< L [1]< U4 10 >
>
特定的项类型可以通过在构造函数中使用 Python 值来进行初始化。
>>> secsgem.secs.items.ItemI4(10)
< I4 10 >
>>>
>>> secsgem.secs.items.ItemI4([10, 20])
< I4 10 20 >
>>>
>>> secsgem.secs.items.ItemL([10, 20])
< L [2]< U1 10 >< U1 20 >
>
>>>
>>> secsgem.secs.items.ItemI4("text")
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/secsgem/secs/item.py", line 132, in __init__self._value = self.validate_value(value)^^^^^^^^^^^^^^^^^^^^^^^^^^File "/secsgem/secs/item_number.py", line 77, in validate_valueraise self._invalid_type_exception(value)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "/secsgem/secs/item.py", line 508, in _invalid_type_exceptionraise TypeError(f"Invalid value '{data}' of '{data.__class__.__name__}' for '{cls.__name__}'")
TypeError: Invalid value 'text' of 'str' for 'ItemI4'
3.2.1.2、From SML text
还可以通过 SML 字符串创建一个项目。
>>> secsgem.secs.items.Item.from_sml("< U4 10 >")
< U4 10 >
>>>
>>> type(secsgem.secs.items.Item.from_sml("< U4 10 >"))
<class 'secsgem.secs.item_number.ItemU4'>
>>>
>>> secsgem.secs.items.Item.from_sml("""
< L [3]< L [2]< A "Hallo">< B 0x57 0x65 0x6c 0x74 >>< U4 10 >< F4 2.5 >
>
""")
< L [3]< L [2]< A "Hallo">< B 0x57 0x65 0x6c 0x74 >>< U4 10 >< F4 2.5 >
>
3.2.1.3、 From protocol text
通过 SECS-I/HSMS 接收的二进制数据可以使用 decode 方法进行解码。
>>> secsgem.secs.items.Item.decode(b'\x01\x03\x01\x02A\x05Hallo!\x04Welt\xa5\x01\n\x91\x04@ \x00\x00')
< L [3]< L [2]< A "Hallo">< B 0x57 0x65 0x6c 0x74 >>< U1 10 >< F4 2.5 >
>
3.2.3、 Getting data
3.2.3.1、 Python value
通过使用 value
属性,可以获取一个项目的 Python 值。
>>> item = secsgem.secs.items.Item.from_value([["Hallo", b"Welt"], 10, 2.5])
>>> item.value
[['Hallo', b'Welt'], 10, 2.5]
3.2.3.2、 SML text
通过使用 sml
属性可以生成一个 SML 字符串。
>>> item = secsgem.secs.items.Item.from_value([["Hallo", b"Welt"], 10, 2.5])
>>> item.sml
'< L [3]\n < L [2]\n < A "Hallo">\n < B 0x57 0x65 0x6c 0x74 >\n >\n < U1 10 >\n < F4 2.5 >\n>'
该条目对象的 Python 输出还会打印出 SML 文本。
>>> item = secsgem.secs.items.Item.from_value([["Hallo", b"Welt"], 10, 2.5])
>>> item
< L [3]< L [2]< A "Hallo">< B 0x57 0x65 0x6c 0x74 >>< U1 10 >< F4 2.5 >
>
3.2.1.3、 Protocol text
SECS-I/HSMS 协议文本可以通过 encode
方法生成。
>>> item = secsgem.secs.items.Item.from_value([["Hallo", b"Welt"], 10, 2.5])
>>> item.encode()
b'\x01\x03\x01\x02A\x05Hallo!\x04Welt\xa5\x01\n\x91\x04@ \x00\x00'
3.3、Functions
该函数是从 {py:class}secsgem.secs.functionbase.SecsStreamFunction
继承而来的。
示例:
class SecsS02F33(SecsStreamFunction):_stream = 2_function = 33_data_format = [DATAID,[[RPTID,[VID]]]]_to_host = False_to_equipment = True_has_reply = True_is_reply_required = True_is_multi_block = True
函数的数据可以通过与变量相同的功能进行读取和操作。
{py:func}secsgem.secs.functionbase.SecsStreamFunction.set
、{py:func}secsgem.secs.functionbase.SecsStreamFunction.get
、{py:func}secsgem.secs.functionbase.SecsStreamFunction.append
、索引运算符以及对象属性。
这些对象还可以自行编码和解码。
Usage:
>>> f=secsgem.secs.functions.SecsS02F33()
>>> f.DATAID=10
>>> f.DATA.append({"RPTID": 5, "VID": ["Hello", "Hallo"]})
>>> f.DATA.append({"RPTID": 6, "VID": ["1", "2"]})
>>> f
S2F33 W
<L [2]
<U1 10 >
<L [2]
<L [2]<U1 5 ><L [2]<A "Hello"><A "Hallo">>
>
<L [2]<U1 6 ><L [2]<A "1"><A "2">>
>
>
> .
>>> f.DATA[1].VID[0]="Goodbye"
>>> f.DATA[1].VID[1]="Auf Wiedersehen"
>>> f
S2F33 W
<L [2]
<U1 10 >
<L [2]
<L [2]<U1 5 ><L [2]<A "Hello"><A "Hallo">>
>
<L [2]<U1 6 ><L [2]<A "Goodbye"><A "Auf Wiedersehen">>
>
>
> .
>>> secsgem.common.format_hex(f.encode())
'01:02:a5:01:0a:01:02:01:02:a5:01:05:01:02:41:05:48:65:6c:6c:6f:41:05:48:61:6c:6c:6f:01:02:a5:01:06:01:02:41:07:47:6f:6f:64:62:79:65:41:0f:41:75:66:20:57:69:65:64:65:72:73:65:68:65:6e'
编码后的数据可作为数据字符串与 {py:class}secsgem.hsms.HsmsMessage
中的 {py:class}secsgem.hsms.HsmsStreamFunctionHeader
一同使用。
请参阅 {doc}/hsms/messages
。
3.4、Handler
SecsHandler 具有为特定流和函数添加回调的功能。
>>> def s01f13_handler(connection, packet):
... print "S1F13 received"
...
>>> def on_connect(event, data):
... print "Connected"
...
>>> settings = secsgem.hsms.Settings(address="127.0.0.1", port=5000, connect_mode=secsgem.hsms.HsmsConnectMode.PASSIVE, device_type=secsgem.common.DeviceType.HOST)
>>> client = secsgem.secs.SecsHandler(settings)
>>> client.events.connected += on_connect
>>> client.register_stream_function(1, 13, s01f13_handler)
>>>
>>> client.enable()
Connected
S1F13 received
>>> client.disable()
此外,还涉及有关收集事件、服务变量和设备常量的其他功能。
4、HSMS
SEMI E37
HSMS 定义主机和设备之间通过 TCP 协议的通信。 它指定用于启动和终止连接的数据包,检查链路是否仍处于活动状态并传输实际数据。
4.1、消息快
一个 HSMS 消息 {py:class}secsgem.hsms.HsmsMessage
由一个头部 {py:class}secsgem.hsms.HsmsHeader
和一个块列表 {py:class}secsgem.hsms.HsmsBlock
组成。
它初始化时会有一个数据字段,该字段用于自动生成数据块。
这些数据块是数据字段的一部分,会进行传输。
对于 HSMS 数据而言,在一条消息中只存在一个数据块,因为该消息无需分割。
>>> secsgem.hsms.HsmsMessage(secsgem.hsms.HsmsLinktestReqHeader(2), b"")
HsmsMessage({'header': HsmsLinktestReqHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x05, system:0x00000002, require_response:False}), 'data': ''})
每个头部都有一个系统标识符,用于匹配针对某一请求的响应。
系统标识符是 headers
构造函数的第一个参数。
连接会跟踪系统标识符,可以通过调用 {py:func}
secsgem.hsms.HsmsProtocol.get_next_system_counter
函数来请求一个新的系统标识符。
HSMS 块对象可以通过调用 {py:func}secsgem.hsms.HsmsBlock.encode
函数将自身编码为一个字节数组,该字节数组可以通过 TCP 连接进行传输。
>>> message = secsgem.hsms.HsmsMessage(secsgem.hsms.HsmsLinktestReqHeader(2), b"")
>>> secsgem.common.format_hex(message.blocks[0].encode())
'00:00:00:0a:ff:ff:00:00:00:05:00:00:00:02'
反过来讲,HSMS 块对象可以通过使用 {py:func}secsgem.hsms.HsmsBlock.decode
函数对字节数组进行处理来创建。
>>> secsgem.hsms.HsmsBlock.decode(packetData)
<secsgem.hsms.message.HsmsBlock object at 0x1082c4f80>
对于所有可用的 HSMS 数据包类型,都有从 {py:class}secsgem.hsms.HsmsHeader
继承而来的类:
Type | Class | SType |
---|---|---|
Select Request | {py:class}secsgem.hsms.HsmsSelectReqHeader | 1 |
Select Response | {py:class}secsgem.hsms.HsmsSelectRspHeader | 2 |
Deselect Request | {py:class}secsgem.hsms.HsmsDeselectReqHeader | 3 |
Deselect Response | {py:class}secsgem.hsms.HsmsDeselectRspHeader | 4 |
Linktest Request | {py:class}secsgem.hsms.HsmsLinktestReqHeader | 5 |
Linktest Response | {py:class}secsgem.hsms.HsmsLinktestRspHeader | 6 |
Reject Request | {py:class}secsgem.hsms.HsmsRejectReqHeader | 7 |
Separate Request | {py:class}secsgem.hsms.HsmsSeparateReqHeader | 9 |
Data Message | {py:class}secsgem.hsms.HsmsStreamFunctionHeader | 0 |
4.1.1、选择 请求
建立 HSMS 通信
>>> secsgem.hsms.HsmsSelectReqHeader(14)
HsmsSelectReqHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x01, system:0x0000000e, require_response:False})
4.1.2、选择响应
select 请求的结果
>>> secsgem.hsms.HsmsSelectRspHeader(24)
HsmsSelectRspHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x02, system:0x00000018, require_response:False})
4.1.3、取消选择请求
在断开连接之前关闭 HSMS 通信
>>> secsgem.hsms.HsmsDeselectReqHeader(1)
HsmsDeselectReqHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x03, system:0x00000001, require_response:False})
4.1.4、取消选择响应
取消选择请求的结果
>>> secsgem.hsms.HsmsDeselectRspHeader(1)
HsmsDeselectRspHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x04, system:0x00000001, require_response:False})
4.1.5、Linktest 请求
检查 HSMS 连接链接是否正常
>>> secsgem.hsms.HsmsLinktestReqHeader(2)
HsmsLinktestReqHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x05, system:0x00000002, require_response:False})
4.1.6、Linktest 响应
linktest 请求的结果
>>> secsgem.hsms.HsmsLinktestRspHeader(10)
HsmsLinktestRspHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x06, system:0x0000000a, require_response:False})
4.1.7、拒绝请求
对不支持的 HSMS 消息的响应
>>> secsgem.hsms.HsmsRejectReqHeader(17, secsgem.hsms.HsmsSType.DESELECT_REQ, 4)
HsmsRejectReqHeader({device_id:0xffff, stream:03, function:04, p_type:0x00, s_type:0x07, system:0x00000011, require_response:False})
4.1.8、单独请求
立即终止 HSMS 连接
>>> secsgem.hsms.HsmsSeparateReqHeader(17)
HsmsSeparateReqHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x09, system:0x00000011, require_response:False})
4.1.9、数据消息
秒流和函数消息
>>> secsgem.hsms.HsmsStreamFunctionHeader(22, 1, 1, True, 100)
HsmsStreamFunctionHeader({device_id:0x0064, stream:01, function:01, p_type:0x00, s_type:0x00, system:0x00000016, require_response:True})
4.2、 协议
{py:class}secsgem.hsms.protocol.HsmsProtocol
类内置了基本的 HSMS 连接处理功能。
它会自动选择和取消选择链路,并定期执行链路测试。
它还会自动回复诸如链路测试之类的来自 HSMS 的请求。
>>> def on_connect(event, data):
... print "Connected"
...
>>> client = secsgem.hsms.HsmsProtocol("10.211.55.33", 5000, False, 0, "test")
>>> client.events.connected += on_connect
>>> client.enable()
Connected
>>> client.protocol.send_linktest_req()
HsmsMessage({'header': HsmsHeader({device_id:0xffff, stream:00, function:00, p_type:0x00, s_type:0x06, system:0x75b78c48, require_response:False}), 'data': ''})
>>> client.disable()
该处理程序具备发送请求和响应以及等待特定响应的功能。
4.2.1、 事件
可以通过使用 secsgem.common.EventHandler
类来接收事件处理程序所引发的事件。该事件处理程序会发送以下事件:
Event name | Description |
---|---|
connected | Connection was established |
communicating | Connection was selected |
disconnected | Connection was terminated |
For an example on how to use these events see the code fragment above.