半导体设备通信标准—secsgem v0.3.0版本使用说明文档(1)之概述、如何安装及使用方法
文章目录
- 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、测试
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