24.3 HDevEngine/Python 接口

24.3.1 全局功能

虽然您可以拥有 HDevEngine/Python 的多个 Python 实例,但它们都共享相同的可变单例实现。

这主要影响配置。具体的程序和函数调用实例或多或少是相互独立的。

在这里,HALCON 会查询示例目录路径,并据此构建包含 "detect_fin" 函数的路径。然后初始化一个 HDevEngine 实例,并将函数搜索路径设置为该实例。

example_dir = ha.get_system_s('example_dir')
procedure_path = os.path.join(example_dir, 'hdevengine', 'procedures')

hdev_engine = ha.HDevEngine()
hdev_engine.set_procedure_path(procedure_path)

24.3.1.1 属性

def set_attribute(self, name: str, value: HTupleType) -> None:
def get_attribute(self, name: str) -> HTupleType:

通过这些函数可以设置特定属性。例如,默认情况下,"ignore_invalid_results" 属性设置为 true。这意味着,如果查询的变量或参数不是程序或函数先前设置的,则默认情况下会返回空区域对象或空元组。试想一下:访问一个未初始化的变量是返回默认值还是引发异常?

hdev_engine = ha.HDevEngine()
assert hdev_engine.get_attribute('ignore_invalid_results') == 1

hdev_engine.set_attribute('ignore_invalid_results', 0)
assert hdev_engine.get_attribute('ignore_invalid_results') == 0

对于 HDevEngine 属性,0 表示 false,1 表示 true。

有关可用属性的列表,参见 "类概述" 一节

24.3.1.2 调试服务器

HDevEngine

def start_debug_server(self) -> None:
def stop_debug_server(self) -> None:

通过这些函数,可以启动和停止调试引擎执行所需的服务器。您可以通过属性 "debug_port" 控制端口。

24.3.1.3 函数搜索路径

HDevEngine

def set_procedure_path(self, path: str) -> None:
def add_procedure_path(self, path: str) -> None:

通过这些函数,您可以设置和扩展在尝试加载函数时搜索的路径列表。

24.3.1.4 全局元数据

HDevEngine

def get_procedure_names(self) -> List[str]:
def get_loaded_procedure_names(self) -> List[str]:
def get_global_control_var_names(self) -> List[str]:
def get_global_iconic_var_dimension(self, name: str) -> int:
def get_global_control_var_dimension(self, name: str) -> int:

通过这些函数可以查询全局元数据,例如加载了哪些函数、全局变量的向量维数等。

24.3.1.5 读写全局变量

HDevEngine

def set_global_iconic_var(self, name: str, value: HObject) -> None:
def set_global_iconic_vector_var(self, name: str, value: IconicVectorType) -> None:
def set_global_control_var(self, name: str, value: HTupleType) -> None:
def set_global_tuple_vector_var(self, name: str, value: TupleVectorType) -> None:
def get_global_iconic_var(self  , name: str) -> HObject:
def get_global_iconic_vector_var(self, name: str) -> TupleVectorType:
def get_global_control_var(self, name: str) -> HTupleType:
def get_global_tuple_vector_var(self, name: str) -> TupleVectorType:

通过这些函数可以读写全局变量,包括图标变量和控制变量。

24.3.2 调用 HDevelop 函数

从 Python 调用 HDevelop 函数的一般概念如下:

  1. 通过从文件系统加载函数来初始化 HDevProcedure 实例。
  2. HDevProcedure 实例创建一个特定的 HDevProcedureCall 实例。
  3. 设置图标和控制输入参数。
  4. 调用 execute
  5. 读取输出参数。

procedure = ha.HDevProcedure.load_external('detect_fin')
proc_call = ha.HDevProcedureCall(procedure)

proc_call.set_input_iconic_param_by_name('Image', img)
proc_call.execute()

fin_area = proc_call.get_output_control_param_by_name('FinArea')[0]
print(f'Fin Area: {fin_area}')

24.3.2.1 加载函数

HDevProcedure

@staticmethod
def load_external(name: str) -> 'HDevProcedure':

@staticmethod
def load_local(
    program: Union[HDevProgram, str],
    name: str
) -> 'HDevProcedure':

函数通过这些静态方法加载。只能使用这些函数来初始化 HDevProcedure 实例。

hdev_engine.set_procedure_path(proc_dir)

external_proc = ha.HDevProcedure.load_external('detect_fin')

program = ha.HDevProgram(os.path.join(proc_dir, 'program.hdev'))
local_proc_program = ha.HDevProcedure.load_local(program, 'count_nuts')
local_proc_name = ha.HDevProcedure.load_local('program.hdev', 'count_nuts')

虽然 Python 允许在实例上调用静态方法,例如:ha.HDevProcedure().load_external,但不建议这样做,因为这会不必要地浪费资源。

24.3.2.2 卸载函数

HDevEngine

def unload_procedure(self, name: str) -> None:
def unload_all_procedures(self) -> None:

函数加载后,这些函数可以让您再次卸载函数。例如,这对释放未使用的内存非常有用。

24.3.2.3 函数元数据

通过这些只读成员变量访问函数元数据:

HDevProcedure

- name : str
- short_description : str
- loaded : bool
- input_iconic_param_names : List[str]
- output_iconic_param_names : List[str]
- input_control_param_names : List[str]
- output_control_param_names : List[str]
- input_iconic_param_dimensions : List[int]
- output_iconic_param_dimensions : List[int]
- input_control_param_dimensions : List[int]
- output_control_param_dimensions : List[int]

HDevProcedure

def get_used_procedure_names(self) -> List[str]:
def get_info(self, slot: str) -> HTupleType:
def get_param_info(self, name: str, slot: str) -> HTupleType:
def get_input_iconic_param_info(self, idx: int, slot: str) -> HTupleType:
def get_output_iconic_param_info(self, idx: int, slot: str) -> HTupleType:
def get_input_control_param_info(self, idx: int, slot: str) -> HTupleType:
def get_output_control_param_info(self, idx: int, slot: str) -> HTupleType:
def query_slots(self) -> List[str]:
def query_param_slots(self) -> List[str]:

使用这些函数可以查询函数元数据。

24.3.2.4 即时编译

HDevProcedure

def compile_used_procedures(self) -> bool:

编译程序中使用的所有函数,这些函数可以用即时(JIT:Just-In-Time)编译器编译。无法编译的函数将由 HDevEngine 解释器以常规方式调用。要检查哪些函数无法编译以及原因是什么,请启动 HDevelop 并检查编译状态。

此函数返回所有使用的函数是否都经过 JIT 即时编译。

24.3.2.5 函数调用初始化

proc_call = ha.HDevProcedureCall(procedure)

初始化 HDevProcedureCall 实例的唯一方法是使用已加载的 HDevProcedure 实例。

24.3.2.6 设置输入参数

HDevProcedureCall

def set_input_control_param_by_index(self, idx: int, value: HTupleType) -> None:
def set_input_tuple_vector_by_index(self, idx: int, value: TupleVectorType) -> None:
def set_input_control_param_by_name(self, name: str, value: HTupleType) -> None:
def set_input_tuple_vector_by_name(self, name: str, value: TupleVectorType) -> None:
def set_input_iconic_param_by_index(self, idx: int, value: HObject) -> None:
def set_input_iconic_vector_by_index(self, idx: int, value: IconicVectorType) -> None:
def set_input_iconic_param_by_name(self, name: str, value: HObject) -> None:
def set_input_iconic_vector_by_name(self, name: str, value: IconicVectorType) -> None:

这些函数允许您设置输入参数。我们建议您在每次执行函数调用时都重新设置所有输入参数。这有助于使您的代码在面对错误条件时更加稳健。

这里的索引从 1 开始,而不是从 0 开始。

24.3.2.7 执行

HDevProcedureCall

def execute(self) -> None:

当您设置好执行函数的所有输入参数后,请使用此功能。

24.3.2.8 读取输出参数

HDevProcedureCall

def get_output_control_param_by_index(self, idx: int) -> HTupleType:
def get_output_tuple_vector_by_index(self, idx: int) -> TupleVectorType:
def get_output_control_param_by_name(self, name: str) -> HTupleType:
def get_output_tuple_vector_by_name(self, name: str) -> TupleVectorType:
def get_output_iconic_param_by_index(self, idx: int) -> HObject:
def get_output_iconic_vector_by_index(self, idx: int) -> IconicVectorType:
def get_output_iconic_param_by_name(self, name: str) -> HObject:
def get_output_iconic_vector_by_name(self, name: str) -> IconicVectorType:

调用 execute 成功后,使用这些函数读取输出参数。

这里的索引从 1 开始,而不是从 0 开始。

24.3.2.9 等待调试器

HDevProcedureCall

def wait_for_debug_connection(self) -> None:

将此函数与 start_debug_server 结合使用,可调试程序的执行。

24.3.2.10 重置

HDevProcedureCall

def reset(self) -> None:

这主要适用于想从另一个线程中止执行,或在某些实例仍存活时释放本地资源的情况。

24.3.3 调用 HDevelop 程序

从 Python 调用 HDevelop 程序的一般概念如下:

  1. 通过从文件系统加载程序来初始化 HDevProgram 实例。
  2. HDevProgram 实例创建一个特定的 HDevProgramCall 实例。
  3. 调用 execute
  4. 读取变量

example_dir = ha.get_system_s('example_dir')
program_path = os.path.join(
    example_dir,
    'hdevelop',
    'Transformations',
    'Poses'
)

program = ha.HDevProgram(os.path.join(program_path, 'pose_compose.hdev'))
program_call = ha.HDevProgramCall(program)
program_call.execute()

pose = program_call.get_control_var_by_name('PoseComposeAlternative')
rounded_pose = [round(x, 8) for x in pose]

assert rounded_pose == [0.3, -0.0498838, 0.33986422, 77.0, 90.0, 0.0, 0]

24.3.3.1 加载程序

program = ha.HDevProgram(os.path.join(program_path, 'pose_compose.hdev'))

程序是通过 HDevProgram 初始化器加载的,该初始化器需要操作系统格式的 HDevelop 程序完整路径,包括文件名。

24.3.3.2 程序元数据

通过这些只读成员变量访问程序元数据:

HDevProgram

- name : str
- loaded : bool
- inconic_var_names : List[str]
- control_var_names : List[str]
- inconic_var_dimensions : List[int]
- control_var_dimensions : List[int]

HDevProgram

def get_used_procedure_names(self) -> List[str]:
def get_local_procedure_names(self) -> List[str]:

使用这些函数来查询程序元数据。

24.3.3.3 即时编译

HDevProgram

def compile_used_procedures(self) -> bool:

编译程序中使用的所有函数,这些函数可以用即时编译器编译。无法编译的函数将由 HDevEngine 解释器以常规方式调用。要检查哪些函数无法编译以及原因是什么,请启动 HDevelop 并检查编译状态。

返回是否所有使用的函数都经过即时编译。

24.3.3.4 程序调用初始化

program_call = ha.HDevProgramCall(program)

初始化 HDevProgramCall 实例的唯一方法是使用有效的 HDevProgram 实例。

24.3.3.5 执行

HDevProgramCall

def execute(self) -> None:

使用此功能执行程序。

24.3.3.6 读取变量

HDevProgramCall

def get_control_var_by_index(self, idx: int) -> HTupleType:
def get_tuple_vector_var_by_index(self, idx: int) -> TupleVectorType:
def get_control_var_by_name(self, name: str) -> HTupleType:
def get_tuple_vector_var_by_name(self, name: str) -> TupleVectorType:
def get_iconic_var_by_index(self, idx: int) -> HObject:
def get_iconic_vector_var_by_index(self, idx: int) -> IconicVectorType:
def get_iconic_var_by_name(self, name: str) -> HObject:
def get_iconic_vector_var_by_name(self, name: str) -> IconicVectorType:

execute 调用成功结束后,使用这些函数读取变量。

这里的索引从 1 开始,而不是从 0 开始。

24.3.3.7 等待调试器

HDevProgramCall

def wait_for_debug_connection(self) -> None:

将此函数与 start_debug_server 结合使用,可调试程序的执行。

24.3.3.8 重置

HDevProgramCall

def reset(self) -> None:

这主要适用于想从另一个线程中止执行,或在某些实例仍存活时释放本地资源的情况。

24.3.4 Dev 算子

在HDevelop中,dev_* 算子的使用非常方便。在应用程序中嵌入HDevelop程序或函数时,潜在的dev_* 算子调用没有直接的映射。例如,可能需要将 dev_* 算子调用映射到应用程序中的可视化。因此,HDevEngine/Python 提供了一个基类,您可以继承该基类,并用适合您应用程序的逻辑覆盖它。

例如,这个简单的 HDevelop 程序可以读取图像,然后在开发窗口中显示 PCB 图像。

read_image(Image, 'pcb')
dev_display(Image)

默认情况下,该程序在使用 HDevEngine/Python 时不会打开窗口。不过,您可以指定调用 dev_display 时应该发生的逻辑。

例如,这段 Python 代码注册了 dev_display 的一个非常基本的实现。

class DevImpl(ha.HDevOperatorBase):
    @staticmethod
    def dev_display(object):
        print(object)

hdev_engine = ha.HDevEngine()
hdev_engine.set_hdev_operator_impl(DevImpl())

要了解哪些 dev_* 算子可用,以及每个算子需要哪些签名,请查看 HDevOperatorBase 的实现。

请注意,一次只能注册一个实现。这适用于所有当前和未来的 HDevEngine 实例,直至发生更改。

默认情况下不会注册任何实现。一旦注册了实现,就可以通过调用:

hdev_engine.unset_hdev_operator_impl()

请注意,调用 register_dev_operators 可能会对 Python 变量的生命周期产生令人惊讶的影响。详情请参见该函数的文档。

如果调用未被 HDevOperatorBase 实现覆盖的每个函数,都会引发异常。

在执行 dev_* 算子的 Python 代码中引发异常是安全的。不过,只会引发一个通用的 HDevEngineError,原始异常回溯将被记录到 stderr。这是由于技术上的限制。

24.3.5 HALCON 向量

HDevEngine/Python 将 HALCON 向量映射为嵌套的 Python 列表。有两种不同的、互不兼容的向量类型:tuple_vectoriconic_vector。以下示例演示了在 HDevEngine/Python 中用于读写 HALCON 向量的 Python 表示法:

empty_tuple_vector = ha.HDevEmptyVector(dimension=1)
1d_tuple_vector = [[23, 'a'], ['ec', 2.5, 77]]
2d_tuple_vector = [[[8], ['b', 'c']], [], [[]]]

empty_iconic_vector = ha.HDevEmptyVector(dimension=1)
1d_iconic_vector = [img1, img2, img3]
2d_iconic_vector = [[img1, img2], [img1, img3, img4], []]

对于 tuple_vector,最内层列表被视为 HALCON 元组。

向量的所有元素必须具有相同的维度:

[ [234], [2, 5] ] # ok
[ 234, [2, 5] ] # not ok

24.3.6 多线程

由于 HDevEngine 是一个可变的单例,因此某些功能会影响整个应用程序:

  1. 全局功能: 参见 "全局功能" 一节
  2. 加载程序和函数。
  3. 注册 dev_* 算子实现。

线程应用程序在处理此类共享可变状态时必须小心谨慎。例如,如果多个函数需要不同的路径设置,而应用程序内部又没有进行适当的同步,那么就不能安全地使用全局函数路径并行加载多个函数。不采取必要的谨慎措施将导致未定义的行为,包括崩溃和更严重的情况。

为避免上述问题,我们建议在程序开始时配置 HDevEngine 以及加载函数和程序,然后再启动其他应用程序线程。

HDevEngineHDevProcedureHDevProgram 不同,调用实例 HDevProcedureCallHDevProgramCall 相互独立。