跳到主要内容

IO 控制案例

该教程会实现控制吸盘吸取和释放的插件。

该插件的工作流程如下:

创建插件

# 需要使用 node v20 及以上版本
dpt create

在初始化插件时,需要提供

  • 插件的名字,必填,当前文件夹下不得有同名文件夹
  • 插件的描述,非必填,可以后续在配置文件中进行修改
  • 插件的版本号,默认是 1-0-0
  • 机械臂控制器的IP地址,默认是 192.168.5.1,可在配置文件中修改
$ dpt create
? Please input plugin name: io
? Please input plugin description: A plugin demo for io control
? Please input plugin version: 1-0-0
? Please input device IP: 192.168.5.1

在完成基础信息的配置和填写后,程序会自动执行安装程序

Packages: +587
Downloading antd@5.20.3: 9.80 MB/9.80 MB, done
Progress: resolved 588, reused 582, downloaded 5, added 587, done

dependencies:
+ @dobot-plus/components 0.0.0
+ antd 5.20.3
+ axios 1.7.5
+ i18next 23.14.0
+ pubsub-js 1.9.4
+ react 18.3.1
+ react-dom 18.3.1
+ react-i18next 15.0.1
+ react-redux 9.1.2
+ redux 5.0.1

devDependencies:
+ @types/node 20.16.1 (22.5.0 is available)
+ @types/pubsub-js 1.8.6
+ @types/react 18.3.4
+ @types/react-dom 18.3.0
+ @types/react-redux 7.1.33
+ @typescript-eslint/eslint-plugin 7.18.0 (8.3.0 is available)
+ @typescript-eslint/parser 7.18.0 (8.3.0 is available)
+ add 2.0.6
+ css-loader 7.1.2
+ eslint 8.57.0 (9.9.1 is available)
+ eslint-plugin-react-hooks 4.6.2
+ eslint-plugin-react-refresh 0.4.11
+ postcss-loader 8.1.1
+ sass 1.77.8
+ sass-loader 16.0.1
+ style-loader 4.0.0
+ ts-loader 9.5.1
+ typescript 5.5.4
+ url-loader 4.1.1
+ webpack 5.94.0

Done in 39.7s

⚠️注意: 在插件文件夹初始化完成后,vscode 会根据配置进行一些插件的安装,请允许该安装过程,并确保该过程顺利,否则会影响后续的Lua脚本调试。

当命令行输出类似上面的内容后,表示一个插件项目的文件夹已经创建完毕。

目录结构如下

io
├── Resources
│ ├── document
│ │ └── config.json
│ ├── i18n
│ │ ├── client
│ │ │ ├── de.json
│ │ │ ├── en.json
│ │ │ ├── es.json
│ │ │ ├── hk.json
│ │ │ ├── ja.json
│ │ │ ├── ko.json
│ │ │ ├── ru.json
│ │ │ └── zh.json
│ │ └── plugin
│ │ ├── de.json
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── hk.json
│ │ ├── ja.json
│ │ ├── ko.json
│ │ ├── ru.json
│ │ └── zh.json
│ └── images
│ └── pallet.svg
├── configs
│ ├── Blocks.json
│ ├── Main.json
│ ├── Scripts.json
│ └── Toolbar.json
├── dpt.json
├── lua
│ ├── daemon.lua
│ ├── httpAPI.lua
│ ├── userAPI.lua
│ └── utils
│ ├── await485.lua
│ ├── mqtt.lua
│ ├── num_convert.lua
│ ├── util.lua
│ └── variables.lua
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
└── ui
├── Blocks.tsx
├── Main.tsx
└── Toolbar.tsx

机械臂&末端控制

机械臂和末端的控制逻辑主要在 userApi.lua 文件中进行编写。 针对该插件的场景,实际需要对外提供三个函数:

  • 吸取函数
  • 释放函数
  • 获取状态函数

针对这三个函数,我们需要依次编写 userApi.luahttpApi.lua和对应的 UI 文件。

  • 编辑 userAPI.lua

    -- 定义函数grip用于控制吸取操作
    function userApiModule.grip()
    -- 设置第一个端子的输入信号为 ON
    ToolDO(1, 1)
    end

    -- 定义函数release用于释放操作
    function userApiModule.release()
    -- 设置第一个端子的输入信号为 OFF
    ToolDO(1, 0)
    end

    -- 定义函数getStatus用于获取吸盘的状态
    function userApiModule.getStatus()
    local status = 'ERROR'
    local STATUS = {
    ERROR = 'error',
    GRIPPING = 'gripping',
    IDLE = 'idle'
    }
    -- 读取末端数据
    local di1 = ToolDI(1)
    local di2 =ToolDI(2)

    -- 根据末端端子的输出,判断当前设备状态
    if di1 == nil or di2 == nil or di2 == 1 then
    status = STATUS.ERROR
    elseif di1 == 1 then
    status = STATUS.IDLE
    elseif di1 == 0 then
    status = STATUS.GRIPPING
    end

    return status
    end

    function userApiModule.OnRegist()
    EcoLog(" --- OnRegist .... --- ")
    -- 0. 接口导出

    -- 将 grip、release、getStatus 三个函数导出该模块
    local isErr = ExportFunction("[export name you want]", userApiModule.grip) or
    ExportFunction("[export name you want]", userApiModule.release) or
    ExportFunction("[export name you want]", userApiModule.getStatus)

    -- 1. 错误的处理
    if isErr then
    EcoLog(" --- ERR to register .... --- ", isErr)
    dobotTool.SetError(0)
    end
    end

    return userApiModule

    当鼠标悬停到函数上时,会出现函数的说明、参数类型和返回值

  • 编写 httpAPI.lua

    -- 添加一个名为 grip 的 http post 请求的处理函数
    httpModule.grip = function()
    userAPI.grip()
    return {
    --- Your responce data
    --- example: status = true
    }
    end

    -- 添加一个名为 release 的 http post 请求的处理函数
    httpModule.release = function()
    userAPI.release()
    return {
    --- Your responce data
    --- example: status = true
    }
    end

    httpModule.status = function()
    local status = userAPI.getStatus()
    -- http 接口返回io状态
    return {
    status = status
    --- Your responce data
    --- example: status = true
    }
    end

  • 编写 daemon.lua 文件

    local mqtt = require('utils.mqtt')

    local function handleInLoop()
    local data = {}
    mqtt.publish(data)
    end

    local function EventLoop()
    while true do
    handleInLoop()
    Wait(1000)
    end
    end

    local thread = systhread.create(EventLoop, 1)
    thread:wait()

    控制器和末端的状态同步通过mqtt协议发送给远端,在lua的进程中,会执行一个轮询操作,每隔一秒执行一次,开发者可以将需要进行状态同步和消息发送的操作加入 handleInLoop 函数。

    如果当前的开发环境工作正常时,在编写代码的过程中会有自动代码提示和说明,如下所示:

    code auto complete

    ⚠️在编码过程中会对 Lua 进行静态的代码检查,包括但不限于参数异常、类型不匹配、未声明的变量就使用等异常情况,请在编码的过程中注意这些黄色的波浪线告警,这会导致Lua执行的失败。

  • Lua 预调试

    • 在当前项目根目录下运行 dpt lua
    • 根据提示选择要在本地执行的 lua 脚本
    • 开发者可自行打印日志,查看调试的 lua 脚本的模块引入、语法、逻辑等是否存在问题

控制界面

接口请求

在正常情况下会根据开发者在 lua/httpAPI.lua 模块中编写的方法,自动的生成一个前端请求文件,开发者使用

import * as http from '@dobot/http/http'

的方式引入,之后使用同名函数的形式就可以发起 http 请求。

// 函数名与 httpAPI.lua 对应函数同名
http.grip()

http.release()

⚠️如果用户想自行编写请求配置,可编写 ui/http/api.json 文件

{
"requestGrip": {
"method": "post",
"url": "grip"
},
"requestRelease": {
"method": "post",
"url": "release"
},
"requestStatus": {
"method": "get",
"url": "status"
}
}

⚠️请求参数中URL部分需要和 httpAPI.lua 模块中的函数名称对应,表示控制器需要调用的是 httpAPI.lua 模块中对应的方法

  • 编写 ui/pages/Main.tsx

    import { Button } from '@dobot-plus/components'
    import { useState } from 'react'
    import { useTranslation } from 'react-i18next'
    import * as http from '@dobot/http/http'
    import { DobotPlusApp } from '@dobot/index'

    function App() {
    const { t } = useTranslation()

    const [text, setText] = useState('Test')

    function handleButton1Click() { http.grip() }

    function handleButton2Click() { http.release() }

    function handleMessage(topic: string, msg: Buffer) {
    http.status().then(res => {
    const { data } = res
    setText(data)
    })
    }

    return (
    <div className="app">
    <DobotPlusApp useMqtt={true} onMessage={handleMessage}>
    <h1>{t('testKey')}</h1>
    <Button type="primary" onClick={handleButton1Click}>
    Grip
    </Button>
    <Button type="primary" onClick={handleButton2Click}>
    Release
    </Button>
    <span>{text}</span>
    </DobotPlusApp>
    </div>)
    }

    export default App

    • http 模块根据 lua/httpAPI.lua 自动生成,开发者使用同名函数即可调用,也可以自行创建 ui/http/api.json 的配置

      {
      "requestName": {
      "url": "/testURL",
      "method": "post"
      }
      }

      调用时,使用如下方式

      import { http } from '@dobot/http/api'

      http.requestName()
    • DobotPlusApp 是一个 react 的高阶组件,内部执行了 Websocket 的建立和插件端口的获取操作

调试和验证

调试插件指令可进行以下两种情形的开发工作:

  • 仅调试页面
  • 连接真机进行调试
dpt dev

在执行上述命令时,命令行会提示开发者是否连接真机进行测试

$ dpt dev
? Debug lua on real device? Yes
? Please check the device IP: 192.168.5.1 and port: 22001 (y/n)

开发者需要确定:

  • 控制器的真实IP是否正确,默认是 192.168.5.1
  • 控制器服务端口是否正确,默认是 22001
  • SFTP 服务相关配置是否正确

上述配置的详细信息请查看 dpt.json 配置文件

{
"ip": "192.168.5.1", // 控制器 IP
"port": 22001, // 控制器端口
"pluginPort": 22100 // 插件端口号
}

在连接真机进行调试时,保存lua文件会自动同步至控制器,开发者可在浏览器的界面交互中对机械表末端进行控制。

构建插件

在完成插件的开发、调试、优化后,可执行最终的构建工作,执行

dpt build
  • 在构建的过程中,ui/pages 路径下的第一层的 .tsx 文件会被构建成对应的页面
    • Main.tsx 对应插件主页面
    • Toolbar.tsx 对应插件工具栏
    • Blocks.tsx 对应插件的积木弹窗页面
    • 其他一级自定义页面也会进行同类型构建,所以请开发者注意 ui/pages 文件夹下一级目录中 .tsx 文件的命名

在程序顺利执行完毕后,当前文件夹下会出现 dist 文件夹和 output 文件夹。

  • dist 文件夹中存放着本次构建后的插件代码,用于开发者检查构建结果
  • output 文件夹存放着压缩后的 zip 文件,文件名格式为 <插件名>-<版本号>.zip,该文件为实际在客户端导入使用的的插件。