当前位置: 首页 > news >正文

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_communicatingConnection is setup–连接已设置
collection_event_receivedCollection event was received–收到 Collection 事件
terminal_receivedTerminal 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 ModelsYes ✓No
Equipment Processing StatesNoNo
Host-Initiated S1,F13/F14 ScenarioYes ✓Yes ✓
Event NotificationYes ✓Yes ✓
On-Line IdentificationYes ✓Yes ✓
Error MessagesYes ✓Yes ✓
DocumentationYes ✓No
Control (Operator Initiated)Yes ✓No
其他功能实现符合 GEM 标准t
Establish CommunicationsYes ✓Yes ✓
Dynamic Event Report ConfigurationYes ✓No
Variable Data CollectionYes ✓Yes ✓
Trace Data CollectionNoNo
Status Data CollectionYes ✓Yes ✓
Alarm ManagementYes ✓No
Remote ControlYes ✓Yes ✓
Equipment ConstantsYes ✓No
Process RecipeManagementNoNo
Material MovementNoNo
Equipment Terminal ServicesYes ✓Yes ✓
ClockNoNo
Limits MonitoringNoNo
SpoolingNoNo
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 TypeClassCode
List{py:class}secsgem.secs.variables.ArrayL
List{py:class}secsgem.secs.variables.ListL
Binary{py:class}secsgem.secs.variables.BinaryB
Boolean{py:class}secsgem.secs.variables.BooleanTF
ASCII{py:class}secsgem.secs.variables.StringA
8-Byte integer{py:class}secsgem.secs.variables.I8I8
1-Byte integer{py:class}secsgem.secs.variables.I1I1
2-Byte integer{py:class}secsgem.secs.variables.I2I2
4-Byte integer{py:class}secsgem.secs.variables.I4I4
8-Byte float{py:class}secsgem.secs.variables.F8F8
4-Byte float{py:class}secsgem.secs.variables.F4F8
8-Byte unsigned integer{py:class}secsgem.secs.variables.U8U8
1-Byte unsigned integer{py:class}secsgem.secs.variables.U1U1
2-Byte unsigned integer{py:class}secsgem.secs.variables.U2U2
4-Byte unsigned integer{py:class}secsgem.secs.variables.U4U4

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

TypeClassCode
List{py:class}secsgem.secs.items.ItemLL
Binary{py:class}secsgem.secs.items.ItemBB
Boolean{py:class}secsgem.secs.items.ItemBOOLEANTF
ASCII{py:class}secsgem.secs.items.ItemAA
JIS-8{py:class}secsgem.secs.items.ItemJJ
8-Byte integer{py:class}secsgem.secs.items.ItemI8I8
1-Byte integer{py:class}secsgem.secs.items.ItemI1I1
2-Byte integer{py:class}secsgem.secs.items.ItemI2I2
4-Byte integer{py:class}secsgem.secs.items.ItemI4I4
8-Byte float{py:class}secsgem.secs.items.ItemF8F8
4-Byte float{py:class}secsgem.secs.items.ItemF4F8
8-Byte unsigned integer{py:class}secsgem.secs.items.ItemU8U8
1-Byte unsigned integer{py:class}secsgem.secs.items.ItemU1U1
2-Byte unsigned integer{py:class}secsgem.secs.items.ItemU2U2
4-Byte unsigned integer{py:class}secsgem.secs.items.ItemU4U4

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 继承而来的类:

TypeClassSType
Select Request{py:class}secsgem.hsms.HsmsSelectReqHeader1
Select Response{py:class}secsgem.hsms.HsmsSelectRspHeader2
Deselect Request{py:class}secsgem.hsms.HsmsDeselectReqHeader3
Deselect Response{py:class}secsgem.hsms.HsmsDeselectRspHeader4
Linktest Request{py:class}secsgem.hsms.HsmsLinktestReqHeader5
Linktest Response{py:class}secsgem.hsms.HsmsLinktestRspHeader6
Reject Request{py:class}secsgem.hsms.HsmsRejectReqHeader7
Separate Request{py:class}secsgem.hsms.HsmsSeparateReqHeader9
Data Message{py:class}secsgem.hsms.HsmsStreamFunctionHeader0

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 nameDescription
connectedConnection was established
communicatingConnection was selected
disconnectedConnection was terminated

For an example on how to use these events see the code fragment above.

相关文章:

  • 探索 C 与 Java/Kotlin 的语言差异:从指针到高阶函数
  • 深入定制 QSlider——实现精准点击跳转与拖拽区分
  • 用Python手搓一个简单的饭店管理系统(上篇)
  • 依赖注入(DI)与自动装配的深度分析:优势、局限与实践考量
  • 智慧城市:如同为城市装上智能大脑,开启智慧生活
  • 用 Depcheck 去除Vue项目没有用到的依赖
  • GitHub action中的 jq 是什么? 常用方法有哪些
  • 计算机保研机试准备——C++算法题
  • 【cmake-笔记】
  • CANDENCE 原理图元件有多个相同名称引脚报错
  • 2.区间dp
  • QML TableView:基础用法和自定义样式实现
  • ROW_NUMBER 函数
  • 嵌入式开发_电能计量芯片HLW8110与HLW8112
  • Mysql-视图和存储过程
  • 在 K8s 上构建和部署容器化应用程序(Building and Deploying Containerized Applications on k8s)
  • THCON 2025
  • 向量数据库Qdrant 安装 不使用docker
  • Windows进行磁盘分区/扩容
  • vue3项目如何在index.html中根据环境(开发,生产)动态切换js src路径
  • “明制美学”的舞台呈现,陆川导演首部舞剧《天工开物》
  • 北京理工大学解除宫某聘用关系,该教师被指涉嫌骚扰猥亵学生
  • 人民日报:当阅读成为“刚需”
  • 武汉一超高层住宅顶楼违建成“不死小强”,相关部门回应
  • 小伙称被骗婚骗惨了:存款金条怀孕全是假的,岳父岳母找人演
  • 从绿绒蒿到植物远征计划:植物学知识的生产与权力(下)