AddOn 插件开发技术文档

概述

本文档旨在为开发者提供节卡(JAKA)机器人 AddOn 插件的开发指导,包括插件的基本结构、开发流程、API 使用说明以及常见问题解决方案。

适用对象

  • 节卡机器人二次开发工程师

  • 第三方插件开发者

  • 系统集成工程师

插件简介

节卡 AddOn 是一种可扩展的功能模块,允许开发者基于 AddOn API 开发自定义功能,并集成到 JAKA 机器人的控制系统中。插件可以实现诸如任务调度、数据采集、外部设备通信等功能。

开发环境准备

硬件要求

  • 支持运行 JAKA 控制系统的工业计算机

  • 可选:连接外部传感器或执行器的硬件接口(如串口、网口等)

软件依赖

  • 操作系统:Debian 9(节卡机器人 V1.7.2.x 控制版本)/ Debian 12(节卡机器人 V3.3.x 控制器版本)

  • 编程语言:Python 2.7(节卡机器人 V1.7.2.x 控制版本)/ Python 3.11(节卡机器人 V3.3.x 控制器版本)/ C++(根据项目需求选择)

  • IDE 工具:Visual Studio Code

  • 开发工具:jaka-addon-plugin.vsix

安装步骤

  1. 配置 Python/C++ 开发环境

  2. 打开Visual Studio Code,在扩展中栏的更多选择中选择从vsix安装...,安装jaka-addon-plugin.vsix扩展。

插件开发流程

插件目录结构

一个标准的 AddOn 插件应包含以下目录和文件:

::

jaka_addon/
├── client/                 # AddOn前端网页文件(可选)
├── config/                 # 配置文件目录 (名称和数量由开发者自由定义)
├── src                     # 功能模块(C++/Python)(名称和数量由开发者自由定义)
├── requirement/            # python第三方库安装包
├── docs/                   # 文档目录
├── main.py                 # 主程序入口(Python)
├── **_config.ini           # AddOn项目配置文件
├── requirement.list        # 依赖库列表
└── README.md               # 插件说明文档

核心组件说明

配置文件(**_config.ini)

用于存储插件启动所需的参数,例如 AddOn 插件类型、端口号、版本、插件描述等。

::

[AddOnInfo]
convention = 2.0
name = JAKA_AddOn
description = "A JAKA AddOn plugin"
version = 1.0
type = 3
portal = 10007
url = http://localhost/JAKA_AddOn/client
languagetype = python
service = Main.py
serviceenabled = 1

[Requirement]
dir = requirement
list = requirement.list
  • AddOnInfo:插件信息

    • convention:插件规范版本号,推荐使用2.0或3.0

    • name:插件名称

    • description:插件描述

    • version:插件版本号

    • type:插件类型,可选值有1、2、3。 1表示插件是后台进程类型插件,2表示插件是带前端网页的服务插件,3表示插件带前端服务又包含自定义指令。

    • portal:插件服务端口号,该端口号有JAKA控制器管理分配,主要用于处理前端访问和自定义指令请求

    • url:插件服务端URL,用于前端访问。格式为 http://localhost/{AddOn_name}/{前端网页路径}

    • languagetype:插件语言类型,取决于项目实际开发语言,可选值有python、 python3 、node-red。

    • service:插件服务入口文件,该文件为插件的启动入口,通常为main.py。

    • serviceenabled:插件服务是否自启动,1表示开机自启动,0表示手动启动。

    • Requirement:插件依赖库列表(节卡机器人V3.3.x控制器版本)

      • dir:插件依赖库目录,该目录为插件依赖的库文件目录,通常为requirement。

      • list:插件依赖库列表,该列表为插件依赖的库文件列表,通常为requirement.list。一般由pip freeze命令生成。

入口文件 (main.py)

负责初始化插件逻辑并与 JAKA 系统交互。 示例代码如下:

import argparse
import shutil

shutil.copy("../grpc_status/libs/jkzuc3.so","./jkzuc3.so")
import jkzuc3 # noqa: E402

def get_token():
    # 获取token
    token = ""
    parse = argparse.ArgumentParser()
    parse.add_argument("-T", "--token", help="token")
    args = parse.parse_args()
    if args.token:
        token = args.token
    return token

def main():
    RobotControl = jkzuc3.command(token=get_token()) # 创建机器人控制对象
    RobotStatus = jkzuc3.stat() # 创建机器人状态对象

    RobotStatus.poll() # 刷新机器人状态
    ...

if __name__ == "__main__":
    main()

jkzuc3 是JAKA提供的与JAKA控制器交互的接口, 通常默认安装在AddOn部署目录中,在启动时主动将jkzuc3.so复制到AddOn插件目录中即可使用。在启动插件时,JAKA控制器中的AddOn Manager会注入一个token。在传教机器人控制器对象时,需要传入token,否则将被视为非法登录。token的生命周期与插件的生命周期一致,插件退出时,token将自动失效。

使用jaka-addon-plugin快速开始

  1. 创建AddOn插件的开发目录

  2. Visual Studio Code打开此开发目录

  3. 通过 Ctrl+Shift+P 打开命令面板,输入以下命令:

  4. 输入命令 addon-plugin:new addon 创建AddOn项目 ##

  5. 根据提示,按照要求填写插件信息。

  6. jaka-addon-plugin 会生成一个插件项目的基本配置。

jkzuc3 API 使用说明

本节描述了JAKA控制器提供的API接口,以及如何使用这些接口。 示例版本:grpc_status v1.5.3

数据结构

# 扩展IO基本信息配置
class ExtioConfig:
    submod_name:str   # 扩展IO名称
    submod:int        # 扩展IO在控制器中的唯一标识

# 外部轴状态
class ExtAxisStatus:
    is_powered:bool   # 是否已上电
    is_powering:bool  # 是否正在上电`
    is_enabled:bool   # 是否已使能
    is_enabling:bool  # 是否正在使能
    is_inpos:bool     # 是否运动已到位
    is_on_limit:bool  # 是否已到达限位
    pos_cmd:float     # 轴指令位置
    pos_fdb:float     # 轴反馈位置
    x:float           # 轴位置X
    y:float           # 轴位置Y
    z:float           # 轴位置Z
    a:float           # 轴位置A
    b:float           # 轴位置B
    c:float           # 轴位置C

# 外部轴组状态
class ExtGroupStatus:
    group_id:int      # 轴组ID
    is_powered:bool   # 是否已上电
    is_enabled:bool   # 是否已使能

机器人控制类(jkzuc3.command)

  • 创建机器人控制对象

import jkzuc3
RobotControl = jkzuc3.command(hostname="host.docker.internal",token="")
  • 关机

jkzuc3.command.shutdown() -> int:...
jkzuc3.command.set_digital_output(type:int, index:int, value:bool ,submod=0)->int:...
jkzuc3.command.set_analog_output(type:int, index:int, value:float ,submod=0)->int:...
  • 获取扩展IO配置列表

jkzuc3.command.get_extio_config()->list[ExtioConfig]:...
  • 运行程序

    • program_name:程序名称

jkzuc3.command.program_run(program_name:str)->int:...
  • 暂停程序

jkzuc3.command.program_pause()->int:...
  • 恢复程序

jkzuc3.command.program_resume()->int:...
  • 停止程序

jkzuc3.command.program_abort()->int:...
  • 设置程序运行倍率

    • rapid_rate:倍率,取值范围:0[STRIKEOUT:1,对应比例关系:0%]100% ##

jkzuc3.command.set_rapidrate(rapid_rate:float)->int:...
  • 设置手自动模式

    • mode:模式,0-手动模式,1-自动模式

jkzuc3.command.set_automanualmode(mode:int)->int:...
  • 运动学正解

jkzuc3.command.kine_forward(joint_pos:list[float])->list[float]:...
  • 运动学逆解

jkzuc3.command.kine_inverse(ref_pos:list[float],cartesian_pose:list[float])->list[float]:...
  • 示教运动接口

    • aj_num:运动轴索引号,空间运动对应为x,y,z,a,b,c,关节运动对应的1~6关节

    • move_mode:运动模式,0-绝对运动,1-连续运动,2-相对运动

    • coord_type:运动坐标系类型,0-关节坐标系,1-用户坐标系,2-工具坐标系

    • vel_cmd:运动速度

    • pos_cmd:运动位置

jog运动需持续下发以保证接口存活,否则控制器将认为指令中断而停止运动。下发周期推荐10ms。

jkzuc3.command.jog(aj_num:int,move_mode:int,coord_type:int,vel_cmd:float,pos_cmd:float)->int:...
  • 关节运动到目标位置

jkzuc3.command.joint_move(joint_pos:list[float],speed:float)->int:...
  • 直线运动到目标位置

jkzuc3.command.linear_move(end_pos:list[float],speed:float)->int:...
  • 退出碰撞保护模式

    • intid:内部机械臂ID,用于多臂场景控制,单臂场景下 intid=0 jkzuc3.command.exit_protect(intid:int)->int:...

  • 切换工具坐标系ID

jkzuc3.command.set_tool_id(intid:int)->int:...
  • 设置工具坐标系参数

jkzuc3.command.set_multi_tool_offsets(tool_offsets:list[float],method:int,id:int,name:str)->int:...
  • 获取当前工具坐标系参数

jkzuc3.command.get_current_tool_offsets(self)->list[float]:...
  • 切换用户坐标系ID

jkzuc3.command.set_user_id(intid:int)->int:...
  • 设置用户坐标系参数

jkzuc3.command.set_multi_user_offsets(user_offsets:list[float],method:int,id:int,name:str)->int:...
  • 获取当前用户坐标系参数

jkzuc3.command.get_current_user_offsets(self)->list[float]:...
  • 外部轴使能

    • ext_id:外部轴ID, 默认为0

jkzuc3.command.ExtEnable(ext_id:int=0) -> int:...
  • 外部轴下使能

    • ext_id:外部轴ID, 默认为0

jkzuc3.command.ExtDisable(ext_id:int=0) -> int:...
  • 外部轴 jog

    • ext_id:外部轴ID, 默认为0

    • vel_scale: jog速度比例,取值范围: 0[STRIKEOUT:10,对应比例关系:0%]1000% ##

    • is_abs: jog模式,0-相对 jog,1-绝对

    • step: jog步进,取值范围:0.1~1000

jkzuc3.command.ExtJog(ext_id:int = 0,vel_scale:float=2,is_abs:int=1,step:float=1) -> int:...

机器人状态对象(jkzuc3.stat)

class stat:
    joint_position: list[float]       # 关节位置
    position:list[float]              # 笛卡尔位置
    errtype:int                       # 错误类型
    errcode:int                       # 错误码
    powered_on:int                    # 是否已上电
    enabled:int                       # 是否已使能
    protective_stop:int               # 是否处于保护停止
    emergency_stop:int                # 是否处于急停
    interp_state:int                  # 插补状态
    inpos:int                         # 是否已到位
    rapidrate:float                   # 运行倍率
    current_tool_id:int               # 当前工具ID
    current_user_id:int               # 当前用户ID
    percentage_mode_level:int         # 运行模式等级
    extio_mode:int                    # 外部IO模式
    installation_angle:list[float]    # 安装角度
    joint_actual_position:list[float] # 关节实际位置
    actual_position:list[float]       # 笛卡尔实际位置
    joint_position:list[float]        # 笛卡尔关节位置
    position_desired:list[float]      # 下一周期笛卡尔规划位置
    cab_din:list[bool]                # 电控柜DI信号
    cab_dout:list[bool]               # 电控柜DO信号
    cab_ain:list[float]               # 电控柜AI信号
    cab_aout:list[float]              # 电控柜AO信号

    tio_din:list[bool]                # 工具IO DI信号
    tio_dout:list[bool]               # 工具IO DO信号
    tio_ain:list[float]               # 工具IO AI信号
    tio_aout:list[float]              # 工具IO AO信号

    extio_din:list[bool]              # 扩展IO DI信号
    extio_dout:list[bool]             # 扩展IO DO信号
    extio_ain_uint:list[int]          # 扩展IO 无符号整型AI信号
    extio_ain_int:list[int]           # 扩展IO 有符号整型AI信号
    extio_ain_float:list[float]       # 扩展IO 浮点型AI信号
    extio_aout_uint:list[int]         # 扩展IO 无符号整型AO信号
    extio_aout_int:list[int]          # 扩展IO 有符号整型AO信号
    extio_aout_float:list[float]      # 扩展IO 浮点型AO信号

    mb_slave_din:list[bool]           # Modbus从站DI信号
    mb_slave_dout:list[bool]          # Modbus从站DO信号
    mb_slave_ain_uint:list[int]       # Modbus从站无符号整型AI信号
    mb_slave_ain_int:list[int]        # Modbus从站有符号整型AI信号
    mb_slave_ain_float:list[float]    # Modbus从站浮点型AI信号
    mb_slave_aout_uint:list[int]      # Modbus从站无符号整型AO信号
    mb_slave_aout_int:list[int]       # Modbus从站有符号整型AO信号
    mb_slave_aout_float:list[float]   # Modbus从站浮点型AO信号

    ext_axis_num:int                      # 外部轴数量
    ext_axis_status:list[ExtAxisStatus]   # 外部轴状态
    ext_group_num:int                     # 外部轴组数量
    ext_group_status:list[ExtGroupStatus] # 外部轴组状态

    # 刷新
    def poll(self, mode=0) -> int: ...

自定义指令开发

自定义指令是指开发者根据自己的需求,在符合 JAKA 脚本规则的情况下,开发出的除 JAKA App 内标准程序指令之外的其他指令。

自定义指令的作用

当机器人末端连接了新的第三方工具,或是应用于具体工作场景中去操作具体工艺时,往往需要在 JAKA App 编程界面编写繁杂的指令、设置繁多的参数。这一步骤较复杂,且不易于二次复用,所以此时就需要把这些繁、杂、多的指令和参数封装为一条直接易懂、简单易用的指令块,以节约时间成本、提升操作效率。

自定义指令的工作过程

  1. Coboπ通过自定义指令描述文件生成指令块

  2. 用户在编程界面中,将自定义指令拖入程序中

  3. 在保存程序时,Coboπ根据每个自定义指令描述文件中的URL,向AddOn插件发送GET请求。

  4. 在GET请求中,将指令块中的参数发送给AddOn插件,AddOn插件根据参数生成jks脚本,并返回给App。

  5. Coboπ将从AddOn插件返回的jks脚本拼接到主程序文件中,并保存。

自定义指令设计

由于AddOn框架中对自定义指令的描述过于复杂。jaka-addon-plugin新增了对自定义指令的图形化界面设计支持,开发者需要做的是在可视化界面中编辑自定义指令的描述内容,保存后请参考7. 插件部署与调试进行项目压缩和JAKA App导包,随后即可在编程界面刷新出设计的自定义指令

自定义指令的设计步骤

  1. Visual Studio Code打开AddOn插件的开发目录

  2. 通过 Ctrl+Shift+P 打开命令面板,输入以下命令:

  3. 输入命令 addon-plugin:open scratch editor 进入自定义指令设计界面

  4. 根据界面提示并按照实际需求编辑自定义指令名称、外观、文本内容和指令参数等信息

  5. 保存后,jaka-addon-plugin会在开发目录中生成自定义图形指令的描述配置文件./CustomCommand.json

  6. 可以通过jaka-addon-plugin的addon-plugin:make custom programs 或者 addon-plugin:build addon 自动扫描当前AddOn开发目录的./CustomCommand.json文件,生成自定义图形指令的标准描述文件./OldToNew_CustomPrograms.json

./CustomCommand.json自定义指令块描述配置文件中允许放入多个自定义指令描述,其中包含存储所有自定义编程方块的界面样式、输入参数、类型标识等 ./OldToNew_CustomPrograms.json描述配置文件是控制器正式加载运行标准文件,其中包含自定义图形指令的全局唯一标识、完整渲染配置、后端接口路由jksUrl等

指令块列表管理

执行 addon-plugin:open scratch editor 命令后,首先进入的是指令块列表管理界面。该界面以表格形式展示当前 AddOn 项目中已创建的所有自定义指令块,并提供创建、编辑和删除操作。

指令块列表

指令块列表包含以下列信息:

列名

说明

名称

自定义指令块的标识名称,推荐使用英文字母、数字和下划线命名

类型

指令块的类型,默认为"指令块"

语言

指令块的默认语言,如 zh(中文)、en(英语)、ja(日语)

字段数

指令块中包含的参数(属性)数量

操作

提供"编辑"和"删除"两个操作按钮

点击界面右上角的"创建新块"按钮,即可进入自定义指令的编辑界面。若已有指令块,可点击对应行的"编辑"按钮进入编辑,或点击"删除"按钮移除指令块。

指令块编辑界面

点击"创建新块"或"编辑"按钮后,进入指令块编辑界面。该界面分为左侧预览区和右侧配置面板两部分。

指令编辑界面

左侧预览区(编辑块):

  • 预览区以网格为背景,实时展示指令块的可视化外观。

  • 指令块以带颜色的圆角图形呈现,图形形状由指令块类型决定。

  • 预览区右侧提供缩放工具按钮(放大、缩小、居中视图),方便开发者查看指令块细节。

右侧配置面板:

右侧配置面板自上而下分为以下区域:

(1)指令基本信息

配置项

说明

名称

指令块的唯一标识名称,推荐使用英文字母、数字和下划线组合,如 commandtest、New_Block

指令块类型

决定指令块的执行返回类型,详见下方"指令块类型"说明

颜色

指令块在编程界面中的显示颜色,通过颜色选择器设置

指令界面

可选配置,用于关联指令的前端交互界面

语言

指令块的默认语言,支持中文(zh)、英语(en)、日语(ja)

(2)指令块类型说明

根据指令执行后的返回结果,指令块分为以下五种类型,不同类型对应不同的外形:

指令块类型

类型

外形

说明

指令块(statement)

矩形带缺口(标准积木形状)

无返回值,作为独立语句执行,是最常用的指令类型

布尔值(boolean)

菱形

返回布尔值(true/false),可嵌入条件判断中

字符串(string)

椭圆形

返回字符串结果,可嵌入到其他指令的参数中

数字(number)

椭圆形

返回数字结果,可嵌入到其他指令的数值参数中

数组(array)

椭圆形

返回数字数组结果,可嵌入到其他指令的数组参数中

说明: 指令块(statement)类型用于执行操作但不需要返回值;其余四种类型均为值类型(value),执行后会产生对应类型的返回值,可在其他指令中作为参数使用。

(3)指令参数(属性)配置

指令参数以表格形式配置,每个参数代表指令块上的一个可输入项。参数表格包含以下列:

列名

说明

属性名

参数的标识名称,在 GET 请求中作为参数键名传递给 AddOn 后端,建议使用有意义的英文命名

类型

参数的输入类型,详见下方"参数类型"说明

默认值

参数的初始值。文本类型支持多语言配置(zh/en/ja),其他类型为单值

隐藏

开关控制,开启后该参数在编程界面中不可见,但仍会传递给后端

操作

提供"删除"按钮,移除该参数

点击配置面板右上角的 "+" 按钮可添加新的参数行。

参数类型说明:

类型

说明

默认值

文本(text)

纯标签文本,用于在指令块上显示说明性文字。支持多语言(zh/en/ja)配置

无

布尔(boolean)

布尔值输入,在编程界面中以下拉框形式呈现

false

字符串(string)

字符串输入,在编程界面中可输入文本或拖入字符串变量

空字符串

数字(number)

数值输入,在编程界面中可输入数字或拖入数字变量

0

数组(array)

数组输入,在编程界面中可拖入数组变量

不支持默认值

下拉框(dropdown)

下拉选择框,需要配置选项列表

第一个选项

自定义指令设计示例——加法指令

以下以一个"加法指令"为例,完整说明自定义指令的设计过程。

加法指令设计示例

设计目标: 创建一个加法指令块,接收两个数字输入,计算并返回它们的和。

步骤一:配置指令基本信息

  • 名称:commandtest

  • 指令块类型:指令块(statement),因为加法结果通过变量输出,指令本身无需返回值

  • 颜色:选择青绿色(如 #64f2c7)

  • 语言:中文(zh)

步骤二:配置指令参数

加法指令共需要5个参数,按从左到右的排列顺序配置如下:

序号

属性名

类型

默认值

说明

1

arg_1_rzicg2

文本(text)

加法指令

指令块的标题标签,显示在指令块最左侧

2

number1

字符串(string)

(空)

第一个加数输入,用户可输入数值或拖入变量

3

number2

数字(number)

0

第二个加数输入,用户可输入数值或拖入变量

4

resultdfsf

文本(text)

结果:

结果标签文字,用于标识输出结果的位置

5

result

数字(number)

0

计算结果输出变量,AddOn后端会将计算结果写入此变量

步骤三:预览与保存

在左侧预览区可以实时查看指令块的外观效果。预览区中,指令块以设定的颜色显示,文本类型的参数作为标签直接显示在指令块上,字符串和数字类型的参数显示为可输入的圆形端口。

确认配置无误后,点击右下角的"确定"按钮保存指令块。点击"取消"则放弃当前修改。

步骤四:生成描述文件

保存后,jaka-addon-plugin 会在开发目录中生成 ./CustomCommand.json 文件。该文件的结构示例如下:

{
  "id": "block_1778750188735",
  "name": "commandtest",
  "kind": "statement",
  "output": "shape_statement",
  "color": "#64f2c7",
  "lang": "zh",
  "fields": [
    {
      "label": "arg_1_rzicg2",
      "type": "text",
      "defaultValue": { "zh": "加法指令", "en": "", "ja": "" },
      "hidden": false
    },
    {
      "label": "number1",
      "type": "number",
      "defaultValue": "0",
      "hidden": false
    },
    {
      "label": "number2",
      "type": "number",
      "defaultValue": "0",
      "hidden": false
    },
    {
      "label": "resultdfsf",
      "type": "text",
      "defaultValue": { "zh": "结果:", "en": "", "ja": "" },
      "hidden": false
    },
    {
      "label": "result",
      "type": "number",
      "defaultValue": "0",
      "hidden": false
    }
  ],
  "openUrl": "",
  "selectedType": "statement"
}

各字段说明如下:

字段

说明

id

指令块的唯一ID,由系统自动生成

name

指令块名称,与配置中的"名称"对应

kind

指令块种类,statement 表示命令型,value 表示值型

output

输出形状,shape_statement 对应命令型,output_string/output_number/output_boolean/output_array 分别对应字符串/数字/布尔/数组型

color

指令块颜色,十六进制色值

lang

默认语言

fields

参数列表,每个参数包含 label(属性名)、type(类型)、defaultValue(默认值)、hidden(是否隐藏)

openUrl

可选,关联的前端界面URL

selectedType

指令块选中的类型标识

随后通过 addon-plugin:make custom programs 或 addon-plugin:build addon 命令,将 CustomCommand.json 转换为控制器可识别的标准描述文件 ./OldToNew_CustomPrograms.json。该文件中包含每条指令的全局唯一标识(BLOCK_TYPE)、完整渲染配置(JAKA_BLOCK_DATA)、后端接口路由(jksUrl)等信息。

注意: jksUrl 的格式为 connected_robot/{指令块名称},AddOn 插件需要在 Flask 服务中创建与该名称对应的 GET 路由来处理指令请求。例如指令名称为 commandtest,则 Flask 路由应为 /commandtest。

多语言文本配置

出于多语言场景考虑,文本类型(text)参数在默认值配置中提供了中文(zh)、日语(ja)、英语(en)三种语言选项。在 CustomCommand.json 中,文本类型的 defaultValue 以对象形式存储各语言版本的文本内容:

"defaultValue": {
  "zh": "加法指令",
  "en": "Addition",
  "ja": "加法命令"
}

当用户在 Coboπ 中切换系统语言时,指令块上显示的文本会自动对应到相应语言的版本。若某种语言未配置内容(留空),则显示为空文本。

非文本类型的参数(如字符串、数字等)的 defaultValue 为单一字符串值,不支持多语言配置。

隐藏参数

每个参数均支持"隐藏"开关。开启隐藏后,该参数在 Coboπ 编程界面的指令块上将不可见,但在保存程序时,该参数的值仍然会通过 GET 请求传递给 AddOn 后端。

隐藏参数适用于以下场景:

  • 需要在后端使用但不希望用户修改的内部参数

  • 固定不变配置项

  • 用于版本标识或功能开关等

AddOn插件处理指令

在自定义指令的工作过程中,AddOn插件需要接收来自JAKA App的GET请求,并根据请求参数生成相应的JKS脚本。以下是具体的实现步骤和技术细节:

使用 flask 框架处理GET请求

为了处理来自Coboπ的GET请求,推荐使用Python的Flask框架搭建HTTP服务。Flask轻量、灵活,适合快速构建RESTful API接口。

在创建Flask服务时,需使用AddOn配置文件中的端口号。

import configparser
import flask


FlaskServer = flask.Flask(__name__)
@FlaskServer.route('/myCommand', methods=['GET'])
def myCommand():
  jks = "# This is a test command\n"
  return {
    "error_code":0,
    "jks_str":[jks],
    "error_msg": ""
  }

addonConfig = configparser.ConfigParser()
addonConfig.read("./addon_config.ini")
flask_port = addonConfig.getint("AddOnInfo", "portal")

FlaskServer.run(host='0.0.0.0', port=flask_port)

GET请求中的参数格式为:

{
  "param1": { "value": "value1", "type": 0 },
  "param2": { "value": "value2", "type": 1 }
}

若参数的type为0,则参数值为字符串,内容是用户在自定义指令块中的输入值。 若参数的type为1,则参数值为jks变量名,内容是用户在自定义指令块中放入的变量。

JKS脚本拼接

JKS(JAKA Script)是JAKA机器人使用的脚本语言。在生成JKS脚本时,需注意以下几点:

语法正确性:确保生成的脚本符合JKS语法规则。 参数映射:将GET请求中的参数正确映射到JKS指令中。 动态逻辑:根据不同参数组合生成不同的脚本内容。

GET请求的返回格式

JAKA App期望AddOn插件返回特定格式的响应数据。返回的数据必须是JSON格式:\

{
  "error_code": 0,
  "errormsg": "",
  "jks_str": {
    "jks": [],
    "jks_return": ""
  },
  "line": []
}
  • error_code: 错误码,0表示成功,非0表示失败,为了避免与Coboπ的错误码冲突,请使用大于0xAF0000的错误码。

  • errormsg: 错误信息,用于提示用户。

  • jks_str: JKS脚本对象,用于保存生成的JKS脚本。

    • jks_str: JKS脚本字符串本身,需要放入列表中传回。

    • return_value: 返回变量名,用于数字型、字符串型、数组型的自定义指令,将运行自定义时,某个jks变量视为自定义指令的结果。

  • line: jks脚本的行号,通过计算换行符"n"的个数确定。

AddOn插件的前端界面开发

插件开发过程中,需要为插件提供相应的前端界面,用于用户配置插件参数。

前端界面开发

我们推荐使用Vue框架开发前端界面。Vue是一个基于Vue.js的Web应用程序开发框架,它提供了一套完整的组件库,用于构建用户界面。

AddOn后端交互

为了安全考虑,AddOn后端的端口号是不能被外部直接访问的。所以,前端界面向后端服务发出的请求,需要通过跨域代理来解决。 URL 格式为:https://${机器人的IP}/addonApi/{port}/${路由方法}

由此,引申出一个新的问题:AddOn的端口号是由AddOn Manager配置的,前端界面如何获取到AddOn的端口号? 其实,可以直接通过跨域代理获取到AddOn插件的配置文件信息。只需要访问https://${机器人的IP}/addon/${addon名字}/${addon配置文件}即可。如:

import axios from "axios";

const INIcontext = await axios.get(
  `https://${robot_ip.value}/addon/AddOnDemo/AddOnDemo_config.ini`,
);
const portMatch = INIcontext.data.match(/portal\s*=\s*(\d+)/);
const Port = portMatch[1];

跨域问题

在开发 AddOn 插件的前端界面时,由于浏览器的安全策略限制,前端页面无法直接访问不同域名或端口的后端服务,这就产生了跨域问题。

为了解决这一问题,通常采用以下方式:

使用 OPTIONS 方法预检请求

现代浏览器在发送跨域请求之前,会先发送一个 OPTIONS 请求(也称为“预检请求”),用于确认服务器是否允许该跨域请求。服务器需要正确响应这个 OPTIONS 请求,才能让后续的实际请求顺利发送。 实现方式: 在 Flask 后端中,可以通过添加 CORS(跨域资源共享)中间件来支持 OPTIONS 请求。以下是使用 Flask-CORS 扩展的示例代码:

import configparser
from flask import Flask, request
from flask_cors import CORS

app = Flask(__name__)
CORS(app, supports_credentials=True, resources=r'/*')  # 启用全局 CORS 支持

@app.route('/myMethod', methods=['POST', 'OPTIONS'])
def myMethod():
    if request.method == 'OPTIONS':
        # 处理 OPTIONS 预检请求
        return '', 200
    else:
        # 处理实际的 GET 请求
        jks = "# This is a test command\n"
        return {
            "error_code": 0,
            "jks_str": [jks],
            "error_msg": ""
        }

if __name__ == '__main__':
    addonConfig = configparser.ConfigParser()
    addonConfig.read('addon_config.ini')
    app.run(host='0.0.0.0', port=addonConfig.getint("AddOnInfo","portal"))

关键点说明:

  • CORS(app):启用全局跨域支持,自动处理 OPTIONS 请求。

  • 对于 OPTIONS 请求,直接返回空响应体和 200 状态码即可。

  • 实际业务逻辑放在非 OPTIONS 请求的处理分支中。

手动设置响应头

如果不使用 Flask-CORS, 也可以手动设置响应头来支持跨域请求:

import configparser
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/myCommand', methods=['GET', 'OPTIONS'])
def myCommand():
    # 设置跨域响应头
    response_headers = {
        'Access-Control-Allow-Origin': '*',  # 允许所有来源访问
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',  # 允许的方法
        'Access-Control-Allow-Headers': 'Content-Type'  # 允许的请求头
    }

    if request.method == 'OPTIONS':
        # 处理 OPTIONS 预检请求
        return '', 200, response_headers
    else:
        # 处理实际的 GET 请求
        jks = "# This is a test command\n"
        return jsonify({
            "error_code": 0,
            "jks_str": [jks],
            "error_msg": ""
        }), 200, response_headers

if __name__ == '__main__':
    addonConfig = configparser.ConfigParser()
    addonConfig.read('addon_config.ini')
    app.run(host='0.0.0.0', port=addonConfig.getint("AddOnInfo","portal"))

关键响应头说明:

  • Access-Control-Allow-Origin:指定允许访问的源(* 表示允许所有源)。

  • Access-Control-Allow-Methods:指定允许的 HTTP 方法。

  • Access-Control-Allow-Headers:指定允许的请求头字段。

插件部署与调试

AddOn打包流程

方案一:

  1. AddOn 插件开发完成后,将前端界面打包,放入AddOn目录下。

  2. 将AddOn目录压缩成tar.gz文件。

方案二:

  1. jaka-addon-plugin在创建插件时,会自动生成一个打包配置文件package_log_pro.json,里面配置了AddOn的打包信息。

  2. 再次执行 addon-plugin:make new addon 命令,jaka-addon-plugin如果识别到当前工作目录是一个AddOn开发目录,则会自动读取python脚本,将相关文件加入到打包配置中。

  3. 检查package_log_pro.json文件,确认no_transform和transform参数中包含了所有需要打包的文件,若有缺失,推荐手动添加到no_transform参数中。

  4. 执行 addon-plugin:build addon 命令,将AddOn目录的./output路径下打包成tar.gz文件。

AddOn部署流程

  1. 打开Coboπ并登录机器人。

  2. 在应用界面,上传AddOn压缩包。

AddOn调试方法

AddOn在运行时,AddOn Manager会在AddOn的目录下自动创建一个log目录。 目录中包含两个日志文件:

  • AddOnerrlog.log: AddOn抛出的异常记录。

  • AddOnlog.log: addon运行的日志记录。

可在浏览中,通过跨域代理下载。 URL: https://${机器人IP}/addon/${AddOn名称}/log/AddOnerrlog.log和https://${机器人IP}/addon/${AddOn名称}/log/AddOnlog.log

附录

控制系统IO类型

IO类型

类型代码

描述

控制柜IO

0

面板IO,兼容Modbus从站IO,Profinet从站IO

TIO

1

随TIO版本

Modbus扩展IO

2

控制器做Modbus主站

继电器IO

3

目前仅CAB V3支持D0

Modbus从站IO

4

原Modbus从站IO从0索引

Profinet从站IO

5

原Profinet从站IO从O索引

EtherNet/IP从IO

6

新增EtherNet/IP从站IO从0索引