diff --git a/.github/workflows/2_build_and_test.yml b/.github/workflows/2_build_and_test.yml
index cd471f72e..3b7e6d77e 100644
--- a/.github/workflows/2_build_and_test.yml
+++ b/.github/workflows/2_build_and_test.yml
@@ -18,8 +18,15 @@ jobs:
- name: Linux 构建测试
run: |
sudo apt update -y
- sudo apt install jq cloc protobuf-compiler -y
+ sudo apt install jq cloc protobuf-compiler \
+ gcc-mingw-w64-x86-64 \
+ gcc-arm-linux-gnueabi \
+ gcc-mips-linux-gnu \
+ gcc-mingw-w64 \
+ gcc-aarch64-linux-gnu -y
cloc ./
+ go get
+ go get -t github.com/hootrhino/rulex/test
make
# for test mqtt
echo "Start Mqtt Server"
@@ -37,3 +44,5 @@ jobs:
go test -timeout 30s -run ^Test_data_to_tdengine$ github.com/hootrhino/rulex/test -v -count=1
#
go test -timeout 30s -run ^Test_DataToMongo$ github.com/hootrhino/rulex/test -v -count=1
+ chmod +x ./release_pkg.sh
+ bash ./release_pkg.sh
diff --git a/.github/workflows/4_build-arm-32-v7.yml b/.github/workflows/3_build-arm-32-v7.yml
similarity index 100%
rename from .github/workflows/4_build-arm-32-v7.yml
rename to .github/workflows/3_build-arm-32-v7.yml
diff --git a/.github/workflows/3_build-windows.yaml b/.github/workflows/3_build-windows.yaml
deleted file mode 100644
index 7c8bee936..000000000
--- a/.github/workflows/3_build-windows.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: 3- Windows构建测试
-on:
- push:
- branches: [master]
- pull_request:
- branches: [master]
-
-jobs:
- create_release:
- name: Windows构建测试
- runs-on: windows-2022
- steps:
- - name: Install Chocolate
- shell: powershell
- run: |
- Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString("https://community.chocolatey.org/install.ps1"))
- choco feature enable -n=allowGlobalConfirmation
-
- - name: Install Visualcpp-build-tools
- shell: powershell
- run: choco install visualcpp-build-tools -Y
-
- - name: Install golang
- shell: powershell
- run: |
- choco upgrade chocolatey
- choco install make -Y
- choco install golang --version 1.20.1 -Y
-
- # - name: Build
- # shell: powershell
- # run: |
- # go mod tidy
- # go build
- # go.exe test -timeout 30s -run ^TestFullyRun$ github.com/hootrhino/rulex/test -v -count=1
diff --git a/.gitignore b/.gitignore
index 654332c27..482bcfd22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,13 +17,10 @@ profile*
_release*
_build*
lua-log*
-*.txt
-*.txt.gz
-plugin/http_server/www/*
-plugin/http_server/server/www/*
+*.txt*
dist/
apps/*.lua
-plugin/http_server/www/index.html
-plugin/http_server/server/www/index.html
test/script/_temp/*
upload/*
+go.sum
+component/rulex_api_server/server/www/*
diff --git a/Makefile b/Makefile
index 075825100..2103bd47c 100644
--- a/Makefile
+++ b/Makefile
@@ -8,61 +8,70 @@ ip=$(shell hostname -I)
memory=$(shell free -m | awk 'NR==2{printf "%.2fGB\n", $$2/1000}')
disk=$(shell df -h | awk '$$NF=="/"{printf "%s\n", $$2}')
arch=$(uname -m)
-
+version=$(shell git describe --tags $(git rev-list --tags --max-count=1))
+shortVersion=-X 'github.com/hootrhino/rulex/typex.MainVersion=$(version)'
.PHONY: all
all:
@echo "\e[41m[*] Distro \e[0m: \e[36m ${distro} \e[0m"
- @echo "\e[41m[*] Arch \e[0m: \e[36m ${arch} \e[0m"
+ @echo "\e[41m[*] Arch \e[0m: \e[36m ${arch} \e[0m"
@echo "\e[41m[*] Kernel \e[0m: \e[36m ${kernel} \e[0m"
@echo "\e[41m[*] Cpu \e[0m: \e[36m ${cpu} \e[0m"
@echo "\e[41m[*] Memory \e[0m: \e[36m ${memory} \e[0m"
@echo "\e[41m[*] Host \e[0m: \e[36m ${host} \e[0m"
@echo "\e[41m[*] IP \e[0m: \e[36m ${ip} \e[0m"
@echo "\e[41m[*] Disk \e[0m: \e[36m ${disk} \e[0m"
+ go generate
make build
.PHONY: build
build:
CGO_ENABLED=1 GOOS=linux
- go build -v -ldflags "-s -w" -o ${APP}
+ go generate
+ go build -ldflags "$(shortVersion) -s -w" -v -o ${APP}
.PHONY: x64linux
x64linux:
+ go generate
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-x64linux
+ go build -ldflags "$(shortVersion) -s -w" -v -o ${APP}-x64linux
.PHONY: windows
windows:
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc\
- go build -ldflags "-s -w" -o ${APP}-windows.exe
+ go build -ldflags "$(shortVersion) -s -w" -o ${APP}-windows.exe
.PHONY: arm32
arm32:
+ go generate
CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabi-gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-arm32linux
+ go build -ldflags "$(shortVersion) -s -w -linkmode external -extldflags -static" -o ${APP}-arm32linux
.PHONY: arm64
arm64:
+ go generate
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-arm64linux
+ go build -ldflags "$(shortVersion) -s -w -linkmode external -extldflags -static" -o ${APP}-arm64linux
.PHONY: mips32
mips32:
+ go generate
# sudo apt-get install gcc-mips-linux-gnu
GOOS=linux GOARCH=mips CGO_ENABLED=1 CC=mips-linux-gnu-gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-mips32linux
+ go build -ldflags "$(shortVersion) -s -w -linkmode external -extldflags -static" -o ${APP}-mips32linux
.PHONY: mips64
mips64:
+ go generate
# sudo apt-get install gcc-mips-linux-gnu
GOOS=linux GOARCH=mips64 CGO_ENABLED=1 CC=mips-linux-gnu-gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-mips64linux
+ go build -ldflags "$(shortVersion) -s -w -linkmode external -extldflags -static" -o ${APP}-mips64linux
.PHONY: mipsel
mipsle:
+ go generate
# sudo apt-get install gcc-mipsel-linux-gnu
GOOS=linux GOARCH=mipsle CGO_ENABLED=1 GOMIPS=softfloat CC=mipsel-linux-gnu-gcc\
- go build -ldflags "-s -w -linkmode external -extldflags -static" -o ${APP}-mipslelinux
+ go build -ldflags "$(shortVersion) -s -w -linkmode external -extldflags -static" -o ${APP}-mipslelinux
.PHONY: release
release:
diff --git a/README.md b/README.md
index 31315fa13..1cf605548 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,23 @@
## 快速开始
+### 源码编译
+#### 环境安装
+下面是Ubuntu上搭建环境的指令:
+```bash
+sudo apt install jq cloc protobuf-compiler \
+ gcc-mingw-w64-x86-64 \
+ gcc-arm-linux-gnueabi \
+ gcc-mips-linux-gnu \
+ gcc-mingw-w64 \
+ gcc-aarch64-linux-gnu -y
+```
+> 推荐使用ubuntu开发。
+
+#### 编译
+```sh
+make
+```
### HelloWorld
下面展示一个最简单的设备数据转发案例:
diff --git a/bspsupport/eekit_adda_linux.go b/bspsupport/eekit_adda_linux.go
index 03a5e6e01..65c867665 100644
--- a/bspsupport/eekit_adda_linux.go
+++ b/bspsupport/eekit_adda_linux.go
@@ -25,14 +25,18 @@ import (
DI1 -> PA8
DI2 -> PA9
DI3 -> PA10
+ USER_GPIO -> PA20
*/
const (
+ // DO
eekit_DO1 string = "6"
eekit_DO2 string = "7"
-
+ // DI
eekit_DI1 string = "8"
eekit_DI2 string = "9"
eekit_DI3 string = "10"
+ // Use LED
+ eekit_USER_GPIO string = "20"
)
const (
@@ -56,11 +60,13 @@ func _EEKIT_GPIOAllInit() int {
gpio8 := "/sys/class/gpio/gpio8/value"
gpio9 := "/sys/class/gpio/gpio9/value"
gpio10 := "/sys/class/gpio/gpio10/value"
+ gpio20 := "/sys/class/gpio/gpio20/value"
_, err1 := os.Stat(gpio6)
_, err2 := os.Stat(gpio7)
_, err3 := os.Stat(gpio8)
_, err4 := os.Stat(gpio9)
_, err5 := os.Stat(gpio10)
+ _, err6 := os.Stat(gpio20)
if err1 != nil {
if strings.Contains(err1.Error(), "no such file or directory") {
_EEKIT_GPIOInit(eekit_DO1, eekit_Out)
@@ -91,6 +97,12 @@ func _EEKIT_GPIOAllInit() int {
fmt.Println("EEKIT_GPIOAllInit DI3 In Mode Ok")
}
}
+ if err6 != nil {
+ if strings.Contains(err5.Error(), "no such file or directory") {
+ _EEKIT_GPIOInit(eekit_USER_GPIO, eekit_Out)
+ fmt.Println("EEKIT_GPIOAllInit USER_GPIO Out Mode Ok")
+ }
+ }
// 返回值无用
return 1
}
diff --git a/bspsupport/rhinopi_gpio_ctrl.go b/bspsupport/rhinopi_gpio_ctrl.go
index 4385b81b6..008ddeaf1 100644
--- a/bspsupport/rhinopi_gpio_ctrl.go
+++ b/bspsupport/rhinopi_gpio_ctrl.go
@@ -15,6 +15,19 @@
package archsupport
+//-----------------------------------------------
+// 这是EEKIT网关的DI-DO支持库
+//-----------------------------------------------
+/*
+ pins map
+
+ DO1 -> PA6
+ DO2 -> PA7
+ DI1 -> PA8
+ DI2 -> PA9
+ DI3 -> PA10
+ USER_GPIO -> PA20
+*/
import (
"fmt"
"os"
@@ -27,11 +40,6 @@ import (
*/
const (
__h3_GPIO_PATH = "/sys/class/gpio/gpio%v/value"
- // __h3_DO1 = "/sys/class/gpio/gpio6/value" // gpio6
- // __h3_DO2 = "/sys/class/gpio/gpio7/value" // gpio7
- // __h3_DI1 = "/sys/class/gpio/gpio8/value" // gpio8
- // __h3_DI2 = "/sys/class/gpio/gpio9/value" // gpio9
- // __h3_DI3 = "/sys/class/gpio/gpio10/value" // gpio10
)
/*
@@ -54,6 +62,9 @@ func EEKIT_GPIOGetDI2() (int, error) {
func EEKIT_GPIOGetDI3() (int, error) {
return EEKIT_GPIOGetByFile(10)
}
+func EEKIT_GPIOGetUserGpio() (int, error) {
+ return EEKIT_GPIOGetByFile(20)
+}
func EEKIT_GPIOGetByFile(pin byte) (int, error) {
return __GPIOGet(fmt.Sprintf(__h3_GPIO_PATH, pin))
}
@@ -91,6 +102,9 @@ func EEKIT_GPIOSetDI2(value int) error {
func EEKIT_GPIOSetDI3(value int) error {
return EEKIT_GPIOSetByFile(10, value)
}
+func EEKIT_GPIOSetUserGpio(value int) error {
+ return EEKIT_GPIOSetByFile(20, value)
+}
func EEKIT_GPIOSetByFile(pin, value int) error {
return __GPIOSet(fmt.Sprintf(__h3_GPIO_PATH, pin), value)
diff --git a/common/modbus_config.go b/common/modbus_config.go
index 0cdc35e8d..b85dd016f 100644
--- a/common/modbus_config.go
+++ b/common/modbus_config.go
@@ -39,13 +39,14 @@ type Registers struct {
*
*/
type RegisterRW struct {
- Tag string `json:"tag" validate:"required" title:"数据Tag"` // 数据Tag
- Alias string `json:"alias" validate:"required" title:"别名"` // 别名
- Function int `json:"function" validate:"required" title:"Modbus功能"` // Function
- SlaverId byte `json:"slaverId" validate:"required" title:"从机ID"` // 从机ID
- Address uint16 `json:"address" validate:"required" title:"地址"` // Address
- Quantity uint16 `json:"quantity" validate:"required" title:"数量"` // Quantity
- Value string `json:"value" title:"值" info:"本地系统的串口路径"` // Value
+ Tag string `json:"tag" validate:"required" title:"数据Tag"` // 数据Tag
+ Alias string `json:"alias" validate:"required" title:"别名"` // 别名
+ Function int `json:"function" validate:"required" title:"Modbus功能"` // Function
+ SlaverId byte `json:"slaverId" validate:"required" title:"从机ID"` // 从机ID
+ Address uint16 `json:"address" validate:"required" title:"地址"` // Address
+ Frequency int64 `json:"frequency" validate:"required" title:"采集频率"` // 间隔
+ Quantity uint16 `json:"quantity" validate:"required" title:"数量"` // Quantity
+ Value string `json:"value"` // 运行时数据
}
/*
@@ -61,28 +62,11 @@ type RegisterW struct {
Values []byte `json:"values"` // Value
}
-// Uart "/dev/ttyUSB0"
-// BaudRate = 115200
-// DataBits = 8
-// Parity = "N"
-// StopBits = 1
-// SlaveId = 1
-// Timeout = 5 * time.Second
-// TODO: 在0.5以后删除这两个结构体
-type RTUConfig struct {
- Uart string `json:"uart" validate:"required" title:"串口路径" info:"本地系统的串口路径"`
- BaudRate int `json:"baudRate" validate:"required" title:"波特率" info:"串口通信波特率"`
- DataBits int `json:"dataBits" validate:"required" title:"数据位" info:"串口通信数据位"`
- Parity string `json:"parity" validate:"required" title:"奇偶校验" info:"奇偶校验"`
- StopBits int `json:"stopBits" validate:"required" title:"停止位" info:"串口通信停止位"`
-}
type ModBusConfig struct {
- Mode string `json:"mode" title:"工作模式" info:"UART/TCP"`
- Timeout int `json:"timeout" validate:"required" title:"连接超时"`
- // Weather allow AutoRequest?
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
- // Request Frequency, default 5 second
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
- Config interface{} `json:"config" validate:"required" title:"工作模式"`
- Registers []RegisterRW `json:"registers" validate:"required" title:"寄存器配置"`
+ Mode string `json:"mode" title:"工作模式" info:"UART/TCP"`
+ Timeout *int `json:"timeout" validate:"required" title:"连接超时"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
+ Frequency *int64 `json:"frequency" validate:"required" title:"采集频率"`
+ Config interface{} `json:"config" validate:"required" title:"工作模式"`
+ Registers []RegisterRW `json:"registers" validate:"required" title:"寄存器配置"`
}
diff --git a/common/siemens_config.go b/common/siemens_config.go
index d0b70af9d..9f2882212 100644
--- a/common/siemens_config.go
+++ b/common/siemens_config.go
@@ -6,30 +6,21 @@ package common
*
*/
type S1200Config struct {
- Host string `json:"host" validate:"required" title:"IP地址"` // 127.0.0.1
- Port *int `json:"port" validate:"required" title:"端口号"` // 0
- Rack *int `json:"rack" validate:"required" title:"架号"` // 0
- Slot *int `json:"slot" validate:"required" title:"槽号"` // 1
- Model string `json:"model" validate:"required" title:"型号"` // s7-200 s7 1500
- Timeout *int `json:"timeout" validate:"required" title:"连接超时时间"` // 5s
- IdleTimeout *int `json:"idleTimeout" validate:"required" title:"心跳超时时间"` // 5s
- //
- // Weather allow AutoRequest?
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
- // Request Frequency, default 5 second
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
- Blocks []S1200Block `json:"blocks" validate:"required" title:"采集配置"` // Db
+ Host string `json:"host" validate:"required" title:"IP地址:端口号"` // 127.0.0.1
+ Model string `json:"model" validate:"required" title:"型号"` // s7-200 s7 1500
+ Rack *int `json:"rack" validate:"required" title:"架号"` // 0
+ Slot *int `json:"slot" validate:"required" title:"槽号"` // 1
+ Timeout *int `json:"timeout" validate:"required" title:"连接超时时间"` // 5s
+ IdleTimeout *int `json:"idleTimeout" validate:"required" title:"心跳超时时间"` // 5s
+ AutoRequest *bool `json:"autoRequest" title:"启动轮询"`
+ Blocks []S1200Block `json:"blocks" validate:"required" title:"采集配置"` // Db
}
type S1200Block struct {
- Tag string `json:"tag" title:"数据tag"` // 数据tag
- Address int `json:"address" title:"地址"` // 地址
- Start int `json:"start" title:"起始地址"` // 起始地址
- Size int `json:"size" title:"数据长度"` // 数据长度
-}
-type S1200BlockValue struct {
- Tag string `json:"tag" title:"数据tag"` // 数据tag
- Address int `json:"address" title:"地址"` // 地址
- Start int `json:"start" title:"起始地址"` // 起始地址
- Size int `json:"size" title:"服务地址"` // 数据长度
- Value []byte `json:"value" title:"数据长度"` // 值
+ Tag string `json:"tag" validate:"required" title:"数据tag"` // 数据tag
+ Type string `json:"type" validate:"required" title:"地址"` // MB | DB |FB
+ Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
+ Address int `json:"address" validate:"required" title:"地址"` // 地址
+ Start int `json:"start" validate:"required" title:"起始地址"` // 起始地址
+ Size int `json:"size" validate:"required" title:"服务地址"` // 数据长度
+ Value string `json:"value,omitempty" validate:"required" title:"数据Hex"` // 值
}
diff --git a/common/target_config.go b/common/target_config.go
index c2d54ea3e..7287a9d99 100644
--- a/common/target_config.go
+++ b/common/target_config.go
@@ -17,13 +17,11 @@ type MongoConfig struct {
// db_name: 可选参数,指定本次所执行的 SQL 语句的默认数据库库名
// curl -u root:taosdata -d 'show databases;' 106.15.225.172:6041/rest/sql
type TDEngineConfig struct {
- Fqdn string `json:"fqdn" validate:"required" title:"地址"` // 服务地址
- Port int `json:"port" validate:"required" title:"端口"` // 服务端口
- Username string `json:"username" validate:"required" title:"用户"` // 用户
- Password string `json:"password" validate:"required" title:"密码"` // 密码
- DbName string `json:"dbName" validate:"required" title:"数据库名"` // 数据库名
- CreateDbSql string `json:"createDbSql" validate:"required" title:"建库SQL"` // 建库SQL
- CreateTableSql string `json:"createTableSql" validate:"required" title:"建表SQL"` // 建表SQL
+ Fqdn string `json:"fqdn" validate:"required" title:"地址"` // 服务地址
+ Port int `json:"port" validate:"required" title:"端口"` // 服务端口
+ Username string `json:"username" validate:"required" title:"用户"` // 用户
+ Password string `json:"password" validate:"required" title:"密码"` // 密码
+ DbName string `json:"dbName" validate:"required" title:"数据库名"` // 数据库名
}
/*
diff --git a/component/appstack/appstack_lua_runtime.go b/component/appstack/appstack_lua_runtime.go
index c83f70279..f0e51e1b4 100644
--- a/component/appstack/appstack_lua_runtime.go
+++ b/component/appstack/appstack_lua_runtime.go
@@ -104,6 +104,10 @@ func LoadAppLib(app *Application, e typex.RuleX) {
// 浮点数处理
addAppLib(app, e, "binary", "Bin2F32", rulexlib.BinToFloat32(e))
addAppLib(app, e, "binary", "Bin2F64", rulexlib.BinToFloat64(e))
+ addAppLib(app, e, "binary", "Bin2F32Big", rulexlib.BinToFloat32(e))
+ addAppLib(app, e, "binary", "Bin2F64Big", rulexlib.BinToFloat64(e))
+ addAppLib(app, e, "binary", "Bin2F32Little", rulexlib.BinToFloat32Little(e))
+ addAppLib(app, e, "binary", "Bin2F64Little", rulexlib.BinToFloat64Little(e))
}
{
addAppLib(app, e, "hex", "HToN", rulexlib.HToN(e))
@@ -186,6 +190,9 @@ func LoadAppLib(app *Application, e typex.RuleX) {
addAppLib(app, e, "rhinopi", "DI1Get", rulexlib.H3DI1Get(e))
addAppLib(app, e, "rhinopi", "DI2Get", rulexlib.H3DI2Get(e))
addAppLib(app, e, "rhinopi", "DI3Get", rulexlib.H3DI3Get(e))
+ // LED
+ addAppLib(app, e, "rhinopi", "Led1On", rulexlib.Led1On(e))
+ addAppLib(app, e, "rhinopi", "Led1Off", rulexlib.Led1Off(e))
}
{
@@ -210,4 +217,10 @@ func LoadAppLib(app *Application, e typex.RuleX) {
addAppLib(app, e, "rpc", "Request", rulexlib.Request(e))
// jq
addAppLib(app, e, "jq", "Execute", rulexlib.JqSelect(e))
+ // Ping
+ addAppLib(app, e, "network", "Ping", rulexlib.PingIp(e))
+ // http
+ addAppLib(app, e, "http", "Get", rulexlib.HttpGet(e))
+ addAppLib(app, e, "http", "Post", rulexlib.HttpPost(e))
+
}
diff --git a/component/appstack/appstack_runtime.go b/component/appstack/appstack_runtime.go
index 76b97adb9..bde1c32b5 100644
--- a/component/appstack/appstack_runtime.go
+++ b/component/appstack/appstack_runtime.go
@@ -92,8 +92,8 @@ func StartApp(uuid string) error {
glogger.GLogger.Debugf("Ready to run app:%s", app.UUID)
app.AppState = 1
err := app.VM().CallByParam(lua.P{
- Fn: app.GetMainFunc(),
- NRet: 1,
+ Fn: app.GetMainFunc(),
+ NRet: 1,
// Protect: true, // If ``Protect`` is false,
// GopherLua will panic instead of returning an ``error`` value.
Handler: &lua.LFunction{
@@ -119,11 +119,11 @@ func StartApp(uuid string) error {
if err != nil {
time.Sleep(5 * time.Second)
if app.KilledBy == "RULEX" {
- glogger.GLogger.Infof("App %s Killed By RULEX, No need to rescue", app.UUID)
+ glogger.GLogger.Errorf("App %s Killed By RULEX, No need to rescue", app.UUID)
return
}
if app.KilledBy == "NORMAL" {
- glogger.GLogger.Infof("App %s NORMAL Exited, No need to rescue", app.UUID)
+ glogger.GLogger.Errorf("App %s NORMAL Exited, No need to rescue", app.UUID)
return
}
glogger.GLogger.Warnf("App %s Exited With error: %s, May be accident, Try to survive",
@@ -131,7 +131,7 @@ func StartApp(uuid string) error {
// TODO 到底要不要设置一个尝试重启的阈值?
// if tryTimes >= Max -> return
if app.AutoStart {
- glogger.GLogger.Infof("App %s Try to restart", app.UUID)
+ glogger.GLogger.Warnf("App %s Try to restart", app.UUID)
go StartApp(uuid)
return
}
diff --git a/component/cron_task/constant.go b/component/cron_task/constant.go
index 92338b5de..5a908cfe1 100644
--- a/component/cron_task/constant.go
+++ b/component/cron_task/constant.go
@@ -2,12 +2,19 @@ package cron_task
// CRON_TASK_TYPE
const (
- CRON_TASK_TYPE_LINUX_SHELL = 1
- CRON_TASK_TYPE_WINDOWS_CMD = CRON_TASK_TYPE_LINUX_SHELL + 1
+ CRON_TASK_TYPE_LINUX_SHELL = "LINUX_SHELL"
)
+// CRON
+const LINUX_SHELL = "/bin/bash"
+
const PERM_0777 = 0777
-const CRON_ASSETS = "cron_assets"
+// CRON_RESULT_STATUS
+const (
+ CRON_RESULT_STATUS_RUNNING = "RUNNING"
+ CRON_RESULT_STATUS_END = "END"
+)
-const CRON_LOGS = "cron_logs"
+var CRON_TASK_EANBLE = true
+var CRON_TASK_DISABLE = false
diff --git a/component/cron_task/cron_manager.go b/component/cron_task/cron_manager.go
index c10b4fa67..78702ffa5 100644
--- a/component/cron_task/cron_manager.go
+++ b/component/cron_task/cron_manager.go
@@ -4,14 +4,13 @@ import (
"os"
"os/exec"
"path"
- "path/filepath"
"strconv"
"sync"
"time"
"github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/plugin/http_server/model"
"github.com/robfig/cron/v3"
)
@@ -46,39 +45,6 @@ func NewCronManager() *CronManager {
processManager: NewProcessManager(),
mtx: sync.Mutex{},
}
- // 每天0点10分清理日志
- engine.AddFunc("0 10 0 * * *", func() {
- thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
- // 指定文件夹路径
- folderPath := "cron_logs"
- // 遍历文件夹下的所有文件
- err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- // 检查是否为文件夹
- if info.IsDir() {
- // 解析文件夹名称为日期
- dirName := filepath.Base(path)
- date, err := time.Parse("2006-01-02", dirName)
- if err != nil {
- return err
- }
-
- // 检查是否为 30 天前的文件夹
- if date.Before(thirtyDaysAgo) {
- // 删除文件夹及其内容
- err := os.RemoveAll(path)
- if err != nil {
- return err
- }
- glogger.GLogger.Info("Deleted folder: %s", path)
- }
- }
- return nil
- })
- glogger.GLogger.Error("clean cron logs failed, err=", err)
- })
engine.Start()
return &manager
}
@@ -102,7 +68,7 @@ func (m *CronManager) AddTask(task model.MCronTask) error {
// create a task execute log record
result := model.MCronResult{
TaskUuid: task.UUID,
- Status: "1",
+ Status: CRON_RESULT_STATUS_RUNNING,
StartTime: time.Now(),
}
saveResults(&result)
@@ -127,7 +93,7 @@ func (m *CronManager) AddTask(task model.MCronTask) error {
taskLogger.Info("---------------End task---------------")
result.EndTime = time.Now()
- result.Status = "2"
+ result.Status = CRON_RESULT_STATUS_END
result.ExitCode = strconv.Itoa(exitCode)
saveResults(&result)
})
diff --git a/docs/docs.go b/component/cron_task/docs/docs.go
similarity index 90%
rename from docs/docs.go
rename to component/cron_task/docs/docs.go
index f2c5cfc7b..85dd34328 100644
--- a/docs/docs.go
+++ b/component/cron_task/docs/docs.go
@@ -9,15 +9,9 @@ const docTemplate = `{
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
- "termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
- "url": "http://www.swagger.io/support",
- "email": "support@swagger.io"
- },
- "license": {
- "name": "Apache 2.0",
- "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ "url": "https://github.com/hootrhino/rulex"
},
"version": "{{.Version}}"
},
@@ -57,7 +51,7 @@ const docTemplate = `{
}
}
},
- "/crontask/delete": {
+ "/crontask/del": {
"delete": {
"consumes": [
"application/json"
@@ -262,19 +256,16 @@ const docTemplate = `{
"type": "string"
}
},
- "isRoot": {
- "description": "0-false 1-true",
- "type": "string"
- },
"name": {
"type": "string"
},
"script": {
+ "description": "脚本内容,base64编码",
"type": "string"
},
"taskType": {
- "description": "1-shell 2-cmd",
- "type": "integer"
+ "description": "CRON_TASK_TYPE",
+ "type": "string"
}
}
},
@@ -298,19 +289,16 @@ const docTemplate = `{
"type": "string"
}
},
- "isRoot": {
- "description": "0-false 1-true",
- "type": "string"
- },
"name": {
"type": "string"
},
"script": {
+ "description": "脚本内容,base64编码",
"type": "string"
},
"taskType": {
- "description": "1-shell 2-cmd",
- "type": "integer"
+ "description": "CRON_TASK_TYPE",
+ "type": "string"
},
"uuid": {
"type": "string"
@@ -333,15 +321,6 @@ const docTemplate = `{
}
}
}
- },
- "securityDefinitions": {
- "BasicAuth": {
- "type": "basic"
- }
- },
- "externalDocs": {
- "description": "OpenAPI",
- "url": "https://swagger.io/resources/open-api/"
}
}`
@@ -351,8 +330,8 @@ var SwaggerInfo = &swag.Spec{
Host: "",
BasePath: "/api/v1",
Schemes: []string{},
- Title: "Swagger Example API",
- Description: "This is a sample server celler server.",
+ Title: "Rulex API",
+ Description: "Rulex Swagger API",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
diff --git a/docs/swagger.json b/component/cron_task/docs/swagger.json
similarity index 90%
rename from docs/swagger.json
rename to component/cron_task/docs/swagger.json
index f6bcc66fc..7b46b0c7a 100644
--- a/docs/swagger.json
+++ b/component/cron_task/docs/swagger.json
@@ -1,17 +1,11 @@
{
"swagger": "2.0",
"info": {
- "description": "This is a sample server celler server.",
- "title": "Swagger Example API",
- "termsOfService": "http://swagger.io/terms/",
+ "description": "Rulex Swagger API",
+ "title": "Rulex API",
"contact": {
"name": "API Support",
- "url": "http://www.swagger.io/support",
- "email": "support@swagger.io"
- },
- "license": {
- "name": "Apache 2.0",
- "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ "url": "https://github.com/hootrhino/rulex"
},
"version": "1.0"
},
@@ -50,7 +44,7 @@
}
}
},
- "/crontask/delete": {
+ "/crontask/del": {
"delete": {
"consumes": [
"application/json"
@@ -255,19 +249,16 @@
"type": "string"
}
},
- "isRoot": {
- "description": "0-false 1-true",
- "type": "string"
- },
"name": {
"type": "string"
},
"script": {
+ "description": "脚本内容,base64编码",
"type": "string"
},
"taskType": {
- "description": "1-shell 2-cmd",
- "type": "integer"
+ "description": "CRON_TASK_TYPE",
+ "type": "string"
}
}
},
@@ -291,19 +282,16 @@
"type": "string"
}
},
- "isRoot": {
- "description": "0-false 1-true",
- "type": "string"
- },
"name": {
"type": "string"
},
"script": {
+ "description": "脚本内容,base64编码",
"type": "string"
},
"taskType": {
- "description": "1-shell 2-cmd",
- "type": "integer"
+ "description": "CRON_TASK_TYPE",
+ "type": "string"
},
"uuid": {
"type": "string"
@@ -326,14 +314,5 @@
}
}
}
- },
- "securityDefinitions": {
- "BasicAuth": {
- "type": "basic"
- }
- },
- "externalDocs": {
- "description": "OpenAPI",
- "url": "https://swagger.io/resources/open-api/"
}
}
\ No newline at end of file
diff --git a/docs/swagger.yaml b/component/cron_task/docs/swagger.yaml
similarity index 86%
rename from docs/swagger.yaml
rename to component/cron_task/docs/swagger.yaml
index 3e85b6c4a..dd11b16d1 100644
--- a/docs/swagger.yaml
+++ b/component/cron_task/docs/swagger.yaml
@@ -12,16 +12,14 @@ definitions:
items:
type: string
type: array
- isRoot:
- description: 0-false 1-true
- type: string
name:
type: string
script:
+ description: 脚本内容,base64编码
type: string
taskType:
- description: 1-shell 2-cmd
- type: integer
+ description: CRON_TASK_TYPE
+ type: string
required:
- cronExpr
- name
@@ -39,16 +37,14 @@ definitions:
items:
type: string
type: array
- isRoot:
- description: 0-false 1-true
- type: string
name:
type: string
script:
+ description: 脚本内容,base64编码
type: string
taskType:
- description: 1-shell 2-cmd
- type: integer
+ description: CRON_TASK_TYPE
+ type: string
uuid:
type: string
required:
@@ -65,20 +61,12 @@ definitions:
- code
- msg
type: object
-externalDocs:
- description: OpenAPI
- url: https://swagger.io/resources/open-api/
info:
contact:
- email: support@swagger.io
name: API Support
- url: http://www.swagger.io/support
- description: This is a sample server celler server.
- license:
- name: Apache 2.0
- url: http://www.apache.org/licenses/LICENSE-2.0.html
- termsOfService: http://swagger.io/terms/
- title: Swagger Example API
+ url: https://github.com/hootrhino/rulex
+ description: Rulex Swagger API
+ title: Rulex API
version: "1.0"
paths:
/crontask/create:
@@ -102,7 +90,7 @@ paths:
summary: 创建定时任务
tags:
- crontask
- /crontask/delete:
+ /crontask/del:
delete:
consumes:
- application/json
@@ -218,7 +206,4 @@ paths:
summary: 更新定时任务
tags:
- crontask
-securityDefinitions:
- BasicAuth:
- type: basic
swagger: "2.0"
diff --git a/component/cron_task/process_manager.go b/component/cron_task/process_manager.go
index b57534126..439e131fd 100644
--- a/component/cron_task/process_manager.go
+++ b/component/cron_task/process_manager.go
@@ -4,11 +4,12 @@ import (
"encoding/base64"
"encoding/json"
"errors"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/plugin/http_server/model"
"io"
"os/exec"
"sync"
+
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/glogger"
)
// ProcessManager
@@ -30,25 +31,22 @@ func (pm *ProcessManager) RunProcess(file io.Writer, task model.MCronTask) error
var command *exec.Cmd
args := make([]string, 0)
- var name string
script, err := base64.StdEncoding.DecodeString(task.Script)
if err != nil {
glogger.GLogger.Error("parse script failed", err)
return err
}
if task.TaskType == CRON_TASK_TYPE_LINUX_SHELL {
- name = "/bin/bash"
args = append(args, "-c")
args = append(args, string(script))
args = append(args, "bash") // 占据$0位置
if task.Args != nil {
args = append(args, *task.Args) // 占据$1位置,此时$@与$1相同
}
-
} else {
return errors.New("unknown taskType")
}
- command = exec.Command(name, args...)
+ command = exec.Command(task.Command, args...)
command.SysProcAttr = GetSysProcAttr()
command.Stderr = file
command.Stdout = file
@@ -60,10 +58,13 @@ func (pm *ProcessManager) RunProcess(file io.Writer, task model.MCronTask) error
}
command.Env = envSlice
+ if err = command.Start(); err != nil {
+ return err
+ }
pm.runningProcess.Store(task.ID, command)
defer pm.runningProcess.Delete(task.ID)
- err = command.Run()
+ err = command.Wait()
if err != nil {
return err
}
diff --git a/component/datacenter/datacenter.go b/component/datacenter/datacenter.go
index 2a03c09f2..5702eedb0 100644
--- a/component/datacenter/datacenter.go
+++ b/component/datacenter/datacenter.go
@@ -54,7 +54,7 @@ func InitDataCenter(rulex typex.RuleX) {
func SchemaList() []SchemaDetail {
Schemas := []SchemaDetail{}
// 本地内部数据中心
- Schemas = append(Schemas, __DefaultDataCenter.LocalDb.GetSchemaDetail("INTERNAL_DATACENTER"))
+ Schemas = append(Schemas, __DefaultDataCenter.LocalDb.GetSchemaDetail("RULEX_INTERNAL_DATACENTER"))
// RPC的
trailer.AllGoods().Range(func(key, value any) bool {
goodsPs := (value.(*trailer.GoodsProcess))
@@ -82,10 +82,10 @@ func SchemaList() []SchemaDetail {
func GetSchemaDefine(goodsId string) (SchemaDefine, error) {
schemaDefine := SchemaDefine{}
// Rows 来自本地Sqlite查询
- if goodsId == "INTERNAL_DATACENTER" {
+ if goodsId == "RULEX_INTERNAL_DATACENTER" {
return SchemaDefine{
// 本地是固定写法INTERNAL_DATACENTER
- UUID: "INTERNAL_DATACENTER",
+ UUID: "RULEX_INTERNAL_DATACENTER",
Columns: []Column{},
}, nil
}
@@ -193,7 +193,7 @@ func Query(goodsId, query string) ([]map[string]any, error) {
// 本地
// Rows 来自本地Sqlite查询
- if goodsId == "INTERNAL_DATACENTER" {
+ if goodsId == "RULEX_INTERNAL_DATACENTER" {
LocalResult, err := __DefaultDataCenter.LocalDb.Query(goodsId, query)
return LocalResult, err
}
diff --git a/component/datacenter/datacenter_localdb.go b/component/datacenter/datacenter_localdb.go
index a9bd0f0ce..158278b43 100644
--- a/component/datacenter/datacenter_localdb.go
+++ b/component/datacenter/datacenter_localdb.go
@@ -44,8 +44,8 @@ func (ldb *LocalDb) Init() error {
func (ldb *LocalDb) GetSchemaDetail(goodsId string) SchemaDetail {
return SchemaDetail{
- UUID: "INTERNAL_DATACENTER",
- SchemaType: "INTERNAL_DATACENTER",
+ UUID: "RULEX_INTERNAL_DATACENTER",
+ SchemaType: "RULEX_INTERNAL_DATACENTER",
Name: "RULEX内置轻量级数据仓库",
LocalPath: ".local",
NetAddr: ".local",
@@ -87,6 +87,5 @@ func (ldb *LocalDb) Query(goodsId, query string) ([]map[string]any, error) {
result := []map[string]any{}
return result, ldb.Sqlite.db.Raw(query).Scan(&result).Error
}
-
- return []map[string]any{}, ldb.Sqlite.db.Raw(query).Error
+ return []map[string]any{}, ldb.Sqlite.db.Exec(query).Error
}
diff --git a/component/datacenter/datecenter_sqlite_driver.go b/component/datacenter/datecenter_sqlite_driver.go
index 3d2eedc2f..fa8b4fe6e 100644
--- a/component/datacenter/datecenter_sqlite_driver.go
+++ b/component/datacenter/datecenter_sqlite_driver.go
@@ -13,7 +13,7 @@ import (
"gorm.io/gorm/logger"
)
-const __DEFAULT_DB_PATH string = "./INTERNAL_DATACENTER.db"
+const __DEFAULT_DB_PATH string = "./RULEX_INTERNAL_DATACENTER.db"
var __Sqlite *SqliteDAO
diff --git a/component/eventbus/eventbus.go b/component/eventbus/eventbus.go
new file mode 100644
index 000000000..403ea4706
--- /dev/null
+++ b/component/eventbus/eventbus.go
@@ -0,0 +1,153 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package eventbus
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+)
+
+var __DefaultEventBus *EventBus
+
+/*
+*
+* 内部消息总线
+*
+ */
+type EventMessage struct {
+ Payload string
+}
+
+func (E EventMessage) String() string {
+ return fmt.Sprintf("Event Message@ Payload: %s", E.Payload)
+}
+
+type Topic struct {
+ Topic string
+ channel chan EventMessage
+ Subscribers map[string]*Subscriber
+ ctx context.Context
+ cancel context.CancelFunc
+}
+type Subscriber struct {
+ id string
+ Callback func(Topic string, Msg EventMessage)
+}
+type EventBus struct {
+ // Topic, chan EventMessage
+ // 给每个订阅者分配一个Channel,实现消息订阅
+ // Topic一样的会挂在同一个树上
+ Topics map[string]*Topic // 订阅树: MAP[]Subscribers
+}
+
+func InitEventBus() *EventBus {
+ __DefaultEventBus = &EventBus{
+ Topics: map[string]*Topic{},
+ }
+ return __DefaultEventBus
+}
+
+/*
+*
+* 订阅
+*
+ */
+func Subscribe(topic string, subscribe *Subscriber) {
+ NewUUID := utils.MakeUUID("SUB")
+ subscribe.id = NewUUID
+ var T *Topic
+ Ok := false
+ if T, Ok = __DefaultEventBus.Topics[topic]; Ok {
+ T.Subscribers[subscribe.id] = subscribe
+ } else {
+ T = new(Topic)
+ T.channel = make(chan EventMessage, 100)
+ T.Subscribers = map[string]*Subscriber{}
+ T.Topic = topic
+ T.Subscribers[subscribe.id] = subscribe
+ __DefaultEventBus.Topics[topic] = T
+ ctx, cancel := context.WithCancel(typex.GCTX)
+ T.ctx = ctx
+ T.cancel = cancel
+ go func(T *Topic) {
+ for {
+ select {
+ case <-T.ctx.Done():
+ {
+ return
+ }
+ case Msg := <-T.channel:
+ for _, Subscriber := range T.Subscribers {
+ if Subscriber.Callback != nil {
+ Subscriber.Callback(T.Topic, Msg)
+ }
+ }
+ }
+ }
+
+ }(T)
+ }
+
+}
+
+/*
+*
+* 取消订阅
+*
+ */
+func UnSubscribe(topic string, subscribe Subscriber) {
+ T, Ok1 := __DefaultEventBus.Topics[topic]
+ if Ok1 {
+ if _, Ok2 := T.Subscribers[subscribe.id]; Ok2 {
+ delete(__DefaultEventBus.Topics[topic].Subscribers, subscribe.id)
+ }
+ }
+ // 当没有订阅者的时候直接删除这个Topic
+ if len(T.Subscribers) == 0 {
+ T.cancel()
+ delete(__DefaultEventBus.Topics, topic)
+ }
+}
+
+/*
+*
+* 发布
+*
+ */
+func Publish(topic string, Msg EventMessage) {
+ T, Ok1 := __DefaultEventBus.Topics[topic]
+ if Ok1 {
+ T.channel <- Msg
+ }
+}
+
+/*
+*
+* 释放所有
+*
+ */
+func Flush() {
+ for _, T := range __DefaultEventBus.Topics {
+ for _, S := range T.Subscribers {
+ delete(T.Subscribers, S.id)
+ }
+ T.cancel()
+ delete(__DefaultEventBus.Topics, T.Topic)
+ }
+}
diff --git a/component/eventbus/eventbus.md b/component/eventbus/eventbus.md
new file mode 100644
index 000000000..f7e50b84a
--- /dev/null
+++ b/component/eventbus/eventbus.md
@@ -0,0 +1,36 @@
+# 内部消息总线
+类似于Nats一样的简单Pub\Sub框架
+## 示例
+```go
+
+package test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/hootrhino/rulex/component/eventbus"
+)
+
+func TestEventBus(t *testing.T) {
+
+ eventbus.InitEventBus()
+ eventbus.Subscribe("hello", &eventbus.Subscriber{
+ Callback: func(Topic string, Msg eventbus.EventMessage) {
+ t.Log("hello:", Msg)
+ },
+ })
+ start := time.Now()
+ for i := 0; i < 100; i++ {
+ eventbus.Publish("hello", eventbus.EventMessage{
+ Payload: fmt.Sprintf("world:%d", i),
+ })
+ }
+ duration := time.Since(start)
+ t.Log("time.Since(start):", duration)
+ time.Sleep(3 * time.Second)
+ eventbus.Flush()
+}
+
+```
\ No newline at end of file
diff --git a/component/hwportmanager/hwport_manager.go b/component/hwportmanager/hwport_manager.go
index 7d7a7b8db..f45adf1db 100644
--- a/component/hwportmanager/hwport_manager.go
+++ b/component/hwportmanager/hwport_manager.go
@@ -171,10 +171,9 @@ func FreeInterfaceBusy(name string) {
defer __HwPortsManager.lock.Unlock()
if Port, ok := __HwPortsManager.Interfaces[name]; ok {
if Port.OccupyBy.Type == "DEVICE" {
- // OccupierDevice := __HwPortsManager.rulex.GetDevice(Port.OccupyBy.UUID)
Port.Busy = false
Port.OccupyBy = HwPortOccupy{
- "", "", "",
+ "-", "-", "-",
}
__HwPortsManager.Interfaces[name] = Port
}
diff --git a/component/intercache/intercache.go b/component/intercache/intercache.go
new file mode 100644
index 000000000..98e1bded7
--- /dev/null
+++ b/component/intercache/intercache.go
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package intercache
+
+/*
+*
+* 内部缓存器
+*
+ */
+type InterCache interface {
+ RegisterSlot(Slot string) // 存储槽位, 释放资源的时候调用
+ UnRegisterSlot(Slot string) // 注销存储槽位, 释放资源的时候调用
+ Size() uint64 // 存储器当前长度
+ Flush() // 释放存储器空间
+}
diff --git a/component/intercache/modbus/modbus_cache.go b/component/intercache/modbus/modbus_cache.go
new file mode 100644
index 000000000..907521253
--- /dev/null
+++ b/component/intercache/modbus/modbus_cache.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) RegisterPoint later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT RegisterPoint WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package modbus
+
+import (
+ "sync"
+
+ "github.com/hootrhino/rulex/component/intercache"
+ "github.com/hootrhino/rulex/typex"
+)
+
+// 点位表
+type RegisterPoint struct {
+ UUID string
+ Status int
+ LastFetchTime uint64
+ Value string
+}
+
+var __DefaultModbusPointCache *ModbusPointCache
+
+func RegisterSlot(Slot string) {
+ __DefaultModbusPointCache.RegisterSlot(Slot)
+}
+func GetSlot(Slot string) map[string]RegisterPoint {
+ return __DefaultModbusPointCache.GetSlot(Slot)
+}
+func SetValue(Slot, K string, V RegisterPoint) {
+ __DefaultModbusPointCache.SetValue(Slot, K, V)
+}
+func GetValue(Slot, K string) RegisterPoint {
+ return __DefaultModbusPointCache.GetValue(Slot, K)
+}
+func DeleteValue(Slot, K string) {
+ __DefaultModbusPointCache.DeleteValue(Slot, K)
+}
+func UnRegisterSlot(Slot string) {
+ __DefaultModbusPointCache.UnRegisterSlot(Slot)
+}
+func Size() uint64 {
+ return __DefaultModbusPointCache.Size()
+}
+func Flush() {
+ __DefaultModbusPointCache.Flush()
+}
+
+//Modbus 点位运行时存储器
+
+type ModbusPointCache struct {
+ Slots map[string]map[string]RegisterPoint
+ ruleEngine typex.RuleX
+ lock sync.Mutex
+}
+
+func InitModbusPointCache(ruleEngine typex.RuleX) intercache.InterCache {
+ __DefaultModbusPointCache = &ModbusPointCache{
+ ruleEngine: ruleEngine,
+ Slots: map[string]map[string]RegisterPoint{},
+ lock: sync.Mutex{},
+ }
+ return __DefaultModbusPointCache
+}
+func (M *ModbusPointCache) RegisterSlot(Slot string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ M.Slots[Slot] = map[string]RegisterPoint{}
+}
+func (M *ModbusPointCache) GetSlot(Slot string) map[string]RegisterPoint {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ return S
+ }
+ return nil
+}
+func (M *ModbusPointCache) SetValue(Slot, K string, V RegisterPoint) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ S[K] = V
+ M.Slots[Slot] = S
+ }
+}
+func (M *ModbusPointCache) GetValue(Slot, K string) RegisterPoint {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ return S[K]
+ }
+ return RegisterPoint{}
+}
+func (M *ModbusPointCache) DeleteValue(Slot, K string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ delete(S, Slot)
+ }
+}
+func (M *ModbusPointCache) UnRegisterSlot(Slot string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ delete(M.Slots, Slot)
+}
+func (M *ModbusPointCache) Size() uint64 {
+ return uint64(len(M.Slots))
+}
+func (M *ModbusPointCache) Flush() {
+ for slotName, slot := range M.Slots {
+ for k, _ := range slot {
+ delete(slot, k)
+ }
+ delete(M.Slots, slotName)
+ }
+}
diff --git a/device/yk8_controller_device.md b/component/intercache/readme.md
similarity index 82%
rename from device/yk8_controller_device.md
rename to component/intercache/readme.md
index 65f4e272d..415804998 100644
--- a/device/yk8_controller_device.md
+++ b/component/intercache/readme.md
@@ -15,3 +15,5 @@
along with this program. If not, see .
-->
+# 内部缓存器
+主要用来做内存缓存加速使用,比如留给物模型来展示实时值等。是一个K-V存储器,其本质是Map。
\ No newline at end of file
diff --git a/component/intercache/siemens/siemens_cache.go b/component/intercache/siemens/siemens_cache.go
new file mode 100644
index 000000000..51050bc1e
--- /dev/null
+++ b/component/intercache/siemens/siemens_cache.go
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) SiemensPoint later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT SiemensPoint WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package siemens
+
+import (
+ "sync"
+
+ "github.com/hootrhino/rulex/component/intercache"
+ "github.com/hootrhino/rulex/typex"
+)
+
+var __DefaultSiemensPointCache *SiemensPointCache
+
+// 点位表
+type SiemensPoint struct {
+ UUID string
+ Status int
+ LastFetchTime uint64
+ Value string
+}
+
+func RegisterSlot(Slot string) {
+ __DefaultSiemensPointCache.RegisterSlot(Slot)
+}
+func GetSlot(Slot string) map[string]SiemensPoint {
+ return __DefaultSiemensPointCache.GetSlot(Slot)
+}
+func SetValue(Slot, K string, V SiemensPoint) {
+ __DefaultSiemensPointCache.SetValue(Slot, K, V)
+}
+func GetValue(Slot, K string) SiemensPoint {
+ return __DefaultSiemensPointCache.GetValue(Slot, K)
+}
+func DeleteValue(Slot, K string) {
+ __DefaultSiemensPointCache.DeleteValue(Slot, K)
+}
+func UnRegisterSlot(Slot string) {
+ __DefaultSiemensPointCache.UnRegisterSlot(Slot)
+}
+func Size() uint64 {
+ return __DefaultSiemensPointCache.Size()
+}
+func Flush() {
+ __DefaultSiemensPointCache.Flush()
+}
+
+type SiemensPointCache struct {
+ Slots map[string]map[string]SiemensPoint
+ ruleEngine typex.RuleX
+ lock sync.Mutex
+}
+
+func InitSiemensPointCache(ruleEngine typex.RuleX) intercache.InterCache {
+ __DefaultSiemensPointCache = &SiemensPointCache{
+ ruleEngine: ruleEngine,
+ Slots: map[string]map[string]SiemensPoint{},
+ lock: sync.Mutex{},
+ }
+ return __DefaultSiemensPointCache
+}
+func (M *SiemensPointCache) RegisterSlot(Slot string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ M.Slots[Slot] = map[string]SiemensPoint{}
+}
+func (M *SiemensPointCache) GetSlot(Slot string) map[string]SiemensPoint {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ return S
+ }
+ return nil
+}
+func (M *SiemensPointCache) SetValue(Slot, K string, V SiemensPoint) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ S[K] = V
+ M.Slots[Slot] = S
+ }
+}
+func (M *SiemensPointCache) GetValue(Slot, K string) SiemensPoint {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ return S[K]
+ }
+ return SiemensPoint{}
+}
+func (M *SiemensPointCache) DeleteValue(Slot, K string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ if S, ok := M.Slots[Slot]; ok {
+ delete(S, Slot)
+ }
+}
+func (M *SiemensPointCache) UnRegisterSlot(Slot string) {
+ M.lock.Lock()
+ defer M.lock.Unlock()
+ delete(M.Slots, Slot)
+}
+func (M *SiemensPointCache) Size() uint64 {
+ return uint64(len(M.Slots))
+}
+func (M *SiemensPointCache) Flush() {
+ for slotName, slot := range M.Slots {
+ for k, _ := range slot {
+ delete(slot, k)
+ }
+ delete(M.Slots, slotName)
+ }
+}
diff --git a/component/intercache/thingsschema/things_schema_cache.go b/component/intercache/thingsschema/things_schema_cache.go
new file mode 100644
index 000000000..d14efc00c
--- /dev/null
+++ b/component/intercache/thingsschema/things_schema_cache.go
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package intercache
+
+type ThingsSchemaCache struct {
+}
diff --git a/component/interdb/inter_sqlite_db.go b/component/interdb/inter_sqlite_db.go
index 50d443be8..08b5ddc1c 100644
--- a/component/interdb/inter_sqlite_db.go
+++ b/component/interdb/inter_sqlite_db.go
@@ -51,6 +51,7 @@ func Init(engine typex.RuleX, dbPath string) error {
if err != nil {
glogger.GLogger.Fatal(err)
}
+ __Sqlite.db.Exec("VACUUM;")
return err
}
diff --git a/component/internotify/internotify.go b/component/internotify/internotify.go
new file mode 100644
index 000000000..84e9dbdee
--- /dev/null
+++ b/component/internotify/internotify.go
@@ -0,0 +1,134 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package internotify
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+)
+
+var __DefaultInternalEventBus *InternalEventBus
+
+// ---------------------------------------------------------
+// Type
+// ---------------------------------------------------------
+// - SOURCE: 南向事件
+// - DEVICE: 设备事件
+// - TARGET: 北向事件
+// - SYSTEM: 系统内部事件
+// - HARDWARE: 硬件事件
+
+type BaseEvent struct {
+ Type string
+ Event string
+ Ts uint64
+ Info interface{}
+}
+
+func (be BaseEvent) String() string {
+ return fmt.Sprintf(
+ `BaseEvent@Type:%s, Event:%s, Ts:%d, Info:%s`,
+ be.Type, be.Event, be.Ts, be.Info)
+
+}
+
+/*
+*
+* Push
+*
+ */
+func Push(e BaseEvent) error {
+ if len(__DefaultInternalEventBus.Queue)+1 > __DefaultInternalEventBus.GetSize() {
+ msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v",
+ __DefaultInternalEventBus.GetSize(), len(__DefaultInternalEventBus.Queue)+1)
+ glogger.GLogger.Error(msg)
+ return errors.New(msg)
+ } else {
+ __DefaultInternalEventBus.Queue <- e
+ return nil
+ }
+}
+
+/*
+*
+* 内部事件总线
+*
+ */
+type InternalEventBus struct {
+ Queue chan BaseEvent
+ Rulex typex.RuleX
+ SourceCount uint
+}
+
+func (q *InternalEventBus) GetSize() int {
+ return cap(q.Queue)
+}
+func RemoveSource() {
+ if __DefaultInternalEventBus.SourceCount > 0 {
+ __DefaultInternalEventBus.SourceCount--
+ }
+}
+func AddSource() {
+ __DefaultInternalEventBus.SourceCount++
+}
+func GetQueue() chan BaseEvent {
+ return __DefaultInternalEventBus.Queue
+}
+
+/*
+*
+ - 内部事件,例如资源挂了或者设备离线、超时等等,该资源是单例模式,
+ 维护一个channel来接收各种事件,将收到的消息吐到InterQueue即可
+
+*
+*/
+func InitInternalEventBus(r typex.RuleX, MaxQueueSize int) *InternalEventBus {
+ __DefaultInternalEventBus = new(InternalEventBus)
+ __DefaultInternalEventBus.Queue = make(chan BaseEvent, 1024)
+ __DefaultInternalEventBus.Rulex = r
+ return __DefaultInternalEventBus
+}
+
+/*
+*
+* 监控chan
+*
+ */
+func StartInternalEventQueue() {
+ go func(ctx context.Context, InternalEventBus *InternalEventBus) {
+ for {
+ // 当无订阅者时,及时释放channel里面的数据
+ if __DefaultInternalEventBus.SourceCount == 0 {
+ select {
+ case <-ctx.Done():
+ return
+ case Event := <-InternalEventBus.Queue:
+ {
+ //
+ // TODO 内部事件应该写入数据库, 主要是起通知作用
+ //
+ glogger.GLogger.Debug("Internal Event:", Event)
+
+ }
+ }
+ }
+ }
+ }(typex.GCTX, __DefaultInternalEventBus)
+}
diff --git a/component/internotify/internotify.md b/component/internotify/internotify.md
new file mode 100644
index 000000000..9f2752a8d
--- /dev/null
+++ b/component/internotify/internotify.md
@@ -0,0 +1,38 @@
+# 内部消息源
+可以使用这个数据源来监控系统内部的消息,比如设备离线,资源连接失败等。
+
+## 消息类型
+- SOURCE: 南向事件
+- DEVICE: 设备事件
+- TARGET: 北向事件
+- SYSTEM: 系统内部事件
+- HARDWARE: 硬件事件
+
+## 示例
+### 设备上线:
+```json
+{
+ "type" :"DEVICE",
+ "event":"event.connected",
+ "ts":121312431432,
+ "device_info":{
+ "uuid":"UUID1234567",
+ "name":"温湿度计"
+ }
+}
+
+```
+
+### 设备离线:
+```json
+{
+ "type" :"DEVICE",
+ "event":"event.disconnected",
+ "ts":121312431432,
+ "device_info":{
+ "uuid":"UUID1234567",
+ "name":"温湿度计"
+ }
+}
+
+```
\ No newline at end of file
diff --git a/component/interpipeline/xpipline.go b/component/interpipeline/xpipline.go
index 1b01e4777..d1eafec40 100644
--- a/component/interpipeline/xpipline.go
+++ b/component/interpipeline/xpipline.go
@@ -66,7 +66,14 @@ func Execute(vm *lua.LState, k string, args ...lua.LValue) (interface{}, error)
return nil, errors.New("target:" + k + " is not a lua function")
}
-// callLuaFunc
+/*
+*
+*
+
+ callLuaFunc
+
+*
+*/
func callLuaFunc(vm *lua.LState, callable *lua.LFunction, args ...lua.LValue) ([]lua.LValue, error) {
if callable == nil {
return nil, errors.New("callable function is not exists")
@@ -79,9 +86,40 @@ func callLuaFunc(vm *lua.LState, callable *lua.LFunction, args ...lua.LValue) ([
if err != nil {
return nil, err
}
- vm.Pop(-1)
- vm.Pop(-2)
- vm.Pop(-3)
+ //vm.Pop(-1)
+ //vm.Pop(-2)
+ //vm.Pop(-3) // 这是我很早以前写的代码,
+ // // 但是今天突然发现看不懂为啥这里要Pop栈?
+ // 2023 12-01 删除
+ // return data, true|false
return []lua.LValue{vm.Get(-2), vm.Get(-1)}, nil
}
+
+// #include
+// #include
+// #include
+
+// int main() {
+// lua_State *L = luaL_newstate();
+// luaL_openlibs(L);
+// if (luaL_dofile(L, "example.lua") != LUA_OK) {
+// fprintf(stderr, "Error running Lua script: %s\n", lua_tostring(L, -1));
+// lua_close(L);
+// return 1;
+// }
+// lua_getglobal(L, "my_lua_function");
+// lua_pushinteger(L, 10);
+// lua_pushinteger(L, 20);
+// if (lua_pcall(L, 2, 2, 0) != LUA_OK) {
+// fprintf(stderr, "Error calling Lua function: %s\n", lua_tostring(L, -1));
+// lua_close(L);
+// return 1;
+// }
+// int result1 = lua_tointeger(L, -2);
+// int result2 = lua_tointeger(L, -1);
+// printf("Result 1: %d\n", result1);
+// printf("Result 2: %d\n", result2);
+// lua_close(L);
+// return 0;
+// }
diff --git a/component/interqueue/xqueue.go b/component/interqueue/xqueue.go
index a88bc46a3..bb13a0f3c 100644
--- a/component/interqueue/xqueue.go
+++ b/component/interqueue/xqueue.go
@@ -5,8 +5,8 @@ import (
"errors"
"fmt"
- "github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/component/intermetric"
+ "github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
)
@@ -19,6 +19,9 @@ var DefaultDataCacheQueue XQueue
*/
type XQueue interface {
GetQueue() chan QueueData
+ GetInQueue() chan QueueData
+ GetOutQueue() chan QueueData
+ GetDeviceQueue() chan QueueData
GetSize() int
Push(QueueData) error
PushQueue(QueueData) error
@@ -52,14 +55,20 @@ func (qd QueueData) String() string {
*
*/
type DataCacheQueue struct {
- Queue chan QueueData
- rulex typex.RuleX
+ Queue chan QueueData
+ OutQueue chan QueueData
+ InQueue chan QueueData
+ DeviceQueue chan QueueData
+ rulex typex.RuleX
}
func InitDataCacheQueue(rulex typex.RuleX, maxQueueSize int) XQueue {
DefaultDataCacheQueue = &DataCacheQueue{
- Queue: make(chan QueueData, maxQueueSize),
- rulex: rulex,
+ Queue: make(chan QueueData, maxQueueSize),
+ OutQueue: make(chan QueueData, maxQueueSize),
+ InQueue: make(chan QueueData, maxQueueSize),
+ DeviceQueue: make(chan QueueData, maxQueueSize),
+ rulex: rulex,
}
return DefaultDataCacheQueue
}
@@ -73,10 +82,12 @@ func (q *DataCacheQueue) GetSize() int {
*
*/
func (q *DataCacheQueue) Push(d QueueData) error {
- // glogger.GLogger.Debug("DataCacheQueue Push:", d.Data)
- // 比较数据和容积
+ // 动态扩容
+ // if len(q.Queue)+1 > q.GetSize() {
+ // }
if len(q.Queue)+1 > q.GetSize() {
- msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v", q.GetSize(), len(q.Queue)+1)
+ msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v",
+ q.GetSize(), len(q.Queue)+1)
glogger.GLogger.Error(msg)
return errors.New(msg)
} else {
@@ -94,8 +105,34 @@ func (q *DataCacheQueue) GetQueue() chan QueueData {
return q.Queue
}
-// 此处内置的消息队列用了go的channel, 看似好像很简单,但是经过测试发现完全满足网关需求,甚至都性能过剩了
-// 因此大家看到这里务必担心, 我也知道有很精美的高级框架, 但是用简单的方法来实现功能不是更好吗?
+/*
+*
+* GetQueue
+*
+ */
+func (q *DataCacheQueue) GetInQueue() chan QueueData {
+ return q.InQueue
+}
+
+/*
+*
+* GetQueue
+*
+ */
+func (q *DataCacheQueue) GetOutQueue() chan QueueData {
+ return q.OutQueue
+}
+
+/*
+*
+*GetDeviceQueue
+*
+ */
+func (q *DataCacheQueue) GetDeviceQueue() chan QueueData {
+ return q.DeviceQueue
+}
+
+// TODO: 下个版本更换为可扩容的Chan
func StartDataCacheQueue() {
go func(ctx context.Context, xQueue XQueue) {
@@ -103,22 +140,45 @@ func StartDataCacheQueue() {
select {
case <-ctx.Done():
return
- case qd := <-xQueue.GetQueue():
+ // 这个地方不能阻塞,需要借助一个外部queue
+ // push qd -> Queue
+ case qd := <-xQueue.GetInQueue():
+ {
+ if qd.I != nil {
+ qd.E.RunSourceCallbacks(qd.I, qd.Data)
+ }
+ }
+ case qd := <-xQueue.GetDeviceQueue():
+ {
+ if qd.D != nil {
+ qd.E.RunDeviceCallbacks(qd.D, qd.Data)
+ }
+ }
+ case qd := <-xQueue.GetOutQueue():
+ {
+ if qd.O != nil {
+ v, ok := qd.E.AllOutEnd().Load(qd.O.UUID)
+ if ok {
+ target := v.(*typex.OutEnd).Target
+ if target == nil {
+ continue
+ }
+ if _, err := target.To(qd.Data); err != nil {
+ glogger.GLogger.Error(err)
+ intermetric.IncOutFailed()
+ } else {
+ intermetric.IncOut()
+ }
+ }
+ }
+ }
+ case qd := <-xQueue.GetQueue(): // 马上废弃
{
- //
- // Rulex内置消息队列用法:
- // 1 进来的数据缓存
- // 2 出去的消息缓存
- // 3 设备数据缓存
- // 只需要判断 in 或者 out 是不是 nil即可
- //
if qd.I != nil {
- // 如果是Debug消息直接打印出来
qd.E.RunSourceCallbacks(qd.I, qd.Data)
}
if qd.D != nil {
qd.E.RunDeviceCallbacks(qd.D, qd.Data)
- qd.E.RunHooks(qd.Data)
}
if qd.O != nil {
v, ok := qd.E.AllOutEnd().Load(qd.O.UUID)
@@ -141,16 +201,27 @@ func StartDataCacheQueue() {
}(typex.GCTX, DefaultDataCacheQueue)
}
+/*
+*
+*
+*
+ */
func (q *DataCacheQueue) PushQueue(qd QueueData) error {
err := DefaultDataCacheQueue.Push(qd)
if err != nil {
- glogger.GLogger.Error("PushQueue error:", err)
- // q.rulex.MetricStatistics.IncInFailed()
+ glogger.GLogger.Error("PushInQueue error:", err)
+ intermetric.IncInFailed()
} else {
- // e.MetricStatistics.IncIn()
+ intermetric.IncIn()
}
return err
}
+
+/*
+*
+*PushInQueue
+*
+ */
func (q *DataCacheQueue) PushInQueue(in *typex.InEnd, data string) error {
qd := QueueData{
E: q.rulex,
@@ -158,19 +229,19 @@ func (q *DataCacheQueue) PushInQueue(in *typex.InEnd, data string) error {
O: nil,
Data: data,
}
- err := DefaultDataCacheQueue.Push(qd)
+ err := q.pushIn(qd)
if err != nil {
- glogger.GLogger.Error("PushInQueue error:", err)
- // e.MetricStatistics.IncInFailed()
+ glogger.GLogger.Error("Push InQueue error:", err)
+ intermetric.IncInFailed()
} else {
- // e.MetricStatistics.IncIn()
+ intermetric.IncIn()
}
return err
}
/*
*
-* 设备数据入流引擎
+* PushDeviceQueue
*
*/
func (q *DataCacheQueue) PushDeviceQueue(Device *typex.Device, data string) error {
@@ -181,15 +252,21 @@ func (q *DataCacheQueue) PushDeviceQueue(Device *typex.Device, data string) erro
O: nil,
Data: data,
}
- err := DefaultDataCacheQueue.Push(qd)
+ err := q.pushDevice(qd)
if err != nil {
- glogger.GLogger.Error("PushInQueue error:", err)
- // q.rulex.MetricStatistics.IncInFailed()
+ glogger.GLogger.Error("Push Device Queue error:", err)
+ intermetric.IncInFailed()
} else {
- // q.rulex.MetricStatistics.IncIn()
+ intermetric.IncIn()
}
return err
}
+
+/*
+*
+* PushOutQueue
+*
+ */
func (q *DataCacheQueue) PushOutQueue(out *typex.OutEnd, data string) error {
qd := QueueData{
E: q.rulex,
@@ -198,12 +275,72 @@ func (q *DataCacheQueue) PushOutQueue(out *typex.OutEnd, data string) error {
O: out,
Data: data,
}
- err := DefaultDataCacheQueue.Push(qd)
+ err := q.pushOut(qd)
if err != nil {
- glogger.GLogger.Error("PushOutQueue error:", err)
- // e.MetricStatistics.IncInFailed()
+ glogger.GLogger.Error("Push OutQueue error:", err)
+ intermetric.IncInFailed()
} else {
- // e.MetricStatistics.IncIn()
+ intermetric.IncIn()
}
return err
}
+
+/*
+*
+* Push
+*
+ */
+func (q *DataCacheQueue) pushIn(d QueueData) error {
+ // 动态扩容
+ // if len(q.Queue)+1 > q.GetSize() {
+ // }
+ if len(q.InQueue)+1 > q.GetSize() {
+ msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v",
+ q.GetSize(), len(q.Queue)+1)
+ glogger.GLogger.Error(msg)
+ return errors.New(msg)
+ } else {
+ q.InQueue <- d
+ return nil
+ }
+}
+
+/*
+*
+* Push
+*
+ */
+func (q *DataCacheQueue) pushOut(d QueueData) error {
+ // 动态扩容
+ // if len(q.Queue)+1 > q.GetSize() {
+ // }
+ if len(q.OutQueue)+1 > q.GetSize() {
+ msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v",
+ q.GetSize(), len(q.Queue)+1)
+ glogger.GLogger.Error(msg)
+ return errors.New(msg)
+ } else {
+ q.OutQueue <- d
+ return nil
+ }
+}
+
+/*
+*
+* Push
+*
+ */
+func (q *DataCacheQueue) pushDevice(d QueueData) error {
+ // 动态扩容
+ // if len(q.Queue)+1 > q.GetSize() {
+ // }
+ if len(q.DeviceQueue)+1 > q.GetSize() {
+ msg := fmt.Sprintf("attached max queue size, max size is:%v, current size is: %v",
+ q.GetSize(), len(q.Queue)+1)
+ glogger.GLogger.Error(msg)
+ return errors.New(msg)
+ } else {
+ q.DeviceQueue <- d
+ return nil
+ }
+}
diff --git a/component/interqueue/yqueue.go b/component/interqueue/yqueue.go
index 1ed4b888d..0a4442d25 100644
--- a/component/interqueue/yqueue.go
+++ b/component/interqueue/yqueue.go
@@ -48,8 +48,8 @@ type InteractQueue struct {
func InitInteractQueue(rulex typex.RuleX, maxQueueSize int) *InteractQueue {
__DefaultInteractQueue = InteractQueue{
- inQueue: make(chan InteractQueueData, maxQueueSize),
- outQueue: make(chan InteractQueueData, maxQueueSize),
+ inQueue: make(chan InteractQueueData, 1),
+ outQueue: make(chan InteractQueueData, 1),
rulex: rulex,
}
return &__DefaultInteractQueue
diff --git a/component/rtspserver/rtsp_server.go b/component/rtspserver/rtsp_server.go
index 5f64ecdd7..c0abed69a 100644
--- a/component/rtspserver/rtsp_server.go
+++ b/component/rtspserver/rtsp_server.go
@@ -53,7 +53,7 @@ type rtspServer struct {
}
// NewRouter Gin 路由配置
-func InitRtspServer() *rtspServer {
+func InitRtspServer(rulex typex.RuleX) *rtspServer {
gin.SetMode(gin.ReleaseMode)
__DefaultRtspServer = &rtspServer{
webServer: gin.New(),
@@ -68,7 +68,7 @@ func InitRtspServer() *rtspServer {
group.POST("/ffmpegPush", func(ctx *gin.Context) {
LiveId := ctx.Query("liveId")
// Token := ctx.Query("token")
- glogger.GLogger.Info("Try to load RTSP From:", LiveId)
+ glogger.GLogger.Info("Receive stream push From:", LiveId)
// http://127.0.0.1:9400 :后期通过参数传进
// 启动一个FFMPEG开始从摄像头拉流
bodyReader := bufio.NewReader(ctx.Request.Body)
@@ -97,10 +97,10 @@ func InitRtspServer() *rtspServer {
return __DefaultRtspServer
}
func pushToWebsocket(liveId string, data []byte) {
- // fmt.Println(liveId, data)
if C, Ok := __DefaultRtspServer.websocketPlayerManager.Clients[liveId]; Ok {
- C.WriteMessage(2, data)
+ C.WriteMessage(websocket.BinaryMessage, data)
}
+
}
/*
@@ -168,13 +168,7 @@ func NewPlayerManager() *websocketPlayerManager {
* 启动服务
*
*/
-type wsToken struct {
- Token string `json:"token"`
- LiveId string `json:"live_id"`
-}
-
func wsServerEndpoint(c *gin.Context) {
- //upgrade get request to websocket protocol
wsConn, err := __DefaultRtspServer.websocketPlayerManager.WsServer.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
@@ -183,19 +177,18 @@ func wsServerEndpoint(c *gin.Context) {
Token := c.Query("token")
if Token != "WebRtspPlayer" {
- wsConn.WriteMessage(1, []byte("Invalid client token"))
+ wsConn.WriteMessage(websocket.CloseMessage, []byte("Invalid client token"))
wsConn.Close()
return
}
glogger.GLogger.Debugf("Request live:%s, Token is :%s", LiveId, Token)
// 最多允许连接10个客户端,实际情况下根本用不了那么多
- if len(__DefaultRtspServer.websocketPlayerManager.Clients) >= 2 {
+ if len(__DefaultRtspServer.websocketPlayerManager.Clients) >= 10 {
wsConn.WriteMessage(websocket.CloseMessage, []byte{})
wsConn.Close()
return
}
__DefaultRtspServer.websocketPlayerManager.Clients[LiveId] = wsConn
- wsConn.WriteMessage(websocket.TextMessage, []byte("Connected"))
glogger.GLogger.Info("WebSocket Player connected:" + wsConn.RemoteAddr().String())
wsConn.SetCloseHandler(func(code int, text string) error {
glogger.GLogger.Info("wsConn CloseHandler:", wsConn.RemoteAddr().String())
@@ -211,14 +204,6 @@ func wsServerEndpoint(c *gin.Context) {
return nil
})
go func(wsConn *websocket.Conn) {
- defer func() {
- if wsConn != nil {
- glogger.GLogger.Info("wsConn Disconnect By accident:", wsConn.RemoteAddr().String())
- __DefaultRtspServer.websocketPlayerManager.lock.Lock()
- delete(__DefaultRtspServer.websocketPlayerManager.Clients, wsConn.RemoteAddr().String())
- __DefaultRtspServer.websocketPlayerManager.lock.Unlock()
- }
- }()
for {
select {
case <-typex.GCTX.Done():
diff --git a/plugin/http_server/apis/aibase_api.go b/component/rulex_api_server/apis/aibase_api.go
similarity index 94%
rename from plugin/http_server/apis/aibase_api.go
rename to component/rulex_api_server/apis/aibase_api.go
index c41fd2537..94e8f1497 100644
--- a/plugin/http_server/apis/aibase_api.go
+++ b/component/rulex_api_server/apis/aibase_api.go
@@ -3,7 +3,7 @@ package apis
import (
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/aibase"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/typex"
)
diff --git a/plugin/http_server/apis/appstack_api.go b/component/rulex_api_server/apis/appstack_api.go
similarity index 91%
rename from plugin/http_server/apis/appstack_api.go
rename to component/rulex_api_server/apis/appstack_api.go
index 90d95f2c9..efae50233 100644
--- a/plugin/http_server/apis/appstack_api.go
+++ b/component/rulex_api_server/apis/appstack_api.go
@@ -3,9 +3,9 @@ package apis
import (
"fmt"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/appstack"
@@ -20,14 +20,14 @@ import (
*
*/
type appStackDto struct {
- UUID string `json:"uuid,omitempty"` // 名称
- Name string `json:"name,omitempty"` // 名称
- Version string `json:"version,omitempty"` // 版本号
- AutoStart *bool `json:"autoStart,omitempty"` // 自动启动
- AppState int `json:"appState,omitempty"` // 状态: 1 运行中, 0 停止
- Type string `json:"type,omitempty"` // 默认就是lua, 留个扩展以后可能支持别的
- LuaSource string `json:"luaSource,omitempty"`
- Description string `json:"description,omitempty"`
+ UUID string `json:"uuid,omitempty"` // 名称
+ Name string `json:"name"` // 名称
+ Version string `json:"version"` // 版本号
+ AutoStart *bool `json:"autoStart"` // 自动启动
+ AppState int `json:"appState"` // 状态: 1 运行中, 0 停止
+ Type string `json:"type"` // 默认就是lua, 留个扩展以后可能支持别的
+ LuaSource string `json:"luaSource"` // Lua源码
+ Description string `json:"description"`
}
/*
diff --git a/plugin/http_server/apis/crontask_api.go b/component/rulex_api_server/apis/crontask_api.go
similarity index 93%
rename from plugin/http_server/apis/crontask_api.go
rename to component/rulex_api_server/apis/crontask_api.go
index 381e1bff7..666e3712c 100644
--- a/plugin/http_server/apis/crontask_api.go
+++ b/component/rulex_api_server/apis/crontask_api.go
@@ -2,12 +2,13 @@ package apis
import (
"errors"
+
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/cron_task"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/dto"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ "github.com/hootrhino/rulex/component/rulex_api_server/dto"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
)
@@ -46,7 +47,7 @@ func CreateCronTask(c *gin.Context, ruleEngine typex.RuleX) (any, error) {
// @Accept json
// @Produce json
// @Success 200 {object} httpserver.R
-// @Router /crontask/delete [delete]
+// @Router /crontask/del [delete]
func DeleteCronTask(c *gin.Context, ruleEngine typex.RuleX) (any, error) {
uuid := c.Query("uuid")
err := service.DeleteScheduleTask(uuid)
@@ -108,7 +109,7 @@ func StartTask(c *gin.Context, ruleEngine typex.RuleX) (any, error) {
// 0. 更新数据库
db := interdb.DB()
task := model.MCronTask{}
- task.Enable = "1"
+ task.Enable = &cron_task.CRON_TASK_EANBLE
tx := db.Where("uuid = ?", uuid).Updates(&task)
if tx.Error != nil {
return nil, tx.Error
@@ -147,7 +148,7 @@ func StopTask(c *gin.Context, ruleEngine typex.RuleX) (any, error) {
// 0. 更新数据库
db := interdb.DB()
task := model.MCronTask{}
- task.Enable = "0"
+ task.Enable = &cron_task.CRON_TASK_DISABLE
tx := db.Where("uuid = ?", uuid).Updates(&task)
if tx.Error != nil {
return nil, tx.Error
diff --git a/plugin/http_server/apis/crontaskresult_api.go b/component/rulex_api_server/apis/crontaskresult_api.go
similarity index 90%
rename from plugin/http_server/apis/crontaskresult_api.go
rename to component/rulex_api_server/apis/crontaskresult_api.go
index c4df50157..5e1610498 100644
--- a/plugin/http_server/apis/crontaskresult_api.go
+++ b/component/rulex_api_server/apis/crontaskresult_api.go
@@ -3,8 +3,8 @@ package apis
import (
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
)
diff --git a/plugin/http_server/apis/datacenter_api.go b/component/rulex_api_server/apis/datacenter_api.go
similarity index 97%
rename from plugin/http_server/apis/datacenter_api.go
rename to component/rulex_api_server/apis/datacenter_api.go
index 07871b35e..5c27544c6 100644
--- a/plugin/http_server/apis/datacenter_api.go
+++ b/component/rulex_api_server/apis/datacenter_api.go
@@ -18,7 +18,7 @@ package apis
import (
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/datacenter"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/typex"
)
diff --git a/plugin/http_server/apis/dataschema_api.go b/component/rulex_api_server/apis/dataschema_api.go
similarity index 95%
rename from plugin/http_server/apis/dataschema_api.go
rename to component/rulex_api_server/apis/dataschema_api.go
index 86b7b6ab1..bfe820107 100644
--- a/plugin/http_server/apis/dataschema_api.go
+++ b/component/rulex_api_server/apis/dataschema_api.go
@@ -4,10 +4,10 @@ import (
"encoding/json"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/core"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
diff --git a/component/rulex_api_server/apis/device_api.go b/component/rulex_api_server/apis/device_api.go
new file mode 100644
index 000000000..7a1afff0e
--- /dev/null
+++ b/component/rulex_api_server/apis/device_api.go
@@ -0,0 +1,261 @@
+package apis
+
+import (
+ "fmt"
+
+ "github.com/hootrhino/rulex/component/interdb"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
+ "gorm.io/gorm"
+
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+
+ "github.com/gin-gonic/gin"
+ "gopkg.in/square/go-jose.v2/json"
+)
+
+type DeviceVo struct {
+ UUID string `json:"uuid"`
+ Gid string `json:"gid"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ State int `json:"state"`
+ Config map[string]interface{} `json:"config"`
+ Description string `json:"description"`
+}
+
+/*
+*
+* 列表先读数据库,然后读内存,合并状态后输出
+*
+ */
+func DeviceDetail(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ mdev, err := service.GetMDeviceWithUUID(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400EmptyObj(err))
+ return
+ }
+ DeviceVo := DeviceVo{}
+ DeviceVo.UUID = mdev.UUID
+ DeviceVo.Name = mdev.Name
+ DeviceVo.Type = mdev.Type
+ DeviceVo.Description = mdev.Description
+ DeviceVo.Config = mdev.GetConfig()
+ //
+ device := ruleEngine.GetDevice(mdev.UUID)
+ if device == nil {
+ DeviceVo.State = int(typex.DEV_STOP)
+ } else {
+ DeviceVo.State = int(device.Device.Status())
+ }
+ Group := service.GetVisualGroup(mdev.UUID)
+ DeviceVo.Gid = Group.UUID
+ c.JSON(common.HTTP_OK, common.OkWithData(DeviceVo))
+}
+
+/*
+*
+* 分组查看
+*
+ */
+func ListDeviceByGroup(c *gin.Context, ruleEngine typex.RuleX) {
+ Gid, _ := c.GetQuery("uuid")
+ devices := []DeviceVo{}
+ MDevices := service.FindDeviceByGroup(Gid)
+ for _, mdev := range MDevices {
+ DeviceVo := DeviceVo{}
+ DeviceVo.UUID = mdev.UUID
+ DeviceVo.Name = mdev.Name
+ DeviceVo.Type = mdev.Type
+ DeviceVo.Description = mdev.Description
+ DeviceVo.Config = mdev.GetConfig()
+ //
+ device := ruleEngine.GetDevice(mdev.UUID)
+ if device == nil {
+ DeviceVo.State = int(typex.DEV_STOP)
+ } else {
+ DeviceVo.State = int(device.Device.Status())
+ }
+ Group := service.GetVisualGroup(mdev.UUID)
+ DeviceVo.Gid = Group.UUID
+ devices = append(devices, DeviceVo)
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(devices))
+}
+
+// 重启
+func RestartDevice(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ err := ruleEngine.RestartDevice(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+// 删除设备
+func DeleteDevice(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ Mdev, err := service.GetMDeviceWithUUID(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // 检查是否有规则被绑定了
+ for _, ruleId := range Mdev.BindRules {
+ if ruleId != "" {
+ _, err0 := service.GetMRuleWithUUID(ruleId)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Error("Can't remove, Already have rule bind:"+Mdev.BindRules.String()))
+ return
+ }
+
+ }
+
+ // 检查是否通用Modbus设备.需要同步删除点位表记录
+ if Mdev.Type == "GENERIC_MODBUS" {
+ if err := service.DeleteAllModbusPointByDevice(uuid); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ }
+ // 西门子的
+ if Mdev.Type == "SIEMENS_PLC" {
+ if err := service.DeleteAllSiemensPointByDevice(uuid); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ }
+ old := ruleEngine.GetDevice(uuid)
+ if old != nil {
+ if old.Device.Status() == typex.DEV_UP {
+ old.Device.Stop()
+ }
+ }
+ // 事务
+ txErr := interdb.DB().Transaction(func(tx *gorm.DB) error {
+ Group := service.GetVisualGroup(uuid)
+ err3 := service.DeleteDevice(uuid)
+ if err3 != nil {
+ return err3
+ }
+ // 解除关联
+ err2 := interdb.DB().Where("gid=? and rid =?", Group.UUID, uuid).
+ Delete(&model.MGenericGroupRelation{}).Error
+ if err2 != nil {
+ return err2
+ }
+ ruleEngine.RemoveDevice(uuid)
+ return nil
+ })
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
+// 创建设备
+func CreateDevice(c *gin.Context, ruleEngine typex.RuleX) {
+ form := DeviceVo{}
+ if err := c.ShouldBindJSON(&form); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ configJson, err := json.Marshal(form.Config)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ isSingle := false
+ // 红外线是单例模式
+ if form.Type == typex.INTERNAL_EVENT.String() {
+ ruleEngine.AllDevices().Range(func(key, value any) bool {
+ In := value.(*typex.Device)
+ if In.Type.String() == form.Type {
+ isSingle = true
+ return false
+ }
+ return true
+ })
+ }
+ if isSingle {
+ msg := fmt.Errorf("the %s is singleton Device, can not create again", form.Name)
+ c.JSON(common.HTTP_OK, common.Error400(msg))
+ return
+ }
+ newUUID := utils.DeviceUuid()
+ MDevice := model.MDevice{
+ UUID: newUUID,
+ Type: form.Type,
+ Name: form.Name,
+ Description: form.Description,
+ Config: string(configJson),
+ BindRules: []string{},
+ }
+ if err := service.InsertDevice(&MDevice); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // 新建大屏的时候必须给一个分组
+ if err := service.BindResource(form.Gid, MDevice.UUID); err != nil {
+ c.JSON(common.HTTP_OK, common.Error("Group not found"))
+ return
+ }
+ if err := server.LoadNewestDevice(newUUID, ruleEngine); err != nil {
+ c.JSON(common.HTTP_OK, common.OkWithMsg(err.Error()))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+// 更新设备
+func UpdateDevice(c *gin.Context, ruleEngine typex.RuleX) {
+
+ form := DeviceVo{}
+ if err := c.ShouldBindJSON(&form); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ configJson, err := json.Marshal(form.Config)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ if form.UUID == "" {
+ c.JSON(common.HTTP_OK, common.Error("missing 'uuid' fields"))
+ return
+ }
+ //
+ // 取消绑定分组,删除原来旧的分组
+ txErr := service.ReBindResource(func(tx *gorm.DB) error {
+ MDevice := model.MDevice{
+ Type: form.Type,
+ Name: form.Name,
+ Description: form.Description,
+ Config: string(configJson),
+ }
+ return tx.Model(MDevice).
+ Where("uuid=?", form.UUID).
+ Updates(&MDevice).Error
+ }, form.UUID, form.Gid)
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
+ return
+ }
+ if err := server.LoadNewestDevice(form.UUID, ruleEngine); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
diff --git a/component/rulex_api_server/apis/device_modbus_data_sheet_api.go b/component/rulex_api_server/apis/device_modbus_data_sheet_api.go
new file mode 100644
index 000000000..904b76fd5
--- /dev/null
+++ b/component/rulex_api_server/apis/device_modbus_data_sheet_api.go
@@ -0,0 +1,345 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package apis
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ modbuscache "github.com/hootrhino/rulex/component/intercache/modbus"
+ "github.com/hootrhino/rulex/component/interdb"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+ "github.com/jinzhu/copier"
+ "github.com/xuri/excelize/v2"
+)
+
+type ModbusPointVo struct {
+ UUID string `json:"uuid,omitempty"`
+ DeviceUUID string `json:"device_uuid"`
+ Tag string `json:"tag"`
+ Alias string `json:"alias"`
+ Function *int `json:"function"`
+ SlaverId *byte `json:"slaverId"`
+ Address *uint16 `json:"address"`
+ Frequency *int64 `json:"frequency"`
+ Quantity *uint16 `json:"quantity"`
+ Status int `json:"status"` // 运行时数据
+ LastFetchTime uint64 `json:"lastFetchTime"` // 运行时数据
+ Value string `json:"value"` // 运行时数据
+}
+
+/*
+*
+* 特殊设备需要和外界交互,这里主要就是一些设备的点位表导入导出等支持
+* http://127.0.0.1:2580/api/v1/modbus_data_sheet/export
+ */
+
+// ModbusPoints 获取modbus_excel类型的点位数据
+func ModbusPointsExport(c *gin.Context, ruleEngine typex.RuleX) {
+ c.Header("Content-Type", "text/csv")
+ c.Header("Content-Disposition", fmt.Sprintf("attachment;filename=%v.csv", time.Now().UnixMilli()))
+ csvWriter := csv.NewWriter(c.Writer)
+ csvWriter.WriteAll([][]string{
+ {"h1", "h2", "h3"},
+ {"11", "12", "13"},
+ {"21", "22", "23"},
+ {"31", "32", "33"},
+ })
+ csvWriter.Flush()
+}
+
+// 分页获取
+// SELECT * FROM `m_modbus_data_points` WHERE
+// `m_modbus_data_points`.`device_uuid` = "DEVICEDQNLO8"
+// ORDER BY
+// created_at DESC LIMIT 2 OFFSET 10
+func ModbusSheetPageList(c *gin.Context, ruleEngine typex.RuleX) {
+ pager, err := service.ReadPageRequest(c)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ deviceUuid, _ := c.GetQuery("device_uuid")
+ db := interdb.DB()
+ tx := db.Scopes(service.Paginate(*pager))
+ var count int64
+ err1 := interdb.DB().Model(&model.MModbusDataPoint{}).Count(&count).Error
+ if err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err1))
+ return
+ }
+ var records []model.MModbusDataPoint
+ result := tx.Order("created_at DESC").Find(&records,
+ &model.MModbusDataPoint{DeviceUuid: deviceUuid})
+ if result.Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(result.Error))
+ return
+ }
+ recordsVo := []ModbusPointVo{}
+
+ for _, record := range records {
+ Slot := modbuscache.GetSlot(deviceUuid)
+ Value, ok := Slot[record.UUID]
+ Vo := ModbusPointVo{
+ UUID: record.UUID,
+ DeviceUUID: record.DeviceUuid,
+ Tag: record.Tag,
+ Alias: record.Alias,
+ Function: record.Function,
+ SlaverId: record.SlaverId,
+ Address: record.Address,
+ Frequency: record.Frequency,
+ Quantity: record.Quantity,
+ LastFetchTime: Value.LastFetchTime, // 运行时
+ Value: Value.Value, // 运行时
+ }
+ if ok {
+ Vo.Status = func() int {
+ if Value.Value == "" {
+ return 0
+ }
+ return 1
+ }() // 运行时
+ Vo.LastFetchTime = Value.LastFetchTime // 运行时
+ Vo.Value = Value.Value // 运行时
+ recordsVo = append(recordsVo, Vo)
+ } else {
+ recordsVo = append(recordsVo, Vo)
+ }
+ }
+ Result := service.WrapPageResult(*pager, recordsVo, count)
+ c.JSON(common.HTTP_OK, common.OkWithData(Result))
+}
+
+/*
+*
+* 删除单行
+*
+ */
+func ModbusSheetDeleteAll(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ UUIDs []string `json:"uuids"`
+ DeviceUUID string `json:"device_uuid"`
+ }
+ form := Form{}
+ if Error := c.ShouldBindJSON(&form); Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(Error))
+ return
+ }
+ err := service.DeleteAllModbusPointByDevice(form.DeviceUUID)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+func ModbusSheetDelete(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ UUIDs []string `json:"uuids"`
+ DeviceUUID string `json:"device_uuid"`
+ }
+ form := Form{}
+ if Error := c.ShouldBindJSON(&form); Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(Error))
+ return
+ }
+ err := service.DeleteModbusPointByDevice(form.UUIDs, form.DeviceUUID)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+/*
+*
+* 更新点位表
+*
+ */
+func ModbusSheetUpdate(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ DeviceUUID string `json:"device_uuid"`
+ ModbusDataPoints []ModbusPointVo `json:"modbus_data_points"`
+ }
+ // ModbusDataPoints := []ModbusPointVo{}
+ form := Form{}
+ err := c.ShouldBindJSON(&form)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ for _, ModbusDataPoint := range form.ModbusDataPoints {
+ if ModbusDataPoint.UUID == "" {
+ NewRow := model.MModbusDataPoint{}
+ copier.Copy(&NewRow, &ModbusDataPoint)
+ NewRow.DeviceUuid = ModbusDataPoint.DeviceUUID
+ NewRow.UUID = utils.ModbusPointUUID()
+ err0 := service.InsertModbusPointPosition(NewRow)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ } else {
+ OldRow := model.MModbusDataPoint{}
+ copier.Copy(&OldRow, &ModbusDataPoint)
+ OldRow.DeviceUuid = ModbusDataPoint.DeviceUUID
+ OldRow.UUID = ModbusDataPoint.UUID
+ err0 := service.UpdateModbusPoint(OldRow)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ }
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+// ModbusSheetImport 上传Excel文件
+func ModbusSheetImport(c *gin.Context, ruleEngine typex.RuleX) {
+ // 解析 multipart/form-data 类型的请求体
+ err := c.Request.ParseMultipartForm(1024 * 1024 * 10)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
+ // 获取上传的文件
+ file, header, err := c.Request.FormFile("file")
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ defer file.Close()
+ deviceUuid := c.Request.Form.Get("device_uuid")
+ type DeviceDto struct {
+ UUID string
+ Name string
+ Type string
+ }
+ Device := DeviceDto{}
+ errDb := interdb.DB().Table("m_devices").
+ Where("uuid=?", deviceUuid).Find(&Device).Error
+ if errDb != nil {
+ c.JSON(common.HTTP_OK, common.Error400(errDb))
+ return
+ }
+ if Device.Type != typex.GENERIC_MODBUS.String() {
+ c.JSON(common.HTTP_OK,
+ common.Error("Invalid Device Type, Only Support Import Modbus Device"))
+ return
+ }
+ contentType := header.Header.Get("Content-Type")
+ if contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" &&
+ contentType != "application/vnd.ms-excel" {
+ c.JSON(common.HTTP_OK, common.Error("File Must be Excel Sheet"))
+ return
+ }
+ // 判断文件大小是否符合要求(10MB)
+ if header.Size > 1024*1024*10 {
+ c.JSON(common.HTTP_OK, common.Error("Excel file size cannot be greater than 10MB"))
+ return
+ }
+ list, err := parseModbusPointExcel(file, "Sheet1", deviceUuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ if err = service.InsertModbusPointPositions(list); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(deviceUuid)
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
+func parseModbusPointExcel(
+ r io.Reader,
+ sheetName string,
+ deviceUuid string) (list []model.MModbusDataPoint, err error) {
+ excelFile, err := excelize.OpenReader(r)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ excelFile.Close()
+ }()
+ // 读取表格
+ rows, err := excelFile.GetRows(sheetName)
+ if err != nil {
+ return nil, err
+ }
+ // 判断首行标头
+ // tag, alias, function, frequency, slaverId, address, quality
+ err1 := errors.New("invalid Sheet Header")
+ if len(rows[0]) < 7 {
+ return nil, err1
+ }
+ if rows[0][0] != "tag" ||
+ rows[0][1] != "alias" ||
+ rows[0][2] != "function" ||
+ rows[0][3] != "frequency" ||
+ rows[0][4] != "slaverId" ||
+ rows[0][5] != "address" ||
+ rows[0][6] != "quality" {
+ return nil, err1
+ }
+
+ list = make([]model.MModbusDataPoint, 0)
+ // tag, alias, function, frequency, slaverId, address, quality
+ for i := 1; i < len(rows); i++ {
+ row := rows[i]
+ tag := row[0]
+ alias := row[1]
+ function, _ := strconv.ParseInt(row[2], 10, 8)
+ frequency, _ := strconv.ParseInt(row[3], 10, 8)
+ slaverId, _ := strconv.ParseInt(row[4], 10, 8)
+ address, _ := strconv.ParseUint(row[5], 10, 16)
+ quantity, _ := strconv.ParseUint(row[6], 10, 16)
+ Function := int(function)
+ SlaverId := byte(slaverId)
+ Address := uint16(address)
+ Frequency := int64(frequency)
+ Quantity := uint16(quantity)
+ model := model.MModbusDataPoint{
+ UUID: utils.ModbusPointUUID(),
+ DeviceUuid: deviceUuid,
+ Tag: tag,
+ Alias: alias,
+ Function: &Function,
+ SlaverId: &SlaverId,
+ Address: &Address,
+ Frequency: &Frequency, //ms
+ Quantity: &Quantity,
+ }
+ list = append(list, model)
+ }
+ return list, nil
+}
diff --git a/component/rulex_api_server/apis/device_s1200_data_sheet_api.go b/component/rulex_api_server/apis/device_s1200_data_sheet_api.go
new file mode 100644
index 000000000..917765311
--- /dev/null
+++ b/component/rulex_api_server/apis/device_s1200_data_sheet_api.go
@@ -0,0 +1,345 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package apis
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ siemenscache "github.com/hootrhino/rulex/component/intercache/siemens"
+ "github.com/hootrhino/rulex/component/interdb"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+ "github.com/jinzhu/copier"
+ "github.com/xuri/excelize/v2"
+)
+
+type SiemensPointVo struct {
+ UUID string `json:"uuid,omitempty"`
+ DeviceUUID string `json:"device_uuid"`
+ Tag string `json:"tag"`
+ Alias string `json:"alias"`
+ Type string `json:"type"`
+ Frequency *int64 `json:"frequency"`
+ Address *int `json:"address"`
+ Start *int `json:"start"`
+ Size *int `json:"size"`
+ Status int `json:"status"` // 运行时数据
+ LastFetchTime uint64 `json:"lastFetchTime"` // 运行时数据
+ Value string `json:"value"` // 运行时数据
+}
+
+/*
+*
+* 特殊设备需要和外界交互,这里主要就是一些设备的点位表导入导出等支持
+* http://127.0.0.1:2580/api/v1/Siemens_data_sheet/export
+ */
+
+// SiemensPoints 获取Siemens_excel类型的点位数据
+func SiemensPointsExport(c *gin.Context, ruleEngine typex.RuleX) {
+ c.Header("Content-Type", "text/csv")
+ c.Header("Content-Disposition", fmt.Sprintf("attachment;filename=%v.csv", time.Now().UnixMilli()))
+ csvWriter := csv.NewWriter(c.Writer)
+ csvWriter.WriteAll([][]string{
+ {"h1", "h2", "h3"},
+ {"11", "12", "13"},
+ {"21", "22", "23"},
+ {"31", "32", "33"},
+ })
+ csvWriter.Flush()
+}
+
+// 分页获取
+// SELECT * FROM `m_Siemens_data_points` WHERE
+// `m_Siemens_data_points`.`device_uuid` = "DEVICEDQNLO8"
+// ORDER BY
+// created_at DESC LIMIT 2 OFFSET 10
+func SiemensSheetPageList(c *gin.Context, ruleEngine typex.RuleX) {
+ pager, err := service.ReadPageRequest(c)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ deviceUuid, _ := c.GetQuery("device_uuid")
+ db := interdb.DB()
+ tx := db.Scopes(service.Paginate(*pager))
+ var count int64
+ err1 := interdb.DB().Model(&model.MSiemensDataPoint{}).Count(&count).Error
+ if err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err1))
+ return
+ }
+ var records []model.MSiemensDataPoint
+ result := tx.Order("created_at DESC").Find(&records,
+ &model.MSiemensDataPoint{DeviceUuid: deviceUuid})
+ if result.Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(result.Error))
+ return
+ }
+ recordsVo := []SiemensPointVo{}
+ for _, record := range records {
+ Slot := siemenscache.GetSlot(deviceUuid)
+ Value, ok := Slot[record.UUID]
+ Vo := SiemensPointVo{
+ UUID: record.UUID,
+ DeviceUUID: record.DeviceUuid,
+ Tag: record.Tag,
+ Type: record.Type,
+ Alias: record.Alias,
+ Address: record.Address,
+ Frequency: record.Frequency,
+ Start: record.Start,
+ Size: record.Size,
+ LastFetchTime: Value.LastFetchTime, // 运行时
+ Value: Value.Value, // 运行时
+ }
+ if ok {
+ Vo.Status = func() int {
+ if Value.Value == "" {
+ return 0
+ }
+ return 1
+ }() // 运行时
+ Vo.LastFetchTime = Value.LastFetchTime // 运行时
+ Vo.Value = Value.Value // 运行时
+ recordsVo = append(recordsVo, Vo)
+ } else {
+ recordsVo = append(recordsVo, Vo)
+ }
+ }
+ Result := service.WrapPageResult(*pager, recordsVo, count)
+ c.JSON(common.HTTP_OK, common.OkWithData(Result))
+}
+
+/*
+*
+* 删除单行
+*
+ */
+func SiemensSheetDeleteAll(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ UUIDs []string `json:"uuids"`
+ DeviceUUID string `json:"device_uuid"`
+ }
+ form := Form{}
+ if Error := c.ShouldBindJSON(&form); Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(Error))
+ return
+ }
+ err := service.DeleteAllSiemensPointByDevice(form.DeviceUUID)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+func SiemensSheetDelete(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ UUIDs []string `json:"uuids"`
+ DeviceUUID string `json:"device_uuid"`
+ }
+ form := Form{}
+ if Error := c.ShouldBindJSON(&form); Error != nil {
+ c.JSON(common.HTTP_OK, common.Error400(Error))
+ return
+ }
+ err := service.DeleteSiemensPointByDevice(form.UUIDs, form.DeviceUUID)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+/*
+*
+* 更新点位表
+*
+ */
+func SiemensSheetUpdate(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ DeviceUUID string `json:"device_uuid"`
+ SiemensDataPoints []SiemensPointVo `json:"siemens_data_points"`
+ }
+ form := Form{}
+ // SiemensDataPoints := []SiemensPointVo{}
+ err := c.ShouldBindJSON(&form)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ for _, SiemensDataPoint := range form.SiemensDataPoints {
+ if SiemensDataPoint.UUID == "" {
+ NewRow := model.MSiemensDataPoint{}
+ copier.Copy(&NewRow, &SiemensDataPoint)
+ NewRow.DeviceUuid = SiemensDataPoint.DeviceUUID
+ NewRow.UUID = utils.SiemensPointUUID()
+ err0 := service.InsertSiemensPointPosition(NewRow)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ } else {
+ OldRow := model.MSiemensDataPoint{}
+ copier.Copy(&OldRow, &SiemensDataPoint)
+ OldRow.DeviceUuid = SiemensDataPoint.DeviceUUID
+ OldRow.UUID = SiemensDataPoint.UUID
+ err0 := service.UpdateSiemensPoint(OldRow)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ }
+ }
+ ruleEngine.RestartDevice(form.DeviceUUID)
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+
+// SiemensSheetImport 上传Excel文件
+func SiemensSheetImport(c *gin.Context, ruleEngine typex.RuleX) {
+ // 解析 multipart/form-data 类型的请求体
+ err := c.Request.ParseMultipartForm(1024 * 1024 * 10)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
+ // 获取上传的文件
+ file, header, err := c.Request.FormFile("file")
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ defer file.Close()
+ deviceUuid := c.Request.Form.Get("device_uuid")
+ type DeviceDto struct {
+ UUID string
+ Name string
+ Type string
+ }
+ Device := DeviceDto{}
+ errDb := interdb.DB().Table("m_devices").
+ Where("uuid=?", deviceUuid).Find(&Device).Error
+ if errDb != nil {
+ c.JSON(common.HTTP_OK, common.Error400(errDb))
+ return
+ }
+ if Device.Type != typex.SIEMENS_PLC.String() {
+ c.JSON(common.HTTP_OK,
+ common.Error("Invalid Device Type, Only Support Import Siemens Device"))
+ return
+ }
+ contentType := header.Header.Get("Content-Type")
+ if contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" &&
+ contentType != "application/vnd.ms-excel" {
+ c.JSON(common.HTTP_OK, common.Error("File Must be Excel Sheet"))
+ return
+ }
+ // 判断文件大小是否符合要求(10MB)
+ if header.Size > 1024*1024*10 {
+ c.JSON(common.HTTP_OK, common.Error("Excel file size cannot be greater than 10MB"))
+ return
+ }
+ list, err := parseSiemensPointExcel(file, "Sheet1", deviceUuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ if err = service.InsertSiemensPointPositions(list); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ ruleEngine.RestartDevice(deviceUuid)
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
+func parseSiemensPointExcel(
+ r io.Reader,
+ sheetName string,
+ deviceUuid string) (list []model.MSiemensDataPoint, err error) {
+ excelFile, err := excelize.OpenReader(r)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ excelFile.Close()
+ }()
+ // 读取表格
+ rows, err := excelFile.GetRows(sheetName)
+ if err != nil {
+ return nil, err
+ }
+ // 判断首行标头
+ //
+ err1 := errors.New("invalid Sheet Header")
+ if len(rows[0]) < 7 {
+ return nil, err1
+ }
+ //tag alias type frequency address size
+ if rows[0][0] != "tag" ||
+ rows[0][1] != "alias" ||
+ rows[0][2] != "type" ||
+ rows[0][3] != "frequency" ||
+ rows[0][4] != "address" ||
+ rows[0][5] != "start" ||
+ rows[0][6] != "size" {
+ return nil, err1
+ }
+
+ list = make([]model.MSiemensDataPoint, 0)
+ //tag alias type frequency address start size
+ for i := 1; i < len(rows); i++ {
+ row := rows[i]
+ tag := row[0]
+ alias := row[1]
+ DbType := row[2]
+ frequency, _ := strconv.ParseInt(row[3], 10, 8)
+ address, _ := strconv.ParseUint(row[4], 10, 16)
+ start, _ := strconv.ParseUint(row[5], 10, 16)
+ size, _ := strconv.ParseUint(row[6], 10, 16)
+ Address := int(address)
+ Frequency := int64(frequency)
+ Start := int(start)
+ Size := int(size)
+ model := model.MSiemensDataPoint{
+ UUID: utils.SiemensPointUUID(),
+ DeviceUuid: deviceUuid,
+ Tag: tag,
+ Alias: alias,
+ Type: DbType,
+ Frequency: &Frequency,
+ Address: &Address,
+ Start: &Start,
+ Size: &Size,
+ }
+ list = append(list, model)
+ }
+ return list, nil
+}
diff --git a/plugin/http_server/apis/group_api.go b/component/rulex_api_server/apis/group_api.go
similarity index 92%
rename from plugin/http_server/apis/group_api.go
rename to component/rulex_api_server/apis/group_api.go
index 69bf99301..18771e785 100644
--- a/plugin/http_server/apis/group_api.go
+++ b/component/rulex_api_server/apis/group_api.go
@@ -4,9 +4,9 @@ import (
"fmt"
"github.com/gin-gonic/gin"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
@@ -33,8 +33,9 @@ func CreateGroup(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
- if !utils.SContains([]string{"VISUAL", "DEVICE"}, vvo.Type) {
- c.JSON(common.HTTP_OK, common.Error400(fmt.Errorf("invalid type [%s]", vvo.Type)))
+ if !utils.SContains([]string{"VISUAL", "DEVICE", "USER_LUA_TEMPLATE"}, vvo.Type) {
+ c.JSON(common.HTTP_OK, common.Error400(fmt.Errorf("invalid group type [%s]", vvo.Type)))
+ return
}
Model := model.MGenericGroup{
UUID: utils.GroupUuid(),
diff --git a/plugin/http_server/apis/hw_port_api.go b/component/rulex_api_server/apis/hw_port_api.go
similarity index 95%
rename from plugin/http_server/apis/hw_port_api.go
rename to component/rulex_api_server/apis/hw_port_api.go
index c3d6ad960..888680db2 100644
--- a/plugin/http_server/apis/hw_port_api.go
+++ b/component/rulex_api_server/apis/hw_port_api.go
@@ -20,9 +20,9 @@ import (
"github.com/gin-gonic/gin"
"github.com/hootrhino/rulex/component/hwportmanager"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
diff --git a/plugin/http_server/apis/inend_api.go b/component/rulex_api_server/apis/inend_api.go
similarity index 76%
rename from plugin/http_server/apis/inend_api.go
rename to component/rulex_api_server/apis/inend_api.go
index ea02dc48d..f7e842bca 100644
--- a/plugin/http_server/apis/inend_api.go
+++ b/component/rulex_api_server/apis/inend_api.go
@@ -1,11 +1,13 @@
package apis
import (
+ "fmt"
+
"github.com/gin-gonic/gin"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/server"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
"gopkg.in/square/go-jose.v2/json"
@@ -38,52 +40,29 @@ func InEndDetail(c *gin.Context, ruleEngine typex.RuleX) {
// Get all inends
func InEnds(c *gin.Context, ruleEngine typex.RuleX) {
- uuid, _ := c.GetQuery("uuid")
- if uuid == "" {
- inEnds := []typex.InEnd{}
- for _, v := range service.AllMInEnd() {
- var inEnd *typex.InEnd
- if inEnd = ruleEngine.GetInEnd(v.UUID); inEnd == nil {
- tmpInEnd := typex.InEnd{
- UUID: v.UUID,
- Type: typex.InEndType(v.Type),
- Name: v.Name,
- Description: v.Description,
- BindRules: map[string]typex.Rule{},
- Config: v.GetConfig(),
- State: typex.SOURCE_STOP,
- }
- inEnds = append(inEnds, tmpInEnd)
- }
- if inEnd != nil {
- inEnd.State = inEnd.Source.Status()
- inEnds = append(inEnds, *inEnd)
+
+ inEnds := []typex.InEnd{}
+ for _, v := range service.AllMInEnd() {
+ var inEnd *typex.InEnd
+ if inEnd = ruleEngine.GetInEnd(v.UUID); inEnd == nil {
+ tmpInEnd := typex.InEnd{
+ UUID: v.UUID,
+ Type: typex.InEndType(v.Type),
+ Name: v.Name,
+ Description: v.Description,
+ BindRules: map[string]typex.Rule{},
+ Config: v.GetConfig(),
+ State: typex.SOURCE_STOP,
}
+ inEnds = append(inEnds, tmpInEnd)
}
- c.JSON(common.HTTP_OK, common.OkWithData(inEnds))
- return
- }
- Model, err := service.GetMInEndWithUUID(uuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- inEnd := ruleEngine.GetInEnd(Model.UUID)
- if inEnd == nil {
- tmpInEnd := typex.InEnd{
- UUID: Model.UUID,
- Type: typex.InEndType(Model.Type),
- Name: Model.Name,
- Description: Model.Description,
- BindRules: map[string]typex.Rule{},
- Config: Model.GetConfig(),
- State: typex.SOURCE_STOP,
+ if inEnd != nil {
+ inEnd.State = inEnd.Source.Status()
+ inEnds = append(inEnds, *inEnd)
}
- c.JSON(common.HTTP_OK, common.OkWithData(tmpInEnd))
- return
}
- inEnd.State = inEnd.Source.Status()
- c.JSON(common.HTTP_OK, common.OkWithData(inEnd))
+ c.JSON(common.HTTP_OK, common.OkWithData(inEnds))
+ return
}
@@ -107,7 +86,23 @@ func CreateInend(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err1))
return
}
-
+ isSingle := false
+ // 内部消息总线是单例模式
+ if form.Type == typex.INTERNAL_EVENT.String() {
+ ruleEngine.AllInEnd().Range(func(key, value any) bool {
+ In := value.(*typex.InEnd)
+ if In.Type.String() == form.Type {
+ isSingle = true
+ return false
+ }
+ return true
+ })
+ }
+ if isSingle {
+ msg := fmt.Errorf("the %s is singleton Source, can not create again", form.Name)
+ c.JSON(common.HTTP_OK, common.Error400(msg))
+ return
+ }
newUUID := utils.InUuid()
if err := service.InsertMInEnd(&model.MInEnd{
@@ -127,6 +122,16 @@ func CreateInend(c *gin.Context, ruleEngine typex.RuleX) {
}
c.JSON(common.HTTP_OK, common.Ok())
+}
+func RestartInEnd(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ err := ruleEngine.RestartInEnd(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+
}
/*
diff --git a/component/rulex_api_server/apis/internal_notify_api.go b/component/rulex_api_server/apis/internal_notify_api.go
new file mode 100644
index 000000000..b361c696b
--- /dev/null
+++ b/component/rulex_api_server/apis/internal_notify_api.go
@@ -0,0 +1,130 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package apis
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+)
+
+/*
+*
+* 内部事件
+*
+ */
+type InternalNotifyVo struct {
+ UUID string `json:"uuid"` // UUID
+ Type string `json:"type"` // INFO | ERROR | WARNING
+ Status int `json:"status"` // 1 未读 2 已读
+ Event string `json:"event"` // 字符串
+ Ts uint64 `json:"ts"` // 时间戳
+ Summary string `json:"summary"` // 概览,为了节省流量,在消息列表只显示这个字段,Info值为“”
+ Info string `json:"info,omitempty"` // 消息内容,是个文本,详情显示
+}
+
+/*
+*
+* 站内消息
+*
+ */
+func InternalNotifiesHeader(c *gin.Context, ruleEngine typex.RuleX) {
+ data := []InternalNotifyVo{}
+ models := service.AllInternalNotifiesHeader()
+ for _, model := range models {
+ data = append(data, InternalNotifyVo{
+ UUID: model.UUID,
+ Type: model.Type,
+ Event: model.Event,
+ Ts: model.Ts,
+ Summary: model.Summary,
+ Status: model.Status,
+ })
+
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(data))
+}
+
+/*
+*
+* 站内消息
+*
+ */
+func InternalNotifies(c *gin.Context, ruleEngine typex.RuleX) {
+ data := []InternalNotifyVo{}
+ models := service.AllInternalNotifies()
+ for _, model := range models {
+ data = append(data, InternalNotifyVo{
+ UUID: model.UUID,
+ Type: model.Type,
+ Event: model.Event,
+ Ts: model.Ts,
+ Summary: model.Summary,
+ Info: model.Info,
+ Status: model.Status,
+ })
+
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(data))
+}
+
+/*
+*
+* 清空
+*
+ */
+func ClearInternalNotifies(c *gin.Context, ruleEngine typex.RuleX) {
+ if err := service.ClearInternalNotifies(); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
+/*
+*
+* 阅读
+*
+ */
+func ReadInternalNotifies(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ if err := service.ReadInternalNotifies(uuid); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
+func TestCreateNotifies(c *gin.Context, ruleEngine typex.RuleX) {
+ for i := 0; i < 50; i++ {
+ service.InsertInternalNotify(model.MInternalNotify{
+ UUID: utils.MakeUUID("NOTIFY"), // UUID
+ Type: `WARNING`, // INFO | ERROR | WARNING
+ Status: 1,
+ Event: `event.down`, // 字符串
+ Ts: uint64(time.Now().UnixMilli()),
+ Info: "位于某处的某个设备已经离线, 请及时检查处理,这是测试数据而已",
+ Summary: fmt.Sprintf(`测试数据:设备 %d 离线`, i),
+ })
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
diff --git a/plugin/http_server/apis/outend_api.go b/component/rulex_api_server/apis/outend_api.go
similarity index 91%
rename from plugin/http_server/apis/outend_api.go
rename to component/rulex_api_server/apis/outend_api.go
index b98028894..6c2d0e33e 100644
--- a/plugin/http_server/apis/outend_api.go
+++ b/component/rulex_api_server/apis/outend_api.go
@@ -1,10 +1,10 @@
package apis
import (
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/server"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
@@ -144,6 +144,16 @@ func CreateOutEnd(c *gin.Context, ruleEngine typex.RuleX) {
}
c.JSON(common.HTTP_OK, common.Ok())
+}
+func RestartOutEnd(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ err := ruleEngine.RestartOutEnd(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+
}
// 更新
diff --git a/plugin/http_server/apis/plugin_api.go b/component/rulex_api_server/apis/plugin_api.go
similarity index 94%
rename from plugin/http_server/apis/plugin_api.go
rename to component/rulex_api_server/apis/plugin_api.go
index 93a168552..b2b08c05e 100644
--- a/plugin/http_server/apis/plugin_api.go
+++ b/component/rulex_api_server/apis/plugin_api.go
@@ -3,7 +3,7 @@ package apis
import (
"fmt"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/typex"
"github.com/gin-gonic/gin"
diff --git a/plugin/http_server/apis/protocolapp_api.go b/component/rulex_api_server/apis/protocolapp_api.go
similarity index 100%
rename from plugin/http_server/apis/protocolapp_api.go
rename to component/rulex_api_server/apis/protocolapp_api.go
diff --git a/plugin/http_server/apis/rhinoh3_firmware_api.go b/component/rulex_api_server/apis/rhinoh3_firmware_api.go
similarity index 63%
rename from plugin/http_server/apis/rhinoh3_firmware_api.go
rename to component/rulex_api_server/apis/rhinoh3_firmware_api.go
index c6cbee5b6..02f7abfb0 100644
--- a/plugin/http_server/apis/rhinoh3_firmware_api.go
+++ b/component/rulex_api_server/apis/rhinoh3_firmware_api.go
@@ -17,11 +17,14 @@
package apis
import (
+ "fmt"
+ "net/http"
"os"
+ "time"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
"github.com/hootrhino/rulex/typex"
)
@@ -42,7 +45,7 @@ func ReStartRulex(c *gin.Context, ruleEngine typex.RuleX) {
func Reboot(c *gin.Context, ruleEngine typex.RuleX) {
err := ossupport.Reboot()
if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ c.JSON(common.HTTP_OK, common.OkWithData(err))
return
}
c.JSON(common.HTTP_OK, common.Ok())
@@ -50,11 +53,10 @@ func Reboot(c *gin.Context, ruleEngine typex.RuleX) {
/*
*
-* 回复出厂
+* 回复出厂, 直接删除配置即可,但是现阶段暂时不实现
*
*/
func RecoverNew(c *gin.Context, ruleEngine typex.RuleX) {
-
c.JSON(common.HTTP_OK, common.Ok())
}
@@ -64,10 +66,34 @@ func RecoverNew(c *gin.Context, ruleEngine typex.RuleX) {
*
*/
func GetUpGradeLog(c *gin.Context, ruleEngine typex.RuleX) {
- byteS, _ := os.ReadFile("local-upgrade-log.txt")
- // if err != nil {
- // c.JSON(common.HTTP_OK, common.Error400(err))
- // return
- // }
+ byteS, _ := os.ReadFile(ossupport.UpgradeLogPath)
c.JSON(common.HTTP_OK, common.OkWithData(string(byteS)))
}
+
+/*
+*
+* 下载运行日志
+*
+ */
+func GetRunningLog(c *gin.Context, ruleEngine typex.RuleX) {
+ c.Writer.WriteHeader(http.StatusOK)
+ if RunningLogPathExists(ossupport.RunningLogPath) {
+ c.FileAttachment(ossupport.RunningLogPath,
+ fmt.Sprintf("running_log_%d_.txt", time.Now().UnixNano()))
+ } else {
+ js := ``
+ c.Writer.Write([]byte(js))
+ }
+ c.Writer.Flush()
+
+}
+func RunningLogPathExists(path string) bool {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true
+ }
+ if os.IsNotExist(err) {
+ return false
+ }
+ return false
+}
diff --git a/plugin/http_server/apis/rhinoh3_iproute_api.go b/component/rulex_api_server/apis/rhinoh3_iproute_api.go
similarity index 96%
rename from plugin/http_server/apis/rhinoh3_iproute_api.go
rename to component/rulex_api_server/apis/rhinoh3_iproute_api.go
index 5937e68f2..834441e55 100644
--- a/plugin/http_server/apis/rhinoh3_iproute_api.go
+++ b/component/rulex_api_server/apis/rhinoh3_iproute_api.go
@@ -19,10 +19,10 @@ import (
"fmt"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
diff --git a/plugin/http_server/apis/rhinopi_4g_api.go b/component/rulex_api_server/apis/rhinopi_4g_api.go
similarity index 92%
rename from plugin/http_server/apis/rhinopi_4g_api.go
rename to component/rulex_api_server/apis/rhinopi_4g_api.go
index e59db5d2d..a788b4368 100644
--- a/plugin/http_server/apis/rhinopi_4g_api.go
+++ b/component/rulex_api_server/apis/rhinopi_4g_api.go
@@ -20,7 +20,7 @@ import (
"github.com/gin-gonic/gin"
archsupport "github.com/hootrhino/rulex/bspsupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/typex"
)
@@ -120,11 +120,11 @@ func Get4GCOPS(c *gin.Context, ruleEngine typex.RuleX) {
*/
// ptype int, apn, username, password string, auth, cdmaPwd int
type APNFormVo struct {
- SenceId int `json:"senceId"`
- PTytpe int `json:"ptytpe"`
- Auth int `json:"auth"`
- CDMAPWD int `json:"cdmapwd"`
- APN string `json:"apn"`
+ SenceId int `json:"senceId"`
+ PTytpe int `json:"ptytpe"`
+ Auth int `json:"auth"`
+ CDMAPWD int `json:"cdmapwd"`
+ APN string `json:"apn"`
Username string `json:"apn_username"`
Password string `json:"apn_password"`
}
@@ -169,10 +169,10 @@ func Get4GICCID(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err))
} else {
// +QCCID: 89860426102180397625
- len1:=len("+QCCID: ")
- iccid:=""
- if len(result)>len1 {
- iccid=result[len1:]
+ len1 := len("+QCCID: ")
+ iccid := ""
+ if len(result) > len1 {
+ iccid = result[len1:]
}
c.JSON(common.HTTP_OK, common.OkWithData(iccid))
}
diff --git a/plugin/http_server/apis/rhinopi_wifi_api.go b/component/rulex_api_server/apis/rhinopi_wifi_api.go
similarity index 93%
rename from plugin/http_server/apis/rhinopi_wifi_api.go
rename to component/rulex_api_server/apis/rhinopi_wifi_api.go
index 9fe6185bf..164f626a7 100644
--- a/plugin/http_server/apis/rhinopi_wifi_api.go
+++ b/component/rulex_api_server/apis/rhinopi_wifi_api.go
@@ -17,8 +17,8 @@ package apis
import (
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
"github.com/hootrhino/rulex/typex"
)
diff --git a/plugin/http_server/apis/rtsp_stream_api.go b/component/rulex_api_server/apis/rtsp_stream_api.go
similarity index 100%
rename from plugin/http_server/apis/rtsp_stream_api.go
rename to component/rulex_api_server/apis/rtsp_stream_api.go
diff --git a/plugin/http_server/apis/rule_api.go b/component/rulex_api_server/apis/rule_api.go
similarity index 56%
rename from plugin/http_server/apis/rule_api.go
rename to component/rulex_api_server/apis/rule_api.go
index 6023fe969..801be9810 100644
--- a/plugin/http_server/apis/rule_api.go
+++ b/component/rulex_api_server/apis/rule_api.go
@@ -4,11 +4,11 @@ import (
"fmt"
"github.com/hootrhino/rulex/component/interqueue"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/glogger"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/server"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/sirupsen/logrus"
"github.com/hootrhino/rulex/core"
@@ -42,11 +42,10 @@ func RuleDetail(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.OkWithData(ruleVo{
UUID: rule.UUID,
Name: rule.Name,
- Type: rule.Type,
Status: 1,
Description: rule.Description,
- FromSource: rule.FromSource,
- FromDevice: rule.FromDevice,
+ FromSource: []string{rule.SourceId},
+ FromDevice: []string{rule.DeviceId},
Success: rule.Success,
Failed: rule.Failed,
Actions: rule.Actions,
@@ -55,44 +54,22 @@ func RuleDetail(c *gin.Context, ruleEngine typex.RuleX) {
// Get all rules
func Rules(c *gin.Context, ruleEngine typex.RuleX) {
- uuid, _ := c.GetQuery("uuid")
- if uuid == "" {
- DataList := []ruleVo{}
- allRules, _ := service.GetAllMRule()
- for _, rule := range allRules {
- DataList = append(DataList, ruleVo{
- UUID: rule.UUID,
- Name: rule.Name,
- Type: rule.Type,
- Status: 1,
- Description: rule.Description,
- FromSource: rule.FromSource,
- FromDevice: rule.FromDevice,
- Success: rule.Success,
- Failed: rule.Failed,
- Actions: rule.Actions,
- })
- }
- c.JSON(common.HTTP_OK, common.OkWithData(DataList))
- } else {
- rule, err := service.GetMRuleWithUUID(uuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- c.JSON(common.HTTP_OK, common.OkWithData(ruleVo{
+ DataList := []ruleVo{}
+ allRules, _ := service.GetAllMRule()
+ for _, rule := range allRules {
+ DataList = append(DataList, ruleVo{
UUID: rule.UUID,
Name: rule.Name,
- Type: rule.Type,
Status: 1,
Description: rule.Description,
- FromSource: rule.FromSource,
- FromDevice: rule.FromDevice,
+ FromSource: []string{rule.SourceId},
+ FromDevice: []string{rule.DeviceId},
Success: rule.Success,
Failed: rule.Failed,
Actions: rule.Actions,
- }))
+ })
}
+ c.JSON(common.HTTP_OK, common.OkWithData(DataList))
}
// Create rule
@@ -134,117 +111,161 @@ func CreateRule(c *gin.Context, ruleEngine typex.RuleX) {
}
// tmpRule 是一个一次性的临时rule,用来验证规则,这么做主要是为了防止真实Lua Vm 被污染
- tmpRule := typex.NewRule(nil, "_", "_", "_", []string{}, []string{},
+ tmpRule := typex.NewRule(nil, "_", "_", "_", "", "",
form.Success, form.Actions, form.Failed)
if err := core.VerifyLuaSyntax(tmpRule); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
- //
- mRule := &model.MRule{
- Name: form.Name,
- Type: form.Type,
- UUID: utils.RuleUuid(),
- Description: form.Description,
- FromSource: form.FromSource,
- FromDevice: form.FromDevice,
- Success: form.Success,
- Failed: form.Failed,
- Actions: form.Actions,
- }
rule := typex.NewLuaRule(
ruleEngine,
- mRule.UUID,
- mRule.Name,
- mRule.Description,
- mRule.FromSource,
- mRule.FromDevice,
- mRule.Success,
- mRule.Actions,
- mRule.Failed)
+ utils.RuleUuid(),
+ form.Name,
+ form.Description,
+ func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromSource),
+ func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromDevice),
+ form.Success,
+ form.Actions,
+ form.Failed)
ruleEngine.RemoveRule(rule.UUID)
if err := ruleEngine.LoadRule(rule); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
- // 更新FromSource RULE到Device表中
- for _, inId := range form.FromSource {
- InEnd, _ := service.GetMInEndWithUUID(inId)
- if InEnd == nil {
- c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+inId))
- return
- }
- // 去重旧的
- ruleMap := map[string]string{}
- for _, rule := range InEnd.BindRules {
- ruleMap[rule] = rule
- }
- // 追加新的ID
- ruleMap[inId] = mRule.UUID
- // 最后ID列表
- BindRules := []string{}
- for _, iid := range ruleMap {
- BindRules = append(BindRules, iid)
- }
- InEnd.BindRules = BindRules
- if err := service.UpdateMInEnd(InEnd.UUID, &model.MInEnd{
- BindRules: BindRules,
- }); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // SaveDB
- if err := service.InsertMRule(mRule); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // LoadNewest!!!
- if err := server.LoadNewestInEnd(inId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
+ go func() {
+ // 更新FromSource RULE到Device表中
+ for _, inId := range form.FromSource {
+ InEnd, _ := service.GetMInEndWithUUID(inId)
+ if InEnd == nil {
+ // c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+inId))
+ return
+ }
+ // SaveDB
+ //
+ mRule := &model.MRule{
+ Name: form.Name,
+ UUID: rule.UUID,
+ Description: form.Description,
+ SourceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromSource),
+ DeviceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromDevice),
+ Success: form.Success,
+ Failed: form.Failed,
+ Actions: form.Actions,
+ }
+ // 去重旧的
+ ruleMap := map[string]string{}
+ for _, rule := range InEnd.BindRules {
+ ruleMap[rule] = rule
+ }
+ // 追加新的ID
+ ruleMap[inId] = mRule.UUID
+ // 最后ID列表
+ BindRules := []string{}
+ for _, iid := range ruleMap {
+ BindRules = append(BindRules, iid)
+ }
+ InEnd.BindRules = BindRules
+ if err := service.UpdateMInEnd(InEnd.UUID, &model.MInEnd{
+ BindRules: BindRules,
+ }); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
- }
+ if err := service.InsertMRule(mRule); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // LoadNewest!!!
+ if err := server.LoadNewestInEnd(inId, ruleEngine); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
- // FromDevice
- for _, devId := range form.FromDevice {
- Device, _ := service.GetMDeviceWithUUID(devId)
- if Device == nil {
- c.JSON(common.HTTP_OK, common.Error(`device not exists: `+devId))
- return
- }
- // 去重旧的
- ruleMap := map[string]string{}
- for _, rule := range Device.BindRules {
- ruleMap[rule] = rule
- }
- // 追加新的ID
- ruleMap[devId] = mRule.UUID
- // 最后ID列表
- BindRules := []string{}
- for _, iid := range ruleMap {
- BindRules = append(BindRules, iid)
- }
- Device.BindRules = BindRules
- if err := service.UpdateDevice(Device.UUID, &model.MDevice{
- BindRules: BindRules,
- }); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // SaveDB
- if err := service.InsertMRule(mRule); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
}
- // LoadNewest!!!
- if err := server.LoadNewestDevice(devId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
+
+ // FromDevice
+ for _, devId := range form.FromDevice {
+ Device, _ := service.GetMDeviceWithUUID(devId)
+ if Device == nil {
+ // c.JSON(common.HTTP_OK, common.Error(`device not exists: `+devId))
+ return
+ }
+ // 去重旧的
+ ruleMap := map[string]string{}
+ for _, rule := range Device.BindRules {
+ ruleMap[rule] = rule // for ["", "", "" ....]
+ }
+ // SaveDB
+ mRule := &model.MRule{
+ Name: form.Name,
+ UUID: rule.UUID,
+ Description: form.Description,
+ SourceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromSource),
+ DeviceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromDevice),
+ Success: form.Success,
+ Failed: form.Failed,
+ Actions: form.Actions,
+ }
+ // 追加新的ID
+ ruleMap[devId] = mRule.UUID // append New Rule UUID
+ // 最后ID列表
+ BindRules := []string{}
+ for _, iid := range ruleMap {
+ BindRules = append(BindRules, iid)
+ }
+ Device.BindRules = BindRules
+ if err := service.UpdateDevice(Device.UUID, &model.MDevice{
+ BindRules: BindRules,
+ }); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
+ if err := service.InsertMRule(mRule); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // LoadNewest!!!
+ if err := server.LoadNewestDevice(devId, ruleEngine); err != nil {
+ // c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
}
+ }()
- }
c.JSON(common.HTTP_OK, common.Ok())
}
@@ -260,7 +281,6 @@ func UpdateRule(c *gin.Context, ruleEngine typex.RuleX) {
FromDevice []string `json:"fromDevice" binding:"required"`
Name string `json:"name" binding:"required"`
Type string `json:"type"`
- Expression string `json:"expression"`
Description string `json:"description"`
Actions string `json:"actions"`
Success string `json:"success"`
@@ -271,6 +291,13 @@ func UpdateRule(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
+ // tmpRule 是一个一次性的临时rule,用来验证规则,这么做主要是为了防止真实Lua Vm 被污染
+ tmpRule := typex.NewRule(nil, "_", "_", "_", "", "",
+ form.Success, form.Actions, form.Failed)
+ if err := core.VerifyLuaSyntax(tmpRule); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
for _, id := range form.FromSource {
in := ruleEngine.GetInEnd(id)
if in == nil {
@@ -285,126 +312,125 @@ func UpdateRule(c *gin.Context, ruleEngine typex.RuleX) {
return
}
}
- // tmpRule 是一个一次性的临时rule,用来验证规则,这么做主要是为了防止真实Lua Vm 被污染
- tmpRule := typex.NewRule(nil, "_", "_", "_", []string{}, []string{},
- form.Success, form.Actions, form.Failed)
-
- if err := core.VerifyLuaSyntax(tmpRule); err != nil {
+ OldRule, err := service.GetMRuleWithUUID(form.UUID)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ rule := typex.NewLuaRule(
+ ruleEngine,
+ OldRule.UUID,
+ OldRule.Name,
+ OldRule.Description,
+ OldRule.SourceId,
+ OldRule.DeviceId,
+ OldRule.Success,
+ OldRule.Actions,
+ OldRule.Failed)
+ ruleEngine.RemoveRule(rule.UUID)
+ if err := ruleEngine.LoadRule(rule); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // SaveDB
+ //
+ if err := service.UpdateMRule(OldRule.UUID, &model.MRule{
+ Name: form.Name,
+ Description: form.Description,
+ SourceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromSource),
+ DeviceId: func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromDevice),
+ Success: form.Success,
+ Failed: form.Failed,
+ Actions: form.Actions,
+ }); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
- if form.Type == "lua" {
- mRule, err := service.GetMRuleWithUUID(form.UUID)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- rule := typex.NewLuaRule(
- ruleEngine,
- mRule.UUID,
- mRule.Name,
- mRule.Description,
- mRule.FromSource,
- mRule.FromDevice,
- mRule.Success,
- mRule.Actions,
- mRule.Failed)
- ruleEngine.RemoveRule(rule.UUID)
- if err := ruleEngine.LoadRule(rule); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // SaveDB
- //
- if err := service.UpdateMRule(mRule.UUID, &model.MRule{
- Name: form.Name,
- Type: form.Type,
- Description: form.Description,
- FromSource: form.FromSource,
- FromDevice: form.FromDevice,
- Success: form.Success,
- Failed: form.Failed,
- Actions: form.Actions,
- }); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ // 耗时操作直接后台执行
+ go func() {
+ select {
+ case <-typex.GCTX.Done():
return
- }
- // 更新FromSource RULE到Device表中
- for _, inId := range form.FromSource {
- if inId != "" {
- InEnd, _ := service.GetMInEndWithUUID(inId)
- if InEnd == nil {
- c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+inId))
- return
- }
- // 去重旧的
- ruleMap := map[string]string{}
- for _, rule := range InEnd.BindRules {
- ruleMap[rule] = rule
- }
- // 追加新的ID
- ruleMap[inId] = mRule.UUID
- // 最后ID列表
- BindRules := []string{}
- for _, iid := range ruleMap {
- BindRules = append(BindRules, iid)
- }
- InEnd.BindRules = BindRules
- if err := service.UpdateMInEnd(InEnd.UUID, &model.MInEnd{
- BindRules: BindRules,
- }); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // LoadNewest!!!
- if err := server.LoadNewestInEnd(inId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
+ default:
+ }
+ if len(form.FromSource) > 0 {
+ // 更新FromSource RULE到Device表中
+ InEnd, _ := service.GetMInEndWithUUID(form.FromSource[0])
+ if InEnd == nil {
+ //c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+inId))
+ return
+ }
+ // 去重旧的
+ ruleMap := map[string]string{}
+ for _, rule := range InEnd.BindRules {
+ ruleMap[rule] = rule
+ }
+ // 追加新的ID
+ ruleMap[OldRule.UUID] = OldRule.UUID // append New Rule UUID
+ // 最后ID列表
+ BindRules := []string{}
+ for _, iid := range ruleMap {
+ BindRules = append(BindRules, iid)
+ }
+ InEnd.BindRules = BindRules
+ if err := service.UpdateMInEnd(InEnd.UUID, &model.MInEnd{
+ BindRules: BindRules,
+ }); err != nil {
+ //c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // LoadNewest!!!
+ if err := server.LoadNewestInEnd(InEnd.UUID, ruleEngine); err != nil {
+ //c.JSON(common.HTTP_OK, common.Error400(err))
+ return
}
-
}
// FromDevice
- for _, devId := range form.FromDevice {
- if devId != "" {
- Device, _ := service.GetMDeviceWithUUID(devId)
- if Device == nil {
- c.JSON(common.HTTP_OK, common.Error(`device not exists: `+devId))
- return
- }
- // 去重旧的
- ruleMap := map[string]string{}
- for _, rule := range Device.BindRules {
- ruleMap[rule] = rule
- }
- // 追加新的ID
- ruleMap[devId] = mRule.UUID
- // 最后ID列表
- BindRules := []string{}
- for _, iid := range ruleMap {
- BindRules = append(BindRules, iid)
- }
- Device.BindRules = BindRules
- if err := service.UpdateDevice(Device.UUID, &model.MDevice{
- BindRules: BindRules,
- }); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // LoadNewest!!!
- if err := server.LoadNewestDevice(devId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
+ if len(form.FromDevice) > 0 {
+
+ Device, _ := service.GetMDeviceWithUUID(form.FromDevice[0])
+ if Device == nil {
+ //c.JSON(common.HTTP_OK, common.Error(`device not exists: `+devId))
+ return
+ }
+ // 去重旧的
+ ruleMap := map[string]string{}
+ for _, rule := range Device.BindRules {
+ ruleMap[rule] = rule // for ["", "", "" ....]
+ }
+ // 追加新的ID
+ ruleMap[OldRule.UUID] = OldRule.UUID // append New Rule UUID
+ // 最后ID列表
+ BindRules := []string{}
+ for _, iid := range ruleMap {
+ BindRules = append(BindRules, iid)
+ }
+ Device.BindRules = BindRules
+ if err := service.UpdateDevice(Device.UUID, &model.MDevice{
+ BindRules: BindRules,
+ }); err != nil {
+ //c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ // LoadNewest!!!
+ if err := server.LoadNewestDevice(Device.UUID, ruleEngine); err != nil {
+ //c.JSON(common.HTTP_OK, common.Error400(err))
+ return
}
}
-
- c.JSON(common.HTTP_OK, common.Ok())
- return
- }
- c.JSON(common.HTTP_OK, common.Error("rule type invalid:"+form.Type))
-
+ }()
+ c.JSON(common.HTTP_OK, common.Ok())
}
// Delete rule by UUID
@@ -415,69 +441,68 @@ func DeleteRule(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err0))
return
}
- // 更新FromSource RULE到Device表中
- for _, id := range mRule.FromSource {
- InEnd, _ := service.GetMInEndWithUUID(id)
+ if len(mRule.SourceId) > 0 {
+ InEnd, _ := service.GetMInEndWithUUID(mRule.SourceId)
if InEnd == nil {
- c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+id))
+ c.JSON(common.HTTP_OK, common.Error(`inend not exists: `+mRule.SourceId))
return
}
// 去重旧的
- BindRules := []string{}
+ InEndBindRules := []string{}
for _, iid := range InEnd.BindRules {
if iid != mRule.UUID {
- BindRules = append(BindRules, iid)
+ InEndBindRules = append(InEndBindRules, iid)
}
}
if err := service.UpdateMInEnd(InEnd.UUID, &model.MInEnd{
- BindRules: BindRules,
+ BindRules: InEndBindRules,
}); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
+
+ if err := server.LoadNewestInEnd(mRule.SourceId, ruleEngine); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
}
+
// FromDevice
- for _, devId := range mRule.FromDevice {
- Device, _ := service.GetMDeviceWithUUID(devId)
+ if len(mRule.DeviceId) > 0 {
+ Device, _ := service.GetMDeviceWithUUID(mRule.DeviceId)
if Device == nil {
- c.JSON(common.HTTP_OK, common.Error(`device not exists: `+devId))
+ c.JSON(common.HTTP_OK, common.Error(`device not exists: `+mRule.DeviceId))
return
}
// 去重旧的
- BindRules := []string{}
+ DeviceBindRules := []string{}
for _, iid := range Device.BindRules {
if iid != mRule.UUID {
- BindRules = append(BindRules, iid)
+ DeviceBindRules = append(DeviceBindRules, iid)
}
}
if err := service.UpdateDevice(Device.UUID, &model.MDevice{
- BindRules: BindRules,
+ BindRules: DeviceBindRules,
}); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
+ //
+ // 内存里的数据刷新完了以后更新数据库,最后重启
+ //
+ if err := server.LoadNewestDevice(mRule.DeviceId, ruleEngine); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
}
+
if err := service.DeleteMRule(uuid); err != nil {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
ruleEngine.RemoveRule(uuid)
- //
- // 内存里的数据刷新完了以后更新数据库,最后重启
- //
- for _, devId := range mRule.FromDevice {
- if err := server.LoadNewestDevice(devId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- }
- for _, devId := range mRule.FromSource {
- if err := server.LoadNewestInEnd(devId, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- }
c.JSON(common.HTTP_OK, common.Ok())
}
@@ -504,8 +529,18 @@ func ValidateLuaSyntax(c *gin.Context, ruleEngine typex.RuleX) {
"", // 不需要该字段
"", // 不需要该字段
"", // 不需要该字段
- form.FromSource,
- form.FromDevice,
+ func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromSource),
+ func(s []string) string {
+ if len(s) > 0 {
+ return s[0]
+ }
+ return ""
+ }(form.FromDevice),
form.Success,
form.Actions,
form.Failed)
@@ -570,9 +605,6 @@ func TestOutEndCallback(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error(fmt.Sprintf("'OutEnd' not exists: %v", form.UUID)))
return
}
- glogger.GLogger.WithFields(logrus.Fields{
- "topic": "rule/test/" + form.UUID,
- }).Debug(form.TestData)
err1 := interqueue.DefaultDataCacheQueue.PushOutQueue(outend, form.TestData)
if err1 != nil {
c.JSON(common.HTTP_OK, common.Error400(err1))
@@ -627,13 +659,12 @@ func ListByDevice(c *gin.Context, ruleEngine typex.RuleX) {
mRules := service.AllMRules() // 这个效率太低了, 后期写个SQL优化一下
ruleVos := []ruleVo{}
for _, rule := range mRules {
- if utils.SContains(rule.FromDevice, MDevice.UUID) {
+ if utils.SContains([]string{rule.DeviceId}, MDevice.UUID) {
ruleVos = append(ruleVos, ruleVo{
UUID: rule.UUID,
- FromSource: rule.FromSource,
- FromDevice: rule.FromDevice,
+ FromSource: []string{rule.SourceId},
+ FromDevice: []string{rule.DeviceId},
Name: rule.Name,
- Type: rule.Type,
Status: 1,
Description: rule.Description,
Actions: rule.Actions,
@@ -662,13 +693,12 @@ func ListByInend(c *gin.Context, ruleEngine typex.RuleX) {
mRules := service.AllMRules() // 这个效率太低了, 后期写个SQL优化一下
ruleVos := []ruleVo{}
for _, rule := range mRules {
- if utils.SContains(rule.FromSource, MInend.UUID) {
+ if utils.SContains([]string{rule.SourceId}, MInend.UUID) {
ruleVos = append(ruleVos, ruleVo{
UUID: rule.UUID,
- FromSource: rule.FromSource,
- FromDevice: rule.FromDevice,
+ FromSource: []string{rule.SourceId},
+ FromDevice: []string{rule.DeviceId},
Name: rule.Name,
- Type: rule.Type,
Status: 1,
Description: rule.Description,
Actions: rule.Actions,
diff --git a/plugin/http_server/apis/site_config_api.go b/component/rulex_api_server/apis/site_config_api.go
similarity index 89%
rename from plugin/http_server/apis/site_config_api.go
rename to component/rulex_api_server/apis/site_config_api.go
index 54c2a8bf7..b45793ee9 100644
--- a/plugin/http_server/apis/site_config_api.go
+++ b/component/rulex_api_server/apis/site_config_api.go
@@ -17,9 +17,9 @@ package apis
import (
"github.com/gin-gonic/gin"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
)
diff --git a/plugin/http_server/apis/sysconfig_backup_api.go b/component/rulex_api_server/apis/sysconfig_backup_api.go
similarity index 82%
rename from plugin/http_server/apis/sysconfig_backup_api.go
rename to component/rulex_api_server/apis/sysconfig_backup_api.go
index 3c6b86971..6ab35a3b8 100644
--- a/plugin/http_server/apis/sysconfig_backup_api.go
+++ b/component/rulex_api_server/apis/sysconfig_backup_api.go
@@ -9,8 +9,8 @@ import (
"time"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
"github.com/hootrhino/rulex/typex"
)
@@ -41,24 +41,24 @@ func UploadSqlite(c *gin.Context, ruleEngine typex.RuleX) {
// single file
file, err := c.FormFile("file")
if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ c.JSON(common.HTTP_OK, common.OkWithData(err))
return
}
fileName := "recovery.db"
dir := "./upload/Backup/"
if err := os.MkdirAll(filepath.Dir(dir), os.ModePerm); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ c.JSON(common.HTTP_OK, common.OkWithData(err))
return
}
if err := c.SaveUploadedFile(file, dir+fileName); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ c.JSON(common.HTTP_OK, common.OkWithData(err))
return
}
if _, err := ReadSQLiteFileMagicNumber(dir + fileName); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ c.JSON(common.HTTP_OK, common.OkWithData(err))
return
}
- c.JSON(common.HTTP_OK, common.Ok())
+ c.JSON(common.HTTP_OK, common.OkWithData(""))
ossupport.StartRecoverProcess()
}
diff --git a/plugin/http_server/apis/system_api.go b/component/rulex_api_server/apis/system_api.go
similarity index 88%
rename from plugin/http_server/apis/system_api.go
rename to component/rulex_api_server/apis/system_api.go
index 3d6c5412a..0549ff10c 100644
--- a/plugin/http_server/apis/system_api.go
+++ b/component/rulex_api_server/apis/system_api.go
@@ -3,6 +3,7 @@ package apis
import (
"fmt"
"net"
+ "runtime"
// "runtime"
"strconv"
@@ -10,10 +11,10 @@ import (
"github.com/hootrhino/rulex/component/appstack"
"github.com/hootrhino/rulex/component/intermetric"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/component/trailer"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/utils"
"github.com/hootrhino/rulex/device"
@@ -109,12 +110,13 @@ func System(c *gin.Context, ruleEngine typex.RuleX) {
// ip, err0 := utils.HostNameI()
memPercent, _ := service.GetMemPercent()
hardWareInfo := map[string]interface{}{
- "version": ruleEngine.Version().Version,
+ "version": typex.MainVersion,
"diskInfo": calculateDiskInfo(diskInfo),
"memPercent": memPercent,
"cpuPercent": calculateCpuPercent(cpuPercent),
"osArch": ruleEngine.Version().Arch,
"osDist": ruleEngine.Version().Dist,
+ "product": typex.DefaultVersionInfo.Product,
"startedAt": __StartedAt,
"osUpTime": func() string {
result, err := ossupport.GetUptime()
@@ -137,7 +139,11 @@ func System(c *gin.Context, ruleEngine typex.RuleX) {
*
*/
func SnapshotDump(c *gin.Context, ruleEngine typex.RuleX) {
- c.JSON(common.HTTP_OK, common.OkWithData(ruleEngine.SnapshotDump()))
+ c.Header("Content-Type", "text/plain")
+ c.Header("Content-Disposition",
+ fmt.Sprintf("attachment;filename=SnapshotDump_%v.json", time.Now().UnixMilli()))
+ c.Writer.Write([]byte(ruleEngine.SnapshotDump()))
+ c.Writer.Flush()
}
// Get all Drivers
@@ -254,7 +260,12 @@ func DType(c *gin.Context, ruleEngine typex.RuleX) {
*
*/
func GetUarts(c *gin.Context, ruleEngine typex.RuleX) {
- ports, _ := serial.GetPortsList()
+ var ports []string
+ if runtime.GOOS == "windows" {
+ ports, _ = serial.GetPortsList()
+ } else {
+ ports, _ = ossupport.GetPortsListUnix()
+ }
c.JSON(common.HTTP_OK, common.OkWithData(ports))
}
@@ -264,40 +275,7 @@ func GetUarts(c *gin.Context, ruleEngine typex.RuleX) {
*
*/
func GetUartList(c *gin.Context, ruleEngine typex.RuleX) {
- ports, _ := serial.GetPortsList()
- List := []map[string]interface{}{}
- for _, port := range ports {
- // 明确知道有这么多端口,啰嗦代码是为了标记以免以后忘记
- if port == "/dev/ttyS0" {
- continue
- }
- if port == "/dev/ttyS3" {
- continue
- }
- if port == "/dev/ttyUSB0" {
- continue
- }
- if port == "/dev/ttyUSB1" {
- continue
- }
- if port == "/dev/ttyUSB2" {
- continue
- }
-
- List = append(List, map[string]interface{}{
- "port": port,
- "alias": func(port string) string {
- if port == "/dev/ttyS1" {
- return "RS4851(A1B1)"
- }
- if port == "/dev/ttyS2" {
- return "RS4851(A2B2)"
- }
- return port
- }(port)})
-
- }
- c.JSON(common.HTTP_OK, common.OkWithData(List))
+ c.JSON(common.HTTP_OK, common.OkWithData(service.GetOsPort()))
}
/*
diff --git a/plugin/http_server/apis/system_config_api.go b/component/rulex_api_server/apis/system_config_api_linux.go
similarity index 98%
rename from plugin/http_server/apis/system_config_api.go
rename to component/rulex_api_server/apis/system_config_api_linux.go
index f749385c7..5ac5fc658 100644
--- a/plugin/http_server/apis/system_config_api.go
+++ b/component/rulex_api_server/apis/system_config_api_linux.go
@@ -11,11 +11,11 @@ import (
"strings"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
diff --git a/plugin/http_server/apis/system_firmware_api.go b/component/rulex_api_server/apis/system_firmware_api.go
similarity index 88%
rename from plugin/http_server/apis/system_firmware_api.go
rename to component/rulex_api_server/apis/system_firmware_api.go
index a22c6087d..96423a2fd 100644
--- a/plugin/http_server/apis/system_firmware_api.go
+++ b/component/rulex_api_server/apis/system_firmware_api.go
@@ -24,10 +24,10 @@ import (
"path/filepath"
"github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
"github.com/hootrhino/rulex/component/trailer"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/ossupport"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
"github.com/hootrhino/rulex/typex"
)
@@ -55,7 +55,7 @@ func UploadFirmWare(c *gin.Context, ruleEngine typex.RuleX) {
return
}
- c.JSON(common.HTTP_OK, common.OkWithData(saveDir+fileName))
+ c.JSON(common.HTTP_OK, common.Ok())
}
/*
@@ -94,19 +94,12 @@ func UpgradeFirmWare(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error("invalid sum md5!"))
return
}
- // 将其移动到一个临时目录
- if err := MoveFile(tempPath+"/rulex", tempPath+"/rulex-temp"); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- if err := chmodX(tempPath + "/rulex-temp"); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ if err3 := os.RemoveAll(tempPath); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err3))
return
}
-
c.JSON(common.HTTP_OK, common.Ok())
- ossupport.StartUpgradeProcess(tempPath+"/rulex-temp",
- []string{"upgrade", "-oldpid", fmt.Sprintf("%d", os.Getpid())})
+ ossupport.StartUpgradeProcess()
}
diff --git a/plugin/http_server/apis/system_settings_linux.go b/component/rulex_api_server/apis/system_settings_linux.go
similarity index 97%
rename from plugin/http_server/apis/system_settings_linux.go
rename to component/rulex_api_server/apis/system_settings_linux.go
index ed12f128b..641592afa 100644
--- a/plugin/http_server/apis/system_settings_linux.go
+++ b/component/rulex_api_server/apis/system_settings_linux.go
@@ -1,6 +1,6 @@
package apis
-import "github.com/hootrhino/rulex/plugin/http_server/server"
+import "github.com/hootrhino/rulex/component/rulex_api_server/server"
// Copyright (C) 2023 wwhai
//
diff --git a/plugin/http_server/apis/system_settings_windows.go b/component/rulex_api_server/apis/system_settings_windows.go
similarity index 78%
rename from plugin/http_server/apis/system_settings_windows.go
rename to component/rulex_api_server/apis/system_settings_windows.go
index 648b42716..bd4fafbfe 100644
--- a/plugin/http_server/apis/system_settings_windows.go
+++ b/component/rulex_api_server/apis/system_settings_windows.go
@@ -1,5 +1,7 @@
package apis
+import "github.com/hootrhino/rulex/component/rulex_api_server/server"
+
// Copyright (C) 2023 wwhai
//
// This program is free software: you can redistribute it and/or modify
@@ -16,4 +18,6 @@ package apis
// along with this program. If not, see .
func LoadSystemSettingsAPI() {
+ settingsFirmware := server.RouteGroup(server.ContextUrl("/firmware"))
+ settingsFirmware.GET("/vendorKey", server.AddRoute(GetVendorKey))
}
diff --git a/plugin/http_server/apis/trailer_api.go b/component/rulex_api_server/apis/trailer_api.go
similarity index 98%
rename from plugin/http_server/apis/trailer_api.go
rename to component/rulex_api_server/apis/trailer_api.go
index dbf547cdc..2ff640f7d 100644
--- a/plugin/http_server/apis/trailer_api.go
+++ b/component/rulex_api_server/apis/trailer_api.go
@@ -10,11 +10,11 @@ import (
"runtime"
"time"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/component/trailer"
"github.com/hootrhino/rulex/glogger"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
"google.golang.org/grpc"
diff --git a/plugin/http_server/apis/user_api.go b/component/rulex_api_server/apis/user_api.go
similarity index 85%
rename from plugin/http_server/apis/user_api.go
rename to component/rulex_api_server/apis/user_api.go
index 7c7603f57..109410229 100644
--- a/plugin/http_server/apis/user_api.go
+++ b/component/rulex_api_server/apis/user_api.go
@@ -12,9 +12,9 @@ import (
"time"
"unicode/utf8"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/dgrijalva/jwt-go"
@@ -85,6 +85,38 @@ func CreateUser(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error("user already exists:"+form.Username))
}
+// UpdateUser
+func UpdateUser(c *gin.Context, ruleEngine typex.RuleX) {
+ type Form struct {
+ Username string `json:"username" binding:"required"`
+ Password string `json:"password" binding:"required"`
+ Description string `json:"description"`
+ }
+ form := Form{}
+ if err := c.ShouldBindJSON(&form); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ if !isLengthBetween8And16(form.Username) {
+ c.JSON(common.HTTP_OK, common.Error("Username Length must Between 8 ~ 16"))
+ return
+ }
+ if !isLengthBetween8And16(form.Password) {
+ c.JSON(common.HTTP_OK, common.Error("Password Length must Between 8 ~ 16"))
+ return
+ }
+
+ if err := service.UpdateMUser(&model.MUser{
+ Username: form.Username,
+ Password: md5Hash(form.Password),
+ Description: form.Description,
+ }); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+}
+
/*
*
* Md5 计算
@@ -120,21 +152,6 @@ func Login(c *gin.Context, ruleEngine typex.RuleX) {
}
}
-/*
-*
-* 日志管理
-*
- */
-func Logs(c *gin.Context, ruleEngine typex.RuleX) {
- type Data struct {
- Id int `json:"id" binding:"required"`
- Content string `json:"content" binding:"required"`
- }
- //TODO 日志暂时不记录
- logs := []Data{}
- c.JSON(common.HTTP_OK, common.OkWithData(logs))
-}
-
func LogOut(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Ok())
}
diff --git a/component/rulex_api_server/apis/user_lua_template_api.go b/component/rulex_api_server/apis/user_lua_template_api.go
new file mode 100644
index 000000000..f575df5cf
--- /dev/null
+++ b/component/rulex_api_server/apis/user_lua_template_api.go
@@ -0,0 +1,259 @@
+package apis
+
+import (
+ "github.com/gin-gonic/gin"
+ "github.com/hootrhino/rulex/component/interdb"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/dto"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+ "gorm.io/gorm"
+)
+
+type UserLuaTemplateVo struct {
+ Gid string `json:"gid,omitempty"` // 分组ID
+ UUID string `json:"uuid,omitempty"` // 名称
+ Label string `json:"label"` // 快捷代码名称
+ Apply string `json:"apply"` // 快捷代码
+ Type string `json:"type"` // 类型 固定为function类型detail
+ Detail string `json:"detail"` // 细节
+ Variables []dto.LuaTemplateVariables `json:"variables"` // 变量
+}
+
+/*
+*
+* 新建用户模板
+*
+ */
+
+func CreateUserLuaTemplate(c *gin.Context, ruleEngine typex.RuleX) {
+ form := UserLuaTemplateVo{Type: "function"}
+ if err := c.ShouldBindJSON(&form); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ _, err0 := service.GetGenericGroupWithUUID(form.Gid)
+ if err0 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err0))
+ return
+ }
+ MUserLuaTemplate := model.MUserLuaTemplate{
+ UUID: utils.UserLuaUuid(),
+ Label: form.Label,
+ Type: "function",
+ Apply: form.Apply,
+ Detail: form.Detail,
+ Gid: form.Gid,
+ }
+ Variables, err1 := MUserLuaTemplate.GenVariables(form.Variables)
+ if err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error("Group not found"))
+ return
+ }
+ MUserLuaTemplate.Variables = Variables
+ if err := service.InsertUserLuaTemplate(MUserLuaTemplate); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+
+ // 新建用户模板的时候必须给一个分组
+ if err := service.BindResource(form.Gid, MUserLuaTemplate.UUID); err != nil {
+ c.JSON(common.HTTP_OK, common.Error("Group not found"))
+ return
+ }
+ // 返回新建的用户模板字段 用来跳转编辑器
+ c.JSON(common.HTTP_OK, common.OkWithData(map[string]string{
+ "uuid": MUserLuaTemplate.UUID,
+ }))
+
+}
+
+/*
+*
+* 更新用户模板
+*
+ */
+func UpdateUserLuaTemplate(c *gin.Context, ruleEngine typex.RuleX) {
+ form := UserLuaTemplateVo{}
+ if err := c.ShouldBindJSON(&form); err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ MUserLuaTemplate := model.MUserLuaTemplate{
+ UUID: form.UUID,
+ Label: form.Label,
+ Type: form.Type,
+ Apply: form.Apply,
+ Detail: form.Detail,
+ Gid: form.Gid,
+ }
+ Variables, errVariables := MUserLuaTemplate.GenVariables(form.Variables)
+ if errVariables != nil {
+ c.JSON(common.HTTP_OK, common.Error400(errVariables))
+ return
+ }
+ MUserLuaTemplate.Variables = Variables
+ // 事务
+ txErr := service.ReBindResource(func(tx *gorm.DB) error {
+ return tx.Model(MUserLuaTemplate).
+ Where("uuid=?", MUserLuaTemplate.UUID).
+ Updates(&MUserLuaTemplate).Error
+ }, form.UUID, form.Gid)
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
+ return
+ }
+ // 返回新建的用户模板字段 用来跳转编辑器
+ c.JSON(common.HTTP_OK, common.OkWithData(map[string]string{
+ "uuid": MUserLuaTemplate.UUID,
+ }))
+}
+
+/*
+*
+* 删除用户模板
+*
+ */
+func DeleteUserLuaTemplate(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ txErr := interdb.DB().Transaction(func(tx *gorm.DB) error {
+ Group := service.GetVisualGroup(uuid)
+ if err1 := service.DeleteUserLuaTemplate(uuid); err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err1))
+ return err1
+ }
+ // 解除关联
+ err2 := interdb.DB().Where("gid=? and rid =?", Group.UUID, uuid).
+ Delete(&model.MGenericGroupRelation{}).Error
+ if err2 != nil {
+ return err2
+ }
+ return nil
+ })
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
+ return
+ }
+ c.JSON(common.HTTP_OK, common.Ok())
+
+}
+func ListUserLuaTemplateGroup(c *gin.Context, ruleEngine typex.RuleX) {
+ visuals := []MGenericGroupVo{}
+ for _, vv := range service.ListByGroupType("USER_LUA_TEMPLATE") {
+ visuals = append(visuals, MGenericGroupVo{
+ UUID: vv.UUID,
+ Name: vv.Name,
+ Type: vv.Type,
+ Parent: vv.Parent,
+ })
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(visuals))
+}
+
+/*
+*
+* 模糊查询
+*
+ */
+func SearchUserLuaTemplateGroup(c *gin.Context, ruleEngine typex.RuleX) {
+ visuals := []UserLuaTemplateVo{}
+ keyword, _ := c.GetQuery("keyword")
+ for _, vv := range service.SearchUserLuaTemplate(keyword, keyword) {
+ visuals = append(visuals, UserLuaTemplateVo{
+ UUID: vv.UUID,
+ Label: vv.Label,
+ Type: vv.Type,
+ Apply: vv.Apply,
+ Detail: vv.Detail,
+ Gid: vv.Gid,
+ Variables: vv.GetVariables(),
+ })
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(visuals))
+}
+
+/*
+*
+* 用户模板列表
+*
+ */
+func ListUserLuaTemplate(c *gin.Context, ruleEngine typex.RuleX) {
+ UserLuaTemplates := []UserLuaTemplateVo{}
+ for _, vv := range service.AllUserLuaTemplate() {
+ Vo := UserLuaTemplateVo{
+ UUID: vv.UUID,
+ Label: vv.Label,
+ Type: vv.Type,
+ Apply: vv.Apply,
+ Detail: vv.Detail,
+ Gid: vv.Gid,
+ Variables: vv.GetVariables(),
+ }
+ Group := service.GetUserLuaTemplateGroup(vv.UUID)
+ if Group.UUID != "" {
+ Vo.Gid = Group.UUID
+ } else {
+ Vo.Gid = ""
+ }
+ UserLuaTemplates = append(UserLuaTemplates, Vo)
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(UserLuaTemplates))
+
+}
+
+/*
+*
+* 用户模板分组查看
+*
+ */
+func ListUserLuaTemplateByGroup(c *gin.Context, ruleEngine typex.RuleX) {
+ Gid, _ := c.GetQuery("uuid")
+ UserLuaTemplates := []UserLuaTemplateVo{}
+ MUserLuaTemplates := service.FindUserTemplateByGroup(Gid)
+ for _, vv := range MUserLuaTemplates {
+ Vo := UserLuaTemplateVo{
+ UUID: vv.UUID,
+ Label: vv.Label,
+ Type: vv.Type,
+ Apply: vv.Apply,
+ Detail: vv.Detail,
+ Gid: vv.Gid,
+ Variables: vv.GetVariables(),
+ }
+ Group := service.GetUserLuaTemplateGroup(vv.UUID)
+ Vo.Gid = Group.UUID
+ UserLuaTemplates = append(UserLuaTemplates, Vo)
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(UserLuaTemplates))
+}
+
+/*
+*
+* 用户模板详情
+*
+ */
+func UserLuaTemplateDetail(c *gin.Context, ruleEngine typex.RuleX) {
+ uuid, _ := c.GetQuery("uuid")
+ mUserLuaTemplate, err := service.GetUserLuaTemplateWithUUID(uuid)
+ if err != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err))
+ return
+ }
+ Vo := UserLuaTemplateVo{
+ UUID: mUserLuaTemplate.UUID,
+ Label: mUserLuaTemplate.Label,
+ Type: mUserLuaTemplate.Type,
+ Apply: mUserLuaTemplate.Apply,
+ Detail: mUserLuaTemplate.Detail,
+ Variables: mUserLuaTemplate.GetVariables(),
+ }
+ Group := service.GetUserLuaTemplateGroup(mUserLuaTemplate.UUID)
+ if Group.UUID != "" {
+ Vo.Gid = Group.UUID
+ } else {
+ Vo.Gid = ""
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(Vo))
+}
diff --git a/component/rulex_api_server/apis/vendor_security_api.go b/component/rulex_api_server/apis/vendor_security_api.go
new file mode 100644
index 000000000..5585d5c11
--- /dev/null
+++ b/component/rulex_api_server/apis/vendor_security_api.go
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package apis
+
+import (
+ "os"
+ "strings"
+
+ "github.com/gin-gonic/gin"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/core"
+ "github.com/hootrhino/rulex/typex"
+ "gopkg.in/ini.v1"
+)
+
+/*
+*
+* 获取一机一密
+*
+ */
+
+func GetVendorKey(c *gin.Context, ruleEngine typex.RuleX) {
+ cfg, _ := ini.ShadowLoad(core.INIPath)
+ sections := cfg.ChildSections("plugin")
+ license := ""
+ for _, section := range sections {
+ name := strings.TrimPrefix(section.Name(), "plugin.")
+ if name == "license_manager" {
+ license_path, err1 := section.GetKey("license_path")
+ if err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err1))
+ return
+ }
+ readBytes, err2 := os.ReadFile(license_path.String())
+ if err2 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err2))
+ return
+ }
+ license += string(readBytes)
+ break
+ }
+ }
+ c.JSON(common.HTTP_OK, common.OkWithData(license))
+}
diff --git a/plugin/http_server/apis/visual_api.go b/component/rulex_api_server/apis/visual_api.go
similarity index 82%
rename from plugin/http_server/apis/visual_api.go
rename to component/rulex_api_server/apis/visual_api.go
index 316872c2e..197e0515a 100644
--- a/plugin/http_server/apis/visual_api.go
+++ b/component/rulex_api_server/apis/visual_api.go
@@ -9,11 +9,13 @@ import (
"time"
"github.com/gin-gonic/gin"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
+ "github.com/hootrhino/rulex/component/interdb"
+ common "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
+ "gorm.io/gorm"
)
type VisualVo struct {
@@ -77,31 +79,25 @@ func UpdateVisual(c *gin.Context, ruleEngine typex.RuleX) {
c.JSON(common.HTTP_OK, common.Error400(err))
return
}
- MVisual := model.MVisual{
- UUID: form.UUID,
- Name: form.Name,
- Type: form.Type,
- Content: form.Content,
- }
-
- if err := service.UpdateVisual(MVisual); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
// 取消绑定分组,删除原来旧的分组
- Group := service.GetVisualGroup(MVisual.UUID)
- if err := service.UnBindResource(Group.UUID, MVisual.UUID); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // 重新绑定分组
- if err := service.BindResource(form.Gid, MVisual.UUID); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ txErr := service.ReBindResource(func(tx *gorm.DB) error {
+ MVisual := model.MVisual{
+ UUID: form.UUID,
+ Name: form.Name,
+ Type: form.Type,
+ Content: form.Content,
+ }
+ return tx.Model(MVisual).
+ Where("uuid=?", MVisual.UUID).
+ Updates(&MVisual).Error
+ }, form.UUID, form.Gid)
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
return
}
// 返回新建的大屏字段 用来跳转编辑器
c.JSON(common.HTTP_OK, common.OkWithData(map[string]string{
- "uuid": MVisual.UUID,
+ "uuid": form.UUID,
}))
}
@@ -136,12 +132,26 @@ func PublishVisual(c *gin.Context, ruleEngine typex.RuleX) {
*/
func DeleteVisual(c *gin.Context, ruleEngine typex.RuleX) {
uuid, _ := c.GetQuery("uuid")
- if err := service.DeleteVisual(uuid); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
+ // 事务
+ txErr := interdb.DB().Transaction(func(tx *gorm.DB) error {
+ Group := service.GetVisualGroup(uuid)
+ if err1 := service.DeleteVisual(uuid); err1 != nil {
+ c.JSON(common.HTTP_OK, common.Error400(err1))
+ return err1
+ }
+ // 解除关联
+ err2 := interdb.DB().Where("gid=? and rid =?", Group.UUID, uuid).
+ Delete(&model.MGenericGroupRelation{}).Error
+ if err2 != nil {
+ return err2
+ }
+ return nil
+ })
+ if txErr != nil {
+ c.JSON(common.HTTP_OK, common.Error400(txErr))
return
}
c.JSON(common.HTTP_OK, common.Ok())
-
}
/*
@@ -180,7 +190,7 @@ func ListVisual(c *gin.Context, ruleEngine typex.RuleX) {
func ListVisualByGroup(c *gin.Context, ruleEngine typex.RuleX) {
Gid, _ := c.GetQuery("uuid")
visuals := []VisualVo{}
- MVisuals, _ := service.FindByType(Gid, "VISUAL")
+ MVisuals := service.FindVisualByGroup(Gid)
for _, vv := range MVisuals {
Vo := VisualVo{
UUID: vv.UUID,
diff --git a/plugin/http_server/common/response.go b/component/rulex_api_server/common/response.go
similarity index 100%
rename from plugin/http_server/common/response.go
rename to component/rulex_api_server/common/response.go
diff --git a/plugin/http_server/dto/crontask_dto.go b/component/rulex_api_server/dto/crontask_dto.go
similarity index 63%
rename from plugin/http_server/dto/crontask_dto.go
rename to component/rulex_api_server/dto/crontask_dto.go
index 6bd54f628..9c0615dab 100644
--- a/plugin/http_server/dto/crontask_dto.go
+++ b/component/rulex_api_server/dto/crontask_dto.go
@@ -3,20 +3,18 @@ package dto
type CronTaskCreateDTO struct {
Name string `form:"name" binding:"required" json:"name"`
CronExpr string `form:"cronExpr" binding:"required" json:"cronExpr"`
- TaskType int `form:"taskType" binding:"required" json:"taskType"` // 1-shell 2-cmd
+ TaskType string `form:"taskType" binding:"required" json:"taskType"` // CRON_TASK_TYPE
Args *string `form:"args" json:"args"` // "param1 param2 param3"
- IsRoot string `form:"isRoot" json:"isRoot"` // 0-false 1-true
Env []string `form:"env" json:"env"` // ["A=e1", "B=e2", "C=e3"]
- Script string `form:"script" json:"script"`
+ Script string `form:"script" json:"script"` // 脚本内容,base64编码
}
type CronTaskUpdateDTO struct {
UUID string `form:"uuid" binding:"required" json:"uuid"`
Name string `form:"name" json:"name"`
CronExpr string `form:"cronExpr" json:"cronExpr"`
- TaskType int `form:"taskType" json:"taskType"` // 1-shell 2-cmd
+ TaskType string `form:"taskType" json:"taskType"` // CRON_TASK_TYPE
Args *string `form:"args" json:"args"` // "param1 param2 param3"
- IsRoot string `form:"isRoot" json:"isRoot"` // 0-false 1-true
Env []string `form:"env" json:"env"` // ["A=e1", "B=e2", "C=e3"]
- Script string `form:"script" json:"script"`
+ Script string `form:"script" json:"script"` // 脚本内容,base64编码
}
diff --git a/plugin/http_server/dto/data_schema.go b/component/rulex_api_server/dto/data_schema.go
similarity index 100%
rename from plugin/http_server/dto/data_schema.go
rename to component/rulex_api_server/dto/data_schema.go
diff --git a/plugin/http_server/dto/etcnet_config.go b/component/rulex_api_server/dto/etcnet_config.go
similarity index 100%
rename from plugin/http_server/dto/etcnet_config.go
rename to component/rulex_api_server/dto/etcnet_config.go
diff --git a/plugin/http_server/dto/netplan_config.go b/component/rulex_api_server/dto/netplan_config.go
similarity index 100%
rename from plugin/http_server/dto/netplan_config.go
rename to component/rulex_api_server/dto/netplan_config.go
diff --git a/component/rulex_api_server/dto/pager_dto.go b/component/rulex_api_server/dto/pager_dto.go
new file mode 100644
index 000000000..a20a7a52e
--- /dev/null
+++ b/component/rulex_api_server/dto/pager_dto.go
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package dto
+
+type PageRequest struct {
+ Current int `json:"current,omitempty"`
+ Size int `json:"size,omitempty"`
+ SearchCount int `json:"searchCount,omitempty"`
+}
+
+type PageResult struct {
+ Current int `json:"current"`
+ Size int `json:"size"`
+ Total int `json:"total"`
+ Records any `json:"records"`
+}
diff --git a/component/rulex_api_server/dto/user_lua_template.go b/component/rulex_api_server/dto/user_lua_template.go
new file mode 100644
index 000000000..09d4f9271
--- /dev/null
+++ b/component/rulex_api_server/dto/user_lua_template.go
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package dto
+
+// 变量
+type LuaTemplateVariables struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Label string `json:"label"`
+ Value any `json:"value"`
+}
diff --git a/plugin/http_server/http_api_server.go b/component/rulex_api_server/http_api_server.go
similarity index 69%
rename from plugin/http_server/http_api_server.go
rename to component/rulex_api_server/http_api_server.go
index 8bc9f2fa8..4006bd2ba 100644
--- a/plugin/http_server/http_api_server.go
+++ b/component/rulex_api_server/http_api_server.go
@@ -3,16 +3,17 @@ package httpserver
import (
"encoding/json"
+ "github.com/hootrhino/rulex/component/cron_task"
"github.com/hootrhino/rulex/component/hwportmanager"
"github.com/hootrhino/rulex/component/appstack"
"github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/apis"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/component/trailer"
"github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/plugin/http_server/apis"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/server"
- "github.com/hootrhino/rulex/plugin/http_server/service"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
@@ -27,6 +28,7 @@ import (
type _serverConfig struct {
DbPath string `ini:"dbpath"`
+ Port int `ini:"port"`
}
type ApiServerPlugin struct {
uuid string
@@ -37,7 +39,7 @@ type ApiServerPlugin struct {
func NewHttpApiServer(ruleEngine typex.RuleX) *ApiServerPlugin {
return &ApiServerPlugin{
uuid: "HTTP-API-SERVER",
- mainConfig: _serverConfig{},
+ mainConfig: _serverConfig{Port: 2580},
ruleEngine: ruleEngine,
}
}
@@ -134,6 +136,14 @@ func initRulex(engine typex.RuleX) {
}
}
}
+ //
+ // load Cron Task
+ for _, task := range service.AllEnabledCronTask() {
+ if err := cron_task.GetCronManager().AddTask(task); err != nil {
+ glogger.GLogger.Error(err)
+ continue
+ }
+ }
}
@@ -178,7 +188,9 @@ func (hs *ApiServerPlugin) Init(config *ini.Section) error {
if err := utils.InIMapToStruct(config, &hs.mainConfig); err != nil {
return err
}
- server.StartRulexApiServer(hs.ruleEngine)
+ server.StartRulexApiServer(hs.ruleEngine, hs.mainConfig.Port)
+
+ interdb.DB().Exec("VACUUM;")
interdb.RegisterModel(
&model.MInEnd{},
&model.MOutEnd{},
@@ -187,12 +199,9 @@ func (hs *ApiServerPlugin) Init(config *ini.Section) error {
&model.MDevice{},
&model.MGoods{},
&model.MApp{},
- &model.MAiBase{},
- &model.MModbusPointPosition{},
&model.MVisual{},
&model.MGenericGroup{},
&model.MGenericGroupRelation{},
- &model.MProtocolApp{},
&model.MNetworkConfig{},
&model.MWifiConfig{},
&model.MDataSchema{},
@@ -201,6 +210,10 @@ func (hs *ApiServerPlugin) Init(config *ini.Section) error {
&model.MCronTask{},
&model.MCronResult{},
&model.MHwPort{},
+ &model.MInternalNotify{},
+ &model.MUserLuaTemplate{},
+ &model.MModbusDataPoint{},
+ &model.MSiemensDataPoint{},
)
// 初始化所有预制参数
server.DefaultApiServer.InitializeGenericOSData()
@@ -220,39 +233,25 @@ func (hs *ApiServerPlugin) LoadRoute() {
systemApi := server.RouteGroup(server.ContextUrl("/"))
{
systemApi.GET(("/ping"), server.AddRoute(apis.Ping))
- systemApi.POST(("/logout"), server.AddRoute(apis.LogOut))
}
- //
- // Get all inends
- //
- server.DefaultApiServer.Route().GET(server.ContextUrl("inends"), server.AddRoute(apis.InEnds))
- server.DefaultApiServer.Route().GET(server.ContextUrl("inends/detail"), server.AddRoute(apis.InEndDetail))
+
//
//
//
server.DefaultApiServer.Route().GET(server.ContextUrl("drivers"), server.AddRoute(apis.Drivers))
- //
- // Get all outends
- //
- server.DefaultApiServer.Route().GET(server.ContextUrl("outends"), server.AddRoute(apis.OutEnds))
- server.DefaultApiServer.Route().GET(server.ContextUrl("outends/detail"), server.AddRoute(apis.OutEndDetail))
- //
- // Get all rules
- //
- server.DefaultApiServer.Route().GET(server.ContextUrl("rules"), server.AddRoute(apis.Rules))
- server.DefaultApiServer.Route().GET(server.ContextUrl("rules/detail"), server.AddRoute(apis.RuleDetail))
+
//
// Get statistics data
//
server.DefaultApiServer.Route().GET(server.ContextUrl("statistics"), server.AddRoute(apis.Statistics))
- server.DefaultApiServer.Route().GET(server.ContextUrl("snapshot"), server.AddRoute(apis.SnapshotDump))
//
// Auth
//
userApi := server.RouteGroup(server.ContextUrl("/users"))
{
- userApi.GET(("/"), server.AddRoute(apis.Users))
+ // userApi.GET(("/"), server.AddRoute(apis.Users))
userApi.POST(("/"), server.AddRoute(apis.CreateUser))
+ userApi.PUT(("/update"), server.AddRoute(apis.UpdateUser))
userApi.GET(("/detail"), server.AddRoute(apis.UserDetail))
userApi.POST(("/logout"), server.AddRoute(apis.LogOut))
@@ -267,49 +266,39 @@ func (hs *ApiServerPlugin) LoadRoute() {
//
server.DefaultApiServer.Route().GET(server.ContextUrl("info"), server.AddRoute(apis.Info))
//
- // Create InEnd
- //
- server.DefaultApiServer.Route().POST(server.ContextUrl("inends"), server.AddRoute(apis.CreateInend))
- //
- // Update Inend
- //
- server.DefaultApiServer.Route().PUT(server.ContextUrl("inends"), server.AddRoute(apis.UpdateInend))
- //
- // 配置表
- //
- server.DefaultApiServer.Route().GET(server.ContextUrl("inends/config"), server.AddRoute(apis.GetInEndConfig))
- //
- // 数据模型表
- //
- server.DefaultApiServer.Route().GET(server.ContextUrl("inends/models"), server.AddRoute(apis.GetInEndModels))
- //
- // Create OutEnd
- //
- server.DefaultApiServer.Route().POST(server.ContextUrl("outends"), server.AddRoute(apis.CreateOutEnd))
- //
- // Update OutEnd
- //
- server.DefaultApiServer.Route().PUT(server.ContextUrl("outends"), server.AddRoute(apis.UpdateOutEnd))
+ InEndApi := server.RouteGroup(server.ContextUrl("/inends"))
+ {
+ InEndApi.GET(("/detail"), server.AddRoute(apis.InEndDetail))
+ InEndApi.GET(("/list"), server.AddRoute(apis.InEnds))
+ InEndApi.POST(("/create"), server.AddRoute(apis.CreateInend))
+ InEndApi.DELETE(("/del"), server.AddRoute(apis.DeleteInEnd))
+ InEndApi.PUT(("/update"), server.AddRoute(apis.UpdateInend))
+ InEndApi.PUT("/restart", server.AddRoute(apis.RestartInEnd))
+ }
+
rulesApi := server.RouteGroup(server.ContextUrl("/rules"))
{
- rulesApi.POST(("/"), server.AddRoute(apis.CreateRule))
- rulesApi.PUT(("/"), server.AddRoute(apis.UpdateRule))
- rulesApi.DELETE(("/"), server.AddRoute(apis.DeleteRule))
+ rulesApi.POST(("/create"), server.AddRoute(apis.CreateRule))
+ rulesApi.PUT(("/update"), server.AddRoute(apis.UpdateRule))
+ rulesApi.DELETE(("/del"), server.AddRoute(apis.DeleteRule))
+ rulesApi.GET(("/list"), server.AddRoute(apis.Rules))
+ rulesApi.GET(("/detail"), server.AddRoute(apis.RuleDetail))
+ //
rulesApi.POST(("/testIn"), server.AddRoute(apis.TestSourceCallback))
rulesApi.POST(("/testOut"), server.AddRoute(apis.TestOutEndCallback))
rulesApi.POST(("/testDevice"), server.AddRoute(apis.TestDeviceCallback))
rulesApi.GET(("/byInend"), server.AddRoute(apis.ListByInend))
rulesApi.GET(("/byDevice"), server.AddRoute(apis.ListByDevice))
}
-
- //
- // Delete inend by UUID
- //
- server.DefaultApiServer.Route().DELETE(server.ContextUrl("inends"), server.AddRoute(apis.DeleteInEnd))
- //
- // Delete outEnd by UUID
- //
- server.DefaultApiServer.Route().DELETE(server.ContextUrl("outends"), server.AddRoute(apis.DeleteOutEnd))
+ OutEndApi := server.RouteGroup(server.ContextUrl("/outends"))
+ {
+ OutEndApi.GET(("/detail"), server.AddRoute(apis.OutEndDetail))
+ OutEndApi.GET(("/list"), server.AddRoute(apis.OutEnds))
+ OutEndApi.POST(("/create"), server.AddRoute(apis.CreateOutEnd))
+ OutEndApi.DELETE(("/del"), server.AddRoute(apis.DeleteOutEnd))
+ OutEndApi.PUT(("/update"), server.AddRoute(apis.UpdateOutEnd))
+ OutEndApi.PUT("/restart", server.AddRoute(apis.RestartOutEnd))
+ }
//
// 验证 lua 语法
@@ -323,31 +312,48 @@ func (hs *ApiServerPlugin) LoadRoute() {
{
osApi.GET(("/netInterfaces"), server.AddRoute(apis.GetNetInterfaces))
osApi.GET(("/osRelease"), server.AddRoute(apis.CatOsRelease))
- osApi.GET(("/uarts"), server.AddRoute(apis.GetUartList))
osApi.GET(("/system"), server.AddRoute(apis.System))
osApi.GET(("/startedAt"), server.AddRoute(apis.StartedAt))
-
}
backupApi := server.RouteGroup(server.ContextUrl("/backup"))
{
backupApi.GET(("/download"), server.AddRoute(apis.DownloadSqlite))
backupApi.POST(("/upload"), server.AddRoute(apis.UploadSqlite))
+ backupApi.GET(("/snapshot"), server.AddRoute(apis.SnapshotDump))
+ backupApi.GET(("/runningLog"), server.AddRoute(apis.GetRunningLog))
}
//
// 设备管理
//
deviceApi := server.RouteGroup(server.ContextUrl("/devices"))
{
- deviceApi.POST(("/"), server.AddRoute(apis.CreateDevice))
- deviceApi.PUT(("/"), server.AddRoute(apis.UpdateDevice))
- deviceApi.DELETE(("/"), server.AddRoute(apis.DeleteDevice))
+ deviceApi.POST(("/create"), server.AddRoute(apis.CreateDevice))
+ deviceApi.PUT(("/update"), server.AddRoute(apis.UpdateDevice))
+ deviceApi.DELETE(("/del"), server.AddRoute(apis.DeleteDevice))
deviceApi.GET(("/detail"), server.AddRoute(apis.DeviceDetail))
- deviceApi.POST(("/modbus/sheetImport"), server.AddRoute(apis.ModbusSheetImport))
- deviceApi.PUT(("/modbus/point"), server.AddRoute(apis.UpdateModbusPoint))
- deviceApi.GET(("/modbus"), server.AddRoute(apis.ModbusPoints))
deviceApi.GET("/group", server.AddRoute(apis.ListDeviceGroup))
deviceApi.GET("/listByGroup", server.AddRoute(apis.ListDeviceByGroup))
-
+ deviceApi.PUT("/restart", server.AddRoute(apis.RestartDevice))
+ }
+ // Modbus 点位表
+ modbusApi := server.RouteGroup(server.ContextUrl("/modbus_data_sheet"))
+ {
+ modbusApi.POST(("/sheetImport"), server.AddRoute(apis.ModbusSheetImport))
+ modbusApi.GET(("/sheetExport"), server.AddRoute(apis.ModbusPointsExport))
+ modbusApi.GET(("/list"), server.AddRoute(apis.ModbusSheetPageList))
+ modbusApi.POST(("/update"), server.AddRoute(apis.ModbusSheetUpdate))
+ modbusApi.DELETE(("/delIds"), server.AddRoute(apis.ModbusSheetDelete))
+ modbusApi.DELETE(("/delAll"), server.AddRoute(apis.ModbusSheetDeleteAll))
+ }
+ // S1200 点位表
+ SIEMENS_PLC := server.RouteGroup(server.ContextUrl("/s1200_data_sheet"))
+ {
+ SIEMENS_PLC.POST(("/sheetImport"), server.AddRoute(apis.SiemensSheetImport))
+ SIEMENS_PLC.GET(("/sheetExport"), server.AddRoute(apis.SiemensPointsExport))
+ SIEMENS_PLC.GET(("/list"), server.AddRoute(apis.SiemensSheetPageList))
+ SIEMENS_PLC.POST(("/update"), server.AddRoute(apis.SiemensSheetUpdate))
+ SIEMENS_PLC.DELETE(("/delIds"), server.AddRoute(apis.SiemensSheetDelete))
+ SIEMENS_PLC.DELETE(("/delAll"), server.AddRoute(apis.SiemensSheetDeleteAll))
}
// ----------------------------------------------------------------------------------------------
@@ -355,33 +361,22 @@ func (hs *ApiServerPlugin) LoadRoute() {
// ----------------------------------------------------------------------------------------------
appApi := server.RouteGroup(server.ContextUrl("/app"))
{
- appApi.GET(("/"), server.AddRoute(apis.Apps))
- appApi.POST(("/"), server.AddRoute(apis.CreateApp))
- appApi.PUT(("/"), server.AddRoute(apis.UpdateApp))
- appApi.DELETE(("/"), server.AddRoute(apis.RemoveApp))
+ appApi.GET(("/list"), server.AddRoute(apis.Apps))
+ appApi.POST(("/create"), server.AddRoute(apis.CreateApp))
+ appApi.PUT(("/update"), server.AddRoute(apis.UpdateApp))
+ appApi.DELETE(("/del"), server.AddRoute(apis.RemoveApp))
appApi.PUT(("/start"), server.AddRoute(apis.StartApp))
appApi.PUT(("/stop"), server.AddRoute(apis.StopApp))
appApi.GET(("/detail"), server.AddRoute(apis.AppDetail))
}
// ----------------------------------------------------------------------------------------------
- // AI BASE
- // ----------------------------------------------------------------------------------------------
- aiApi := server.RouteGroup(server.ContextUrl("/aibase"))
- {
- aiApi.GET(("/"), server.AddRoute(apis.AiBase))
- aiApi.DELETE(("/"), server.AddRoute(apis.DeleteAiBase))
- }
- // ----------------------------------------------------------------------------------------------
// Plugin
// ----------------------------------------------------------------------------------------------
- pluginsApi := server.RouteGroup(server.ContextUrl("/plugins"))
- {
- pluginsApi.GET(("/"), server.AddRoute(apis.Plugins))
- }
- pluginApi := server.RouteGroup(server.ContextUrl("/plugin"))
+ pluginsApi := server.RouteGroup(server.ContextUrl("/plugware"))
{
- pluginApi.POST(("/service"), server.AddRoute(apis.PluginService))
- pluginApi.GET(("/detail"), server.AddRoute(apis.PluginDetail))
+ pluginsApi.GET(("/list"), server.AddRoute(apis.Plugins))
+ pluginsApi.POST(("/service"), server.AddRoute(apis.PluginService))
+ pluginsApi.GET(("/detail"), server.AddRoute(apis.PluginDetail))
}
//
@@ -395,19 +390,9 @@ func (hs *ApiServerPlugin) LoadRoute() {
groupApi.GET("/detail", server.AddRoute(apis.GroupDetail))
groupApi.POST("/bind", server.AddRoute(apis.BindResource))
groupApi.PUT("/unbind", server.AddRoute(apis.UnBindResource))
- groupApi.DELETE("/", server.AddRoute(apis.DeleteGroup))
+ groupApi.DELETE("/del", server.AddRoute(apis.DeleteGroup))
}
- //
- // 协议应用管理
- //
- protoAppApi := server.RouteGroup(server.ContextUrl("/protoapp"))
- {
- protoAppApi.POST("/create", server.AddRoute(apis.CreateProtocolApp))
- protoAppApi.DELETE("/delete", server.AddRoute(apis.DeleteProtocolApp))
- protoAppApi.PUT("/update", server.AddRoute(apis.UpdateProtocolApp))
- protoAppApi.GET("/list", server.AddRoute(apis.ListProtocolApp))
- }
//
// 大屏应用管理
//
@@ -424,6 +409,19 @@ func (hs *ApiServerPlugin) LoadRoute() {
visualApi.POST("/thumbnail", server.AddRoute(apis.UploadFile))
visualApi.GET("/thumbnail", server.AddRoute(apis.GetThumbnail))
}
+ //
+ // 用户LUA代码段管理
+ //
+ userLuaApi := server.RouteGroup(server.ContextUrl("/userlua"))
+ {
+ userLuaApi.POST("/create", server.AddRoute(apis.CreateUserLuaTemplate))
+ userLuaApi.PUT("/update", server.AddRoute(apis.UpdateUserLuaTemplate))
+ userLuaApi.GET("/listByGroup", server.AddRoute(apis.ListUserLuaTemplateByGroup))
+ userLuaApi.GET("/detail", server.AddRoute(apis.UserLuaTemplateDetail))
+ userLuaApi.GET("/group", server.AddRoute(apis.ListUserLuaTemplateGroup))
+ userLuaApi.DELETE("/del", server.AddRoute(apis.DeleteUserLuaTemplate))
+ userLuaApi.GET("/search", server.AddRoute(apis.SearchUserLuaTemplateGroup))
+ }
/*
*
* 模型管理
@@ -432,7 +430,7 @@ func (hs *ApiServerPlugin) LoadRoute() {
schemaApi := server.RouteGroup(server.ContextUrl("/schema"))
{
schemaApi.POST("/create", server.AddRoute(apis.CreateDataSchema))
- schemaApi.DELETE("/delete", server.AddRoute(apis.DeleteDataSchema))
+ schemaApi.DELETE("/del", server.AddRoute(apis.DeleteDataSchema))
schemaApi.PUT("/update", server.AddRoute(apis.UpdateDataSchema))
schemaApi.GET("/list", server.AddRoute(apis.ListDataSchema))
schemaApi.GET(("/detail"), server.AddRoute(apis.DataSchemaDetail))
@@ -475,6 +473,15 @@ func (hs *ApiServerPlugin) LoadRoute() {
HwIFaceApi.POST("/update", server.AddRoute(apis.UpdateHwPortConfig))
HwIFaceApi.GET("/refresh", server.AddRoute(apis.RefreshPortList))
}
+ // 站内公告
+ internalNotifyApi := server.DefaultApiServer.GetGroup(server.ContextUrl("/notify"))
+ {
+ internalNotifyApi.GET("/header", server.AddRoute(apis.InternalNotifiesHeader))
+ internalNotifyApi.GET("/list", server.AddRoute(apis.InternalNotifies))
+ internalNotifyApi.PUT("/clear", server.AddRoute(apis.ClearInternalNotifies))
+ internalNotifyApi.PUT("/read", server.AddRoute(apis.ReadInternalNotifies))
+ internalNotifyApi.POST("/test", server.AddRoute(apis.TestCreateNotifies))
+ }
//
// 系统设置
//
@@ -486,7 +493,7 @@ func (hs *ApiServerPlugin) LoadRoute() {
crontaskApi := server.DefaultApiServer.GetGroup(server.ContextUrl("/crontask"))
{
crontaskApi.POST("/create", server.AddRouteV2(apis.CreateCronTask))
- crontaskApi.DELETE("/delete", server.AddRouteV2(apis.DeleteCronTask))
+ crontaskApi.DELETE("/del", server.AddRouteV2(apis.DeleteCronTask))
crontaskApi.PUT("/update", server.AddRouteV2(apis.UpdateCronTask))
crontaskApi.GET("/list", server.AddRouteV2(apis.ListCronTask))
crontaskApi.GET("/results/page", server.AddRouteV2(apis.PageCronTaskResult))
@@ -506,7 +513,7 @@ func (hs *ApiServerPlugin) LoadRoute() {
func (hs *ApiServerPlugin) Start(r typex.RuleX) error {
hs.ruleEngine = r
hs.LoadRoute()
- glogger.GLogger.Infof("Http server started on :%v", hs.mainConfig.DbPath)
+ glogger.GLogger.Infof("Http server started on :%v", 2580)
return nil
}
@@ -521,9 +528,9 @@ func (hs *ApiServerPlugin) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v1.0.0",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/http_server/model/data_schema.go b/component/rulex_api_server/model/data_schema.go
similarity index 100%
rename from plugin/http_server/model/data_schema.go
rename to component/rulex_api_server/model/data_schema.go
diff --git a/plugin/http_server/model/hw_interface.go b/component/rulex_api_server/model/hw_interface.go
similarity index 100%
rename from plugin/http_server/model/hw_interface.go
rename to component/rulex_api_server/model/hw_interface.go
diff --git a/component/rulex_api_server/model/internal_notify.go b/component/rulex_api_server/model/internal_notify.go
new file mode 100644
index 000000000..9cb016538
--- /dev/null
+++ b/component/rulex_api_server/model/internal_notify.go
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package model
+
+/*
+*
+* 内部通知
+*
+ */
+type MInternalNotify struct {
+ RulexModel
+ UUID string `gorm:"not null"` // UUID
+ Type string `gorm:"not null"` // INFO | ERROR | WARNING
+ Status int `gorm:"not null"` // 1 未读 2 已读
+ Event string `gorm:"not null"` // 字符串
+ Ts uint64 `gorm:"not null"` // 时间戳
+ Summary string `gorm:"not null"` // 概览,为了节省流量,在消息列表只显示这个字段,Info值为“”
+ Info string `gorm:"not null"` // 消息内容,是个文本,详情显示
+}
diff --git a/plugin/http_server/model/iproute_config.go b/component/rulex_api_server/model/iproute_config.go
similarity index 100%
rename from plugin/http_server/model/iproute_config.go
rename to component/rulex_api_server/model/iproute_config.go
diff --git a/component/rulex_api_server/model/modebus_data_point.go b/component/rulex_api_server/model/modebus_data_point.go
new file mode 100644
index 000000000..e416088e6
--- /dev/null
+++ b/component/rulex_api_server/model/modebus_data_point.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package model
+
+// modbus数据点位表
+type MModbusDataPoint struct {
+ RulexModel `json:"-"`
+ UUID string `gorm:"not null" json:"uuid"`
+ DeviceUuid string `gorm:"not null" json:"device_uuid"`
+ Tag string `gorm:"not null" json:"tag"`
+ Alias string `gorm:"not null" json:"alias"`
+ Function *int `gorm:"not null" json:"function"`
+ SlaverId *byte `gorm:"not null" json:"slaverId"`
+ Address *uint16 `gorm:"not null" json:"address"`
+ Frequency *int64 `gorm:"not null" json:"frequency"`
+ Quantity *uint16 `gorm:"not null" json:"quantity"`
+}
diff --git a/plugin/http_server/model/model.go b/component/rulex_api_server/model/model.go
similarity index 64%
rename from plugin/http_server/model/model.go
rename to component/rulex_api_server/model/model.go
index ca8f9290b..22166c779 100644
--- a/plugin/http_server/model/model.go
+++ b/component/rulex_api_server/model/model.go
@@ -8,8 +8,8 @@ import (
)
type RulexModel struct {
- ID uint `gorm:"primarykey"`
- CreatedAt time.Time
+ ID uint `gorm:"primaryKey" json:"id"`
+ CreatedAt time.Time `json:"created_at"`
}
type StringList []string
@@ -42,21 +42,20 @@ func (f StringList) Len() int {
type MRule struct {
RulexModel
- UUID string `gorm:"not null"`
- Name string `gorm:"not null"`
- Type string // 脚本类型,目前支持"lua"
- FromSource StringList `gorm:"not null type:string[]"`
- FromDevice StringList `gorm:"not null type:string[]"`
- Actions string `gorm:"not null"`
- Success string `gorm:"not null"`
- Failed string `gorm:"not null"`
+ UUID string `gorm:"not null"`
+ Name string `gorm:"not null"`
+ SourceId string `gorm:"not null"`
+ DeviceId string `gorm:"not null"`
+ Actions string `gorm:"not null"`
+ Success string `gorm:"not null"`
+ Failed string `gorm:"not null"`
Description string
}
type MInEnd struct {
RulexModel
// UUID for origin source ID
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
Type string `gorm:"not null"`
Name string `gorm:"not null"`
BindRules StringList `json:"bindRules"` // 与之关联的规则表["A","B","C"]
@@ -78,7 +77,7 @@ func (md MInEnd) GetConfig() map[string]interface{} {
type MOutEnd struct {
RulexModel
// UUID for origin source ID
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
Type string `gorm:"not null"`
Name string `gorm:"not null"`
Description string
@@ -105,7 +104,7 @@ type MUser struct {
// 设备元数据
type MDevice struct {
RulexModel
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
Name string `gorm:"not null"`
Type string `gorm:"not null"`
Config string
@@ -128,7 +127,7 @@ func (md MDevice) GetConfig() map[string]interface{} {
type MGoods struct {
RulexModel
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
LocalPath string `gorm:"not null"`
GoodsType string `gorm:"not null"` // LOCAL, EXTERNAL
ExecuteType string `gorm:"not null"` // exe,elf,js,py....
@@ -145,12 +144,12 @@ type MGoods struct {
*/
type MApp struct {
RulexModel
- UUID string `gorm:"not null"` // 名称
- Name string `gorm:"not null"` // 名称
- Version string `gorm:"not null"` // 版本号
- AutoStart *bool `gorm:"not null"` // 允许启动
- LuaSource string `gorm:"not null"` // LuaSource
- Description string `gorm:"not null"` // 文件路径, 是相对于main的apps目录
+ UUID string `gorm:"uniqueIndex"` // 名称
+ Name string `gorm:"not null"` // 名称
+ Version string `gorm:"not null"` // 版本号
+ AutoStart *bool `gorm:"not null"` // 允许启动
+ LuaSource string `gorm:"not null"` // LuaSource
+ Description string `gorm:"not null"` // 文件路径, 是相对于main的apps目录
}
/*
@@ -160,26 +159,15 @@ type MApp struct {
*/
type MAiBase struct {
RulexModel
- UUID string `gorm:"not null"` // 名称
- Name string `gorm:"not null"` // 名称
- Type string `gorm:"not null"` // 类型
- IsBuildIn bool `gorm:"not null"` // 是否内建
- Version string `gorm:"not null"` // 版本号
- Filepath string `gorm:"not null"` // 文件路径, 是相对于main的apps目录
+ UUID string `gorm:"uniqueIndex"` // 名称
+ Name string `gorm:"not null"` // 名称
+ Type string `gorm:"not null"` // 类型
+ IsBuildIn bool `gorm:"not null"` // 是否内建
+ Version string `gorm:"not null"` // 版本号
+ Filepath string `gorm:"not null"` // 文件路径, 是相对于main的apps目录
Description string `gorm:"not null"`
}
-// MModbusPointPosition modbus数据点位
-type MModbusPointPosition struct {
- RulexModel
- DeviceUuid string `json:"deviceUuid" gorm:"not null"`
- Tag string `json:"tag" gorm:"not null"`
- Function int `json:"function" gorm:"not null"`
- SlaverId byte `json:"slaverId" gorm:"not null"`
- StartAddress uint16 `json:"startAddress" gorm:"not null"`
- Quality uint16 `json:"quality" gorm:"not null"`
-}
-
//--------------------------------------------------------------------------------------------------
// 0.6.0
//--------------------------------------------------------------------------------------------------
@@ -190,12 +178,12 @@ type MModbusPointPosition struct {
*/
type MVisual struct {
RulexModel
- UUID string `gorm:"not null"` // 名称
- Name string `gorm:"not null"` // 名称
- Type string `gorm:"not null"` // 类型
- Status bool `gorm:"not null"` // 状态, EDITING, PUBLISH
- Content string `gorm:"not null"` // 大屏的内容
- Thumbnail string `gorm:"not null"` // 缩略图
+ UUID string `gorm:"uniqueIndex"` // 名称
+ Name string `gorm:"not null"` // 名称
+ Type string `gorm:"not null"` // 类型
+ Status bool `gorm:"not null"` // 状态, EDITING, PUBLISH
+ Content string `gorm:"not null"` // 大屏的内容
+ Thumbnail string `gorm:"not null"` // 缩略图
}
/*
@@ -205,7 +193,7 @@ type MVisual struct {
*/
type MGenericGroup struct {
RulexModel
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
Name string `gorm:"not null"` // 名称
Type string `gorm:"not null"` // 组的类型, DEVICE: 设备分组
Parent string `gorm:"not null"` // 上级, 如果是0表示根节点
@@ -218,24 +206,11 @@ type MGenericGroup struct {
*/
type MGenericGroupRelation struct {
RulexModel
- UUID string `gorm:"not null"`
+ UUID string `gorm:"uniqueIndex"`
Gid string `gorm:"not null"` // 分组ID
Rid string `gorm:"not null"` // 被绑定方
}
-/*
-*
-* 应用商店里面的各类协议脚本
-*
- */
-type MProtocolApp struct {
- RulexModel
- UUID string `gorm:"not null"` // 名称
- Name string `gorm:"not null"` // 名称
- Type string `gorm:"not null"` // 类型: IN OUT DEVICE APP
- Content string `gorm:"not null"` // 协议包的内容
-}
-
/*
*
* 系统配置参数, 直接以String保存,完了以后再加工成Dto结构体
@@ -270,17 +245,17 @@ type MWifiConfig struct {
*/
type MCronTask struct {
RulexModel
- UUID string `gorm:"not null; default:''" json:"uuid"`
+ UUID string `gorm:"uniqueIndex,not null; default:''" json:"uuid"`
Name string `gorm:"not null;" json:"name"`
- CronExpr string `gorm:"not null" json:"cronExpr"` // linux cron standard
- Enable string `json:"enable"` // 0-disable 1-enable
- TaskType int `json:"taskType"` // 1-shell 目前
- Command string `json:"command"` // 目前不使用,默认都是linux shell
+ CronExpr string `gorm:"not null" json:"cronExpr"` // quartz cron expr
+ Enable *bool `json:"enable"` // 是否启用定时任务
+ TaskType string `json:"taskType"` // CRON_TASK_TYPE,目前只有LINUX_SHELL
+ Command string `json:"command"` // 根据TaskType而定,TaskType=LINUX_SHELL时Command=/bin/bash
Args *string `json:"args"` // "-param1 -param2 -param3"
- IsRoot string `json:"isRoot"` // 0-false 1-true
+ IsRoot *bool `json:"isRoot"` // 是否使用root用户运行,目前不使用,默认和rulex用户一致
WorkDir string `json:"workDir"` // 目前不使用,默认工作路径和网关工作路径保持一致
Env string `json:"env"` // ["A=e1", "B=e2", "C=e3"]
- Script string `json:"script"` // 脚本内容
+ Script string `json:"script"` // 脚本内容,base64编码
UpdatedAt time.Time `json:"updatedAt"`
}
@@ -290,7 +265,7 @@ type MCronTask struct {
type MCronResult struct {
RulexModel
TaskUuid string `gorm:"not null; default:''" json:"taskUuid,omitempty"`
- Status string `json:"status"` // 1-running 2-end
+ Status string `json:"status"` // CRON_RESULT_STATUS
ExitCode string `json:"exitCode,omitempty"` // 0-success other-failed
LogPath string `json:"logPath,omitempty"`
StartTime time.Time `json:"startTime"`
diff --git a/component/rulex_api_server/model/siemons_data_point.go b/component/rulex_api_server/model/siemons_data_point.go
new file mode 100644
index 000000000..f41a07d4f
--- /dev/null
+++ b/component/rulex_api_server/model/siemons_data_point.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package model
+
+// 西门子数据点位表
+type MSiemensDataPoint struct {
+ RulexModel
+ UUID string `gorm:"not null"`
+ DeviceUuid string `gorm:"not null"`
+ Tag string `gorm:"not null"`
+ Alias string `gorm:"not null"`
+ Type string `gorm:"not null"`
+ Frequency *int64 `gorm:"not null"`
+ Address *int `gorm:"not null"`
+ Start *int `gorm:"not null"`
+ Size *int `gorm:"not null"`
+}
diff --git a/plugin/http_server/model/site_config.go b/component/rulex_api_server/model/site_config.go
similarity index 100%
rename from plugin/http_server/model/site_config.go
rename to component/rulex_api_server/model/site_config.go
diff --git a/plugin/http_server/model/syslogo.go b/component/rulex_api_server/model/syslogo.go
similarity index 100%
rename from plugin/http_server/model/syslogo.go
rename to component/rulex_api_server/model/syslogo.go
diff --git a/component/rulex_api_server/model/user_lua_template.go b/component/rulex_api_server/model/user_lua_template.go
new file mode 100644
index 000000000..0d4103934
--- /dev/null
+++ b/component/rulex_api_server/model/user_lua_template.go
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package model
+
+import (
+ "github.com/hootrhino/rulex/component/rulex_api_server/dto"
+ "gopkg.in/square/go-jose.v2/json"
+)
+
+/*
+*
+* 用户自定义代码模板
+*
+ */
+type MUserLuaTemplate struct {
+ RulexModel
+ UUID string
+ Gid string // 分组
+ Type string // 类型 固定为 'function'
+ Label string //快捷代码名称
+ Apply string //快捷代码
+ Variables string //变量
+ Detail string
+}
+
+/*
+*
+* 获取其变量表
+*
+ */
+func (md MUserLuaTemplate) GetVariables() []dto.LuaTemplateVariables {
+ result := make([]dto.LuaTemplateVariables, 0)
+ err := json.Unmarshal([]byte(md.Variables), &result)
+ if err != nil {
+ return result
+ }
+ return result
+}
+
+/*
+*
+* 生成字符串
+*
+ */
+func (md MUserLuaTemplate) GenVariables(V []dto.LuaTemplateVariables) (string, error) {
+ B, err := json.Marshal(V)
+ if err != nil {
+ return "", err
+ }
+ return string(B), nil
+}
diff --git a/plugin/http_server/readme.md b/component/rulex_api_server/readme.md
similarity index 100%
rename from plugin/http_server/readme.md
rename to component/rulex_api_server/readme.md
diff --git a/plugin/http_server/server/apiserver.go b/component/rulex_api_server/server/apiserver.go
similarity index 90%
rename from plugin/http_server/server/apiserver.go
rename to component/rulex_api_server/server/apiserver.go
index 46c9bfdbe..41582b446 100644
--- a/plugin/http_server/server/apiserver.go
+++ b/component/rulex_api_server/server/apiserver.go
@@ -11,12 +11,12 @@ import (
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
+ response "github.com/hootrhino/rulex/component/rulex_api_server/common"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/device"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/ossupport"
- response "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/source"
"github.com/hootrhino/rulex/target"
"github.com/hootrhino/rulex/typex"
@@ -58,12 +58,12 @@ var err1crash = errors.New("http server crash, try to recovery")
* 开启Server
*
*/
-func StartRulexApiServer(ruleEngine typex.RuleX) {
+func StartRulexApiServer(ruleEngine typex.RuleX, port int) {
gin.SetMode(gin.ReleaseMode)
server := RulexApiServer{
ginEngine: gin.New(),
ruleEngine: ruleEngine,
- config: serverConfig{Port: 2580},
+ config: serverConfig{Port: port},
}
server.ginEngine.Use(static.Serve("/", WWWRoot("")))
server.ginEngine.Use(Authorize())
@@ -156,14 +156,16 @@ func (s *RulexApiServer) InitializeGenericOSData() {
initStaticModel()
}
func (s *RulexApiServer) InitializeUnixData() {
+ glogger.GLogger.Info("Initialize Unix Default Data")
}
func (s *RulexApiServer) InitializeWindowsData() {
+ glogger.GLogger.Info("Initialize Windows Default Data")
}
func (s *RulexApiServer) InitializeEEKITData() {
env := os.Getenv("ARCHSUPPORT")
if env == "EEKITH3" {
-
+ glogger.GLogger.Info("Initialize Rhino EEKIT Pi Default Data")
}
}
@@ -233,6 +235,12 @@ func initStaticModel() {
Name: "默认分组",
Parent: "NULL",
})
+ service.InitGenericGroup(&model.MGenericGroup{
+ UUID: "ULTROOT",
+ Type: "USER_LUA_TEMPLATE",
+ Name: "默认分组",
+ Parent: "NULL",
+ })
// 初始化网站配置
service.InitSiteConfig(model.MSiteConfig{
SiteName: "Rhino EEKit",
diff --git a/plugin/http_server/server/auth.go b/component/rulex_api_server/server/auth.go
similarity index 100%
rename from plugin/http_server/server/auth.go
rename to component/rulex_api_server/server/auth.go
diff --git a/plugin/http_server/server/cros.go b/component/rulex_api_server/server/cros.go
similarity index 100%
rename from plugin/http_server/server/cros.go
rename to component/rulex_api_server/server/cros.go
diff --git a/plugin/http_server/server/hello_api.go b/component/rulex_api_server/server/hello_api.go
similarity index 100%
rename from plugin/http_server/server/hello_api.go
rename to component/rulex_api_server/server/hello_api.go
diff --git a/plugin/http_server/server/rate_limit.go b/component/rulex_api_server/server/rate_limit.go
similarity index 100%
rename from plugin/http_server/server/rate_limit.go
rename to component/rulex_api_server/server/rate_limit.go
diff --git a/plugin/http_server/server/res_loader.go b/component/rulex_api_server/server/res_loader.go
similarity index 96%
rename from plugin/http_server/server/res_loader.go
rename to component/rulex_api_server/server/res_loader.go
index 5609ac52e..d3513b66f 100644
--- a/plugin/http_server/server/res_loader.go
+++ b/component/rulex_api_server/server/res_loader.go
@@ -3,8 +3,8 @@ package server
import (
"errors"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
"gopkg.in/square/go-jose.v2/json"
)
@@ -56,8 +56,8 @@ func LoadNewestInEnd(uuid string, ruleEngine typex.RuleX) error {
mRule.UUID,
mRule.Name,
mRule.Description,
- mRule.FromSource,
- mRule.FromDevice,
+ mRule.SourceId,
+ mRule.DeviceId,
mRule.Success,
mRule.Actions,
mRule.Failed)
@@ -148,8 +148,8 @@ func LoadNewestDevice(uuid string, ruleEngine typex.RuleX) error {
mRule.UUID,
mRule.Name,
mRule.Description,
- mRule.FromSource,
- mRule.FromDevice,
+ mRule.SourceId,
+ mRule.DeviceId,
mRule.Success,
mRule.Actions,
mRule.Failed)
diff --git a/plugin/http_server/server/res_supervisor.go b/component/rulex_api_server/server/res_supervisor.go
similarity index 76%
rename from plugin/http_server/server/res_supervisor.go
rename to component/rulex_api_server/server/res_supervisor.go
index aaf913a5e..c7cf41dcb 100644
--- a/plugin/http_server/server/res_supervisor.go
+++ b/component/rulex_api_server/server/res_supervisor.go
@@ -2,8 +2,10 @@ package server
import (
"context"
+ "fmt"
"time"
+ "github.com/hootrhino/rulex/component/internotify"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
)
@@ -41,8 +43,15 @@ func StartInSupervisor(ctx context.Context, in *typex.InEnd, ruleEngine typex.Ru
}
// 资源可能不会及时DOWN
if currentIn.Source.Status() == typex.SOURCE_DOWN {
- glogger.GLogger.Debugf("Source:%v DOWN, supervisor try to Restart", UUID)
- time.Sleep(2 * time.Second)
+ info := fmt.Sprintf("Source:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "SOURCE",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
go LoadNewestInEnd(UUID, ruleEngine)
return
}
@@ -83,8 +92,15 @@ func StartOutSupervisor(ctx context.Context, out *typex.OutEnd, ruleEngine typex
}
// 资源可能不会及时DOWN
if currentOut.Target.Status() == typex.SOURCE_DOWN {
- glogger.GLogger.Debugf("OutEnd:%v DOWN, supervisor try to Restart", UUID)
- time.Sleep(5 * time.Second)
+ info := fmt.Sprintf("OutEnd:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "TARGET",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
go LoadNewestOutEnd(UUID, ruleEngine)
return
}
@@ -125,8 +141,15 @@ func StartDeviceSupervisor(ctx context.Context, device *typex.Device, ruleEngine
}
// 资源可能不会及时DOWN
if currentDevice.Device.Status() == typex.DEV_DOWN {
- glogger.GLogger.Debugf("Device:%v DOWN, supervisor try to Restart", UUID)
- time.Sleep(2 * time.Second)
+ info := fmt.Sprintf("Device:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "DEVICE",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
go LoadNewestDevice(UUID, ruleEngine)
return
}
diff --git a/plugin/http_server/server/staticfs.go b/component/rulex_api_server/server/staticfs.go
similarity index 100%
rename from plugin/http_server/server/staticfs.go
rename to component/rulex_api_server/server/staticfs.go
diff --git a/plugin/http_server/service/crontask_service.go b/component/rulex_api_server/service/crontask_service.go
similarity index 79%
rename from plugin/http_server/service/crontask_service.go
rename to component/rulex_api_server/service/crontask_service.go
index f221ab06c..757e71bba 100644
--- a/plugin/http_server/service/crontask_service.go
+++ b/component/rulex_api_server/service/crontask_service.go
@@ -3,10 +3,11 @@ package service
import (
"encoding/json"
"errors"
+
"github.com/hootrhino/rulex/component/cron_task"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/dto"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/dto"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"github.com/hootrhino/rulex/utils"
)
@@ -16,10 +17,9 @@ func CreateScheduleTask(data *dto.CronTaskCreateDTO) (*model.MCronTask, error) {
UUID: utils.CronTaskUuid(),
Name: data.Name,
CronExpr: data.CronExpr,
- Enable: "0",
+ Enable: &cron_task.CRON_TASK_DISABLE,
TaskType: data.TaskType,
Args: data.Args,
- IsRoot: data.IsRoot,
Script: data.Script,
}
if data.Env != nil {
@@ -29,6 +29,10 @@ func CreateScheduleTask(data *dto.CronTaskCreateDTO) (*model.MCronTask, error) {
task.Env = "[]"
}
+ if data.TaskType == cron_task.CRON_TASK_TYPE_LINUX_SHELL {
+ task.Command = cron_task.LINUX_SHELL
+ }
+
tx := db.Create(&task)
if tx.Error != nil {
return nil, tx.Error
@@ -50,7 +54,7 @@ func DeleteScheduleTask(uuid string) error {
func ListScheduleTask(task model.MCronTask) (any, error) {
db := interdb.DB()
var records []model.MCronTask
- tx := db.Find(&records)
+ tx := db.Order("created_at desc").Find(&records)
if tx.Error != nil {
return nil, tx.Error
}
@@ -68,17 +72,20 @@ func UpdateScheduleTask(data *dto.CronTaskUpdateDTO) (*model.MCronTask, error) {
if tx.RowsAffected == 0 {
return nil, errors.New("定时任务不存在")
}
- if cronTask.Enable == "1" {
+ if cronTask.Enable != nil && *cronTask.Enable == cron_task.CRON_TASK_DISABLE {
return nil, errors.New("请先暂停任务")
}
task := &model.MCronTask{
Name: data.Name,
CronExpr: data.CronExpr,
TaskType: data.TaskType,
- IsRoot: data.IsRoot,
Args: data.Args,
}
+ if data.TaskType == cron_task.CRON_TASK_TYPE_LINUX_SHELL {
+ task.Command = cron_task.LINUX_SHELL
+ }
+
tx = db.Model(&task)
if data.Env != nil {
marshal, _ := json.Marshal(data.Env)
diff --git a/plugin/http_server/service/dao.go b/component/rulex_api_server/service/dao.go
similarity index 78%
rename from plugin/http_server/service/dao.go
rename to component/rulex_api_server/service/dao.go
index 2e8ab299e..f721c5f58 100644
--- a/plugin/http_server/service/dao.go
+++ b/component/rulex_api_server/service/dao.go
@@ -2,36 +2,22 @@ package service
import (
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
-
- "gorm.io/gorm"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
// -----------------------------------------------------------------------------------
func GetMRule(uuid string) (*model.MRule, error) {
m := new(model.MRule)
- if err := interdb.DB().Where("uuid=?", uuid).First(m).Error; err != nil {
- return nil, err
- } else {
- return m, nil
- }
+ return m, interdb.DB().Where("uuid=?", uuid).First(m).Error
}
func GetAllMRule() ([]model.MRule, error) {
m := []model.MRule{}
- if err := interdb.DB().Find(&m).Error; err != nil {
- return nil, err
- } else {
- return m, nil
- }
+ return m, interdb.DB().Find(&m).Error
}
func GetMRuleWithUUID(uuid string) (*model.MRule, error) {
m := new(model.MRule)
- if err := interdb.DB().Where("uuid=?", uuid).First(m).Error; err != nil {
- return nil, err
- } else {
- return m, nil
- }
+ return m, interdb.DB().Where("uuid=?", uuid).First(m).Error
}
func InsertMRule(r *model.MRule) error {
@@ -147,14 +133,10 @@ func InitMUser(o *model.MUser) error {
return interdb.DB().Table("m_users").FirstOrCreate(o).Error
}
-func UpdateMUser(uuid string, o *model.MUser) error {
- m := model.MUser{}
- if err := interdb.DB().Where("uuid=?", uuid).First(&m).Error; err != nil {
- return err
- } else {
- interdb.DB().Model(m).Updates(*o)
- return nil
- }
+func UpdateMUser(o *model.MUser) error {
+ interdb.DB().Model(o).Where("username=?", o.Username).Updates(*o)
+ return nil
+
}
// -----------------------------------------------------------------------------------
@@ -193,11 +175,7 @@ func AllDevices() []model.MDevice {
// 获取设备列表
func GetMDeviceWithUUID(uuid string) (*model.MDevice, error) {
m := new(model.MDevice)
- if err := interdb.DB().Where("uuid=?", uuid).First(m).Error; err != nil {
- return nil, err
- } else {
- return m, nil
- }
+ return m, interdb.DB().Where("uuid=?", uuid).First(m).Error
}
// 删除设备
@@ -221,51 +199,6 @@ func UpdateDevice(uuid string, o *model.MDevice) error {
}
}
-// -------------------------------------------------------------------------------------
-// ModbusPointPositions
-// -------------------------------------------------------------------------------------
-
-// InsertModbusPointPosition 插入modbus点位表
-func InsertModbusPointPosition(list []model.MModbusPointPosition) error {
- m := model.MModbusPointPosition{}
- return interdb.DB().Model(m).Create(list).Error
-}
-
-// DeleteModbusPointAndDevice 删除modbus点位与设备
-func DeleteModbusPointAndDevice(deviceUuid string) error {
- return interdb.DB().Transaction(func(tx *gorm.DB) (err error) {
-
- err = tx.Where("device_uuid = ?", deviceUuid).Delete(&model.MModbusPointPosition{}).Error
- if err != nil {
- return err
- }
-
- err = tx.Where("uuid = ?", deviceUuid).Delete(&model.MDevice{}).Error
- if err != nil {
- return err
- }
- return nil
- })
-}
-
-// UpdateModbusPoint 更新modbus点位
-func UpdateModbusPoint(mm model.MModbusPointPosition) error {
- m := model.MDevice{}
- if err := interdb.DB().Where("id = ?", mm.ID).First(&m).Error; err != nil {
- return err
- } else {
- interdb.DB().Model(m).Updates(&m)
- return nil
- }
-}
-
-// AllModbusPointByDeviceUuid 根据设备UUID查询设备点位
-func AllModbusPointByDeviceUuid(deviceUuid string) (list []model.MModbusPointPosition, err error) {
-
- err = interdb.DB().Where("device_uuid = ?", deviceUuid).Find(&list).Error
- return
-}
-
// -------------------------------------------------------------------------------------
// Goods
// -------------------------------------------------------------------------------------
diff --git a/plugin/http_server/service/data_schema_service.go b/component/rulex_api_server/service/data_schema_service.go
similarity index 96%
rename from plugin/http_server/service/data_schema_service.go
rename to component/rulex_api_server/service/data_schema_service.go
index 0b6bd0283..3127441b0 100644
--- a/plugin/http_server/service/data_schema_service.go
+++ b/component/rulex_api_server/service/data_schema_service.go
@@ -17,7 +17,7 @@ package service
import (
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
// 获取DataSchema列表
diff --git a/plugin/http_server/service/database_manage_service.go b/component/rulex_api_server/service/database_manage_service.go
similarity index 100%
rename from plugin/http_server/service/database_manage_service.go
rename to component/rulex_api_server/service/database_manage_service.go
diff --git a/component/rulex_api_server/service/device_modbus_data_sheet_service.go b/component/rulex_api_server/service/device_modbus_data_sheet_service.go
new file mode 100644
index 000000000..5d76a72ed
--- /dev/null
+++ b/component/rulex_api_server/service/device_modbus_data_sheet_service.go
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package service
+
+import (
+ "fmt"
+
+ "github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+)
+
+/*
+*
+* Siemens点位表管理
+*
+ */
+// InsertSiemensPointPosition 插入Siemens点位表
+func InsertSiemensPointPositions(list []model.MSiemensDataPoint) error {
+ m := model.MSiemensDataPoint{}
+ return interdb.DB().Model(m).Create(list).Error
+}
+
+// InsertSiemensPointPosition 插入Siemens点位表
+func InsertSiemensPointPosition(P model.MSiemensDataPoint) error {
+ IgnoreUUID := P.UUID
+ Count := int64(0)
+ P.UUID = ""
+ interdb.DB().Model(P).Where(P).Count(&Count)
+ if Count > 0 {
+ return fmt.Errorf("already exists same record:%s", IgnoreUUID)
+ }
+ P.UUID = IgnoreUUID
+ return interdb.DB().Model(P).Create(&P).Error
+}
+
+// DeleteSiemensPointByDevice 删除Siemens点位与设备
+func DeleteSiemensPointByDevice(uuids []string, deviceUuid string) error {
+ return interdb.DB().
+ Where("uuid IN ? AND device_uuid=?", uuids, deviceUuid).
+ Delete(&model.MSiemensDataPoint{}).Error
+}
+
+// DeleteAllSiemensPointByDevice 删除Siemens点位与设备
+func DeleteAllSiemensPointByDevice(deviceUuid string) error {
+ return interdb.DB().
+ Where("device_uuid=?", deviceUuid).
+ Delete(&model.MSiemensDataPoint{}).Error
+}
+
+// 更新DataSchema
+func UpdateSiemensPoint(MSiemensDataPoint model.MSiemensDataPoint) error {
+ return interdb.DB().Model(model.MSiemensDataPoint{}).
+ Where("device_uuid=? AND uuid=?",
+ MSiemensDataPoint.DeviceUuid, MSiemensDataPoint.UUID).
+ Updates(MSiemensDataPoint).Error
+}
diff --git a/component/rulex_api_server/service/device_s1200_data_import_service.go b/component/rulex_api_server/service/device_s1200_data_import_service.go
new file mode 100644
index 000000000..899660388
--- /dev/null
+++ b/component/rulex_api_server/service/device_s1200_data_import_service.go
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package service
+
+import (
+ "fmt"
+
+ "github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+)
+
+/*
+*
+* Modbus点位表管理
+*
+ */
+// InsertModbusPointPosition 插入modbus点位表
+func InsertModbusPointPositions(list []model.MModbusDataPoint) error {
+ m := model.MModbusDataPoint{}
+ return interdb.DB().Model(m).Create(list).Error
+}
+
+// InsertModbusPointPosition 插入modbus点位表
+func InsertModbusPointPosition(P model.MModbusDataPoint) error {
+ IgnoreUUID := P.UUID
+ Count := int64(0)
+ P.UUID = ""
+ interdb.DB().Model(P).Where(P).Count(&Count)
+ if Count > 0 {
+ return fmt.Errorf("already exists same record:%s", IgnoreUUID)
+ }
+ P.UUID = IgnoreUUID
+ return interdb.DB().Model(P).Create(&P).Error
+}
+
+// DeleteModbusPointByDevice 删除modbus点位与设备
+func DeleteModbusPointByDevice(uuids []string, deviceUuid string) error {
+ return interdb.DB().
+ Where("uuid IN ? AND device_uuid=?", uuids, deviceUuid).
+ Delete(&model.MModbusDataPoint{}).Error
+}
+
+// DeleteAllModbusPointByDevice 删除modbus点位与设备
+func DeleteAllModbusPointByDevice(deviceUuid string) error {
+ return interdb.DB().
+ Where("device_uuid=?", deviceUuid).
+ Delete(&model.MModbusDataPoint{}).Error
+}
+
+// 更新DataSchema
+func UpdateModbusPoint(MModbusDataPoint model.MModbusDataPoint) error {
+ return interdb.DB().Model(model.MModbusDataPoint{}).
+ Where("device_uuid=? AND uuid=?",
+ MModbusDataPoint.DeviceUuid, MModbusDataPoint.UUID).
+ Updates(MModbusDataPoint).Error
+}
diff --git a/plugin/http_server/service/group_service.go b/component/rulex_api_server/service/group_service.go
similarity index 57%
rename from plugin/http_server/service/group_service.go
rename to component/rulex_api_server/service/group_service.go
index 2ba743f9c..6a3eeab6f 100644
--- a/plugin/http_server/service/group_service.go
+++ b/component/rulex_api_server/service/group_service.go
@@ -17,7 +17,9 @@ package service
import (
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/utils"
+ "gorm.io/gorm"
)
// 获取GenericGroup列表
@@ -34,30 +36,65 @@ func ListByGroupType(t string) []model.MGenericGroup {
/*
*
- - 根据分组类型查询:DEVICE, VISUAL
+* 查询分组下的设备
+*
+ */
+func FindDeviceByGroup(uuid string) []model.MDevice {
+ sql := `
+WHERE uuid IN (
+ SELECT m_generic_group_relations.rid
+ FROM m_generic_groups
+ LEFT JOIN
+ m_generic_group_relations ON (m_generic_groups.uuid = m_generic_group_relations.gid)
+ WHERE type = 'DEVICE' AND gid = ?
+);`
+
+ m := []model.MDevice{}
+ interdb.DB().Raw(`SELECT * FROM m_devices `+sql, uuid).Find(&m)
+ return m
+
+}
+
+/*
+*
+* 查询分组吓得大屏
+*
+ */
+func FindVisualByGroup(uuid string) []model.MVisual {
+ sql := `
+WHERE uuid IN (
+ SELECT m_generic_group_relations.rid
+ FROM m_generic_groups
+ LEFT JOIN
+ m_generic_group_relations ON (m_generic_groups.uuid = m_generic_group_relations.gid)
+ WHERE type = 'VISUAL' AND gid = ?
+);`
+
+ m := []model.MVisual{}
+ interdb.DB().Raw(`SELECT * FROM m_visuals `+sql, uuid).Find(&m)
+ return m
+
+}
+
+/*
+*
+ - 根据分组类型查询:代码模板
*~
*/
-func FindByType(uuid, t string) ([]model.MVisual, []model.MDevice) {
+func FindUserTemplateByGroup(uuid string) []model.MUserLuaTemplate {
sql := `
WHERE uuid IN (
SELECT m_generic_group_relations.rid
FROM m_generic_groups
LEFT JOIN
m_generic_group_relations ON (m_generic_groups.uuid = m_generic_group_relations.gid)
- WHERE type = ? AND gid = ?
+ WHERE type = 'USER_LUA_TEMPLATE' AND gid = ?
);`
- if t == "VISUAL" {
- m := []model.MVisual{}
- interdb.DB().Raw(`SELECT * FROM m_visuals `+sql, t, uuid).Find(&m)
- return m, nil
- }
- if t == "DEVICE" {
- m := []model.MDevice{}
- interdb.DB().Raw(`SELECT * FROM m_devices `+sql, t, uuid).Find(&m)
- return nil, m
- }
- return nil, nil
+ m := []model.MUserLuaTemplate{}
+ interdb.DB().Raw(`SELECT * FROM m_user_lua_templates `+sql, uuid).Find(&m)
+ return m
+
}
func GetGenericGroupWithUUID(uuid string) (*model.MGenericGroup, error) {
@@ -114,8 +151,9 @@ func BindResource(gid, rid string) error {
return err
}
Relation := model.MGenericGroupRelation{
- Gid: m.UUID,
- Rid: rid,
+ UUID: utils.MakeUUID("GR"),
+ Gid: m.UUID,
+ Rid: rid,
}
if err := interdb.DB().Save(&Relation).Error; err != nil {
return err
@@ -163,3 +201,51 @@ func CheckAlreadyBinding(gid, rid string) (uint, error) {
}
return uint(count), nil
}
+
+/*
+*
+* 重新绑定分组需要事务支持
+*
+ */
+func ReBindResource(action func(tx *gorm.DB) error, Rid, Gid string) error {
+ return interdb.DB().Transaction(func(tx *gorm.DB) error {
+ // 1 执行的操作
+ if err0 := action(tx); err0 != nil {
+ return err0
+ }
+ // 2 解除分组关联
+ sql := `
+SELECT m_generic_groups.*
+FROM m_generic_group_relations
+LEFT JOIN
+m_generic_groups ON (m_generic_groups.uuid = m_generic_group_relations.gid)
+WHERE m_generic_group_relations.rid = ?;
+ `
+ OldGroup := model.MGenericGroup{}
+ if errA := tx.Raw(sql, Rid).Find(&OldGroup).Error; errA != nil {
+ return errA
+ }
+ if err1 := tx.Model(model.MGenericGroupRelation{}).
+ Where("gid=? and rid =?", OldGroup.UUID, Rid).
+ Delete(&model.MGenericGroupRelation{}).Error; err1 != nil {
+ return err1
+ }
+ // 3 重新绑定分组,首先确定分组是否存在
+ MGroup := model.MGenericGroup{}
+ if err2 := tx.Model(MGroup).
+ Where("uuid=?", Gid).
+ First(&MGroup).Error; err2 != nil {
+ return err2
+ }
+ // 4 重新绑定分组
+ err3 := tx.Save(&model.MGenericGroupRelation{
+ UUID: utils.MakeUUID("GRLT"),
+ Gid: Gid,
+ Rid: Rid,
+ }).Error
+ if err3 != nil {
+ return err3
+ }
+ return nil
+ })
+}
diff --git a/plugin/http_server/service/hw_intrerface_service.go b/component/rulex_api_server/service/hw_intrerface_service.go
similarity index 68%
rename from plugin/http_server/service/hw_intrerface_service.go
rename to component/rulex_api_server/service/hw_intrerface_service.go
index 03672f365..ebe8a456f 100644
--- a/plugin/http_server/service/hw_intrerface_service.go
+++ b/component/rulex_api_server/service/hw_intrerface_service.go
@@ -17,9 +17,13 @@ package service
import (
"encoding/json"
+ "runtime"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/ossupport"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
"go.bug.st/serial"
)
@@ -92,7 +96,8 @@ func GetHwPortConfig(uuid string) (model.MHwPort, error) {
*
*/
func InitHwPortConfig() error {
- for _, portName := range getOsPort() {
+ for _, portName := range GetOsPort() {
+
Port := model.MHwPort{
UUID: portName,
Name: portName,
@@ -103,6 +108,17 @@ func InitHwPortConfig() error {
}(),
Description: portName,
}
+ // 兼容代码,识别H3网关的参数
+ if typex.DefaultVersionInfo.Product == "EEKIIH3" {
+ if portName == "/dev/ttyS1" {
+ Port.Alias = "RS485接口1(A1B1)"
+ Port.Name = "RS4851(A1B1)"
+ }
+ if portName == "/dev/ttyS2" {
+ Port.Alias = "RS485接口2(A2B2)"
+ Port.Name = "RS4852(A2B2)"
+ }
+ }
uartCfg := UartConfigDto{
Timeout: 3000,
Uart: portName,
@@ -125,31 +141,33 @@ func InitHwPortConfig() error {
/*
*
-* 获取系统串口
-*
+* 获取系统串口, 这个接口比较特殊,当运行在特殊硬件上的时候,某些系统占用的直接不显示
+* 这个接口需要兼容各类特殊硬件
*/
-func getOsPort() []string {
- ports, _ := serial.GetPortsList()
+func GetOsPort() []string {
+ var ports []string
+ if runtime.GOOS == "windows" {
+ ports, _ = serial.GetPortsList()
+ } else {
+ ports, _ = ossupport.GetPortsListUnix()
+ }
List := []string{}
for _, port := range ports {
- // 明确知道有这么多端口,啰嗦代码是为了标记以免以后忘记
- if port == "COM0" {
- continue
- }
- if port == "/dev/ttyS0" {
- continue
- }
- if port == "/dev/ttyS3" {
- continue
- }
- if port == "/dev/ttyUSB0" {
- continue
- }
- if port == "/dev/ttyUSB1" {
- continue
- }
- if port == "/dev/ttyUSB2" {
- continue
+ if typex.DefaultVersionInfo.Product == "EEKIIH3" {
+ // H3的下列串口被系统占用
+ if utils.SContains([]string{
+ "/dev/ttyS0",
+ "/dev/ttyS3",
+ "/dev/ttyS4", // Linux System
+ "/dev/ttyS5", // Linux System
+ "/dev/ttyS6", // Linux System
+ "/dev/ttyS7", // Linux System
+ "/dev/ttyUSB0", // 4G
+ "/dev/ttyUSB1", // 4G
+ "/dev/ttyUSB2", // 4G
+ }, port) {
+ continue
+ }
}
List = append(List, port)
}
diff --git a/component/rulex_api_server/service/internal_notify_service.go b/component/rulex_api_server/service/internal_notify_service.go
new file mode 100644
index 000000000..7f2a44365
--- /dev/null
+++ b/component/rulex_api_server/service/internal_notify_service.go
@@ -0,0 +1,78 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package service
+
+import (
+ "github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+)
+
+/*
+*
+* InsertInternalNotifies
+*
+ */
+func InsertInternalNotify(m model.MInternalNotify) error {
+ var count int64
+ interdb.DB().Model(&m).Count(&count)
+ // 超过100条记录就清空
+ if count > 100 {
+ if err := ClearInternalNotifies(); err != nil {
+ return err
+ }
+ }
+ return interdb.DB().Model(&m).Save(&m).Error
+}
+
+/*
+*
+* 右上角
+*
+ */
+func AllInternalNotifiesHeader() []model.MInternalNotify {
+ m := []model.MInternalNotify{}
+ interdb.DB().Table("m_internal_notifies").Where("status=1").Limit(6).Find(&m)
+ return m
+}
+
+/*
+*
+* 所有列表
+*
+ */
+func AllInternalNotifies() []model.MInternalNotify {
+ m := []model.MInternalNotify{}
+ interdb.DB().Table("m_internal_notifies").Where("status=1").Limit(100).Find(&m)
+ return m
+}
+
+/*
+*
+* 清空表
+*
+ */
+func ClearInternalNotifies() error {
+ return interdb.DB().Exec("DELETE FROM m_internal_notifies;VACUUM;").Error
+}
+
+/*
+*
+* 点击已读
+*
+ */
+func ReadInternalNotifies(uuid string) error {
+ return interdb.DB().Where("uuid=?", uuid).Delete(&model.MInternalNotify{}).Error
+}
diff --git a/plugin/http_server/service/linux_network_config_service.go b/component/rulex_api_server/service/linux_network_config_service.go
similarity index 98%
rename from plugin/http_server/service/linux_network_config_service.go
rename to component/rulex_api_server/service/linux_network_config_service.go
index 6327702cc..6cdeea9fc 100644
--- a/plugin/http_server/service/linux_network_config_service.go
+++ b/component/rulex_api_server/service/linux_network_config_service.go
@@ -5,8 +5,8 @@ import (
"os/exec"
"github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/plugin/http_server/model"
)
/*
diff --git a/plugin/http_server/service/linux_wlan_config_service.go b/component/rulex_api_server/service/linux_wlan_config_service.go
similarity index 96%
rename from plugin/http_server/service/linux_wlan_config_service.go
rename to component/rulex_api_server/service/linux_wlan_config_service.go
index 7df67de8e..61b10ed95 100644
--- a/plugin/http_server/service/linux_wlan_config_service.go
+++ b/component/rulex_api_server/service/linux_wlan_config_service.go
@@ -1,9 +1,9 @@
package service
import (
- "github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/glogger"
)
/*
diff --git a/plugin/http_server/service/linuxamixer.md b/component/rulex_api_server/service/linuxamixer.md
similarity index 100%
rename from plugin/http_server/service/linuxamixer.md
rename to component/rulex_api_server/service/linuxamixer.md
diff --git a/plugin/http_server/service/linuxnetcfg.md b/component/rulex_api_server/service/linuxnetcfg.md
similarity index 100%
rename from plugin/http_server/service/linuxnetcfg.md
rename to component/rulex_api_server/service/linuxnetcfg.md
diff --git a/plugin/http_server/service/linuxtimecfg.md b/component/rulex_api_server/service/linuxtimecfg.md
similarity index 100%
rename from plugin/http_server/service/linuxtimecfg.md
rename to component/rulex_api_server/service/linuxtimecfg.md
diff --git a/component/rulex_api_server/service/memory_usage_linux.go b/component/rulex_api_server/service/memory_usage_linux.go
new file mode 100644
index 000000000..073fd7271
--- /dev/null
+++ b/component/rulex_api_server/service/memory_usage_linux.go
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package service
+
+import (
+ "fmt"
+ "io/ioutil"
+ "math"
+ "strings"
+)
+
+func GetMemPercent() (float64, error) {
+ content, err := ioutil.ReadFile("/proc/meminfo")
+ if err != nil {
+ return 0.0, err
+ }
+
+ meminfo := string(content)
+
+ var memTotal, memAvailable float64
+ lines := strings.Split(meminfo, "\n")
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ if len(fields) < 2 {
+ continue
+ }
+ switch fields[0] {
+ case "MemTotal:":
+ fmt.Sscan(fields[1], &memTotal)
+ case "MemAvailable:":
+ fmt.Sscan(fields[1], &memAvailable)
+ }
+ }
+
+ memPercent := math.Round(100.0*(1.0-memAvailable/memTotal)*100) / 100
+ return memPercent, nil
+}
diff --git a/plugin/http_server/service/memory_usage_windows.go b/component/rulex_api_server/service/memory_usage_windows.go
similarity index 100%
rename from plugin/http_server/service/memory_usage_windows.go
rename to component/rulex_api_server/service/memory_usage_windows.go
diff --git a/plugin/http_server/service/page_service.go b/component/rulex_api_server/service/page_service.go
similarity index 93%
rename from plugin/http_server/service/page_service.go
rename to component/rulex_api_server/service/page_service.go
index 28fc93247..564540cc9 100644
--- a/plugin/http_server/service/page_service.go
+++ b/component/rulex_api_server/service/page_service.go
@@ -1,10 +1,11 @@
package service
import (
+ "strconv"
+
"github.com/gin-gonic/gin"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"gorm.io/gorm"
- "strconv"
)
func Paginate(page model.PageRequest) func(db *gorm.DB) *gorm.DB {
diff --git a/plugin/http_server/service/rhinoh3_ubuntu18_iproute_service.go b/component/rulex_api_server/service/rhinoh3_ubuntu18_iproute_service.go
similarity index 98%
rename from plugin/http_server/service/rhinoh3_ubuntu18_iproute_service.go
rename to component/rulex_api_server/service/rhinoh3_ubuntu18_iproute_service.go
index 650154693..0ac9d6159 100644
--- a/plugin/http_server/service/rhinoh3_ubuntu18_iproute_service.go
+++ b/component/rulex_api_server/service/rhinoh3_ubuntu18_iproute_service.go
@@ -21,7 +21,7 @@ import (
"strings"
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
/*
diff --git a/plugin/http_server/service/site_config_service.go b/component/rulex_api_server/service/site_config_service.go
similarity index 95%
rename from plugin/http_server/service/site_config_service.go
rename to component/rulex_api_server/service/site_config_service.go
index 089df46db..7e3cadee6 100644
--- a/plugin/http_server/service/site_config_service.go
+++ b/component/rulex_api_server/service/site_config_service.go
@@ -17,7 +17,7 @@ package service
import (
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
func GetSiteConfig() (model.MSiteConfig, error) {
diff --git a/plugin/http_server/apis/vendor_security_api.go b/component/rulex_api_server/service/table_count_service.go
similarity index 62%
rename from plugin/http_server/apis/vendor_security_api.go
rename to component/rulex_api_server/service/table_count_service.go
index 994c967d2..2f0a8b498 100644
--- a/plugin/http_server/apis/vendor_security_api.go
+++ b/component/rulex_api_server/service/table_count_service.go
@@ -13,26 +13,28 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-package apis
+package service
-import (
- "github.com/gin-gonic/gin"
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/typex"
-)
+import "github.com/hootrhino/rulex/component/interdb"
/*
*
-* 获取一机一密
+* 计算 Count
*
*/
+func CountModel(m any) int64 {
+ var count int64
+ interdb.DB().Model(m).Count(&count)
+ return count
+}
-func GetVendorKey(c *gin.Context, ruleEngine typex.RuleX) {
- testK := `
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
------END OPENSSH PRIVATE KEY-----
-`
- c.JSON(common.HTTP_OK, common.OkWithData(testK))
-
+/*
+*
+* 计算 Count
+*
+ */
+func CountTable(table string) int64 {
+ var count int64
+ interdb.DB().Table(table).Count(&count)
+ return count
}
diff --git a/component/rulex_api_server/service/user_lua_template_service.go b/component/rulex_api_server/service/user_lua_template_service.go
new file mode 100644
index 000000000..928fbc5e0
--- /dev/null
+++ b/component/rulex_api_server/service/user_lua_template_service.go
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package service
+
+import (
+ "fmt"
+
+ "github.com/hootrhino/rulex/component/interdb"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+)
+
+// 获取UserLuaTemplate列表
+func AllUserLuaTemplate() []model.MUserLuaTemplate {
+ m := []model.MUserLuaTemplate{}
+ interdb.DB().Find(&m)
+ return m
+
+}
+
+// 模糊查询
+// SELECT * FROM m_user_lua_templates
+// WHERE label like "%%"
+// OR detail like "%%"
+func SearchUserLuaTemplate(label, detail string) []model.MUserLuaTemplate {
+ m := []model.MUserLuaTemplate{}
+ sql := `
+SELECT * FROM m_user_lua_templates
+WHERE label like "%%%s%%"
+OR detail like "%%%s%%"`
+ interdb.DB().Raw(fmt.Sprintf(sql, label, detail)).Scan(&m)
+ return m
+}
+
+/*
+*
+* 获取分组
+*
+ */
+func GetUserLuaTemplateGroup(rid string) model.MGenericGroup {
+ sql := `
+SELECT m_generic_groups.*
+ FROM m_generic_group_relations
+ LEFT JOIN
+ m_generic_groups ON (m_generic_groups.uuid = m_generic_group_relations.gid)
+ WHERE m_generic_group_relations.rid = ?;
+`
+ m := model.MGenericGroup{}
+ interdb.DB().Raw(sql, rid).Find(&m)
+ return m
+}
+
+/*
+*
+* ID获取
+*
+ */
+func GetUserLuaTemplateWithUUID(uuid string) (model.MUserLuaTemplate, error) {
+ m := model.MUserLuaTemplate{}
+ err := interdb.DB().Where("uuid=?", uuid).First(&m).Error
+ return m, err
+}
+
+// 删除UserLuaTemplate
+func DeleteUserLuaTemplate(uuid string) error {
+ return interdb.DB().Where("uuid=?", uuid).Delete(&model.MUserLuaTemplate{}).Error
+}
+
+// 创建UserLuaTemplate
+func InsertUserLuaTemplate(UserLuaTemplate model.MUserLuaTemplate) error {
+ return interdb.DB().Create(&UserLuaTemplate).Error
+}
+
+// 更新UserLuaTemplate
+func UpdateUserLuaTemplate(UserLuaTemplate model.MUserLuaTemplate) error {
+ return interdb.DB().
+ Model(UserLuaTemplate).
+ Where("uuid=?", UserLuaTemplate.UUID).
+ Updates(&UserLuaTemplate).Error
+}
diff --git a/plugin/http_server/service/visual_screen_service.go b/component/rulex_api_server/service/visual_screen_service.go
similarity index 94%
rename from plugin/http_server/service/visual_screen_service.go
rename to component/rulex_api_server/service/visual_screen_service.go
index 93a0a3e5b..3820054b9 100644
--- a/plugin/http_server/service/visual_screen_service.go
+++ b/component/rulex_api_server/service/visual_screen_service.go
@@ -2,7 +2,7 @@ package service
import (
"github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
// 获取Visual列表
diff --git a/plugin/http_server/service/windows.md b/component/rulex_api_server/service/windows.md
similarity index 100%
rename from plugin/http_server/service/windows.md
rename to component/rulex_api_server/service/windows.md
diff --git a/plugin/http_server/service/windows_eth_config.go b/component/rulex_api_server/service/windows_eth_config.go
similarity index 100%
rename from plugin/http_server/service/windows_eth_config.go
rename to component/rulex_api_server/service/windows_eth_config.go
diff --git a/plugin/http_server/structure.png b/component/rulex_api_server/structure.png
similarity index 100%
rename from plugin/http_server/structure.png
rename to component/rulex_api_server/structure.png
diff --git a/component/supervisor/device_supervisor.go b/component/supervisor/device_supervisor.go
new file mode 100644
index 000000000..30ea35534
--- /dev/null
+++ b/component/supervisor/device_supervisor.go
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package supervisor
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/hootrhino/rulex/component/internotify"
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+)
+
+func StartDeviceSupervisor(ctx context.Context, device *typex.Device, ruleEngine typex.RuleX) {
+ UUID := device.UUID
+ ticker := time.NewTicker(time.Duration(time.Second * 5))
+ defer func() {
+ ticker.Stop()
+ }()
+ for {
+ select {
+ case <-ctx.Done():
+ {
+ ticker.Stop()
+ glogger.GLogger.Debugf("Device Context cancel:%v, supervisor exit", UUID)
+ return
+ }
+ case <-typex.GCTX.Done():
+ {
+ return
+ }
+ default:
+ {
+ }
+ }
+ currentDevice := ruleEngine.GetDevice(UUID)
+ if currentDevice == nil {
+ glogger.GLogger.Debugf("Device:%v Deleted, supervisor exit", UUID)
+ return
+ }
+ if currentDevice.Device.Status() == typex.DEV_STOP {
+ glogger.GLogger.Debugf("Device:%v Stopped, supervisor exit", UUID)
+ return
+ }
+ // 资源可能不会及时DOWN
+ if currentDevice.Device.Status() == typex.DEV_DOWN {
+ info := fmt.Sprintf("Device:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "DEVICE",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
+ // go LoadNewestDevice(UUID, ruleEngine)
+ return
+ }
+ // glogger.GLogger.Debugf("Supervisor Get Device :%v state:%v", UUID, currentDevice.Device.Status().String())
+ <-ticker.C
+ }
+}
diff --git a/component/supervisor/inend_supervisor.go b/component/supervisor/inend_supervisor.go
new file mode 100644
index 000000000..3766b0749
--- /dev/null
+++ b/component/supervisor/inend_supervisor.go
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package supervisor
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/hootrhino/rulex/component/internotify"
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+)
+
+func StartInSupervisor(ctx context.Context, in *typex.InEnd, ruleEngine typex.RuleX) {
+ UUID := in.UUID
+ ticker := time.NewTicker(time.Duration(time.Second * 5))
+ defer func() {
+ ticker.Stop()
+ }()
+ for {
+ select {
+ case <-ctx.Done():
+ {
+ ticker.Stop()
+ glogger.GLogger.Debugf("Source Context cancel:%v, supervisor exit", UUID)
+ return
+ }
+ case <-typex.GCTX.Done():
+ {
+ return
+ }
+ default:
+ {
+ }
+ }
+ currentIn := ruleEngine.GetInEnd(UUID)
+ if currentIn == nil {
+ glogger.GLogger.Debugf("Source:%v Deleted, supervisor exit", UUID)
+ return
+ }
+ if currentIn.Source.Status() == typex.SOURCE_STOP {
+ glogger.GLogger.Debugf("Source:%v Stopped, supervisor exit", UUID)
+ return
+ }
+ // 资源可能不会及时DOWN
+ if currentIn.Source.Status() == typex.SOURCE_DOWN {
+ info := fmt.Sprintf("Source:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "SOURCE",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
+ // go LoadNewestInEnd(UUID, ruleEngine)
+ return
+ }
+ // glogger.GLogger.Debugf("Supervisor Get Source :%v state:%v", UUID, currentIn.Source.Status().String())
+ <-ticker.C
+ }
+}
diff --git a/component/supervisor/outend_supervisor.go b/component/supervisor/outend_supervisor.go
new file mode 100644
index 000000000..355969bde
--- /dev/null
+++ b/component/supervisor/outend_supervisor.go
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package supervisor
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/hootrhino/rulex/component/internotify"
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+)
+
+func StartOutSupervisor(ctx context.Context, out *typex.OutEnd, ruleEngine typex.RuleX) {
+ UUID := out.UUID
+ ticker := time.NewTicker(time.Duration(time.Second * 5))
+ defer func() {
+ ticker.Stop()
+ }()
+ for {
+ select {
+ case <-ctx.Done():
+ {
+ ticker.Stop()
+ glogger.GLogger.Debugf("OutEnd Context cancel:%v, supervisor exit", UUID)
+ return
+ }
+ case <-typex.GCTX.Done():
+ {
+ return
+ }
+ default:
+ {
+ }
+ }
+ currentOut := ruleEngine.GetOutEnd(UUID)
+ if currentOut == nil {
+ glogger.GLogger.Debugf("OutEnd:%v Deleted, supervisor exit", UUID)
+ return
+ }
+ if currentOut.Target.Status() == typex.SOURCE_STOP {
+ glogger.GLogger.Debugf("OutEnd:%v Stopped, supervisor exit", UUID)
+ return
+ }
+ // 资源可能不会及时DOWN
+ if currentOut.Target.Status() == typex.SOURCE_DOWN {
+ info := fmt.Sprintf("OutEnd:%v DOWN, supervisor try to Restart", UUID)
+ glogger.GLogger.Debugf(info)
+ internotify.Push(internotify.BaseEvent{
+ Type: "TARGET",
+ Event: "event.down",
+ Ts: uint64(time.Now().UnixNano()),
+ Info: info,
+ })
+ time.Sleep(4 * time.Second)
+ // go LoadNewestOutEnd(UUID, ruleEngine)
+ return
+ }
+ // glogger.GLogger.Debugf("Supervisor Get OutEnd :%v state:%v", UUID, currentOut.Target.Status().String())
+ <-ticker.C
+ }
+}
diff --git a/component/supervisor/readme.md b/component/supervisor/readme.md
new file mode 100644
index 000000000..840a58ac3
--- /dev/null
+++ b/component/supervisor/readme.md
@@ -0,0 +1,2 @@
+## 资源守护
+用来守护监控系统内各种资源的存活状态。因为之前的老版本依赖Http组件,存在高耦合情况,所以需要单独优化,现阶段暂未启用,等以后的版本逐步优化.
\ No newline at end of file
diff --git a/conf/license.key b/conf/license.key
new file mode 100644
index 000000000..bb51973d3
--- /dev/null
+++ b/conf/license.key
@@ -0,0 +1 @@
+914674f13f92175feceac1cd70d566cc
\ No newline at end of file
diff --git a/conf/license.lic b/conf/license.lic
new file mode 100644
index 000000000..57d389d42
--- /dev/null
+++ b/conf/license.lic
@@ -0,0 +1 @@
+6HQVH6rUebN8D0J2KjEHHoza-P-xtVhEFh7pmB-P4Wc5RJ70lf4G8__kWtWC9SJS
\ No newline at end of file
diff --git a/conf/rulex.ini b/conf/rulex.ini
index c7f737879..966601cb2 100644
--- a/conf/rulex.ini
+++ b/conf/rulex.ini
@@ -26,15 +26,15 @@ app_debug_mode = false
# debug
# info
#
-log_level = all
+log_level = info
#
# log path
#
log_path = rulexlog
#
-# Max data cache size, default is 20MB
+# Max data cache size
#
-max_queue_size = 204800
+max_queue_size = 102400
#
# Max store size, default is 20MB
#
@@ -173,7 +173,7 @@ enable = true
#
# Modbus CRC calculator
#
-[plugin.modbuscrc_tools]
+[plugin.modbus_crc_tools]
#
# Enable
#
@@ -186,3 +186,19 @@ enable = false
# Enable
#
enable = false
+#
+# Soft Watchdog
+#
+[plugin.license_manager]
+#
+# Enable
+#
+enable = true
+#
+# Enable
+#
+license_path = ./license.lic
+#
+# Enable
+#
+key_path = ./license.key
\ No newline at end of file
diff --git a/core/internal_kvstore.go b/core/internal_kvstore.go
index b45fbddab..83fc58f39 100644
--- a/core/internal_kvstore.go
+++ b/core/internal_kvstore.go
@@ -16,6 +16,7 @@
package core
import (
+ "strings"
"time"
"github.com/hootrhino/rulex/glogger"
@@ -82,7 +83,18 @@ func (rs *RulexStore) Count() int {
// 模糊查询匹配
// 支持: *AAA AAA* A*B
-func (rs *RulexStore) FuzzyGet(k string) string {
- // TODO 未来实现
+func (rs *RulexStore) FuzzyGet(Key string) any {
+ for k, v := range rs.cache.Items() {
+ if fuzzyMatch(k, Key) {
+ return v
+ }
+ }
return ""
}
+
+// 将主字符串和子字符串都转换为小写,以进行不区分大小写的匹配
+func fuzzyMatch(mainStr, subStr string) bool {
+ mainStr = strings.ToLower(mainStr)
+ subStr = strings.ToLower(subStr)
+ return strings.Contains(mainStr, subStr)
+}
diff --git a/core/lua_validator.go b/core/rule_lua_runtime.go
similarity index 81%
rename from core/lua_validator.go
rename to core/rule_lua_runtime.go
index acc87aa61..2d00863ba 100644
--- a/core/lua_validator.go
+++ b/core/rule_lua_runtime.go
@@ -20,7 +20,6 @@ import (
lua "github.com/hootrhino/gopher-lua"
"github.com/hootrhino/rulex/component/interpipeline"
- "github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
)
@@ -51,27 +50,33 @@ func ExecuteActions(rule *typex.Rule, arg lua.LValue) (lua.LValue, error) {
luaOriginTable := rule.LuaVM.GetGlobal(ACTIONS_KEY)
if luaOriginTable != nil && luaOriginTable.Type() == lua.LTTable {
// 断言成包含回调的 table
- funcsTable := luaOriginTable.(*lua.LTable)
- funcs := make(map[string]*lua.LFunction, funcsTable.Len())
- var err error = nil
- funcsTable.ForEach(func(idx, f lua.LValue) {
- if f.Type() == lua.LTFunction {
- funcs[idx.String()] = f.(*lua.LFunction)
- } else {
- err = errors.New(f.String() + " not a lua function")
- return
+ switch funcsTable := luaOriginTable.(type) {
+ case *lua.LTable:
+ {
+ funcs := make(map[string]*lua.LFunction, funcsTable.Len())
+ var err error = nil
+ funcsTable.ForEach(func(idx, f lua.LValue) {
+ if f.Type() == lua.LTFunction {
+ funcs[idx.String()] = f.(*lua.LFunction)
+ } else {
+ err = errors.New(f.String() + " not a lua function")
+ return
+ }
+ })
+ if err != nil {
+ return nil, err
+ }
+ // Rule may stop
+ if rule.Status != typex.RULE_STOP {
+ return interpipeline.RunPipline(rule.LuaVM, funcs, arg)
+ }
+ return lua.LNil, nil
+ }
+ default:
+ {
+ return nil, errors.New("'Actions' is not functions type Table")
}
- })
- if err != nil {
- return nil, err
- }
- if rule.Status != typex.RULE_STOP {
- return interpipeline.RunPipline(rule.LuaVM, funcs, arg)
}
- // if stopped, log warning information
- glogger.GLogger.Warn("Rule has stopped:" + rule.UUID)
- return lua.LNil, nil
-
}
return nil, errors.New("'Actions' not a lua table or not exist")
diff --git a/core/ui_schema_cache.go b/core/ui_schema_cache.go
index 7a09686b8..674e65520 100644
--- a/core/ui_schema_cache.go
+++ b/core/ui_schema_cache.go
@@ -33,7 +33,7 @@ var __lock sync.Mutex
* 初始化缓冲器
*
*/
-func InitInternalSchemaCache() {
+func InitInternalSchemaCache(rulex typex.RuleX) {
__InternalSchemaCache = make(map[string]typex.DataSchema)
__lock = sync.Mutex{}
}
diff --git a/device/__device_template.go b/device/__device_template.go
new file mode 100644
index 000000000..a2a327ca5
--- /dev/null
+++ b/device/__device_template.go
@@ -0,0 +1,107 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package device
+
+import (
+ "sync"
+
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+)
+
+type TemplateDevice struct {
+ typex.XStatus
+}
+
+func NewTemplateDevice(e typex.RuleX) typex.XDevice {
+ hd := new(TemplateDevice)
+ hd.RuleEngine = e
+ return hd
+}
+
+// 初始化
+func (hd *TemplateDevice) Init(devId string, configMap map[string]interface{}) error {
+ hd.PointId = devId
+ if err := utils.BindSourceConfig(configMap, &hd.mainConfig); err != nil {
+ glogger.GLogger.Error(err)
+ return err
+ }
+
+ return nil
+}
+
+// 启动
+func (hd *TemplateDevice) Start(cctx typex.CCTX) error {
+ hd.Ctx = cctx.Ctx
+ hd.CancelCTX = cctx.CancelCTX
+
+ hd.status = typex.DEV_UP
+ return nil
+}
+
+func (hd *TemplateDevice) OnRead(cmd []byte, data []byte) (int, error) {
+
+ return 0, nil
+}
+
+// 把数据写入设备
+func (hd *TemplateDevice) OnWrite(cmd []byte, b []byte) (int, error) {
+ return 0, nil
+}
+
+// 设备当前状态
+func (hd *TemplateDevice) Status() typex.DeviceState {
+ return typex.DEV_UP
+}
+
+// 停止设备
+func (hd *TemplateDevice) Stop() {
+ hd.status = typex.DEV_STOP
+ hd.CancelCTX()
+}
+
+// 设备属性,是一系列属性描述
+func (hd *TemplateDevice) Property() []typex.DeviceProperty {
+ return []typex.DeviceProperty{}
+}
+
+// 真实设备
+func (hd *TemplateDevice) Details() *typex.Device {
+ return hd.RuleEngine.GetDevice(hd.PointId)
+}
+
+// 状态
+func (hd *TemplateDevice) SetState(status typex.DeviceState) {
+ hd.status = status
+
+}
+
+// 驱动
+func (hd *TemplateDevice) Driver() typex.XExternalDriver {
+ return nil
+}
+
+// --------------------------------------------------------------------------------------------------
+//
+// --------------------------------------------------------------------------------------------------
+
+func (hd *TemplateDevice) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
+ return typex.DCAResult{}
+}
+func (hd *TemplateDevice) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
+ return []byte{}, nil
+}
diff --git a/device/decoder/h264decoder.go b/device/decoder/h264decoder.go
deleted file mode 100644
index 22dfa6374..000000000
--- a/device/decoder/h264decoder.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package decoder
-
-import (
- "fmt"
- "image"
- "unsafe"
-)
-
-// #cgo pkg-config: libavcodec libavutil libswscale
-// #include
-// #include
-// #include
-import "C"
-
-func FrameData(frame *C.AVFrame) **C.uint8_t {
- return (**C.uint8_t)(unsafe.Pointer(&frame.data[0]))
-}
-
-func FrameLineSize(frame *C.AVFrame) *C.int {
- return (*C.int)(unsafe.Pointer(&frame.linesize[0]))
-}
-
-// h264Decoder is a wrapper around ffmpeg's H264 decoder.
-type h264Decoder struct {
- codecCtx *C.AVCodecContext
- srcFrame *C.AVFrame
- swsCtx *C.struct_SwsContext
- dstFrame *C.AVFrame
- dstFramePtr []uint8
-}
-
-// newH264Decoder allocates a new h264Decoder.
-func NewH264Decoder() (*h264Decoder, error) {
- codec := C.avcodec_find_decoder(C.AV_CODEC_ID_H264)
- if codec == nil {
- return nil, fmt.Errorf("avcodec_find_decoder() failed")
- }
-
- codecCtx := C.avcodec_alloc_context3(codec)
- if codecCtx == nil {
- return nil, fmt.Errorf("avcodec_alloc_context3() failed")
- }
-
- res := C.avcodec_open2(codecCtx, codec, nil)
- if res < 0 {
- C.avcodec_close(codecCtx)
- return nil, fmt.Errorf("avcodec_open2() failed")
- }
-
- srcFrame := C.av_frame_alloc()
- if srcFrame == nil {
- C.avcodec_close(codecCtx)
- return nil, fmt.Errorf("av_frame_alloc() failed")
- }
-
- return &h264Decoder{
- codecCtx: codecCtx,
- srcFrame: srcFrame,
- }, nil
-}
-
-// close closes the decoder.
-func (d *h264Decoder) Close() {
- if d.dstFrame != nil {
- C.av_frame_free(&d.dstFrame)
- }
-
- if d.swsCtx != nil {
- C.sws_freeContext(d.swsCtx)
- }
-
- C.av_frame_free(&d.srcFrame)
- C.avcodec_close(d.codecCtx)
-}
-
-func (d *h264Decoder) Decode(nalu []byte) (image.Image, error) {
- nalu = append([]uint8{0x00, 0x00, 0x00, 0x01}, []uint8(nalu)...)
-
- // send frame to decoder
- var avPacket C.AVPacket
- avPacket.data = (*C.uint8_t)(C.CBytes(nalu))
- defer C.free(unsafe.Pointer(avPacket.data))
- avPacket.size = C.int(len(nalu))
- res := C.avcodec_send_packet(d.codecCtx, &avPacket)
- if res < 0 {
- return nil, nil
- }
-
- // receive frame if available
- res = C.avcodec_receive_frame(d.codecCtx, d.srcFrame)
- if res < 0 {
- return nil, nil
- }
-
- // if frame size has changed, allocate needed objects
- if d.dstFrame == nil || d.dstFrame.width != d.srcFrame.width || d.dstFrame.height != d.srcFrame.height {
- if d.dstFrame != nil {
- C.av_frame_free(&d.dstFrame)
- }
-
- if d.swsCtx != nil {
- C.sws_freeContext(d.swsCtx)
- }
-
- d.dstFrame = C.av_frame_alloc()
- d.dstFrame.format = C.AV_PIX_FMT_RGBA
- d.dstFrame.width = d.srcFrame.width
- d.dstFrame.height = d.srcFrame.height
- d.dstFrame.color_range = C.AVCOL_RANGE_JPEG
- res = C.av_frame_get_buffer(d.dstFrame, 1)
- if res < 0 {
- return nil, fmt.Errorf("av_frame_get_buffer() err")
- }
-
- d.swsCtx = C.sws_getContext(d.srcFrame.width, d.srcFrame.height, C.AV_PIX_FMT_YUV420P,
- d.dstFrame.width, d.dstFrame.height, (int32)(d.dstFrame.format), C.SWS_BILINEAR, nil, nil, nil)
- if d.swsCtx == nil {
- return nil, fmt.Errorf("sws_getContext() err")
- }
-
- dstFrameSize := C.av_image_get_buffer_size((int32)(d.dstFrame.format), d.dstFrame.width, d.dstFrame.height, 1)
- d.dstFramePtr = (*[1 << 30]uint8)(unsafe.Pointer(d.dstFrame.data[0]))[:dstFrameSize:dstFrameSize]
- }
-
- // convert frame from YUV420 to RGB
- res = C.sws_scale(d.swsCtx, frameData(d.srcFrame), frameLineSize(d.srcFrame),
- 0, d.srcFrame.height, frameData(d.dstFrame), frameLineSize(d.dstFrame))
- if res < 0 {
- return nil, fmt.Errorf("sws_scale() err")
- }
-
- // embed frame into an image.Image
- return &image.RGBA{
- Pix: d.dstFramePtr,
- Stride: 4 * (int)(d.dstFrame.width),
- Rect: image.Rectangle{
- Max: image.Point{(int)(d.dstFrame.width), (int)(d.dstFrame.height)},
- },
- }, nil
-}
diff --git a/device/generic_ais_txrx_device.go b/device/generic_ais_txrx_device.go
index 40e77407a..5849a2563 100644
--- a/device/generic_ais_txrx_device.go
+++ b/device/generic_ais_txrx_device.go
@@ -29,7 +29,7 @@ var __AisCodec = aislib.CodecNew(false, false, false)
// --------------------------------------------------------------------------------------------------
type _AISCommonConfig struct {
Mode string `json:"mode" title:"工作模式" info:"UART/TCP"`
- ParseAis bool `json:"parseAis"`
+ ParseAis *bool `json:"parseAis"`
GwSN string `json:"gwsn"`
}
type _AISDeviceMasterConfig struct {
@@ -66,7 +66,7 @@ func NewAISDeviceMaster(e typex.RuleX) typex.XDevice {
},
CommonConfig: _AISCommonConfig{
Mode: "TCP",
- ParseAis: false,
+ ParseAis: new(bool),
GwSN: "HR0001",
},
}
@@ -235,7 +235,7 @@ func (aism *AISDeviceMaster) Start(cctx typex.CCTX) error {
}
// 如果不需要解析,直接原文透传
- if !aism.mainConfig.CommonConfig.ParseAis {
+ if !*aism.mainConfig.CommonConfig.ParseAis {
// {
// "ais_receiver_device":"%s",
// "gwsn":"%s"
@@ -425,7 +425,7 @@ func (aism *AISDeviceMaster) handleIO(session *__AISDeviceSession) {
return
}
// 如果不需要解析,直接原文透传
- if !aism.mainConfig.CommonConfig.ParseAis {
+ if !*aism.mainConfig.CommonConfig.ParseAis {
// {
// "ais_receiver_device":"%s",
// "ais_data":"%s"
diff --git a/device/generic_camera_stream.go b/device/generic_camera_stream.go
index 09bf70c1a..ae36ca60e 100644
--- a/device/generic_camera_stream.go
+++ b/device/generic_camera_stream.go
@@ -9,7 +9,6 @@ import (
"regexp"
"syscall"
- "github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
@@ -168,8 +167,10 @@ func (vc *videoCamera) startFFMPEGProcess(rtspUrl, pushAddr string) {
vc.status = typex.DEV_DOWN
}()
paramsVideo := []string{
+ "-hide_banner",
+ "-framerate", "24",
"-f", "dshow",
- "-i", fmt.Sprintf("video=%s", rtspUrl),
+ "-i", fmt.Sprintf("video='%s'", rtspUrl),
"-c:v", "libx264",
"-preset", "veryfast",
"-tune", "zerolatency",
@@ -179,6 +180,8 @@ func (vc *videoCamera) startFFMPEGProcess(rtspUrl, pushAddr string) {
paramsRtsp := []string{
// rtsp://192.168.199.243:554/av0_0
+ "-hide_banner",
+ "-framerate", "24",
"-rtsp_transport",
"tcp",
"-re",
@@ -211,23 +214,10 @@ func (vc *videoCamera) startFFMPEGProcess(rtspUrl, pushAddr string) {
}
glogger.GLogger.Info("Start FFMPEG ffmpegProcess with:", cmd.String())
// 启动 FFmpeg 推流
- if err := cmd.Start(); err != nil {
- glogger.GLogger.Error(err)
- return
- }
- if core.GlobalConfig.AppDebugMode {
- inOut := wsInOut{}
- cmd.Stdin = nil
- cmd.Stdout = inOut
- cmd.Stderr = inOut
- }
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
+ cmd.Env = os.Environ()
vc.ffmpegProcess = cmd
- // 等待 FFmpeg 进程完成
- if err := vc.ffmpegProcess.Wait(); err != nil {
- output, _ := cmd.CombinedOutput()
- glogger.GLogger.Error(err, string(output))
+ if output, err := cmd.CombinedOutput(); err != nil {
+ glogger.GLogger.Error("error: ", err, ", output: ", string(output))
return
}
glogger.GLogger.Info("stop Video Stream Endpoint:", rtspUrl)
diff --git a/device/generic_camera_stream.md b/device/generic_camera_stream.md
index 63601ea25..ac220dc24 100644
--- a/device/generic_camera_stream.md
+++ b/device/generic_camera_stream.md
@@ -17,36 +17,11 @@
}
```
## 测试
-可以通过下面这个HTML页面来测试效果。
-```html
-
-
+勃播放地址:`ws://127.0.0.1:9400/ws?token=WebRtspPlayer&liveId=a97607e47c81d43dba8ef6fa48a2cd45`,其中:
+- URL: 固定路径`ws://127.0.0.1:9400/ws`
+- token:固定值`WebRtspPlayer`
+- liveId:播放源的名称的**md5Hash**,例如`USB2.0 PC CAMERA`的 liveId 是 `a97607e47c81d43dba8ef6fa48a2cd45`。
-
-
-
-
- Document
-
-
-
-
-
-
-
-
-
-
-```
## 维护
-
diff --git a/device/generic_http_device.go b/device/generic_http_device.go
new file mode 100644
index 000000000..22c494049
--- /dev/null
+++ b/device/generic_http_device.go
@@ -0,0 +1,197 @@
+package device
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "sync"
+ "time"
+
+ "github.com/hootrhino/rulex/common"
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+)
+
+type __HttpCommonConfig struct {
+ Timeout *int `json:"timeout" validate:"required"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
+ Frequency *int64 `json:"frequency" validate:"required"`
+}
+type __HttpMainConfig struct {
+ CommonConfig __HttpCommonConfig `json:"commonConfig" validate:"required"`
+ HttpConfig common.HTTPConfig `json:"httpConfig" validate:"required"`
+}
+
+type GenericHttpDevice struct {
+ typex.XStatus
+ client http.Client
+ status typex.DeviceState
+ RuleEngine typex.RuleX
+ mainConfig __HttpMainConfig
+ locker sync.Locker
+}
+
+/*
+*
+* 通用串口透传
+*
+ */
+func NewGenericHttpDevice(e typex.RuleX) typex.XDevice {
+ hd := new(GenericHttpDevice)
+ hd.locker = &sync.Mutex{}
+ hd.client = *http.DefaultClient
+ hd.mainConfig = __HttpMainConfig{
+ CommonConfig: __HttpCommonConfig{
+ AutoRequest: new(bool),
+ },
+ }
+ hd.RuleEngine = e
+ return hd
+}
+
+// 初始化
+func (hd *GenericHttpDevice) Init(devId string, configMap map[string]interface{}) error {
+ hd.PointId = devId
+ if err := utils.BindSourceConfig(configMap, &hd.mainConfig); err != nil {
+ glogger.GLogger.Error(err)
+ return err
+ }
+ if _, err := isValidHTTP_URL(hd.mainConfig.HttpConfig.Url); err != nil {
+ return fmt.Errorf("invalid url format:%s, %s", hd.mainConfig.HttpConfig.Url, err)
+ }
+ return nil
+}
+
+// 启动
+func (hd *GenericHttpDevice) Start(cctx typex.CCTX) error {
+ hd.Ctx = cctx.Ctx
+ hd.CancelCTX = cctx.CancelCTX
+
+ if *hd.mainConfig.CommonConfig.AutoRequest {
+ ticker := time.NewTicker(
+ time.Duration(*hd.mainConfig.CommonConfig.Frequency) * time.Millisecond)
+ go func() {
+ for {
+ select {
+ case <-hd.Ctx.Done():
+ {
+ ticker.Stop()
+ return
+ }
+ default:
+ {
+ }
+ }
+ body := httpGet(hd.client, hd.mainConfig.HttpConfig.Url)
+ if body != "" {
+ hd.RuleEngine.WorkDevice(hd.Details(), body)
+ }
+ <-ticker.C
+ }
+ }()
+
+ }
+ hd.status = typex.DEV_UP
+ return nil
+}
+
+func (hd *GenericHttpDevice) OnRead(cmd []byte, data []byte) (int, error) {
+
+ return 0, nil
+}
+
+// 把数据写入设备
+func (hd *GenericHttpDevice) OnWrite(cmd []byte, b []byte) (int, error) {
+ return 0, nil
+}
+
+// 设备当前状态
+func (hd *GenericHttpDevice) Status() typex.DeviceState {
+ return typex.DEV_UP
+}
+
+// 停止设备
+func (hd *GenericHttpDevice) Stop() {
+ hd.status = typex.DEV_STOP
+ if hd.CancelCTX != nil {
+ hd.CancelCTX()
+ }
+}
+
+// 设备属性,是一系列属性描述
+func (hd *GenericHttpDevice) Property() []typex.DeviceProperty {
+ return []typex.DeviceProperty{}
+}
+
+// 真实设备
+func (hd *GenericHttpDevice) Details() *typex.Device {
+ return hd.RuleEngine.GetDevice(hd.PointId)
+}
+
+// 状态
+func (hd *GenericHttpDevice) SetState(status typex.DeviceState) {
+ hd.status = status
+
+}
+
+// 驱动
+func (hd *GenericHttpDevice) Driver() typex.XExternalDriver {
+ return nil
+}
+
+// --------------------------------------------------------------------------------------------------
+//
+// --------------------------------------------------------------------------------------------------
+
+func (hd *GenericHttpDevice) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
+ return typex.DCAResult{}
+}
+func (hd *GenericHttpDevice) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
+ return []byte{}, nil
+}
+
+/*
+*
+* HTTP GET
+*
+ */
+func httpGet(client http.Client, url string) string {
+ var err error
+ client.Timeout = 2 * time.Second
+ request, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ glogger.GLogger.Warn(err)
+ return ""
+ }
+
+ response, err := client.Do(request)
+ if err != nil {
+ glogger.GLogger.Warn(err)
+ return ""
+ }
+ defer response.Body.Close()
+ body, err := io.ReadAll(response.Body)
+ if err != nil {
+ glogger.GLogger.Warn(err)
+ return ""
+ }
+ return string(body)
+}
+
+/*
+*
+* 验证URL语法
+*
+ */
+func isValidHTTP_URL(urlStr string) (bool, error) {
+ r, err := url.Parse(urlStr)
+ if err != nil {
+ return false, fmt.Errorf("error parsing URL: %w", err)
+ }
+ if r.Scheme != "http" && r.Scheme != "https" {
+ return false, fmt.Errorf("invalid scheme; must be http or https")
+ }
+ return true, nil
+}
diff --git a/device/generic_http_device.md b/device/generic_http_device.md
new file mode 100644
index 000000000..f656bcc69
--- /dev/null
+++ b/device/generic_http_device.md
@@ -0,0 +1,96 @@
+## Http 采集器
+主要用来请求远程HTTP接口,采集这类设备的数据。
+
+## 配置
+
+```json
+{
+ "code": 200,
+ "msg": "Success",
+ "data": {
+ "uuid": "DEVICEP6VNYAAK",
+ "gid": "DROOT",
+ "name": "HTTP请求设备",
+ "type": "GENERIC_HTTP_DEVICE",
+ "state": 1,
+ "config": {
+ "commonConfig": {
+ "autoRequest": true,
+ "frequency": 1000,
+ "timeout": 3000
+ },
+ "httpConfig": {
+ "headers": {
+ "token": "12345"
+ },
+ "url": "http://127.0.0.1:8080"
+ }
+ },
+ "description": ""
+ }
+}
+```
+
+## 测试
+下面是一个传感器采集到的数据:
+```lua
+{
+ "device_id": 1, // 设备的唯一标识符
+ "recv_time": "2023-12-01T15:27:25+08:00", // 数据接收的时间戳
+ "bat_voltage": 0, // 电池电压,单位通常为伏特
+ "longitude": 0, // 经度,表示设备所在位置的东经或西经度数
+ "latitude": 0, // 纬度,表示设备所在位置的北纬或南纬度数
+ "air_height": 0, // 距离海平面的空气高度,单位通常为米
+ "water_temp": 18.91, // 水温,单位通常为摄氏度
+ "salinity": 627.317, // 盐度,表示水中溶解盐的含量
+ "dissolved_oxygen": 0, // 溶解氧含量,单位通常为毫克/升
+ "ph_value": 5.95799, // pH值,表示水质的酸碱程度
+ "wind_speed": 4.48, // 风速,单位通常为米/秒
+ "wind_direction": 37, // 风向,通常以北为0度,顺时针增加
+ "air_temp": 13.9, // 空气温度,单位通常为摄氏度
+ "air_pressure": 102.6, // 空气压力,单位通常为百帕
+ "air_humidity": 63.9, // 空气湿度,通常以百分比表示
+ "noise": 42, // 噪音水平,单位通常为分贝
+ "wave_height": 0, // 波高,单位通常为米
+ "mean_wave_period": 0, // 平均波周期,单位通常为秒
+ "peak_wave_period": 0, // 最大波周期,单位通常为秒
+ "mean_wave_direction": 0 // 平均波向,通常以北为0度,顺时针增加
+}
+
+```
+
+使用LUA脚本解析并且打印出来:
+
+```lua
+Actions = {
+ function(args)
+ local jsonTable = {
+ device_id = "设备唯一标识符",
+ recv_time = "数据接收时间戳",
+ bat_voltage = "电池电压",
+ longitude = "经度",
+ latitude = "纬度",
+ air_height = "空气高度",
+ water_temp = "水温",
+ salinity = "盐度",
+ dissolved_oxygen = "溶解氧含量",
+ ph_value = "pH值",
+ wind_speed = "风速",
+ wind_direction = "风向",
+ air_temp = "空气温度",
+ air_pressure = "空气压力",
+ air_humidity = "空气湿度",
+ noise = "噪音水平",
+ wave_height = "波高",
+ mean_wave_period = "平均波周期",
+ peak_wave_period = "最大波周期",
+ mean_wave_direction = "平均波向"
+ }
+ local dataT = json:J2T(args)
+ for k, v in pairs(dataT) do
+ stdlib:Debug(jsonTable[k] .. ": " .. v)
+ end
+ return true, args
+ end
+}
+```
\ No newline at end of file
diff --git a/device/generic_modbus_device.go b/device/generic_modbus_device.go
index 3f08a883e..c45695323 100644
--- a/device/generic_modbus_device.go
+++ b/device/generic_modbus_device.go
@@ -17,6 +17,9 @@ package device
import (
"context"
+ "encoding/binary"
+ "encoding/hex"
+ "encoding/json"
"errors"
"fmt"
golog "log"
@@ -25,8 +28,9 @@ import (
"github.com/hootrhino/rulex/common"
"github.com/hootrhino/rulex/component/hwportmanager"
+ modbuscache "github.com/hootrhino/rulex/component/intercache/modbus"
+ "github.com/hootrhino/rulex/component/interdb"
"github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/driver"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
@@ -56,26 +60,44 @@ import (
// }
// }
type _GMODCommonConfig struct {
- Mode string `json:"mode" title:"工作模式" info:"UART/TCP"`
- AutoRequest *bool `json:"autoRequest" title:"启动轮询"`
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
+ Mode string `json:"mode"`
+ AutoRequest *bool `json:"autoRequest"`
}
type _GMODConfig struct {
- CommonConfig _GMODCommonConfig `json:"commonConfig" validate:"required"`
- PortUuid string `json:"portUuid"`
- HostConfig common.HostConfig `json:"hostConfig"`
- Registers []common.RegisterRW `json:"registers" validate:"required" title:"寄存器配置"`
+ CommonConfig _GMODCommonConfig `json:"commonConfig" validate:"required"`
+ PortUuid string `json:"portUuid"`
+ HostConfig common.HostConfig `json:"hostConfig"`
+}
+
+/*
+*
+* 点位表
+*
+ */
+type ModbusPoint struct {
+ UUID string `json:"uuid,omitempty"` // 当UUID为空时新建
+ Tag string `json:"tag"`
+ Alias string `json:"alias"`
+ Function int `json:"function"`
+ SlaverId byte `json:"slaverId"`
+ Address uint16 `json:"address"`
+ Frequency int64 `json:"frequency"`
+ Quantity uint16 `json:"quantity"`
+ Value string `json:"value,omitempty"`
}
type generic_modbus_device struct {
typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- rtuHandler *modbus.RTUClientHandler
- tcpHandler *modbus.TCPClientHandler
+ status typex.DeviceState
+ RuleEngine typex.RuleX
+ //
+ rtuHandler *modbus.RTUClientHandler
+ tcpHandler *modbus.TCPClientHandler
+ Client modbus.Client
+ //
mainConfig _GMODConfig
retryTimes int
hwPortConfig hwportmanager.UartConfig
+ Registers map[string]*common.RegisterRW
}
/*
@@ -96,6 +118,7 @@ func NewGenericModbusDevice(e typex.RuleX) typex.XDevice {
PortUuid: "/dev/ttyS0",
HostConfig: common.HostConfig{Host: "127.0.0.1", Port: 502, Timeout: 3000},
}
+ mdev.Registers = map[string]*common.RegisterRW{}
mdev.Busy = false
mdev.status = typex.DEV_DOWN
return mdev
@@ -104,25 +127,43 @@ func NewGenericModbusDevice(e typex.RuleX) typex.XDevice {
// 初始化
func (mdev *generic_modbus_device) Init(devId string, configMap map[string]interface{}) error {
mdev.PointId = devId
+ modbuscache.RegisterSlot(mdev.PointId)
if err := utils.BindSourceConfig(configMap, &mdev.mainConfig); err != nil {
return err
}
- // 频率不能太快
- if mdev.mainConfig.CommonConfig.Frequency < 50 {
- return errors.New("'frequency' must grate than 50 millisecond")
-
- }
- // 检查Tag有没有重复
- tags := []string{}
- for _, register := range mdev.mainConfig.Registers {
- tags = append(tags, register.Tag)
- }
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
- }
if !utils.SContains([]string{"UART", "TCP"}, mdev.mainConfig.CommonConfig.Mode) {
return errors.New("unsupported mode, only can be one of 'TCP' or 'UART'")
}
+ // 合并数据库里面的点位表
+ var list []ModbusPoint
+ errDb := interdb.DB().Table("m_modbus_data_points").
+ Where("device_uuid=?", devId).Find(&list).Error
+ if errDb != nil {
+ return errDb
+ }
+ for _, v := range list {
+ // 频率不能太快
+ if v.Frequency < 50 {
+ return errors.New("'frequency' must grate than 50 millisecond")
+ }
+ mdev.Registers[v.UUID] = &common.RegisterRW{
+ Tag: v.Tag,
+ Alias: v.Alias,
+ Function: v.Function,
+ SlaverId: v.SlaverId,
+ Address: v.Address,
+ Quantity: v.Quantity,
+ Frequency: v.Frequency,
+ }
+ Status := 0
+ LastFetchTime := uint64(time.Now().UnixMilli())
+ modbuscache.SetValue(mdev.PointId, v.UUID, modbuscache.RegisterPoint{
+ UUID: v.UUID,
+ Status: Status,
+ LastFetchTime: LastFetchTime,
+ })
+ }
+
if mdev.mainConfig.CommonConfig.Mode == "UART" {
hwPort, err := hwportmanager.GetHwPort(mdev.mainConfig.PortUuid)
if err != nil {
@@ -179,10 +220,7 @@ func (mdev *generic_modbus_device) Start(cctx typex.CCTX) error {
Type: "DEVICE",
Name: mdev.Details().Name,
})
- client := modbus.NewClient(mdev.rtuHandler)
- mdev.driver = driver.NewModBusRtuDriver(mdev.Details(),
- mdev.RuleEngine, mdev.mainConfig.Registers, mdev.rtuHandler,
- client, mdev.mainConfig.CommonConfig.Frequency)
+ mdev.Client = modbus.NewClient(mdev.rtuHandler)
}
if mdev.mainConfig.CommonConfig.Mode == "TCP" {
mdev.tcpHandler = modbus.NewTCPClientHandler(
@@ -192,21 +230,17 @@ func (mdev *generic_modbus_device) Start(cctx typex.CCTX) error {
mdev.tcpHandler.Logger = golog.New(glogger.GLogger.Writer(),
"Modbus TCP Mode: "+mdev.PointId+", ", golog.LstdFlags)
}
-
if err := mdev.tcpHandler.Connect(); err != nil {
return err
}
- client := modbus.NewClient(mdev.tcpHandler)
- mdev.driver = driver.NewModBusTCPDriver(mdev.Details(),
- mdev.RuleEngine, mdev.mainConfig.Registers, mdev.tcpHandler, client,
- mdev.mainConfig.CommonConfig.Frequency)
+ mdev.Client = modbus.NewClient(mdev.tcpHandler)
}
//---------------------------------------------------------------------------------
// Start
//---------------------------------------------------------------------------------
if *mdev.mainConfig.CommonConfig.AutoRequest {
mdev.retryTimes = 0
- go func(ctx context.Context, Driver typex.XExternalDriver) {
+ go func(ctx context.Context) {
buffer := make([]byte, common.T_64KB)
for {
select {
@@ -218,16 +252,27 @@ func (mdev *generic_modbus_device) Start(cctx typex.CCTX) error {
{
}
}
- n, err := Driver.Read([]byte{}, buffer)
+ n := 0
+ var err error
+ if mdev.mainConfig.CommonConfig.Mode == "TCP" {
+ n, err = mdev.RTURead(buffer)
+ }
+ if mdev.mainConfig.CommonConfig.Mode == "UART" {
+ n, err = mdev.TCPRead(buffer)
+ }
if err != nil {
glogger.GLogger.Error(err)
mdev.retryTimes++
- } else {
- mdev.RuleEngine.WorkDevice(mdev.Details(), string(buffer[:n]))
+ continue
+ }
+ // [] {} ""
+ if n < 3 {
+ continue
}
+ mdev.RuleEngine.WorkDevice(mdev.Details(), string(buffer[:n]))
}
- }(mdev.Ctx, mdev.driver)
+ }(mdev.Ctx)
}
mdev.status = typex.DEV_UP
@@ -236,21 +281,75 @@ func (mdev *generic_modbus_device) Start(cctx typex.CCTX) error {
// 从设备里面读数据出来
func (mdev *generic_modbus_device) OnRead(cmd []byte, data []byte) (int, error) {
-
- n, err := mdev.driver.Read(cmd, data)
- if err != nil {
- glogger.GLogger.Error(err)
- mdev.retryTimes++
- }
- return n, err
+ return 0, nil
}
// 把数据写入设备
func (mdev *generic_modbus_device) OnWrite(cmd []byte, data []byte) (int, error) {
- if mdev.Busy {
- return 0, fmt.Errorf("device busing now")
+ RegisterW := common.RegisterW{}
+ if err := json.Unmarshal(data, &RegisterW); err != nil {
+ return 0, err
+ }
+ dataMap := [1]common.RegisterW{RegisterW}
+ for _, r := range dataMap {
+ if mdev.mainConfig.CommonConfig.Mode == "TCP" {
+ mdev.tcpHandler.SlaveId = r.SlaverId
+ }
+ if mdev.mainConfig.CommonConfig.Mode == "UART" {
+ mdev.rtuHandler.SlaveId = r.SlaverId
+ }
+ // 5
+ if r.Function == common.WRITE_SINGLE_COIL {
+ if len(r.Values) > 0 {
+ if r.Values[0] == 0 {
+ _, err := mdev.Client.WriteSingleCoil(r.Address,
+ binary.BigEndian.Uint16([]byte{0x00, 0x00}))
+ if err != nil {
+ return 0, err
+ }
+ }
+ if r.Values[0] == 1 {
+ _, err := mdev.Client.WriteSingleCoil(r.Address,
+ binary.BigEndian.Uint16([]byte{0xFF, 0x00}))
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ }
+
+ }
+ // 15
+ if r.Function == common.WRITE_MULTIPLE_COILS {
+ _, err := mdev.Client.WriteMultipleCoils(r.Address, r.Quantity, r.Values)
+ if err != nil {
+ return 0, err
+ }
+ }
+ // 6
+ if r.Function == common.WRITE_SINGLE_HOLDING_REGISTER {
+ _, err := mdev.Client.WriteSingleRegister(r.Address, binary.BigEndian.Uint16(r.Values))
+ if err != nil {
+ return 0, err
+ }
+ }
+ // 16
+ if r.Function == common.WRITE_MULTIPLE_HOLDING_REGISTERS {
+
+ _, err := mdev.Client.WriteMultipleRegisters(r.Address,
+ uint16(len(r.Values))/2, maybePrependZero(r.Values))
+ if err != nil {
+ return 0, err
+ }
+ }
+ }
+ return 0, nil
+}
+func maybePrependZero(slice []byte) []byte {
+ if len(slice)%2 != 0 {
+ slice = append([]byte{0}, slice...)
}
- return mdev.driver.Write(cmd, data)
+ return slice
}
// 设备当前状态
@@ -264,14 +363,25 @@ func (mdev *generic_modbus_device) Status() typex.DeviceState {
// 停止设备
func (mdev *generic_modbus_device) Stop() {
- mdev.CancelCTX()
mdev.status = typex.DEV_DOWN
- if mdev.driver != nil {
- mdev.driver.Stop()
+ if mdev.CancelCTX != nil {
+ mdev.CancelCTX()
}
if mdev.mainConfig.CommonConfig.Mode == "UART" {
hwportmanager.FreeInterfaceBusy(mdev.mainConfig.PortUuid)
}
+ if mdev.mainConfig.CommonConfig.Mode == "UART" {
+ if mdev.rtuHandler != nil {
+ mdev.rtuHandler.Close()
+ }
+ }
+ if mdev.mainConfig.CommonConfig.Mode == "TCP" {
+ if mdev.tcpHandler != nil {
+ mdev.tcpHandler.Close()
+ }
+ }
+ modbuscache.UnRegisterSlot(mdev.PointId)
+
}
// 设备属性,是一系列属性描述
@@ -291,11 +401,151 @@ func (mdev *generic_modbus_device) SetState(status typex.DeviceState) {
// 驱动
func (mdev *generic_modbus_device) Driver() typex.XExternalDriver {
- return mdev.driver
+ return nil
}
func (mdev *generic_modbus_device) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
return typex.DCAResult{}
}
-func (mdev *generic_modbus_device) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
+func (mdev *generic_modbus_device) OnCtrl([]byte, []byte) ([]byte, error) {
return []byte{}, nil
}
+
+/*
+*
+* 串口模式
+*
+ */
+func (mdev *generic_modbus_device) modbusRead(buffer []byte) (int, error) {
+ var err error
+ var results []byte
+ RegisterRWs := []common.RegisterRW{}
+ count := len(mdev.Registers)
+ if count == 0 {
+ return 0, nil
+ }
+ if mdev.Client == nil {
+ return 0, fmt.Errorf("modbus client id not valid")
+ }
+ for uuid, r := range mdev.Registers {
+ if mdev.mainConfig.CommonConfig.Mode == "TCP" {
+ mdev.tcpHandler.SlaveId = r.SlaverId
+ }
+ if mdev.mainConfig.CommonConfig.Mode == "UART" {
+ mdev.rtuHandler.SlaveId = r.SlaverId
+ }
+ if r.Function == common.READ_COIL {
+ results, err = mdev.Client.ReadCoils(r.Address, r.Quantity)
+ if err != nil {
+ count--
+ glogger.GLogger.Error(err)
+ }
+ Value := covertEmptyHex(results)
+ Reg := common.RegisterRW{
+ Tag: r.Tag,
+ Function: r.Function,
+ SlaverId: r.SlaverId,
+ Address: r.Address,
+ Quantity: r.Quantity,
+ Alias: r.Alias,
+ Value: Value,
+ }
+ RegisterRWs = append(RegisterRWs, Reg)
+ modbuscache.SetValue(mdev.PointId, uuid, modbuscache.RegisterPoint{
+ UUID: uuid,
+ Status: 0,
+ Value: Value,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ })
+ }
+ if r.Function == common.READ_DISCRETE_INPUT {
+ results, err = mdev.Client.ReadDiscreteInputs(r.Address, r.Quantity)
+ if err != nil {
+ count--
+ glogger.GLogger.Error(err)
+ continue
+ }
+ Value := covertEmptyHex(results)
+ Reg := common.RegisterRW{
+ Tag: r.Tag,
+ Function: r.Function,
+ SlaverId: r.SlaverId,
+ Address: r.Address,
+ Quantity: r.Quantity,
+ Alias: r.Alias,
+ Value: Value,
+ }
+ RegisterRWs = append(RegisterRWs, Reg)
+ modbuscache.SetValue(mdev.PointId, uuid, modbuscache.RegisterPoint{
+ UUID: uuid,
+ Status: 0,
+ Value: Value,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ })
+ }
+ if r.Function == common.READ_HOLDING_REGISTERS {
+ results, err = mdev.Client.ReadHoldingRegisters(r.Address, r.Quantity)
+ if err != nil {
+ count--
+ glogger.GLogger.Error(err)
+ }
+ Value := covertEmptyHex(results)
+ Reg := common.RegisterRW{
+ Tag: r.Tag,
+ Function: r.Function,
+ SlaverId: r.SlaverId,
+ Address: r.Address,
+ Quantity: r.Quantity,
+ Alias: r.Alias,
+ Value: Value,
+ }
+ RegisterRWs = append(RegisterRWs, Reg)
+ modbuscache.SetValue(mdev.PointId, uuid, modbuscache.RegisterPoint{
+ UUID: uuid,
+ Status: 0,
+ Value: Value,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ })
+
+ }
+ if r.Function == common.READ_INPUT_REGISTERS {
+ results, err = mdev.Client.ReadInputRegisters(r.Address, r.Quantity)
+ if err != nil {
+ count--
+ glogger.GLogger.Error(err)
+ }
+ Value := covertEmptyHex(results)
+ Reg := common.RegisterRW{
+ Tag: r.Tag,
+ Function: r.Function,
+ SlaverId: r.SlaverId,
+ Address: r.Address,
+ Quantity: r.Quantity,
+ Alias: r.Alias,
+ Value: Value,
+ }
+ RegisterRWs = append(RegisterRWs, Reg)
+ modbuscache.SetValue(mdev.PointId, uuid, modbuscache.RegisterPoint{
+ UUID: uuid,
+ Status: 0,
+ Value: Value,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ })
+ }
+ time.Sleep(time.Duration(r.Frequency) * time.Millisecond)
+ }
+ bytes, _ := json.Marshal(RegisterRWs)
+ copy(buffer, bytes)
+ return len(bytes), nil
+}
+func (mdev *generic_modbus_device) RTURead(buffer []byte) (int, error) {
+ return mdev.modbusRead(buffer)
+}
+func (mdev *generic_modbus_device) TCPRead(buffer []byte) (int, error) {
+ return mdev.modbusRead(buffer)
+}
+func covertEmptyHex(v []byte) string {
+ if len(v) < 1 {
+ return ""
+ }
+ return hex.EncodeToString(v)
+}
diff --git a/device/generic_modbus_excel_device.go b/device/generic_modbus_excel_device.go
deleted file mode 100644
index 275489966..000000000
--- a/device/generic_modbus_excel_device.go
+++ /dev/null
@@ -1,315 +0,0 @@
-package device
-
-import (
- "context"
- "errors"
- "fmt"
- golog "log"
- "sync"
- "time"
-
- "github.com/plgd-dev/kit/v2/log"
- modbus "github.com/wwhai/gomodbus"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/component/interdb"
- "github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/driver"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
-
- _ "github.com/mattn/go-sqlite3"
-)
-
-// 这是个通用Modbus采集器, 主要用来在通用场景下采集数据,因此需要配合规则引擎来使用
-//
-// Modbus 采集到的数据如下, LUA 脚本可做解析, 示例脚本可参照 generic_modbus_parse.lua
-//
-// {
-// "d1":{
-// "tag":"d1",
-// "function":3,
-// "slaverId":1,
-// "address":0,
-// "quantity":2,
-// "value":"..."
-// },
-// "d2":{
-// "tag":"d2",
-// "function":3,
-// "slaverId":2,
-// "address":0,
-// "quantity":2,
-// "value":"..."
-// }
-// }
-
-const (
- DEFAULT_DB_PATH string = "./rulex.db"
-)
-
-type modbusPointPosition struct {
- DeviceUuid string `json:"deviceUuid" `
- Tag string `json:"tag" `
- Function int `json:"function" `
- SlaverId byte `json:"slaverId" `
- StartAddress uint16 `json:"startAddress"`
- Quality uint16 `json:"quality" `
-}
-
-type _GMODExcelCommonConfig struct {
- Mode string `json:"mode" title:"工作模式" info:"UART/TCP"`
- Timeout int `json:"timeout" validate:"required" title:"连接超时"`
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
-}
-type _GMODExcelHostConfig struct {
- Host string `json:"host" title:"服务地址"`
- Port int `json:"port" title:"服务端口"`
-}
-
-type _GMODExcelConfig struct {
- CommonConfig _GMODExcelCommonConfig `json:"commonConfig" validate:"required"`
- RtuConfig common.CommonUartConfig `json:"rtuConfig" validate:"required"`
- TcpConfig _GMODExcelHostConfig `json:"tcpConfig" validate:"required"`
- Registers []common.RegisterRW `json:"registers" validate:"required" title:"寄存器配置"`
-}
-type generic_modbus_excel_device struct {
- typex.XStatus ``
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- rtuHandler *modbus.RTUClientHandler
- tcpHandler *modbus.TCPClientHandler
- mainConfig _GMODExcelConfig
- locker sync.Locker
- retryTimes int
-}
-
-/*
-*
-* 温湿度传感器
-*
- */
-func NewGenericModbusExcelDevice(e typex.RuleX) typex.XDevice {
-
- var (
- err error
- )
-
- mdev := new(generic_modbus_excel_device)
- mdev.RuleEngine = e
- mdev.locker = &sync.Mutex{}
- mdev.mainConfig = _GMODExcelConfig{
- CommonConfig: _GMODExcelCommonConfig{},
- TcpConfig: _GMODExcelHostConfig{Host: "127.0.0.1", Port: 502},
- RtuConfig: common.CommonUartConfig{},
- }
- mdev.Busy = false
- mdev.status = typex.DEV_DOWN
- if err != nil {
- log.Panic(err)
- return nil
- }
- return mdev
-}
-
-// 初始化
-func (mdev *generic_modbus_excel_device) Init(devId string, configMap map[string]interface{}) (err error) {
- mdev.PointId = devId
- if err := utils.BindSourceConfig(configMap, &mdev.mainConfig); err != nil {
- return err
- }
-
- // 超时大于30秒无意义
- if mdev.mainConfig.CommonConfig.Timeout > 30000 {
- return errors.New("'timeout' must less than 30 second")
- }
- // 频率不能太快
- if mdev.mainConfig.CommonConfig.Frequency < 50 {
- return errors.New("'frequency' must grate than 50 millisecond")
-
- }
-
- var list []modbusPointPosition
- err = interdb.DB().Table("m_modbus_point_position").Where("device_uuid = ?", devId).Find(&list).Error
- if err != nil {
- return err
- }
-
- if len(list) > 0 {
- for _, v := range list {
- mdev.mainConfig.Registers = append(mdev.mainConfig.Registers, common.RegisterRW{
- Tag: v.Tag,
- Function: v.Function,
- SlaverId: v.SlaverId,
- Address: v.StartAddress,
- Quantity: v.Quality,
- })
- }
- }
-
- // 检查Tag有没有重复
- tags := []string{}
- for _, register := range mdev.mainConfig.Registers {
- tags = append(tags, register.Tag)
- }
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
- }
- if !utils.SContains([]string{"UART", "TCP"}, mdev.mainConfig.CommonConfig.Mode) {
- return errors.New("unsupported mode, only can be one of 'TCP' or 'RTU'")
- }
-
- return nil
-}
-
-// 启动
-func (mdev *generic_modbus_excel_device) Start(cctx typex.CCTX) error {
- mdev.Ctx = cctx.Ctx
- mdev.CancelCTX = cctx.CancelCTX
-
- if mdev.mainConfig.CommonConfig.Mode == "UART" {
- mdev.rtuHandler = modbus.NewRTUClientHandler(mdev.mainConfig.RtuConfig.Uart)
- mdev.rtuHandler.BaudRate = mdev.mainConfig.RtuConfig.BaudRate
- mdev.rtuHandler.DataBits = mdev.mainConfig.RtuConfig.DataBits
- mdev.rtuHandler.Parity = mdev.mainConfig.RtuConfig.Parity
- mdev.rtuHandler.StopBits = mdev.mainConfig.RtuConfig.StopBits
- // timeout 最大不能超过20, 不然无意义
- mdev.rtuHandler.Timeout = time.Duration(mdev.mainConfig.RtuConfig.Timeout) * time.Microsecond
- if core.GlobalConfig.AppDebugMode {
- mdev.rtuHandler.Logger = golog.New(glogger.GLogger.Writer(),
- "Modbus: ", golog.LstdFlags)
- }
-
- if err := mdev.rtuHandler.Connect(); err != nil {
- return err
- }
- client := modbus.NewClient(mdev.rtuHandler)
- mdev.driver = driver.NewModBusRtuDriver(mdev.Details(),
- mdev.RuleEngine, mdev.mainConfig.Registers, mdev.rtuHandler,
- client, mdev.mainConfig.CommonConfig.Frequency)
- }
- if mdev.mainConfig.CommonConfig.Mode == "TCP" {
- mdev.tcpHandler = modbus.NewTCPClientHandler(
- fmt.Sprintf("%s:%v", mdev.mainConfig.TcpConfig.Host, mdev.mainConfig.TcpConfig.Port),
- )
- if core.GlobalConfig.AppDebugMode {
- mdev.tcpHandler.Logger = golog.New(glogger.GLogger.Writer(), "Modbus: ", golog.LstdFlags)
- }
-
- if err := mdev.tcpHandler.Connect(); err != nil {
- return err
- }
- client := modbus.NewClient(mdev.tcpHandler)
- mdev.driver = driver.NewModBusTCPDriver(mdev.Details(),
- mdev.RuleEngine, mdev.mainConfig.Registers, mdev.tcpHandler, client,
- mdev.mainConfig.CommonConfig.Frequency)
- }
- // ---------------------------------------------------------------------------------
- // Start
- // ---------------------------------------------------------------------------------
- if !mdev.mainConfig.CommonConfig.AutoRequest {
- mdev.status = typex.DEV_UP
- return nil
- }
- mdev.retryTimes = 0
- go func(ctx context.Context, Driver typex.XExternalDriver) {
-
- mdev.status = typex.DEV_UP
- buffer := make([]byte, common.T_64KB)
- for {
- select {
- case <-ctx.Done():
- {
- return
- }
- default:
- {
- }
- }
- // if mdev.Busy {
- // glogger.GLogger.Warn("Modbus device is busing now")
- // continue
- // }
-
- mdev.Busy = true
- n, err := Driver.Read([]byte{}, buffer)
- mdev.Busy = false
- if err != nil {
- glogger.GLogger.Error(err)
- mdev.retryTimes++
- } else {
- mdev.RuleEngine.WorkDevice(mdev.Details(), string(buffer[:n]))
- }
- }
-
- }(mdev.Ctx, mdev.driver)
- return nil
-}
-
-// 从设备里面读数据出来
-func (mdev *generic_modbus_excel_device) OnRead(cmd []byte, data []byte) (int, error) {
-
- n, err := mdev.driver.Read(cmd, data)
- if err != nil {
- glogger.GLogger.Error(err)
- mdev.retryTimes++
- }
- return n, err
-}
-
-// 把数据写入设备
-func (mdev *generic_modbus_excel_device) OnWrite(cmd []byte, data []byte) (int, error) {
- if mdev.Busy {
- return 0, fmt.Errorf("device busing now")
- }
- return mdev.driver.Write(cmd, data)
-}
-
-// 设备当前状态
-func (mdev *generic_modbus_excel_device) Status() typex.DeviceState {
- // 容错5次
- if mdev.retryTimes > 5 {
- return typex.DEV_DOWN
- }
- return typex.DEV_UP
-}
-
-// 停止设备
-func (mdev *generic_modbus_excel_device) Stop() {
- mdev.status = typex.DEV_DOWN
- if mdev.CancelCTX != nil {
- mdev.CancelCTX()
- }
- if mdev.driver != nil {
- mdev.driver.Stop()
- }
-}
-
-// 设备属性,是一系列属性描述
-func (mdev *generic_modbus_excel_device) Property() []typex.DeviceProperty {
- return []typex.DeviceProperty{}
-}
-
-// 真实设备
-func (mdev *generic_modbus_excel_device) Details() *typex.Device {
- return mdev.RuleEngine.GetDevice(mdev.PointId)
-}
-
-// 状态
-func (mdev *generic_modbus_excel_device) SetState(status typex.DeviceState) {
- mdev.status = status
-}
-
-// 驱动
-func (mdev *generic_modbus_excel_device) Driver() typex.XExternalDriver {
- return mdev.driver
-}
-func (mdev *generic_modbus_excel_device) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
- return typex.DCAResult{}
-}
-func (mdev *generic_modbus_excel_device) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
- return []byte{}, nil
-}
diff --git a/device/generic_modbus_excel_device.md b/device/generic_modbus_excel_device.md
deleted file mode 100644
index 940c5a658..000000000
--- a/device/generic_modbus_excel_device.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# Modbus点表导入支持
-
-## 1. Modbus上传Excel点位文件
-
-### 接口说明
-
-| URL | request | version | status |
-| :--------------------------------------------- | :------ | :------ | :----- |
-| http://server_ip/v1/devices/modbus/sheetImport | POST | 1.0 | true |
-
-### 请求参数说明
-
-注意:请求content-type是multipart/form-data
-
-| 请求参数 | 类型 | 必填 | 必填 |
-| :--------- | :----- | :--- | :-------------------------- |
-| deviceUuid | String | true | 设备UUID |
-| file | file | true | 点位excel文件(具体看模板) |
-
-Excel 模板
-![excel](image/generic_modbus_excel_device/1692706230355.png)
-### 请求响应示例JSON
-响应示例
-
-```json
-{
- "code": 200,
- "msg": "Success",
- "data": {}
-}
-```
-
-## 2. 更新点位数据
-### 接口说明
-
-| URL | request | version | status |
-| :----------------------------------------------- | :------ | :------ | :----- |
-| http://192.168.2.58:8080/v1/devices/modbus/point | PUT | 1.0 | true |
-
-### 请求参数说明
-
-注意:请求content-type是multipart/form-data
-
-| 请求参数 | 类型 | 必填 | 备注 |
-| :----------- | :----- | :--- | :-------------------- |
-| Id | int | true | 点位ID |
-| DeviceUuid | file | true | 设备UUID |
-| Tag | String | true | 数据标签 |
-| Function | int | true | Modbus功能 |
-| SlaverId | int | true | 从设备ID (1-255) |
-| StartAddress | int | true | 起始地址 |
-| Quality | int | true | 偏移量(双字节) |
-
-### 请求示例JSON
-```json
-{
- "id": 1,
- "deviceUuid": "uuid",
- "tag": "tag",
- "function": "1",
- "slaverId": 1,
- "StartAddress": 2,
- "Quality": 1
-}
-```
-
-### 返回示例JSON
-```json
-{
- "code": 200,
- "msg": "Success",
- "data": {}
-}
-```
-
-## 3. 获取点位数据
-
-### 接口说明
-
-| URL | request | version | status |
-| :----------------------------------------------- | :------ | :------ | :----- |
-| http://192.168.2.58:8080/v1/devices/modbus/point | GET | 1.0 | true |
-
-### 请求参数说明
-
-注意:请求content-type是multipart/form-data
-
-| 请求参数 | 类型 | 必填 | 必填 |
-| :--------- | :--- | :--- | :------- |
-| DeviceUuid | file | true | 设备UUID |
-
-### 请求示例JSON
-```json
-{
- "deviceUuid": "uuid"
-}
-```
-
-### 返回示例JSON
-```json
-{
- "code": 200,
- "msg": "Success",
- "data": [{
- "id": 1,
- "deviceUuid": "uuid",
- "tag": "tag",
- "function": "1",
- "slaverId": 1,
- "startAddress": 2,
- "quality": 1,
- "createdAt": "2023-08-22 18:00:00"
- }]
-}
-```
\ No newline at end of file
diff --git a/device/generic_snmp_device.go b/device/generic_snmp_device.go
index a8957ee49..7955a2e4a 100644
--- a/device/generic_snmp_device.go
+++ b/device/generic_snmp_device.go
@@ -14,7 +14,7 @@ import (
)
type _SNMPCommonConfig struct {
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
}
@@ -82,7 +82,7 @@ func (sd *genericSnmpDevice) Start(cctx typex.CCTX) error {
//---------------------------------------------------------------------------------
// Start
//---------------------------------------------------------------------------------
- if !sd.mainConfig.CommonConfig.AutoRequest {
+ if !*sd.mainConfig.CommonConfig.AutoRequest {
sd.status = typex.DEV_UP
return nil
}
diff --git a/device/generic_snmp_device.md b/device/generic_snmp_device.md
index e4168df06..d060ccfb2 100644
--- a/device/generic_snmp_device.md
+++ b/device/generic_snmp_device.md
@@ -14,7 +14,7 @@ SNMP协议支持各种版本,其中最常用的是SNMPv1、SNMPv2c和SNMPv3。
```go
type _SNMPCommonConfig struct {
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
}
type GenericSnmpConfig struct {
diff --git a/device/generic_uart_device.go b/device/generic_uart_device.go
index 09ccf33df..b9f802b0d 100644
--- a/device/generic_uart_device.go
+++ b/device/generic_uart_device.go
@@ -17,8 +17,8 @@ import (
type _GUDCommonConfig struct {
Tag string `json:"tag" validate:"required"`
- Frequency int64 `json:"frequency"`
- AutoRequest bool `json:"autoRequest"`
+ Frequency *int64 `json:"frequency"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
// 协议报文结束符号
Separator string `json:"separator"`
}
@@ -90,7 +90,7 @@ func (uart *genericUartDevice) Start(cctx typex.CCTX) error {
return err
}
uart.driver = driver.NewRawUartDriver(uart.Ctx, uart.RuleEngine, uart.Details(), serialPort)
- if !uart.mainConfig.CommonConfig.AutoRequest {
+ if !*uart.mainConfig.CommonConfig.AutoRequest {
uart.status = typex.DEV_UP
return nil
}
diff --git a/device/generic_uart_device.md b/device/generic_uart_device.md
index f1e5b258c..3c78311f5 100644
--- a/device/generic_uart_device.md
+++ b/device/generic_uart_device.md
@@ -9,7 +9,7 @@ type GenericUartConfig struct {
// 结束符, 默认是 '\n';但是可以自己定义
Decollator string `json:"decollator" title:"协议分隔符"`
// Weather allow AutoRequest?
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
// Request Frequency, default 5 second
Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
Timeout int `json:"timeout" validate:"required" title:"连接超时"`
diff --git a/device/network_icmp_sender.go b/device/network_icmp_sender.go
index c9c3d74b3..fbd88f625 100644
--- a/device/network_icmp_sender.go
+++ b/device/network_icmp_sender.go
@@ -16,7 +16,7 @@ import (
type _IcmpSenderCommonConfig struct {
Timeout int `json:"timeout" validate:"required"`
// // Weather allow AutoRequest?
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
+ AutoRequest *bool `json:"autoRequest" validate:"required"`
// // Request Frequency, default 5 second
Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
}
@@ -55,7 +55,7 @@ func (sender *IcmpSender) Init(devId string, configMap map[string]interface{}) e
return errors.New("invalid ip:" + ip)
}
}
- if !sender.mainConfig.CommonConfig.AutoRequest {
+ if !*sender.mainConfig.CommonConfig.AutoRequest {
sender.status = typex.DEV_UP
return nil
}
diff --git a/device/rhinopi_ir_control_device_linux.go b/device/rhinopi_ir_control_device_linux.go
index 0f0ea2e96..615b54f8b 100644
--- a/device/rhinopi_ir_control_device_linux.go
+++ b/device/rhinopi_ir_control_device_linux.go
@@ -27,27 +27,27 @@ Found /sys/class/rc/rc0/ (/dev/input/event1) with:
bus: 25, vendor/product: 0001:0001, version: 0x0100
Repeat delay = 500 ms, repeat period = 125 ms
*/
+type __IrConfig struct {
+ InputHandle string `json:"inputHandle"` // 信号源
+}
type IR struct {
typex.XStatus
status typex.DeviceState
irFd int
// irFd syscall.Handle windows
RuleEngine typex.RuleX
+ mainConfig __IrConfig
}
func NewIRDevice(e typex.RuleX) typex.XDevice {
uart := new(IR)
uart.RuleEngine = e
+ uart.mainConfig = __IrConfig{
+ InputHandle: __IR_DEV,
+ }
return uart
}
-// 初始化
-func (ird *IR) Init(devId string, configMap map[string]interface{}) error {
- ird.PointId = devId
-
- return nil
-}
-
type timeval struct {
Second int32 `json:"second,omitempty"`
USecond int32 `json:"uSecond,omitempty"`
@@ -64,12 +64,19 @@ func (v irInputEvent) String() string {
return string(b)
}
+// 初始化
+func (ird *IR) Init(devId string, configMap map[string]interface{}) error {
+ ird.PointId = devId
+
+ return nil
+}
+
// 启动
func (ird *IR) Start(cctx typex.CCTX) error {
ird.Ctx = cctx.Ctx
ird.CancelCTX = cctx.CancelCTX
- fd, err := syscall.Open("/dev/input/event1", syscall.O_RDONLY, 0777)
+ fd, err := syscall.Open(ird.mainConfig.InputHandle, syscall.O_RDONLY, 0777)
if err != nil {
fmt.Printf("device open failed\r\n")
syscall.Close(fd)
@@ -89,17 +96,19 @@ func (ird *IR) Start(cctx typex.CCTX) error {
{
}
}
- n, e := syscall.Read(fd, buf)
+ n1, e := syscall.Read(fd, buf)
if e != nil {
glogger.GLogger.Error(e)
continue
}
- if n > 0 {
+ if n1 > 0 {
event := irInputEvent{}
- _, err := syscall.Read(fd, (*[24]byte)(unsafe.Pointer(&event))[:])
+ n2, err := syscall.Read(fd, (*[24]byte)(unsafe.Pointer(&event))[:])
if err != nil {
glogger.GLogger.Error(err)
- } else {
+ continue
+ }
+ if n2 > 0 {
ird.RuleEngine.WorkDevice(ird.Details(), event.String())
}
}
diff --git a/device/rhinopi_ir_control_device_windows.go b/device/rhinopi_ir_control_device_windows.go
index 99379198e..1e7a260c4 100644
--- a/device/rhinopi_ir_control_device_windows.go
+++ b/device/rhinopi_ir_control_device_windows.go
@@ -27,25 +27,26 @@ Found /sys/class/rc/rc0/ (/dev/input/event1) with:
bus: 25, vendor/product: 0001:0001, version: 0x0100
Repeat delay = 500 ms, repeat period = 125 ms
*/
+type __IrConfig struct {
+ InputHandle string `json:"inputHandle"` // 信号源
+}
type IR struct {
typex.XStatus
status typex.DeviceState
irFd syscall.Handle
RuleEngine typex.RuleX
+ mainConfig __IrConfig
}
func NewIRDevice(e typex.RuleX) typex.XDevice {
uart := new(IR)
uart.RuleEngine = e
+ uart.mainConfig = __IrConfig{
+ InputHandle: __IR_DEV,
+ }
return uart
}
-// 初始化
-func (ird *IR) Init(devId string, configMap map[string]interface{}) error {
- ird.PointId = devId
- return nil
-}
-
type timeval struct {
Second int32 `json:"second,omitempty"`
USecond int32 `json:"uSecond,omitempty"`
@@ -62,12 +63,18 @@ func (v irInputEvent) String() string {
return string(b)
}
+// 初始化
+func (ird *IR) Init(devId string, configMap map[string]interface{}) error {
+ ird.PointId = devId
+ return nil
+}
+
// 启动
func (ird *IR) Start(cctx typex.CCTX) error {
ird.Ctx = cctx.Ctx
ird.CancelCTX = cctx.CancelCTX
- fd, err := syscall.Open(__IR_DEV, syscall.O_RDONLY, 0777)
+ fd, err := syscall.Open(ird.mainConfig.InputHandle, syscall.O_RDONLY, 0777)
if err != nil {
fmt.Printf("device open failed\r\n")
syscall.Close(fd)
@@ -84,17 +91,19 @@ func (ird *IR) Start(cctx typex.CCTX) error {
{
}
}
- n, e := syscall.Read(fd, buf)
+ n1, e := syscall.Read(fd, buf)
if e != nil {
glogger.GLogger.Error(e)
continue
}
- if n > 0 {
+ if n1 > 0 {
event := irInputEvent{}
- _, err := syscall.Read(fd, (*[24]byte)(unsafe.Pointer(&event))[:])
+ n2, err := syscall.Read(fd, (*[24]byte)(unsafe.Pointer(&event))[:])
if err != nil {
glogger.GLogger.Error(err)
- } else {
+ continue
+ }
+ if n2 > 0 {
ird.RuleEngine.WorkDevice(ird.Details(), event.String())
}
}
diff --git a/device/rtu485_ther_device.go b/device/rtu485_ther_device.go
deleted file mode 100644
index 062d91fae..000000000
--- a/device/rtu485_ther_device.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package device
-
-import (
- "context"
- "errors"
- golog "log"
- "sync"
- "time"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/driver"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- "github.com/mitchellh/mapstructure"
- modbus "github.com/wwhai/gomodbus"
-)
-
-type rtu485_ther struct {
- typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- rtuHandler *modbus.RTUClientHandler
- mainConfig common.ModBusConfig
- rtuConfig common.RTUConfig
- locker sync.Locker
-}
-
-// Example: 0x02 0x92 0xFF 0x98
-/*
-*
-* 温湿度传感器
-*
- */
-func NewRtu485Ther(e typex.RuleX) typex.XDevice {
- ther := new(rtu485_ther)
- ther.RuleEngine = e
- ther.locker = &sync.Mutex{}
- ther.mainConfig = common.ModBusConfig{}
- ther.rtuConfig = common.RTUConfig{}
- return ther
-}
-
-// 初始化
-func (ther *rtu485_ther) Init(devId string, configMap map[string]interface{}) error {
- ther.PointId = devId
- if err := utils.BindSourceConfig(configMap, &ther.mainConfig); err != nil {
- return err
- }
- if errs := mapstructure.Decode(ther.mainConfig.Config, &ther.rtuConfig); errs != nil {
- glogger.GLogger.Error(errs)
- return errs
- }
- // 检查Tag有没有重复
- tags := []string{}
- for _, register := range ther.mainConfig.Registers {
- tags = append(tags, register.Tag)
- }
-
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
- }
- return nil
-}
-
-// 启动
-func (ther *rtu485_ther) Start(cctx typex.CCTX) error {
- ther.Ctx = cctx.Ctx
- ther.CancelCTX = cctx.CancelCTX
- //
- // 串口配置固定写法
- ther.rtuHandler = modbus.NewRTUClientHandler(ther.rtuConfig.Uart)
- ther.rtuHandler.BaudRate = ther.rtuConfig.BaudRate
- ther.rtuHandler.DataBits = ther.rtuConfig.DataBits
- ther.rtuHandler.Parity = ther.rtuConfig.Parity
- ther.rtuHandler.StopBits = ther.rtuConfig.StopBits
- ther.rtuHandler.Timeout = time.Duration(ther.mainConfig.Timeout) * time.Second
- if core.GlobalConfig.AppDebugMode {
- ther.rtuHandler.Logger = golog.New(glogger.GLogger.Writer(), "485-TEMP-HUMI-DEVICE: ", golog.LstdFlags)
- }
- if err := ther.rtuHandler.Connect(); err != nil {
- return err
- }
- client := modbus.NewClient(ther.rtuHandler)
- ther.driver = driver.NewRtu485THerDriver(ther.Details(),
- ther.RuleEngine, ther.mainConfig.Registers, ther.rtuHandler, client)
- //---------------------------------------------------------------------------------
- // Start
- //---------------------------------------------------------------------------------
- if !ther.mainConfig.AutoRequest {
- ther.status = typex.DEV_UP
- return nil
- }
- go func(ctx context.Context, Driver typex.XExternalDriver) {
- ticker := time.NewTicker(time.Duration(ther.mainConfig.Frequency) * time.Millisecond)
- buffer := make([]byte, common.T_64KB)
- ther.driver.Read([]byte{}, buffer) //清理缓存
- for {
- <-ticker.C
- select {
- case <-ctx.Done():
- {
- ticker.Stop()
- if ther.rtuHandler != nil {
- ther.rtuHandler.Close()
- }
- return
- }
- default:
- {
- }
- }
- ther.locker.Lock()
- n, err := Driver.Read([]byte{}, buffer)
- ther.locker.Unlock()
- if err != nil {
- glogger.GLogger.Error(err)
- } else {
- ther.RuleEngine.WorkDevice(ther.Details(), string(buffer[:n]))
- }
- }
-
- }(ther.Ctx, ther.driver)
- ther.status = typex.DEV_UP
- return nil
-}
-
-// 从设备里面读数据出来
-func (ther *rtu485_ther) OnRead(cmd []byte, data []byte) (int, error) {
-
- n, err := ther.driver.Read(cmd, data)
- if err != nil {
- glogger.GLogger.Error(err)
- ther.status = typex.DEV_DOWN
- }
- return n, err
-}
-
-// 把数据写入设备
-func (ther *rtu485_ther) OnWrite(cmd []byte, _ []byte) (int, error) {
- return 0, nil
-}
-
-// 设备当前状态
-func (ther *rtu485_ther) Status() typex.DeviceState {
- return ther.status
-}
-
-// 停止设备
-func (ther *rtu485_ther) Stop() {
- ther.status = typex.DEV_DOWN
- ther.CancelCTX()
-
-}
-
-// 设备属性,是一系列属性描述
-func (ther *rtu485_ther) Property() []typex.DeviceProperty {
- return []typex.DeviceProperty{}
-}
-
-// 真实设备
-func (ther *rtu485_ther) Details() *typex.Device {
- return ther.RuleEngine.GetDevice(ther.PointId)
-}
-
-// 状态
-func (ther *rtu485_ther) SetState(status typex.DeviceState) {
- ther.status = status
-
-}
-
-// 驱动
-func (ther *rtu485_ther) Driver() typex.XExternalDriver {
- return ther.driver
-}
-
-func (ther *rtu485_ther) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
- return typex.DCAResult{}
-}
-func (ther *rtu485_ther) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
- return []byte{}, nil
-}
diff --git a/device/rtu485_ther_device.md b/device/rtu485_ther_device.md
deleted file mode 100644
index 7ccfff5a8..000000000
--- a/device/rtu485_ther_device.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# TSS200
-## 简介
-
-TC-S200 系列空气质量监测仪内置 PM2.5、TVOC、甲醛、CO2,温湿度等高精度传感器套件,可通过吸顶式或壁挂安装,RS-485 接口通过 Modbus-RTU 协议进行数据输出,通过网关组网,或配合联动模块可以用于新风联动控制。
-
-## 配置
-
-```json
-{
- "name": "RTU485_THER",
- "type": "RTU485_THER",
- "config": {
- "mode": "UART",
- "timeout": 10,
- "frequency": 5,
- "autoRequest": true,
- "config": {
- "uart": "/dev/ttyUSB0",
- "dataBits": 8,
- "parity": "N",
- "stopBits": 1,
- "baudRate": 9600,
- "ip": "127.0.0.1",
- "port": 502
- },
- "registers": [
- {
- "tag": "node1",
- "function": 3,
- "slaverId": 1,
- "address": 0,
- "quantity": 2
- }
- ]
- },
- "description": "RTU485_THER"
-}
-```
-
-## 数据
-
-### 读
-#### 1. CMD
-无指令。
-#### 1. Args
-无参数。
-
-#### 3. 数据样例
-```json
-{
- float32 `json:"temp"` //系数: 0.01
- float32 `json:"hum"` //系数: 0.01
- uint16 `json:"pm1"`
- uint16 `json:"pm25"`
- uint16 `json:"pm10"`
- uint16 `json:"co2"`
- float32 `json:"tvoc"` //系数: 0.001
- float32 `json:"choh"` //系数: 0.001
- float32 `json:"eco2"` //系数: 0.001
-}
-```
-
-### 写
-#### 1. CMD
-无指令。
-
-#### 2. Args
-无参数。
-
-#### 3. 数据样例
-无写数据。
-
-## 案例
-
-```lua
--- Success
-function Success()
- print("Success")
-end
--- Failed
-function Failed(error)
- print("Error:", error)
-end
-
--- Actions
-Actions = {
- function(args)
- local _, err = rulexlib:ReadDevice(device, 0, "all")
- if (err ~= nil) then
- return false, data
- end
- return true, args
- end
-}
-
-```
-
-## 说明
-
-- 暂无
-
-## 社区
-
-### 维护者
-
-- wwhai
diff --git a/device/s1200plc_device.go b/device/s1200plc_device.go
index c20a84c71..d6924a523 100644
--- a/device/s1200plc_device.go
+++ b/device/s1200plc_device.go
@@ -2,29 +2,59 @@ package device
import (
"context"
+ "encoding/hex"
"encoding/json"
"errors"
- "fmt"
"sync"
"time"
+ siemenscache "github.com/hootrhino/rulex/component/intercache/siemens"
+
"github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/driver"
+ "github.com/hootrhino/rulex/component/interdb"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
"github.com/robinson/gos7"
)
-type s1200plc struct {
+// 点位表
+type SiemensDataPoint struct {
+ UUID string `json:"uuid"` // 当UUID为空时新建
+ DeviceUuid string `json:"device_uuid"`
+ Tag string `json:"tag,omitempty"`
+ Type string `json:"type,omitempty"`
+ Frequency *int64 `json:"frequency,omitempty"`
+ Address *int `json:"address,omitempty"`
+ Start *int `json:"start"`
+ Size *int `json:"size"`
+ Value string `json:"value"`
+}
+
+type S1200CommonConfig struct {
+ Host string `json:"host" validate:"required"` // 127.0.0.1:502
+ Model string `json:"model" validate:"required"` // s7-200 s7-1500
+ // https://cloudvpn.beijerelectronics.com/hc/en-us/articles/4406049761169-Siemens-S7
+ Rack *int `json:"rack" validate:"required"` // 0
+ Slot *int `json:"slot" validate:"required"` // 1
+ Timeout *int `json:"timeout" validate:"required"` // 5s
+ IdleTimeout *int `json:"idleTimeout" validate:"required"` // 5s
+ AutoRequest *bool `json:"autoRequest" validate:"required"` // false
+}
+type S1200Config struct {
+ CommonConfig S1200CommonConfig `json:"commonConfig" validate:"required"` // 通用配置
+}
+
+// https://www.ad.siemens.com.cn/productportal/prods/s7-1200_plc_easy_plus/07-Program/02-basic/01-Data_Type/01-basic.html
+type SIEMENS_PLC struct {
typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- mainConfig common.S1200Config
- client gos7.Client
- block []common.S1200Block // PLC 的DB块
- lock sync.Mutex
+ status typex.DeviceState
+ RuleEngine typex.RuleX
+ mainConfig S1200Config
+ client gos7.Client
+ handler *gos7.TCPClientHandler
+ lock sync.Mutex
+ SiemensDataPoints map[string]*SiemensDataPoint
}
/*
@@ -32,92 +62,120 @@ type s1200plc struct {
* 西门子 S1200 系列 PLC
*
*/
-func NewS1200plc(e typex.RuleX) typex.XDevice {
- s1200 := new(s1200plc)
+func NewSIEMENS_PLC(e typex.RuleX) typex.XDevice {
+ s1200 := new(SIEMENS_PLC)
s1200.RuleEngine = e
s1200.lock = sync.Mutex{}
- s1200.mainConfig = common.S1200Config{}
+ Rack := 0
+ Slot := 1
+ Timeout := 1000
+ IdleTimeout := 3000
+ AutoRequest := false
+ s1200.mainConfig = S1200Config{
+ CommonConfig: S1200CommonConfig{
+ Rack: &Rack,
+ Slot: &Slot,
+ Timeout: &Timeout,
+ IdleTimeout: &IdleTimeout,
+ AutoRequest: &AutoRequest,
+ },
+ }
+ s1200.SiemensDataPoints = map[string]*SiemensDataPoint{}
return s1200
}
// 初始化
-func (s1200 *s1200plc) Init(devId string, configMap map[string]interface{}) error {
+func (s1200 *SIEMENS_PLC) Init(devId string, configMap map[string]interface{}) error {
s1200.PointId = devId
+ siemenscache.RegisterSlot(s1200.PointId)
if err := utils.BindSourceConfig(configMap, &s1200.mainConfig); err != nil {
glogger.GLogger.Error(err)
return err
}
- // 检查Tag有没有重复
- tags := []string{}
- for _, block := range s1200.mainConfig.Blocks {
- tags = append(tags, block.Tag)
+ // 合并数据库里面的点位表
+ // TODO 这里需要优化一下,而不是直接查表这种形式,应该从物模型组件来加载
+ // DataSchema = schema.load(uuid)
+ // DataSchema.update(k, v)
+ var list []SiemensDataPoint
+ errDb := interdb.DB().Table("m_siemens_data_points").
+ Where("device_uuid=?", devId).Find(&list).Error
+ if errDb != nil {
+ return errDb
}
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
+ for _, v := range list {
+ // 频率不能太快
+ if *v.Frequency < 50 {
+ return errors.New("'frequency' must grate than 50 millisecond")
+ }
+ s1200.SiemensDataPoints[v.UUID] = &v
+ siemenscache.SetValue(s1200.PointId, v.UUID, siemenscache.SiemensPoint{
+ UUID: v.UUID,
+ Status: 0,
+ LastFetchTime: 0,
+ Value: "0",
+ })
}
return nil
}
// 启动
-func (s1200 *s1200plc) Start(cctx typex.CCTX) error {
+func (s1200 *SIEMENS_PLC) Start(cctx typex.CCTX) error {
s1200.Ctx = cctx.Ctx
s1200.CancelCTX = cctx.CancelCTX
//
- handler := gos7.NewTCPClientHandler(
- // 127.0.0.1:8080
- fmt.Sprintf("%s:%d", s1200.mainConfig.Host, *s1200.mainConfig.Port),
- *s1200.mainConfig.Rack,
- *s1200.mainConfig.Slot)
- handler.Timeout = 5 * time.Second
- if err := handler.Connect(); err != nil {
+ s1200.handler = gos7.NewTCPClientHandler(
+ s1200.mainConfig.CommonConfig.Host, // 127.0.0.1:1500
+ *s1200.mainConfig.CommonConfig.Rack, // 0
+ *s1200.mainConfig.CommonConfig.Slot) // 1
+ s1200.handler.Timeout = time.Duration(
+ *s1200.mainConfig.CommonConfig.Timeout) * time.Millisecond
+ s1200.handler.IdleTimeout = time.Duration(
+ *s1200.mainConfig.CommonConfig.IdleTimeout) * time.Millisecond
+ if err := s1200.handler.Connect(); err != nil {
return err
+ } else {
+ s1200.status = typex.DEV_UP
}
- handler.Timeout = time.Duration(*s1200.mainConfig.Timeout) * time.Second
- handler.IdleTimeout = time.Duration(*s1200.mainConfig.IdleTimeout) * time.Second
- s1200.client = gos7.NewClient(handler)
- s1200.driver = driver.NewS1200Driver(s1200.Details(), s1200.RuleEngine, s1200.client, s1200.block)
- if !s1200.mainConfig.AutoRequest {
+
+ s1200.client = gos7.NewClient(s1200.handler)
+ if !*s1200.mainConfig.CommonConfig.AutoRequest {
s1200.status = typex.DEV_UP
return nil
}
go func(ctx context.Context) {
- ticker := time.NewTicker(time.Duration(s1200.mainConfig.Frequency) * time.Millisecond)
// 数据缓冲区,最大4KB
dataBuffer := make([]byte, common.T_4KB)
- s1200.driver.Read([]byte{}, dataBuffer) //清理缓存
for {
select {
case <-ctx.Done():
{
- ticker.Stop()
- if s1200.driver != nil {
- s1200.driver.Stop()
- }
return
}
default:
{
- // Do nothing
}
}
- if s1200.driver == nil {
- return
- }
s1200.lock.Lock()
- n, err := s1200.driver.Read([]byte{}, dataBuffer)
+ // CMD 参数无用
+ n, err := s1200.Read([]byte(""), dataBuffer)
s1200.lock.Unlock()
if err != nil {
glogger.GLogger.Error(err)
+ s1200.status = typex.DEV_DOWN
return
}
+ // [] {} ""
+ if n < 3 {
+ continue
+ }
ok, err := s1200.RuleEngine.WorkDevice(
s1200.RuleEngine.GetDevice(s1200.PointId),
string(dataBuffer[:n]),
)
+ // glogger.GLogger.Debug(string(dataBuffer[:n]))
if !ok {
glogger.GLogger.Error(err)
}
- <-ticker.C
}
}(cctx.Ctx)
@@ -125,71 +183,181 @@ func (s1200 *s1200plc) Start(cctx typex.CCTX) error {
}
// 从设备里面读数据出来
-func (s1200 *s1200plc) OnRead(cmd []byte, data []byte) (int, error) {
- return s1200.driver.Read(cmd, data)
+func (s1200 *SIEMENS_PLC) OnRead(cmd []byte, data []byte) (int, error) {
+ return s1200.Read(cmd, data)
}
// 把数据写入设备
//
// db.Address:int, db.Start:int, db.Size:int, rData[]
-// [
-//
-// {
-// "tag":"V",
-// "address":1,
-// "start":1,
-// "size":1,
-// "value":"AAECAwQ="
-// }
-//
-// ]
-func (s1200 *s1200plc) OnWrite(cmd []byte, data []byte) (int, error) {
- blocks := []common.S1200BlockValue{}
+
+func (s1200 *SIEMENS_PLC) OnWrite(cmd []byte, data []byte) (int, error) {
+ blocks := []SiemensDataPoint{}
if err := json.Unmarshal(data, &blocks); err != nil {
return 0, err
}
- return s1200.driver.Write(cmd, data)
+ return s1200.Write(cmd, data)
}
// 设备当前状态
-func (s1200 *s1200plc) Status() typex.DeviceState {
- if s1200.driver.State() == typex.DRIVER_UP {
- return typex.DEV_UP
+func (s1200 *SIEMENS_PLC) Status() typex.DeviceState {
+ if s1200.client == nil {
+ return typex.DEV_DOWN
}
- return typex.DEV_DOWN
+ return s1200.status
}
// 停止设备
-func (s1200 *s1200plc) Stop() {
+func (s1200 *SIEMENS_PLC) Stop() {
s1200.status = typex.DEV_DOWN
- s1200.CancelCTX()
-
+ if s1200.CancelCTX != nil {
+ s1200.CancelCTX()
+ }
+ if s1200.handler != nil {
+ s1200.handler.Close()
+ }
+ siemenscache.UnRegisterSlot(s1200.PointId)
}
// 设备属性,是一系列属性描述
-func (s1200 *s1200plc) Property() []typex.DeviceProperty {
+func (s1200 *SIEMENS_PLC) Property() []typex.DeviceProperty {
return []typex.DeviceProperty{}
}
// 真实设备
-func (s1200 *s1200plc) Details() *typex.Device {
+func (s1200 *SIEMENS_PLC) Details() *typex.Device {
return s1200.RuleEngine.GetDevice(s1200.PointId)
}
// 状态
-func (s1200 *s1200plc) SetState(status typex.DeviceState) {
+func (s1200 *SIEMENS_PLC) SetState(status typex.DeviceState) {
s1200.status = status
}
// 驱动
-func (s1200 *s1200plc) Driver() typex.XExternalDriver {
- return s1200.driver
+func (s1200 *SIEMENS_PLC) Driver() typex.XExternalDriver {
+ return nil
}
-func (s1200 *s1200plc) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
+func (s1200 *SIEMENS_PLC) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
return typex.DCAResult{}
}
-func (s1200 *s1200plc) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
+func (s1200 *SIEMENS_PLC) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
return []byte{}, nil
}
+func (s1200 *SIEMENS_PLC) Write(cmd []byte, data []byte) (int, error) {
+ return 0, nil
+}
+
+// 字节格式:[dbNumber1, start1, size1, dbNumber2, start2, size2]
+// 读: db --> dbNumber, start, size, buffer[]
+var rData = [common.T_2KB]byte{} // 一次最大接受2KB数据
+
+func (s1200 *SIEMENS_PLC) Read(cmd []byte, data []byte) (int, error) {
+ values := []SiemensDataPoint{}
+ for uuid, db := range s1200.SiemensDataPoints {
+ //DB 4字节
+ if db.Type == "DB" {
+ // 00.00.00.01 | 00.00.00.02 | 00.00.00.03 | 00.00.00.04
+ if err := s1200.client.AGReadDB(*db.Address, *db.Start, *db.Size, rData[:]); err != nil {
+ glogger.GLogger.Error(err)
+ return 0, err
+ }
+ count := db.Size
+ if *db.Size*2 > 2000 {
+ *count = 2000
+ }
+ Value := hex.EncodeToString(rData[:*count])
+ values = append(values, SiemensDataPoint{
+ DeviceUuid: db.DeviceUuid,
+ Tag: db.Tag,
+ Address: db.Address,
+ Type: db.Type,
+ Start: db.Start,
+ Size: db.Size,
+ Value: Value,
+ })
+ siemenscache.SetValue(s1200.PointId, uuid, siemenscache.SiemensPoint{
+ UUID: uuid,
+ Status: 0,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ Value: Value,
+ })
+ }
+ //
+ if db.Type == "MB" {
+ // 00.00.00.01 | 00.00.00.02 | 00.00.00.03 | 00.00.00.04
+ if err := s1200.client.AGReadMB(*db.Start, *db.Size, rData[:]); err != nil {
+ glogger.GLogger.Error(err)
+ return 0, err
+ }
+ count := db.Size
+ if *db.Size*2 > 2000 {
+ *count = 2000
+ }
+ Value := hex.EncodeToString(rData[:*count])
+ values = append(values, SiemensDataPoint{
+ Tag: db.Tag,
+ Type: db.Type,
+ Address: db.Address,
+ Start: db.Start,
+ Size: db.Size,
+ Value: Value,
+ })
+ siemenscache.SetValue(s1200.PointId, uuid, siemenscache.SiemensPoint{
+ UUID: uuid,
+ Status: 0,
+ LastFetchTime: uint64(time.Now().UnixMilli()),
+ Value: Value,
+ })
+ }
+ if *db.Frequency < 100 {
+ *db.Frequency = 100 // 不能太快
+ }
+ time.Sleep(time.Duration(*db.Frequency) * time.Millisecond)
+ }
+ bytes, _ := json.Marshal(values)
+ copy(data, bytes)
+ return len(bytes), nil
+}
+
+/*
+*
+- 符号地址(Symbolic Addressing):
+使用符号名称来表示变量或输入/输出地址。这种方式更加直观和易于理解,适用于高级编程语言和工程师使用。例如,可以使用变量名"MotorSpeed"或输入名"I1"来表示对应的地址。
+- 基于字节的地址(Byte-based Addressing):
+使用字节地址和位地址的组合来表示变量或输入/输出地址。字节地址表示内存中的字节偏移,而位地址表示字节中的位偏移。例如,使用地址"DB1.DBX10.3"表示数据块1中偏移为10的字节的第3位。
+- 基于字的地址(Word-based Addressing):
+类似于基于字节的地址,但是将地址表示为字(16位)的偏移。例如,使用地址"DB1.DBD20"表示数据块1中偏移为20的字。
+- 基于地址区域的地址(Address Area-based Addressing):
+将地址按照不同的区域进行划分,如输入区域(I),输出区域(Q),数据块区域(DB)等。每个区域都有特定的地址范围。例如,使用地址"I10.3"表示输入区域的第10个输入的第3位。
+*
+*/
+
+// AddressInfo 包含解析后的地址信息
+type AddressInfo struct {
+ DataBlockNumber int // 数据块号
+ DataType string // 数据类型
+ ElementNumber int // 元素号
+}
+
+// 解析DB
+func ParseDB_D(s string) string {
+ return ""
+}
+
+// 解析DBX格式
+func ParseDB_X(s string) string {
+ return ""
+}
+
+// 解析I格式
+func ParseADDR_I(s string) string {
+ return ""
+}
+
+// 解析Q格式
+func ParseADDR_Q(s string) string {
+ return ""
+}
diff --git a/device/s1200plc_device.md b/device/s1200plc_device.md
index 350c891e5..4e19e1467 100644
--- a/device/s1200plc_device.md
+++ b/device/s1200plc_device.md
@@ -7,4 +7,77 @@ PLC S1200系列设备通常用于控制和监视各种工业过程,例如生
PLC S1200系列设备提供了丰富的输入输出接口、通信接口和编程功能,以满足各种自动化控制需求。通过编程,用户可以定义逻辑控制规则、配置输入输出映射、实现数据处理和通信功能等。
-需要注意的是,PLC S1200是西门子(Siemens)公司的商标产品,更详细的信息和技术规格可以参考西门子官方文档或与其联系。
\ No newline at end of file
+需要注意的是,PLC S1200是西门子(Siemens)公司的商标产品,更详细的信息和技术规格可以参考西门子官方文档或与其联系。
+
+## 参数
+```json
+{
+ "name": "SIEMENS_PLC",
+ "type": "SIEMENS_PLC",
+ "gid": "DROOT",
+ "config": {
+ "host": "127.0.0.1:1500",
+ "rack": 0,
+ "slot": 1,
+ "model": "SIEMENS_PLC",
+ "timeout": 1000,
+ "autoRequest": true,
+ "idleTimeout": 1000,
+ "frequency": 1000,
+ "blocks": [
+ {
+ "tag": "Value",
+ "frequency": 1000,
+ "type": "DB",
+ "address": 1,
+ "start": 100,
+ "size": 16
+ }
+ ]
+ },
+ "description": "SIEMENS_PLC"
+}
+```
+## 脚本示例
+```lua
+
+-- Actions
+-- 采集到的数据:
+-- {
+-- "tag":"Value",
+-- "type":"DB",
+-- "frequency":0,
+-- "address":1,
+-- "start":100,
+-- "size":16,
+-- "value":"00000001000000020000000300000004"
+-- }
+Actions =
+{
+ function(args)
+ local dataT, err = json:J2T(args)
+ if (err ~= nil) then
+ stdlib:Debug('parse json error:' .. err)
+ return true, args
+ end
+ for key, value in pairs(dataT) do
+ --data: 00000001000000020000000300000004
+ local MatchHexS = hex:MatchUInt("a:[0,3];b:[4,7];c:[8,11];d:[12,15]", value['value'])
+ local ts = time:Time()
+ local Json = json:T2J(
+ {
+ tag = key,
+ ts = ts,
+ a = MatchHexS['a'],
+ b = MatchHexS['b'],
+ c = MatchHexS['c'],
+ d = MatchHexS['d'],
+ }
+ )
+ stdlib:Debug(Json)
+ end
+ return true, args
+ end
+}
+
+```
\ No newline at end of file
diff --git a/device/tss200_device.go b/device/tss200_device.go
deleted file mode 100644
index 76694e120..000000000
--- a/device/tss200_device.go
+++ /dev/null
@@ -1,186 +0,0 @@
-package device
-
-import (
- "context"
- "errors"
- golog "log"
- "sync"
- "time"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/driver"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- "github.com/mitchellh/mapstructure"
- modbus "github.com/wwhai/gomodbus"
-)
-
-type tss200V2 struct {
- typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- rtuHandler *modbus.RTUClientHandler
- mainConfig common.ModBusConfig
- rtuConfig common.RTUConfig
- locker sync.Locker
-}
-
-/*
-*
-* TSS200环境传感器
-*
- */
-func NewTS200Sensor(e typex.RuleX) typex.XDevice {
- tss := new(tss200V2)
- tss.locker = &sync.Mutex{}
- tss.mainConfig = common.ModBusConfig{}
- tss.rtuConfig = common.RTUConfig{}
- tss.RuleEngine = e
- return tss
-}
-
-// 初始化
-func (tss *tss200V2) Init(devId string, configMap map[string]interface{}) error {
- tss.PointId = devId
- if err := utils.BindSourceConfig(configMap, &tss.mainConfig); err != nil {
- glogger.GLogger.Error(err)
- return err
- }
-
- if errs := mapstructure.Decode(tss.mainConfig.Config, &tss.rtuConfig); errs != nil {
- glogger.GLogger.Error(errs)
- return errs
- }
- // 检查Tag有没有重复
- tags := []string{}
- for _, register := range tss.mainConfig.Registers {
- tags = append(tags, register.Tag)
- }
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
- }
- return nil
-}
-
-// 启动
-func (tss *tss200V2) Start(cctx typex.CCTX) error {
- tss.Ctx = cctx.Ctx
- tss.CancelCTX = cctx.CancelCTX
-
- // 串口配置固定写法
- // 下面的参数是传感器固定写法
- tss.rtuHandler = modbus.NewRTUClientHandler(tss.rtuConfig.Uart)
- tss.rtuHandler.BaudRate = tss.rtuConfig.BaudRate
- tss.rtuHandler.DataBits = tss.rtuConfig.DataBits
- tss.rtuHandler.Parity = tss.rtuConfig.Parity
- tss.rtuHandler.StopBits = tss.rtuConfig.StopBits
- tss.rtuHandler.Timeout = time.Duration(tss.mainConfig.Timeout) * time.Second
- if core.GlobalConfig.AppDebugMode {
- tss.rtuHandler.Logger = golog.New(glogger.GLogger.Writer(), "TSS200-DEVICE: ", golog.LstdFlags)
- }
-
- if err := tss.rtuHandler.Connect(); err != nil {
- return err
- }
- //---------------------------------------------------------------------------------
- // Start
- //---------------------------------------------------------------------------------
- client := modbus.NewClient(tss.rtuHandler)
- tss.driver = driver.NewTSS200Driver(tss.Details(),
- tss.RuleEngine, tss.mainConfig.Registers, tss.rtuHandler, client)
- if !tss.mainConfig.AutoRequest {
- tss.status = typex.DEV_UP
- return nil
- }
- go func(ctx context.Context, Driver typex.XExternalDriver) {
- ticker := time.NewTicker(time.Duration(tss.mainConfig.Frequency) * time.Millisecond)
- defer ticker.Stop()
- buffer := make([]byte, common.T_64KB)
- tss.driver.Read([]byte{}, buffer) //清理缓存
- for {
- <-ticker.C
- select {
- case <-ctx.Done():
- {
- ticker.Stop()
- if tss.rtuHandler != nil {
- tss.rtuHandler.Close()
- }
- return
- }
- default:
- {
- }
- }
- tss.locker.Lock()
- n, err := Driver.Read([]byte{}, buffer)
- tss.locker.Unlock()
- if err != nil {
- glogger.GLogger.Error(err)
- } else {
- tss.RuleEngine.WorkDevice(tss.Details(), string(buffer[:n]))
- }
- }
-
- }(tss.Ctx, tss.driver)
- tss.status = typex.DEV_UP
- return nil
-}
-
-// 从设备里面读数据出来
-func (tss *tss200V2) OnRead(cmd []byte, data []byte) (int, error) {
-
- n, err := tss.driver.Read(cmd, data)
- if err != nil {
- glogger.GLogger.Error(err)
- tss.status = typex.DEV_DOWN
- }
- return n, err
-}
-
-// 把数据写入设备
-func (tss *tss200V2) OnWrite(cmd []byte, b []byte) (int, error) {
- return tss.driver.Write(cmd, b)
-}
-
-// 设备当前状态
-func (tss *tss200V2) Status() typex.DeviceState {
- return typex.DEV_UP
-}
-
-// 停止设备
-func (tss *tss200V2) Stop() {
- tss.status = typex.DEV_DOWN
- tss.CancelCTX()
-}
-
-// 设备属性,是一系列属性描述
-func (tss *tss200V2) Property() []typex.DeviceProperty {
- return []typex.DeviceProperty{}
-}
-
-// 真实设备
-func (tss *tss200V2) Details() *typex.Device {
- return tss.RuleEngine.GetDevice(tss.PointId)
-}
-
-// 状态
-func (tss *tss200V2) SetState(status typex.DeviceState) {
- tss.status = status
-
-}
-
-// 驱动
-func (tss *tss200V2) Driver() typex.XExternalDriver {
- return tss.driver
-}
-
-func (tss *tss200V2) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
- return typex.DCAResult{}
-}
-func (tss *tss200V2) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
- return []byte{}, nil
-}
diff --git a/device/type_loader.go b/device/type_loader.go
index cdc5ed563..622d9da0d 100644
--- a/device/type_loader.go
+++ b/device/type_loader.go
@@ -14,14 +14,10 @@ var DM typex.DeviceRegistry
*/
func LoadDt() {
DM = core.NewDeviceTypeManager()
- DM.Register(typex.TSS200V02, &typex.XConfig{})
- DM.Register(typex.RTU485_THER, &typex.XConfig{})
- DM.Register(typex.YK08_RELAY, &typex.XConfig{})
- DM.Register(typex.S1200PLC, &typex.XConfig{})
+ DM.Register(typex.SIEMENS_PLC, &typex.XConfig{})
DM.Register(typex.GENERIC_MODBUS, &typex.XConfig{})
DM.Register(typex.GENERIC_MODBUS_POINT_EXCEL, &typex.XConfig{})
DM.Register(typex.GENERIC_UART, &typex.XConfig{})
DM.Register(typex.GENERIC_SNMP, &typex.XConfig{})
- DM.Register(typex.USER_G776, &typex.XConfig{})
DM.Register(typex.ICMP_SENDER, &typex.XConfig{})
}
diff --git a/device/usr_g776_4gdtu_device.go b/device/usr_g776_4gdtu_device.go
deleted file mode 100644
index 70b17f49a..000000000
--- a/device/usr_g776_4gdtu_device.go
+++ /dev/null
@@ -1,177 +0,0 @@
-package device
-
-import (
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- serial "github.com/wwhai/goserial"
-)
-
-/*
-*
-* 有人G776串口4G模块
-*
- */
-type _G776CommonConfig struct {
- Tag string `json:"tag" validate:"required" title:"数据Tag" info:"给数据打标签"`
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
-}
-
-type _G776Config struct {
- CommonConfig _G776CommonConfig `json:"commonConfig" validate:"required"`
- UartConfig common.CommonUartConfig `json:"uartConfig" validate:"required"`
-}
-
-// 这是有人G776型号的4G DTU模块,主要用来TCP远程透传数据, 实际上就是个很普通的串口读写程序
-// 详细文档: https://www.usr.cn/Download/806.html
-type UsrG776DTU struct {
- typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- mainConfig _G776Config
- locker sync.Locker
- serialPort serial.Port
- errCount int
-}
-
-/*
-*
-* 有人4G DTU
-*
- */
-func NewUsrG776DTU(e typex.RuleX) typex.XDevice {
- uart := new(UsrG776DTU)
- uart.locker = &sync.Mutex{}
- uart.mainConfig = _G776Config{}
- uart.RuleEngine = e
- uart.serialPort = nil
- return uart
-}
-
-// 初始化
-func (uart *UsrG776DTU) Init(devId string, configMap map[string]interface{}) error {
- uart.PointId = devId
- if err := utils.BindSourceConfig(configMap, &uart.mainConfig); err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- if !utils.SContains([]string{"N", "E", "O"}, uart.mainConfig.UartConfig.Parity) {
- return errors.New("parity value only one of 'N','O','E'")
- }
- return nil
-}
-
-// 启动
-func (uart *UsrG776DTU) Start(cctx typex.CCTX) error {
- uart.Ctx = cctx.Ctx
- uart.CancelCTX = cctx.CancelCTX
- config := serial.Config{
- Address: uart.mainConfig.UartConfig.Uart,
- BaudRate: uart.mainConfig.UartConfig.BaudRate,
- DataBits: uart.mainConfig.UartConfig.DataBits,
- Parity: uart.mainConfig.UartConfig.Parity,
- StopBits: uart.mainConfig.UartConfig.StopBits,
- Timeout: time.Duration(uart.mainConfig.UartConfig.Timeout) * time.Millisecond,
- }
- serialPort, err := serial.Open(&config)
- if err != nil {
- glogger.GLogger.Error("Serial.Open failed:", err)
- return err
- }
- uart.errCount = 0
- uart.serialPort = serialPort
- uart.status = typex.DEV_UP
- return nil
-}
-
-/*
-*
-* 不支持读, 仅仅是个数据透传DTU
-*
- */
-func (uart *UsrG776DTU) OnRead(cmd []byte, data []byte) (int, error) {
- return 0, fmt.Errorf("UsrG776DTU not support read data")
-}
-
-/*
-*
-* 有人G776-DTU写入串口的数据会被不加修改的透传到上层
-* data:ToUsrG776DTU("uuid", "DATA", "data-....")
-*
- */
-func (uart *UsrG776DTU) OnWrite(cmd []byte, b []byte) (int, error) {
- if string(cmd) != ("DATA") {
- return 0, nil
- }
- n, err := uart.serialPort.Write(b)
- if err != nil {
- uart.errCount++
- glogger.GLogger.Error(err)
- if uart.errCount > 5 {
- return n, err
- }
- }
- return n, nil
-}
-
-// 设备当前状态
-func (uart *UsrG776DTU) Status() typex.DeviceState {
- if uart.serialPort != nil {
- // https://www.usr.cn/Download/806.html
- // 发送: AT\r
- // 接收: \r\nOK\r\n\r\n
- _, err := uart.serialPort.Write([]byte("AT\r"))
- if err != nil {
- uart.errCount++
- glogger.GLogger.Error(err)
- if uart.errCount > 5 {
- return typex.DEV_DOWN
- }
- }
- }
- return typex.DEV_UP
-}
-
-// 停止设备
-func (uart *UsrG776DTU) Stop() {
- uart.CancelCTX()
- uart.status = typex.DEV_DOWN
- if uart.serialPort != nil {
- uart.serialPort.Close()
- }
-}
-
-// 设备属性,是一系列属性描述
-func (uart *UsrG776DTU) Property() []typex.DeviceProperty {
- return []typex.DeviceProperty{}
-}
-
-// 真实设备
-func (uart *UsrG776DTU) Details() *typex.Device {
- return uart.RuleEngine.GetDevice(uart.PointId)
-}
-
-// 状态
-func (uart *UsrG776DTU) SetState(status typex.DeviceState) {
- uart.status = status
-
-}
-
-// 驱动
-func (uart *UsrG776DTU) Driver() typex.XExternalDriver {
- return nil
-}
-
-func (uart *UsrG776DTU) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
- return typex.DCAResult{}
-}
-func (uart *UsrG776DTU) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
- return []byte{}, nil
-}
diff --git a/device/usr_g776_4gdtu_device.md b/device/usr_g776_4gdtu_device.md
deleted file mode 100644
index 5b4cad925..000000000
--- a/device/usr_g776_4gdtu_device.md
+++ /dev/null
@@ -1,61 +0,0 @@
-## 简介
-USR-G776 是有人第二代 4G DTU 产品,支持移动,联通,电信 4G 和移动,联通3G和2G 网络制式,以“透
-传”作为功能核心,高度易用性,用户可方便快速的集成于自己的系统中。该 DTU 软件功能完善,覆盖绝大多
-数常规应用场景,用户只需通过简单的设置,即可实现串口到网络的双向数据透明传输。
-
-RULEX对USR-G776做了一个非常简单的支持:直接向串口透传数据,因此在使用该设备之前最好是配置好。
-
-官方文档:https://www.usr.cn/Download/806.html
-## 配置
-```go
-type _G776CommonConfig struct {
- Tag string `json:"tag" validate:"required" title:"数据Tag" info:"给数据打标签"`
- Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
-}
-type CommonUartConfig struct {
- Timeout int `json:"timeout" validate:"required"`
- Uart string `json:"uart" validate:"required"`
- BaudRate int `json:"baudRate" validate:"required"`
- DataBits int `json:"dataBits" validate:"required"`
- Parity string `json:"parity" validate:"required"`
- StopBits int `json:"stopBits" validate:"required"`
-}
-type _G776Config struct {
- CommonConfig _G776CommonConfig `json:"commonConfig" validate:"required"`
- UartConfig common.CommonUartConfig `json:"uartConfig" validate:"required"`
-}
-
-```
-
-## 数据处理示例
-
-```lua
--- Success
-function Success()
-
-end
-
--- Failed
-function Failed(error)
- print("failed:", error)
-end
-
--- Actions
-
-Actions = {
- function(args)
- local dataTable, err1 = rulexlib:J2T(data)
- if err1 ~= nil then
- return true, args
- end
- for _k, entity in pairs(dataTable) do
- data:ToUsrG776DTU("uuid", "DATA" ,rulexlib:T2J(entity["value"]))
- end
- return true, args
- end
-}
-
-```
-### 注意事项
-第二个参数必须是“DATA”,第三个参数可以是字符串、十六进制等.
\ No newline at end of file
diff --git a/device/yk8_controller_device.go b/device/yk8_controller_device.go
deleted file mode 100644
index af5f5a6d5..000000000
--- a/device/yk8_controller_device.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package device
-
-import (
- "context"
- "errors"
- golog "log"
- "sync"
- "time"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/core"
- "github.com/hootrhino/rulex/driver"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- "github.com/mitchellh/mapstructure"
- modbus "github.com/wwhai/gomodbus"
-)
-
-type YK8Controller struct {
- typex.XStatus
- status typex.DeviceState
- RuleEngine typex.RuleX
- driver typex.XExternalDriver
- rtuHandler *modbus.RTUClientHandler
- mainConfig common.ModBusConfig
- rtuConfig common.RTUConfig
- locker sync.Locker
-}
-
-/*
-*
-* 8路继电器
-*
- */
-func NewYK8Controller(e typex.RuleX) typex.XDevice {
- yk8 := new(YK8Controller)
- yk8.locker = &sync.Mutex{}
- yk8.mainConfig = common.ModBusConfig{}
- yk8.rtuConfig = common.RTUConfig{}
- yk8.RuleEngine = e
- return yk8
-}
-
-// 初始化
-func (yk8 *YK8Controller) Init(devId string, configMap map[string]interface{}) error {
- yk8.PointId = devId
- if err := utils.BindSourceConfig(configMap, &yk8.mainConfig); err != nil {
- glogger.GLogger.Error(err)
- return err
- }
-
- if errs := mapstructure.Decode(yk8.mainConfig.Config, &yk8.rtuConfig); errs != nil {
- glogger.GLogger.Error(errs)
- return errs
- }
- // 检查Tag有没有重复
- tags := []string{}
- for _, register := range yk8.mainConfig.Registers {
- tags = append(tags, register.Tag)
- }
- if utils.IsListDuplicated(tags) {
- return errors.New("tag duplicated")
- }
- return nil
-}
-
-// 启动
-func (yk8 *YK8Controller) Start(cctx typex.CCTX) error {
- yk8.Ctx = cctx.Ctx
- yk8.CancelCTX = cctx.CancelCTX
-
- // 串口配置固定写法
- // 下面的参数是传感器固定写法
- yk8.rtuHandler = modbus.NewRTUClientHandler(yk8.rtuConfig.Uart)
- yk8.rtuHandler.BaudRate = yk8.rtuConfig.BaudRate
- yk8.rtuHandler.DataBits = yk8.rtuConfig.DataBits
- yk8.rtuHandler.Parity = yk8.rtuConfig.Parity
- yk8.rtuHandler.StopBits = yk8.rtuConfig.StopBits
- yk8.rtuHandler.Timeout = time.Duration(yk8.mainConfig.Timeout) * time.Second
- if core.GlobalConfig.AppDebugMode {
- yk8.rtuHandler.Logger = golog.New(glogger.GLogger.Writer(), "YK8-DEVICE: ", golog.LstdFlags)
- }
-
- if err := yk8.rtuHandler.Connect(); err != nil {
- return err
- }
- //---------------------------------------------------------------------------------
- // Start
- //---------------------------------------------------------------------------------
- client := modbus.NewClient(yk8.rtuHandler)
- yk8.driver = driver.NewYK8RelayControllerDriver(yk8.Details(),
- yk8.RuleEngine, yk8.mainConfig.Registers, yk8.rtuHandler, client)
- if !yk8.mainConfig.AutoRequest {
- yk8.status = typex.DEV_UP
- return nil
- }
- go func(ctx context.Context, Driver typex.XExternalDriver) {
- ticker := time.NewTicker(time.Duration(yk8.mainConfig.Frequency) * time.Millisecond)
- buffer := make([]byte, common.T_64KB)
- yk8.driver.Read([]byte{}, buffer) //清理缓存
- for {
- select {
- case <-ctx.Done():
- {
- ticker.Stop()
- if yk8.rtuHandler != nil {
- yk8.rtuHandler.Close()
- }
- return
- }
- default:
- {
- }
- }
- yk8.locker.Lock()
- n, err := Driver.Read([]byte{}, buffer)
- yk8.locker.Unlock()
- if err != nil {
- glogger.GLogger.Error(err)
- } else {
- yk8.RuleEngine.WorkDevice(yk8.Details(), string(buffer[:n]))
- }
- <-ticker.C
-
- }
-
- }(yk8.Ctx, yk8.driver)
- yk8.status = typex.DEV_UP
- return nil
-}
-
-// 从设备里面读数据出来
-func (yk8 *YK8Controller) OnRead(cmd []byte, data []byte) (int, error) {
-
- n, err := yk8.driver.Read(cmd, data)
- if err != nil {
- glogger.GLogger.Error(err)
- yk8.status = typex.DEV_DOWN
- }
- return n, err
-}
-
-// 把数据写入设备
-func (yk8 *YK8Controller) OnWrite(cmd []byte, b []byte) (int, error) {
- return yk8.driver.Write(cmd, b)
-}
-
-// 设备当前状态
-func (yk8 *YK8Controller) Status() typex.DeviceState {
- return typex.DEV_UP
-}
-
-// 停止设备
-func (yk8 *YK8Controller) Stop() {
- yk8.status = typex.DEV_DOWN
- yk8.CancelCTX()
-
-}
-
-// 设备属性,是一系列属性描述
-func (yk8 *YK8Controller) Property() []typex.DeviceProperty {
- return []typex.DeviceProperty{}
-}
-
-// 真实设备
-func (yk8 *YK8Controller) Details() *typex.Device {
- return yk8.RuleEngine.GetDevice(yk8.PointId)
-}
-
-// 状态
-func (yk8 *YK8Controller) SetState(status typex.DeviceState) {
- yk8.status = status
-
-}
-
-// 驱动
-func (yk8 *YK8Controller) Driver() typex.XExternalDriver {
- return yk8.driver
-}
-
-func (yk8 *YK8Controller) OnDCACall(UUID string, Command string, Args interface{}) typex.DCAResult {
- return typex.DCAResult{}
-}
-func (yk8 *YK8Controller) OnCtrl(cmd []byte, args []byte) ([]byte, error) {
- return []byte{}, nil
-}
diff --git a/driver/modbus_rtu_driver.go b/driver/modbus_rtu_driver.go
index b0d52744f..43f148378 100644
--- a/driver/modbus_rtu_driver.go
+++ b/driver/modbus_rtu_driver.go
@@ -21,7 +21,7 @@ type modBusRtuDriver struct {
handler *modbus.RTUClientHandler
client modbus.Client
RuleEngine typex.RuleX
- Registers []common.RegisterRW
+ Registers map[string]*common.RegisterRW
device *typex.Device
frequency int64
}
@@ -29,9 +29,9 @@ type modBusRtuDriver struct {
func NewModBusRtuDriver(
d *typex.Device,
e typex.RuleX,
- Registers []common.RegisterRW,
+ Registers map[string]*common.RegisterRW,
handler *modbus.RTUClientHandler,
- client modbus.Client, frequency int64) typex.XExternalDriver {
+ client modbus.Client) typex.XExternalDriver {
return &modBusRtuDriver{
state: typex.DRIVER_UP,
device: d,
@@ -39,7 +39,6 @@ func NewModBusRtuDriver(
client: client,
handler: handler,
Registers: Registers,
- frequency: frequency,
}
}
@@ -64,6 +63,9 @@ func (d *modBusRtuDriver) Read(cmd []byte, data []byte) (int, error) {
var results []byte
dataMap := map[string]common.RegisterRW{}
count := len(d.Registers)
+ if count == 0 {
+ return 0, nil
+ }
for _, r := range d.Registers {
d.handler.SlaveId = r.SlaverId
if r.Function == common.READ_COIL {
@@ -72,13 +74,15 @@ func (d *modBusRtuDriver) Read(cmd []byte, data []byte) (int, error) {
count--
glogger.GLogger.Error(err)
}
+ Value := covertEmptyHex(results)
value := common.RegisterRW{
Tag: r.Tag,
Function: r.Function,
SlaverId: r.SlaverId,
Address: r.Address,
Quantity: r.Quantity,
- Value: covertEmptyHex(results),
+ Alias: r.Alias,
+ Value: Value,
}
dataMap[r.Tag] = value
}
@@ -94,6 +98,7 @@ func (d *modBusRtuDriver) Read(cmd []byte, data []byte) (int, error) {
SlaverId: r.SlaverId,
Address: r.Address,
Quantity: r.Quantity,
+ Alias: r.Alias,
Value: covertEmptyHex(results),
}
dataMap[r.Tag] = value
@@ -111,6 +116,7 @@ func (d *modBusRtuDriver) Read(cmd []byte, data []byte) (int, error) {
SlaverId: r.SlaverId,
Address: r.Address,
Quantity: r.Quantity,
+ Alias: r.Alias,
Value: covertEmptyHex(results),
}
dataMap[r.Tag] = value
@@ -127,11 +133,12 @@ func (d *modBusRtuDriver) Read(cmd []byte, data []byte) (int, error) {
SlaverId: r.SlaverId,
Address: r.Address,
Quantity: r.Quantity,
+ Alias: r.Alias,
Value: covertEmptyHex(results),
}
dataMap[r.Tag] = value
}
- time.Sleep(time.Duration(d.frequency) * time.Millisecond)
+ time.Sleep(time.Duration(r.Frequency) * time.Millisecond)
}
bytes, _ := json.Marshal(dataMap)
copy(data, bytes)
diff --git a/driver/modbus_tcp_driver.go b/driver/modbus_tcp_driver.go
index 20d2aedc9..a30b52b52 100644
--- a/driver/modbus_tcp_driver.go
+++ b/driver/modbus_tcp_driver.go
@@ -21,7 +21,7 @@ type modBusTCPDriver struct {
handler *modbus.TCPClientHandler
client modbus.Client
RuleEngine typex.RuleX
- Registers []common.RegisterRW
+ Registers map[string]*common.RegisterRW
device *typex.Device
frequency int64
}
@@ -29,9 +29,9 @@ type modBusTCPDriver struct {
func NewModBusTCPDriver(
d *typex.Device,
e typex.RuleX,
- Registers []common.RegisterRW,
+ Registers map[string]*common.RegisterRW,
handler *modbus.TCPClientHandler,
- client modbus.Client, frequency int64) typex.XExternalDriver {
+ client modbus.Client) typex.XExternalDriver {
return &modBusTCPDriver{
state: typex.DRIVER_UP,
device: d,
@@ -39,7 +39,6 @@ func NewModBusTCPDriver(
client: client,
handler: handler,
Registers: Registers,
- frequency: frequency,
}
}
@@ -64,6 +63,9 @@ func (d *modBusTCPDriver) Read(cmd []byte, data []byte) (int, error) {
var err error
var results []byte
count := len(d.Registers)
+ if count == 0 {
+ return 0, nil
+ }
for _, r := range d.Registers {
d.handler.SlaveId = r.SlaverId
if r.Function == common.READ_COIL {
@@ -78,6 +80,7 @@ func (d *modBusTCPDriver) Read(cmd []byte, data []byte) (int, error) {
SlaverId: r.SlaverId,
Address: r.Address,
Quantity: r.Quantity,
+ Alias: r.Alias,
Value: covertEmptyHex(results),
}
dataMap[r.Tag] = value
@@ -93,6 +96,7 @@ func (d *modBusTCPDriver) Read(cmd []byte, data []byte) (int, error) {
Function: r.Function,
SlaverId: r.SlaverId,
Address: r.Address,
+ Alias: r.Alias,
Quantity: r.Quantity,
Value: covertEmptyHex(results),
}
@@ -110,6 +114,7 @@ func (d *modBusTCPDriver) Read(cmd []byte, data []byte) (int, error) {
Function: r.Function,
SlaverId: r.SlaverId,
Address: r.Address,
+ Alias: r.Alias,
Quantity: r.Quantity,
Value: covertEmptyHex(results),
}
@@ -126,12 +131,13 @@ func (d *modBusTCPDriver) Read(cmd []byte, data []byte) (int, error) {
Function: r.Function,
SlaverId: r.SlaverId,
Address: r.Address,
+ Alias: r.Alias,
Quantity: r.Quantity,
Value: covertEmptyHex(results),
}
dataMap[r.Tag] = value
}
- time.Sleep(time.Duration(d.frequency) * time.Millisecond)
+ time.Sleep(time.Duration(r.Frequency) * time.Millisecond)
}
bytes, _ := json.Marshal(dataMap)
copy(data, bytes)
diff --git a/driver/rtu485_ther_driver.go b/driver/rtu485_ther_driver.go
deleted file mode 100644
index 5b5deebd5..000000000
--- a/driver/rtu485_ther_driver.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// 485 温湿度传感器驱动案例
-// 这是个很简单的485温湿度传感器驱动, 目的是为了演示厂商如何实现自己的设备底层驱动
-// 本驱动完成于2022年4月28日, 温湿度传感器资料请移步文档
-// 备注:THer 的含义是 ·temperature and humidity detector· 的简写
-package driver
-
-import (
- "encoding/json"
- "sync"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- modbus "github.com/wwhai/gomodbus"
-)
-
-// Example: 0x02 0x92 0xFF 0x98
-type sensor_data struct {
- Temperature float32 `json:"t"` //系数: 0.1
- Humidity float32 `json:"h"` //系数: 0.1
-}
-
-// 协议:UART:485 baud=4800 无校验 数据位1 停止位1
-// 功能码为: 3(ReadHoldingRegisters)
-// 站号为:1
-// 寄存器:0000H保存湿度 0001H保存温度,数据一共两个寄存器,4个字节(uint16*2)
-// ---------------------
-// | 00H 00H | 00H 01H |
-// ---------------------
-// | 湿度 | 温度 |
-// ---------------------
-// ** 其中低位保存小数
-type rtu485_THer_Driver struct {
- state typex.DriverState
- handler *modbus.RTUClientHandler
- client modbus.Client
- RuleEngine typex.RuleX
- Registers []common.RegisterRW
- device *typex.Device
- lock sync.Mutex
-}
-
-func NewRtu485THerDriver(d *typex.Device, e typex.RuleX,
- registers []common.RegisterRW,
- handler *modbus.RTUClientHandler,
- client modbus.Client) typex.XExternalDriver {
- return &rtu485_THer_Driver{
- state: typex.DRIVER_STOP,
- device: d,
- RuleEngine: e,
- handler: handler,
- client: client,
- Registers: registers,
- lock: sync.Mutex{},
- }
-}
-
-func (rtu485 *rtu485_THer_Driver) Test() error {
- _, err := rtu485.client.ReadHoldingRegisters(0x00, 2)
- return err
-}
-
-func (rtu485 *rtu485_THer_Driver) Init(map[string]string) error {
-
- return nil
-}
-
-func (rtu485 *rtu485_THer_Driver) Work() error {
- return nil
-}
-
-func (rtu485 *rtu485_THer_Driver) State() typex.DriverState {
- return typex.DRIVER_UP
-}
-
-func (rtu485 *rtu485_THer_Driver) Read(cmd []byte, data []byte) (int, error) {
- dataMap := map[string]common.RegisterRW{}
- for _, r := range rtu485.Registers {
- rtu485.handler.SlaveId = r.SlaverId
- rtu485.lock.Lock()
- results, err := rtu485.client.ReadHoldingRegisters(0x00, 2)
- rtu485.lock.Unlock()
- if err != nil {
- return 0, err
- }
- if len(results) == 4 {
- sd := sensor_data{
- Humidity: float32(utils.BToU16(results, 0, 2)) * 0.1,
- Temperature: float32(utils.BToU16(results, 2, 4)) * 0.1,
- }
- bytes, _ := json.Marshal(sd)
- value := common.RegisterRW{
- Tag: r.Tag,
- Function: r.Function,
- SlaverId: r.SlaverId,
- Address: r.Address,
- Quantity: r.Quantity,
- Value: string(bytes),
- }
- dataMap[r.Tag] = value
- if err != nil {
- glogger.GLogger.Error(err)
- }
- }
- }
- bytes, _ := json.Marshal(dataMap)
- copy(data, bytes)
- return len(bytes), nil
-}
-
-func (rtu485 *rtu485_THer_Driver) Write(cmd []byte, _ []byte) (int, error) {
- return 0, nil
-
-}
-
-// ---------------------------------------------------
-func (rtu485 *rtu485_THer_Driver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "Temperature And Humidity Sensor Driver",
- Type: "UART",
- Description: "RTU 485 Temperature And Humidity Sensor Driver",
- }
-}
-
-func (rtu485 *rtu485_THer_Driver) Stop() error {
- rtu485.state = typex.DRIVER_STOP
- return nil
-}
diff --git a/driver/siemens_s1200_driver.go b/driver/siemens_s1200_driver.go
deleted file mode 100644
index 23cdd5f8c..000000000
--- a/driver/siemens_s1200_driver.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package driver
-
-import (
- "encoding/json"
- "sync"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
-
- "github.com/robinson/gos7"
-)
-
-/*
-*
-* 西门子s1200驱动
-*
- */
-type siemens_s1200_driver struct {
- state typex.DriverState
- s7client gos7.Client
- device *typex.Device
- RuleEngine typex.RuleX
- dbs []common.S1200Block // PLC 的DB块
- lock sync.Mutex
-}
-
-func NewS1200Driver(d *typex.Device,
- e typex.RuleX,
- s7client gos7.Client,
- dbs []common.S1200Block) typex.XExternalDriver {
- return &siemens_s1200_driver{
- state: typex.DRIVER_STOP,
- device: d,
- RuleEngine: e,
- s7client: s7client,
- dbs: dbs,
- lock: sync.Mutex{},
- }
-}
-
-func (s1200 *siemens_s1200_driver) Test() error {
- _, err := s1200.s7client.GetCPUInfo()
- if err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
-
-// 读取配置
-func (s1200 *siemens_s1200_driver) Init(_ map[string]string) error {
- return nil
-}
-
-func (s1200 *siemens_s1200_driver) Work() error {
- return nil
-}
-
-func (s1200 *siemens_s1200_driver) State() typex.DriverState {
- _, err := s1200.s7client.PLCGetStatus()
- if err != nil {
- glogger.GLogger.Error(err)
- return typex.DRIVER_STOP
- }
- return typex.DRIVER_UP
-}
-
-// 字节格式:[dbNumber1, start1, size1, dbNumber2, start2, size2]
-// 读: db --> dbNumber, start, size, buffer[]
-func (s1200 *siemens_s1200_driver) Read(cmd []byte, data []byte) (int, error) {
- values := []common.S1200BlockValue{}
- for _, db := range s1200.dbs {
- rData := []byte{}
- s1200.lock.Lock()
- if err := s1200.s7client.AGReadDB(db.Address, db.Start, db.Size, rData); err != nil {
- s1200.lock.Unlock()
- return 0, err
- }
- s1200.lock.Unlock()
- values = append(values, common.S1200BlockValue{
- Tag: db.Tag,
- Address: db.Address,
- Start: db.Start,
- Size: db.Size,
- Value: rData,
- })
-
- }
- bytes, _ := json.Marshal(values)
- copy(data, bytes)
- return len(bytes), nil
-}
-
-// db.Address:int, db.Start:int, db.Size:int, rData[]
-// [
-//
-// {
-// "tag":"V",
-// "address":1,
-// "start":1,
-// "size":1,
-// "value":"AAECAwQ="
-// }
-//
-// ]
-func (s1200 *siemens_s1200_driver) Write(cmd []byte, data []byte) (int, error) {
- blocks := []common.S1200BlockValue{}
- if err := json.Unmarshal(data, &blocks); err != nil {
- return 0, err
- }
- //
- for _, block := range blocks {
- s1200.lock.Lock()
- if err := s1200.s7client.AGWriteDB(
- block.Address,
- block.Start,
- block.Size,
- block.Value,
- ); err != nil {
- s1200.lock.Unlock()
- return 0, err
- }
- s1200.lock.Unlock()
- }
- return 0, nil
-}
-
-func (s1200 *siemens_s1200_driver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "SIEMENS_s1200",
- Type: "TCP",
- Description: "SIEMENS s1200 系列 PLC 驱动",
- }
-}
-
-func (s1200 *siemens_s1200_driver) Stop() error {
- return nil
-}
diff --git a/driver/trailer_driver.go b/driver/trailer_driver.go
deleted file mode 100644
index 66a5273e7..000000000
--- a/driver/trailer_driver.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// 拖车驱动
-package driver
-
-import (
- "context"
-
- "github.com/hootrhino/rulex/component/trailer"
- "github.com/hootrhino/rulex/glogger"
- "github.com/hootrhino/rulex/typex"
-
- "google.golang.org/grpc"
-)
-
-type TrailerDriver struct {
- state typex.DriverState
- RuleEngine typex.RuleX
- client trailer.TrailerClient
- config map[string]string
-}
-
-func NewTrailerDriver(e typex.RuleX, grpcConn *grpc.ClientConn) typex.XExternalDriver {
- TrailerDriver := &TrailerDriver{
- state: typex.DRIVER_STOP,
- RuleEngine: e,
- client: trailer.NewTrailerClient(grpcConn),
- }
- return TrailerDriver
-
-}
-func (sc *TrailerDriver) Test() error {
- if err := sc.t(); err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
-
-func (sc *TrailerDriver) Init(config map[string]string) error {
- sc.config = config
- _, err := sc.client.Init(context.Background(), &trailer.Config{
- Kv: []byte{},
- })
- if err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
-
-func (sc *TrailerDriver) Work() error {
- _, err := sc.client.Start(context.Background(), &trailer.Request{})
- if err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
-
-func (sc *TrailerDriver) State() typex.DriverState {
- if sc.t() != nil {
- return typex.DRIVER_STOP
- }
- return typex.DRIVER_UP
-}
-
-/*
-*
-* 读取
-*
- */
-func (sc *TrailerDriver) Read(cmd []byte, data []byte) (int, error) {
- response, err := sc.client.Service(context.Background(),
- &trailer.ServiceRequest{Cmd: cmd, Args: data})
- if err != nil {
- glogger.GLogger.Error(err)
- return 0, err
- }
- copy(data, response.GetData())
- return len(response.Data), nil
-}
-
-/*
-*
-* 写入
-*
- */
-func (sc *TrailerDriver) Write(cmd []byte, data []byte) (int, error) {
- response, err := sc.client.Service(context.Background(),
- &trailer.ServiceRequest{Cmd: cmd, Args: data})
- if err != nil {
- glogger.GLogger.Error(err)
- return 0, err
- }
- return int(response.Code), nil
-}
-
-// ---------------------------------------------------
-func (sc *TrailerDriver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "Trailer-DRIVER",
- Type: "Trailer",
- Description: "Trailer 通用GRPC协议驱动",
- }
-}
-
-func (sc *TrailerDriver) Stop() error {
- _, err := sc.client.Stop(context.Background(), &trailer.Request{})
- if err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
-
-// ----------------------------------------------------------------------
-// 私有函数
-// ----------------------------------------------------------------------
-func (sc *TrailerDriver) t() error {
- _, err := sc.client.Status(context.Background(), &trailer.Request{})
- if err != nil {
- glogger.GLogger.Error(err)
- return err
- }
- return nil
-}
diff --git a/driver/tss200_v_0_2_driver.go b/driver/tss200_v_0_2_driver.go
deleted file mode 100644
index 808e197dd..000000000
--- a/driver/tss200_v_0_2_driver.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// TC-S200 系列空气质量监测仪内置 PM2.5、TVOC、甲醛、CO2,温湿度等高精
-// 度传感器套件,可通过吸顶式或壁挂安装,RS-485 接口通过 Modbus-RTU 协议进行
-// 数据输出,通过网关组网,或配合联动模块可以用于新风联动控制。
-// 该驱动是V0.2版本
-package driver
-
-import (
- "encoding/json"
- "sync"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- modbus "github.com/wwhai/gomodbus"
-)
-
-type tss200_v_0_2_Driver struct {
- state typex.DriverState
- handler *modbus.RTUClientHandler
- client modbus.Client
- RuleEngine typex.RuleX
- Registers []common.RegisterRW
- device *typex.Device
- lock sync.Mutex
-}
-
-func NewTSS200Driver(d *typex.Device, e typex.RuleX,
- registers []common.RegisterRW,
- handler *modbus.RTUClientHandler,
- client modbus.Client) typex.XExternalDriver {
- return &tss200_v_0_2_Driver{
- state: typex.DRIVER_STOP,
- device: d,
- RuleEngine: e,
- handler: handler,
- client: client,
- Registers: registers,
- lock: sync.Mutex{},
- }
-}
-func (tss *tss200_v_0_2_Driver) Test() error {
- return nil
-}
-
-func (tss *tss200_v_0_2_Driver) Init(map[string]string) error {
- return nil
-}
-
-func (tss *tss200_v_0_2_Driver) Work() error {
- return nil
-}
-
-func (tss *tss200_v_0_2_Driver) State() typex.DriverState {
- return typex.DRIVER_UP
-}
-
-type _sensor_data struct {
- TEMP float32 `json:"temp"` //系数: 0.01
- HUM float32 `json:"hum"` //系数: 0.01
- PM1 uint16 `json:"pm1"`
- PM25 uint16 `json:"pm25"`
- PM10 uint16 `json:"pm10"`
- CO2 uint16 `json:"co2"`
- TVOC float32 `json:"tvoc"` //系数: 0.001
- CHOH float32 `json:"choh"` //系数: 0.001
- ECO2 float32 `json:"eco2"` //系数: 0.001
-}
-
-func (tss *tss200_v_0_2_Driver) Read(cmd []byte, data []byte) (int, error) {
- // 获取全部传感器数据:
- // |地址码|功能码|寄存器地址|寄存器长度|校验码|校验码
- // |XX |03 |17 | 长度 |CRC | CRC
- // -----------------------------------------------
- // 01 03 00 11 00 08 14 09 09 45 1A F7 00 6F 00 89 00 89 FF FF FF FF 00 0B
- // TEMP HUM PM1 PM2.5 Pm10 CO2 TVOC CHOH
- //
- dataMap := map[string]common.RegisterRW{}
- for _, r := range tss.Registers {
- tss.handler.SlaveId = r.SlaverId
- tss.lock.Lock()
- result, err := tss.client.ReadHoldingRegisters(17, 9)
- tss.lock.Unlock()
- if err != nil {
- return 0, err
- }
- if len(result) == 18 {
- sd := _sensor_data{
- TEMP: float32(utils.BToU16(result, 0, 2)) * 0.01,
- HUM: float32(utils.BToU16(result, 2, 4)) * 0.01,
- PM1: utils.BToU16(result, 4, 6),
- PM25: utils.BToU16(result, 6, 8),
- PM10: utils.BToU16(result, 8, 10),
- CO2: utils.BToU16(result, 10, 12),
- TVOC: float32(utils.BToU16(result, 12, 14)) * 0.01,
- CHOH: float32(utils.BToU16(result, 14, 16)) * 0.001,
- ECO2: float32(utils.BToU16(result, 16, 18)) * 0.01,
- }
- bytes, _ := json.Marshal(sd)
- value := common.RegisterRW{
- Tag: r.Tag,
- Function: r.Function,
- SlaverId: r.SlaverId,
- Address: r.Address,
- Quantity: r.Quantity,
- Value: string(bytes),
- }
- dataMap[r.Tag] = value
- }
- }
- bytes, _ := json.Marshal(dataMap)
- copy(data, bytes)
- return len(bytes), nil
-}
-func (tss *tss200_v_0_2_Driver) Write(cmd []byte, _ []byte) (int, error) {
- return 0, nil
-}
-
-// ---------------------------------------------------
-func (tss *tss200_v_0_2_Driver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "TC-S200",
- Type: "UART",
- Description: "TC-S200 系列空气质量监测仪",
- }
-}
-
-func (tss *tss200_v_0_2_Driver) Stop() error {
- tss.state = typex.DRIVER_STOP
- return nil
-}
diff --git a/driver/usr_g776_4gdtu_driver.go b/driver/usr_g776_4gdtu_driver.go
deleted file mode 100644
index e4dd4b3d5..000000000
--- a/driver/usr_g776_4gdtu_driver.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package driver
-
-import (
- "context"
- "errors"
-
- "github.com/hootrhino/rulex/typex"
- serial "github.com/wwhai/goserial"
-)
-
-type UsrG776Driver struct {
- state typex.DriverState
- serialPort serial.Port
- ctx context.Context
- RuleEngine typex.RuleX
- device *typex.Device
-}
-
-// 初始化一个驱动
-func NewUsrG776Driver(
- ctx context.Context,
- e typex.RuleX,
- device *typex.Device,
- serialPort serial.Port,
-) typex.XExternalDriver {
- return &UsrG776Driver{
- RuleEngine: e,
- ctx: ctx,
- serialPort: serialPort,
- device: device,
- }
-}
-
-func (d *UsrG776Driver) Init(map[string]string) error {
- d.state = typex.DRIVER_UP
-
- return nil
-}
-
-func (d *UsrG776Driver) Work() error {
-
- return nil
-
-}
-func (d *UsrG776Driver) State() typex.DriverState {
- return d.state
-}
-func (d *UsrG776Driver) Stop() error {
- d.state = typex.DRIVER_STOP
- return d.serialPort.Close()
-}
-
-func (d *UsrG776Driver) Test() error {
- if d.serialPort == nil {
- return errors.New("serialPort is nil")
- }
- _, err := d.serialPort.Write([]byte("AT\n"))
- return err
-
-}
-
-func (d *UsrG776Driver) Read(cmd []byte, b []byte) (int, error) {
- return 0, nil
-}
-
-func (d *UsrG776Driver) Write(cmd []byte, b []byte) (int, error) {
- if string(cmd) == "AT" {
- return d.serialPort.Write(b)
- }
- if string(cmd) == "DATA" {
- return d.serialPort.Write(b)
- }
- return 0, nil
-}
-func (d *UsrG776Driver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "UsrG776Driver Driver",
- Type: "RAW_UART",
- Description: "UsrG776Driver Driver",
- }
-}
diff --git a/driver/yk8_relay_controller_driver.go b/driver/yk8_relay_controller_driver.go
deleted file mode 100644
index b3c451c25..000000000
--- a/driver/yk8_relay_controller_driver.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package driver
-
-//
-// RS232/RS485 控制继电器模块,可利用电脑通过串口(没有串口的可利用 USB 转
-// 串口)连接控制器进行对设备的控制,接口采用开关输出,有常开常闭点。
-// 资料首页:http://www.yi-kun.com
-//
-import (
- "encoding/json"
-
- "github.com/hootrhino/rulex/common"
- "github.com/hootrhino/rulex/typex"
- modbus "github.com/wwhai/gomodbus"
-)
-
-type YK8RelayControllerDriver struct {
- state typex.DriverState
- handler *modbus.RTUClientHandler
- client modbus.Client
- RuleEngine typex.RuleX
- Registers []common.RegisterRW
- device *typex.Device
-}
-
-func NewYK8RelayControllerDriver(d *typex.Device, e typex.RuleX,
- registers []common.RegisterRW,
- handler *modbus.RTUClientHandler,
- client modbus.Client) typex.XExternalDriver {
- return &YK8RelayControllerDriver{
- state: typex.DRIVER_STOP,
- device: d,
- RuleEngine: e,
- handler: handler,
- Registers: registers,
- client: client,
- }
-}
-
-func (yk8 *YK8RelayControllerDriver) Test() error {
- return nil
-}
-
-func (yk8 *YK8RelayControllerDriver) Init(map[string]string) error {
- return nil
-}
-
-func (yk8 *YK8RelayControllerDriver) Work() error {
- return nil
-}
-
-func (yk8 *YK8RelayControllerDriver) State() typex.DriverState {
- return typex.DRIVER_UP
-}
-
-//
-
-type yk08sw struct {
- Sw1 uint8 `json:"sw1"`
- Sw2 uint8 `json:"sw2"`
- Sw3 uint8 `json:"sw3"`
- Sw4 uint8 `json:"sw4"`
- Sw5 uint8 `json:"sw5"`
- Sw6 uint8 `json:"sw6"`
- Sw7 uint8 `json:"sw7"`
- Sw8 uint8 `json:"sw8"`
-}
-
-/*
-*
-* 读出来的是个JSON, 记录了8个开关的状态
-*
- */
-func (yk8 *YK8RelayControllerDriver) Read(cmd []byte, data []byte) (int, error) {
- dataMap := map[string]common.RegisterRW{}
- for _, r := range yk8.Registers {
- yk8.handler.SlaveId = r.SlaverId
- results, err := yk8.client.ReadCoils(0x00, 0x08)
- if err != nil {
- return 0, err
- }
- if len(results) == 1 {
- yks := yk08sw{
- Sw1: common.BitToUint8(results[0], 0),
- Sw2: common.BitToUint8(results[0], 1),
- Sw3: common.BitToUint8(results[0], 2),
- Sw4: common.BitToUint8(results[0], 3),
- Sw5: common.BitToUint8(results[0], 4),
- Sw6: common.BitToUint8(results[0], 5),
- Sw7: common.BitToUint8(results[0], 6),
- Sw8: common.BitToUint8(results[0], 7),
- }
- bytes, _ := json.Marshal(yks)
- value := common.RegisterRW{
- Tag: r.Tag,
- Function: r.Function,
- SlaverId: r.SlaverId,
- Address: r.Address,
- Quantity: r.Quantity,
- Value: string(bytes),
- }
- dataMap[r.Tag] = value
- }
- }
-
- bytes, _ := json.Marshal(dataMap)
- copy(data, bytes)
- return len(bytes), nil
-}
-
-// 写入数据
-func (yk8 *YK8RelayControllerDriver) Write(cmd []byte, data []byte) (int, error) {
- dataMap := []common.RegisterRW{}
- if err := json.Unmarshal(data, &dataMap); err != nil {
- return 0, err
- }
- for _, r := range dataMap {
- yk8.handler.SlaveId = r.SlaverId
- bytes, err0 := common.BitStringToBytes(string(r.Value))
- if err0 != nil {
- return 0, err0
- }
- _, err1 := yk8.client.WriteMultipleCoils(0, 1, bytes)
- if err1 != nil {
- return 0, err1
- }
- }
- return 0, nil
-}
-
-// ---------------------------------------------------
-func (yk8 *YK8RelayControllerDriver) DriverDetail() typex.DriverDetail {
- return typex.DriverDetail{
- Name: "YK-08-RELAY CONTROLLER",
- Type: "UART",
- Description: "一个支持RS232和485的国产8路继电器控制器",
- }
-}
-
-func (yk8 *YK8RelayControllerDriver) Stop() error {
- yk8.state = typex.DRIVER_STOP
- return nil
-}
diff --git a/engine/engine.go b/engine/engine.go
index cfd0c9ea9..a1a313a8c 100644
--- a/engine/engine.go
+++ b/engine/engine.go
@@ -27,8 +27,11 @@ import (
"github.com/hootrhino/rulex/component/appstack"
"github.com/hootrhino/rulex/component/datacenter"
"github.com/hootrhino/rulex/component/hwportmanager"
+ modbuscache "github.com/hootrhino/rulex/component/intercache/modbus"
+ siemenscache "github.com/hootrhino/rulex/component/intercache/siemens"
"github.com/hootrhino/rulex/component/interdb"
"github.com/hootrhino/rulex/component/intermetric"
+ "github.com/hootrhino/rulex/component/internotify"
"github.com/hootrhino/rulex/component/interqueue"
"github.com/hootrhino/rulex/component/rtspserver"
"github.com/hootrhino/rulex/component/trailer"
@@ -40,6 +43,7 @@ import (
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
"github.com/shirou/gopsutil/v3/disk"
+ "github.com/sirupsen/logrus"
)
/*
@@ -82,6 +86,18 @@ func InitRuleEngine(config typex.RulexConfig) typex.RuleX {
}
// Internal DB
interdb.Init(__DefaultRuleEngine, __DEFAULT_DB_PATH)
+ // Init Modbus Point Cache
+ modbuscache.InitModbusPointCache(__DefaultRuleEngine)
+ // Init Siemens Point Cache
+ siemenscache.InitSiemensPointCache(__DefaultRuleEngine)
+ // Internal Bus
+ internotify.InitInternalEventBus(__DefaultRuleEngine, core.GlobalConfig.MaxQueueSize)
+ // 前后交互组件
+ interqueue.InitInteractQueue(__DefaultRuleEngine, core.GlobalConfig.MaxQueueSize)
+ // Web Pipeline
+ core.InitWebDataPipe(__DefaultRuleEngine)
+ // Internal Schema
+ core.InitInternalSchemaCache(__DefaultRuleEngine)
// Load hardware Port Manager
hwportmanager.InitHwPortsManager(__DefaultRuleEngine)
// Internal Metric
@@ -96,21 +112,26 @@ func InitRuleEngine(config typex.RulexConfig) typex.RuleX {
interqueue.InitDataCacheQueue(__DefaultRuleEngine, core.GlobalConfig.MaxQueueSize)
// Data center
datacenter.InitDataCenter(__DefaultRuleEngine)
+ // Rtsp server
+ rtspserver.InitRtspServer(__DefaultRuleEngine)
return __DefaultRuleEngine
}
+/*
+*
+* Engine Start
+*
+ */
func (e *RuleEngine) Start() *typex.RulexConfig {
+ // Resource Manager
e.InitDeviceTypeManager()
e.InitSourceTypeManager()
e.InitTargetTypeManager()
// 内部队列
- interqueue.InitDataCacheQueue(e, core.GlobalConfig.MaxQueueSize)
interqueue.StartDataCacheQueue()
- // 前后交互组件
- interqueue.InitInteractQueue(e, core.GlobalConfig.MaxQueueSize)
- core.InitWebDataPipe(e)
- core.InitInternalSchemaCache()
- rtspserver.InitRtspServer()
+ // InternalEventQueue
+ internotify.StartInternalEventQueue()
+ // WebDataPip
go core.StartWebDataPipe()
return e.Config
}
@@ -122,8 +143,8 @@ func (e *RuleEngine) AllPlugins() *sync.Map {
return e.Plugins
}
-func (e *RuleEngine) Version() typex.Version {
- return typex.DefaultVersion
+func (e *RuleEngine) Version() typex.VersionInfo {
+ return typex.DefaultVersionInfo
}
func (e *RuleEngine) GetConfig() *typex.RulexConfig {
@@ -179,7 +200,10 @@ func (e *RuleEngine) Stop() {
glogger.GLogger.Info("Stop Device:", Device.Name, " Successfully")
return true
})
-
+ glogger.GLogger.Info("Flush Modbus Point sheet Cache")
+ modbuscache.Flush()
+ glogger.GLogger.Info("Flush Siemens Point sheet Cache")
+ siemenscache.Flush()
glogger.GLogger.Info("[√] Stop Rulex successfully")
if err := glogger.Close(); err != nil {
fmt.Println("Close logger error: ", err)
@@ -211,20 +235,18 @@ func (e *RuleEngine) RunSourceCallbacks(in *typex.InEnd, callbackArgs string) {
// 执行来自资源的脚本
for _, rule := range in.BindRules {
if rule.Status == typex.RULE_RUNNING {
- if rule.Type == "lua" {
- _, err := core.ExecuteActions(&rule, lua.LString(callbackArgs))
- if err != nil {
- glogger.GLogger.Error("RunLuaCallbacks error:", err)
- _, err := core.ExecuteFailed(rule.LuaVM, lua.LString(err.Error()))
- if err != nil {
- glogger.GLogger.Error(err)
- }
- } else {
- _, err := core.ExecuteSuccess(rule.LuaVM)
- if err != nil {
- glogger.GLogger.Error(err)
- return // lua 是规则链,有短路原则,中途出错会中断
- }
+ _, errA := core.ExecuteActions(&rule, lua.LString(callbackArgs))
+ if errA != nil {
+ glogger.GLogger.Error("RunLuaCallbacks error:", errA)
+ _, err0 := core.ExecuteFailed(rule.LuaVM, lua.LString(errA.Error()))
+ if err0 != nil {
+ glogger.GLogger.Error(err0)
+ }
+ } else {
+ _, errS := core.ExecuteSuccess(rule.LuaVM)
+ if errS != nil {
+ glogger.GLogger.Error(errS)
+ return // lua 是规则链,有短路原则,中途出错会中断
}
}
}
@@ -238,24 +260,23 @@ func (e *RuleEngine) RunSourceCallbacks(in *typex.InEnd, callbackArgs string) {
*/
func (e *RuleEngine) RunDeviceCallbacks(Device *typex.Device, callbackArgs string) {
for _, rule := range Device.BindRules {
- if rule.Status == typex.RULE_RUNNING {
- if rule.Type == "lua" {
- _, err := core.ExecuteActions(&rule, lua.LString(callbackArgs))
- if err != nil {
- glogger.GLogger.Error("RunLuaCallbacks error:", err)
- _, err := core.ExecuteFailed(rule.LuaVM, lua.LString(err.Error()))
- if err != nil {
- glogger.GLogger.Error(err)
- }
- } else {
- _, err := core.ExecuteSuccess(rule.LuaVM)
- if err != nil {
- glogger.GLogger.Error(err)
- return
- }
- }
+ _, errA := core.ExecuteActions(&rule, lua.LString(callbackArgs))
+ if errA != nil {
+ glogger.GLogger.WithFields(logrus.Fields{
+ "topic": "rule/log/" + rule.UUID,
+ }).Info("RunLuaCallbacks error:", errA)
+ _, err1 := core.ExecuteFailed(rule.LuaVM, lua.LString(errA.Error()))
+ if err1 != nil {
+ glogger.GLogger.Error(err1)
+ }
+ } else {
+ _, err2 := core.ExecuteSuccess(rule.LuaVM)
+ if err2 != nil {
+ glogger.GLogger.WithFields(logrus.Fields{
+ "topic": "rule/log/" + rule.UUID,
+ }).Info("RunLuaCallbacks error:", err2)
+ return
}
-
}
}
}
@@ -380,7 +401,7 @@ func (e *RuleEngine) SnapshotDump() string {
runtime.ReadMemStats(&m)
system := map[string]interface{}{
- "version": e.Version().Version,
+ "version": typex.MainVersion,
"diskInfo": int(diskInfo.UsedPercent),
"system": utils.BToMb(m.Sys),
"alloc": utils.BToMb(m.Alloc),
@@ -407,26 +428,40 @@ func (e *RuleEngine) SnapshotDump() string {
// 重启源
func (e *RuleEngine) RestartInEnd(uuid string) error {
- if _, ok := e.InEnds.Load(uuid); ok {
+ if Value, ok := e.InEnds.Load(uuid); ok {
+ InEnd := Value.(*typex.InEnd)
+ if InEnd.Source != nil {
+ InEnd.Source.Stop() // Down 以后会被自动拉起来
+ }
return nil
}
- return errors.New("InEnd:" + uuid + "not exists")
+ return fmt.Errorf("InendEnd not exists:%s", uuid)
}
// 重启目标
func (e *RuleEngine) RestartOutEnd(uuid string) error {
- if _, ok := e.OutEnds.Load(uuid); ok {
+ if Value, ok := e.OutEnds.Load(uuid); ok {
+ OutEnd := Value.(*typex.OutEnd)
+ if OutEnd.Target != nil {
+ OutEnd.Target.Stop() // Down 以后会被自动拉起来
+ }
return nil
}
- return errors.New("OutEnd:" + uuid + "not exists")
+ return fmt.Errorf("OutEnd not exists:%s", uuid)
+
}
// 重启设备
func (e *RuleEngine) RestartDevice(uuid string) error {
- if _, ok := e.Devices.Load(uuid); ok {
+ if Value, ok := e.Devices.Load(uuid); ok {
+ Device := Value.(*typex.Device)
+ if Device.Device != nil {
+ // Device.Device.Stop() // Down 以后会被自动拉起来
+ Device.Device.SetState(typex.DEV_DOWN) // Down 以后会被自动拉起来
+ }
return nil
}
- return errors.New("Device:" + uuid + "not exists")
+ return fmt.Errorf("Device not exists:%s", uuid)
}
/*
@@ -435,40 +470,28 @@ func (e *RuleEngine) RestartDevice(uuid string) error {
*
*/
func (e *RuleEngine) InitDeviceTypeManager() error {
- e.DeviceTypeManager.Register(typex.GENERIC_CAMERA,
- &typex.XConfig{
- Engine: e,
- NewDevice: device.NewVideoCamera,
- },
- )
- e.DeviceTypeManager.Register(typex.RHINOPI_IR,
- &typex.XConfig{
- Engine: e,
- NewDevice: device.NewIRDevice,
- },
- )
- e.DeviceTypeManager.Register(typex.TSS200V02,
+ e.DeviceTypeManager.Register(typex.GENERIC_HTTP_DEVICE,
&typex.XConfig{
Engine: e,
- NewDevice: device.NewTS200Sensor,
+ NewDevice: device.NewGenericHttpDevice,
},
)
- e.DeviceTypeManager.Register(typex.YK08_RELAY,
+ e.DeviceTypeManager.Register(typex.GENERIC_CAMERA,
&typex.XConfig{
Engine: e,
- NewDevice: device.NewYK8Controller,
+ NewDevice: device.NewVideoCamera,
},
)
- e.DeviceTypeManager.Register(typex.RTU485_THER,
+ e.DeviceTypeManager.Register(typex.RHINOPI_IR,
&typex.XConfig{
Engine: e,
- NewDevice: device.NewRtu485Ther,
+ NewDevice: device.NewIRDevice,
},
)
- e.DeviceTypeManager.Register(typex.S1200PLC,
+ e.DeviceTypeManager.Register(typex.SIEMENS_PLC,
&typex.XConfig{
Engine: e,
- NewDevice: device.NewS1200plc,
+ NewDevice: device.NewSIEMENS_PLC,
},
)
e.DeviceTypeManager.Register(typex.GENERIC_MODBUS,
@@ -489,12 +512,6 @@ func (e *RuleEngine) InitDeviceTypeManager() error {
NewDevice: device.NewGenericSnmpDevice,
},
)
- e.DeviceTypeManager.Register(typex.USER_G776,
- &typex.XConfig{
- Engine: e,
- NewDevice: device.NewUsrG776DTU,
- },
- )
e.DeviceTypeManager.Register(typex.ICMP_SENDER,
&typex.XConfig{
Engine: e,
@@ -582,6 +599,12 @@ func (e *RuleEngine) InitSourceTypeManager() error {
NewSource: source.NewIoTHubSource,
},
)
+ e.SourceTypeManager.Register(typex.INTERNAL_EVENT,
+ &typex.XConfig{
+ Engine: e,
+ NewSource: source.NewInternalEventSource,
+ },
+ )
return nil
}
diff --git a/engine/load_device.go b/engine/load_device.go
index 8222188a8..31e478a9c 100644
--- a/engine/load_device.go
+++ b/engine/load_device.go
@@ -95,7 +95,7 @@ func (e *RuleEngine) loadDevices(abstractDevice typex.XDevice, deviceInfo *typex
config := e.GetDevice(deviceInfo.UUID).Config
if config == nil {
e.RemoveDevice(deviceInfo.UUID)
- err := fmt.Errorf("device [%v] config is nil", deviceInfo.Name)
+ err := fmt.Errorf("Device [%v] config is nil", deviceInfo.Name)
return err
}
if err := abstractDevice.Init(deviceInfo.UUID, config); err != nil {
@@ -103,14 +103,19 @@ func (e *RuleEngine) loadDevices(abstractDevice typex.XDevice, deviceInfo *typex
return err
}
startDevice(abstractDevice, e, ctx, cancelCTX)
- glogger.GLogger.Infof("device [%v, %v] load successfully", deviceInfo.Name, deviceInfo.UUID)
+ glogger.GLogger.Infof("Device [%v, %v] load successfully", deviceInfo.Name, deviceInfo.UUID)
return nil
}
+/*
+*
+* Start是异步进行的,当设备的GetStatus返回状态UP时,正常运行,当Down时重启
+*
+ */
func startDevice(abstractDevice typex.XDevice, e *RuleEngine,
ctx context.Context, cancelCTX context.CancelFunc) error {
if err := abstractDevice.Start(typex.CCTX{Ctx: ctx, CancelCTX: cancelCTX}); err != nil {
- glogger.GLogger.Error("abstractDevice start error:", err)
+ glogger.GLogger.Error("Device start error:", err)
return err
}
// LoadNewestDevice
diff --git a/engine/load_plugin.go b/engine/load_plugin.go
index 2b6a95b78..71cbb4cab 100644
--- a/engine/load_plugin.go
+++ b/engine/load_plugin.go
@@ -29,19 +29,6 @@ import (
// └──────┘ └──────┘ └──────┘
func (e *RuleEngine) LoadPlugin(sectionK string, p typex.XPlugin) error {
section := utils.GetINISection(core.INIPath, sectionK)
- /*key, err1 := section.GetKey("enable")
- if err1 != nil {
- return err1
- }
- enable, err2 := key.Bool()
- if err2 != nil {
- return err2
- }
- if !enable {
- glogger.GLogger.Infof("Plugin is not enable:%s", p.PluginMetaInfo().Name)
- return nil
- }*/
-
if err := p.Init(section); err != nil {
return err
}
diff --git a/engine/load_rule.go b/engine/load_rule.go
index ced720a4b..da932da43 100644
--- a/engine/load_rule.go
+++ b/engine/load_rule.go
@@ -39,23 +39,16 @@ func (e *RuleEngine) LoadRule(r *typex.Rule) error {
// Load LoadBuildInLuaLib
//--------------------------------------------------------------
LoadBuildInLuaLib(e, r)
-
glogger.GLogger.Infof("Rule [%v, %v] load successfully", r.Name, r.UUID)
- // 绑定输入资源
- for _, inUUId := range r.FromSource {
- // 查找输入定义的资源是否存在
- if in := e.GetInEnd(inUUId); in != nil {
- (in.BindRules)[r.UUID] = *r
- return nil
- }
+ // 查找输入定义的资源是否存在
+ if in := e.GetInEnd(r.FromSource); in != nil {
+ (in.BindRules)[r.UUID] = *r
+ return nil
}
- // 绑定设备
- for _, devUUId := range r.FromDevice {
- // 查找输入定义的资源是否存在
- if Device := e.GetDevice(devUUId); Device != nil {
- // 绑定资源和规则,建立关联关系
- (Device.BindRules)[r.UUID] = *r
- }
+ // 查找输入定义的资源是否存在
+ if Device := e.GetDevice(r.FromDevice); Device != nil {
+ // 绑定资源和规则,建立关联关系
+ (Device.BindRules)[r.UUID] = *r
}
return nil
diff --git a/engine/rule_lua_runtime.go b/engine/rule_lua_runtime.go
index f1a083c76..ab182e9ac 100644
--- a/engine/rule_lua_runtime.go
+++ b/engine/rule_lua_runtime.go
@@ -63,6 +63,10 @@ func LoadBuildInLuaLib(e typex.RuleX, r *typex.Rule) {
r.AddLib(e, "binary", "BS2B", rulexlib.BitStringToBytes(e))
r.AddLib(e, "binary", "Bin2F32", rulexlib.BinToFloat32(e))
r.AddLib(e, "binary", "Bin2F64", rulexlib.BinToFloat64(e))
+ r.AddLib(e, "binary", "Bin2F32Big", rulexlib.BinToFloat32(e))
+ r.AddLib(e, "binary", "Bin2F64Big", rulexlib.BinToFloat64(e))
+ r.AddLib(e, "binary", "Bin2F32Little", rulexlib.BinToFloat32Little(e))
+ r.AddLib(e, "binary", "Bin2F64Little", rulexlib.BinToFloat64Little(e))
}
{
// URL处理
@@ -132,6 +136,9 @@ func LoadBuildInLuaLib(e typex.RuleX, r *typex.Rule) {
r.AddLib(e, "rhinopi", "DI1Get", rulexlib.H3DI1Get(e))
r.AddLib(e, "rhinopi", "DI2Get", rulexlib.H3DI2Get(e))
r.AddLib(e, "rhinopi", "DI3Get", rulexlib.H3DI3Get(e))
+ // Led
+ r.AddLib(e, "rhinopi", "Led1On", rulexlib.Led1On(e))
+ r.AddLib(e, "rhinopi", "Led1Off", rulexlib.Led1Off(e))
}
// Modbus
@@ -151,6 +158,11 @@ func LoadBuildInLuaLib(e typex.RuleX, r *typex.Rule) {
r.AddLib(e, "math", "TFloat", rulexlib.TruncateFloat(e))
// LocalDBQuery
r.AddLib(e, "datacenter", "DBQuery", rulexlib.LocalDBQuery(e))
+ //
+ r.AddLib(e, "network", "Ping", rulexlib.PingIp(e))
+ // Http
+ r.AddLib(e, "http", "Get", rulexlib.HttpGet(e))
+ r.AddLib(e, "http", "Post", rulexlib.HttpPost(e))
}
diff --git a/engine/runner.go b/engine/runner.go
index a825777d7..dc66e9940 100644
--- a/engine/runner.go
+++ b/engine/runner.go
@@ -16,26 +16,25 @@
package engine
import (
- "github.com/hootrhino/rulex/component/cron_task"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"os"
"os/signal"
"strings"
"syscall"
wdog "github.com/hootrhino/rulex/plugin/generic_watchdog"
+ modbusscrc "github.com/hootrhino/rulex/plugin/modbus_crc_tools"
modbusscanner "github.com/hootrhino/rulex/plugin/modbus_scanner"
- modbusscrc "github.com/hootrhino/rulex/plugin/modbuscrc_tools"
mqttserver "github.com/hootrhino/rulex/plugin/mqtt_server"
netdiscover "github.com/hootrhino/rulex/plugin/net_discover"
ttyterminal "github.com/hootrhino/rulex/plugin/ttyd_terminal"
usbmonitor "github.com/hootrhino/rulex/plugin/usb_monitor"
"gopkg.in/ini.v1"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
icmpsender "github.com/hootrhino/rulex/plugin/icmp_sender"
+ license_manager "github.com/hootrhino/rulex/plugin/license_manager"
"github.com/hootrhino/rulex/typex"
)
@@ -64,24 +63,24 @@ func RunRulex(iniPath string) {
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT, syscall.SIGTERM)
engine := InitRuleEngine(mainConfig)
engine.Start()
-
- // Load Plugin
- loadPlugin(engine)
// Load Http api Server
httpServer := httpserver.NewHttpApiServer(engine)
if err := engine.LoadPlugin("plugin.http_server", httpServer); err != nil {
glogger.GLogger.Error(err)
return
}
- // load Cron Task
- for _, task := range service.AllEnabledCronTask() {
- if err := cron_task.GetCronManager().AddTask(task); err != nil {
- glogger.GLogger.Error(err)
- continue
- }
+ license_manager := license_manager.NewLicenseManager(engine)
+ if err := engine.LoadPlugin("plugin.license_manager", license_manager); err != nil {
+ glogger.GLogger.Error(err)
+ return
}
+
+ // Load Plugin
+ loadPlugin(engine)
+
s := <-c
glogger.GLogger.Warn("RULEX Receive Stop Signal: ", s)
+ typex.GCancel()
engine.Stop()
os.Exit(0)
}
@@ -94,7 +93,7 @@ func loadPlugin(engine typex.RuleX) {
name := strings.TrimPrefix(section.Name(), "plugin.")
enable, err := section.GetKey("enable")
if err != nil {
- glogger.GLogger.Fatal(err)
+ continue
}
if !enable.MustBool(false) {
glogger.GLogger.Warnf("Plugin is disable:%s", name)
@@ -119,7 +118,7 @@ func loadPlugin(engine typex.RuleX) {
if name == "ttyd" {
plugin = ttyterminal.NewWebTTYPlugin()
}
- if name == "modbuscrc_tools" {
+ if name == "modbus_crc_tools" {
plugin = modbusscrc.NewModbusCrcCalculator()
}
if name == "soft_wdog" {
diff --git a/gen_info.sh b/gen_info.sh
index 17c959394..5868b0abb 100644
--- a/gen_info.sh
+++ b/gen_info.sh
@@ -1,6 +1,6 @@
#! /bin/bash
VERSION="$(git describe --tags $(git rev-list --tags --max-count=1))"
-HASH=$(git rev-list --tags --max-count=1)
+HASH=$(git rev-parse HEAD)
#######################################################################
## Gen Version
@@ -11,15 +11,17 @@ cat >./typex/version.go < 5 {
+ private_GRealtimeLogger.lock.Lock()
wsConn.WriteMessage(websocket.CloseMessage, []byte{})
+ private_GRealtimeLogger.lock.Unlock()
wsConn.Close()
return
}
private_GRealtimeLogger.Clients[wsConn.RemoteAddr().String()] = wsConn
+ private_GRealtimeLogger.lock.Lock()
wsConn.WriteMessage(websocket.TextMessage, []byte("Connected"))
+ private_GRealtimeLogger.lock.Unlock()
GLogger.Info("WebSocket Terminal connected:" + wsConn.RemoteAddr().String())
wsConn.SetCloseHandler(func(code int, text string) error {
GLogger.Info("wsConn Auto Close:", wsConn.RemoteAddr().String())
@@ -166,7 +172,10 @@ func WsLogger(c *gin.Context) {
if err != nil {
break
}
+ private_GRealtimeLogger.lock.Lock()
err = wsConn.WriteMessage(websocket.PingMessage, []byte{})
+ private_GRealtimeLogger.lock.Unlock()
+
if err != nil {
break
}
diff --git a/glogger/ws_logger.md b/glogger/ws_logger.md
index 6f5f9db0e..c72e751a8 100644
--- a/glogger/ws_logger.md
+++ b/glogger/ws_logger.md
@@ -63,7 +63,7 @@ $$
{
"appId":"rulex",
"file":"C:/Users/wangwenhai/workspace/rulex/plugin/http_server/rule_api.go:580",
- "func":"github.com/hootrhino/rulex/plugin/http_server.TestSourceCallback",
+ "func":"github.com/hootrhino/rulex/component/rulex_api_server.TestSourceCallback",
"level":"debug",
"msg":"string",
"time":"2023-06-30T17:52:31+08:00",
@@ -75,7 +75,7 @@ $$
{
"appId":"rulex",
"file":"C:/Users/wangwenhai/workspace/rulex/plugin/http_server/rule_api.go:580",
- "func":"github.com/hootrhino/rulex/plugin/http_server.TestSourceCallback",
+ "func":"github.com/hootrhino/rulex/component/rulex_api_server.TestSourceCallback",
"level":"debug",
"msg":"string",
"time":"2023-06-30T17:52:31+08:00",
diff --git a/go.mod b/go.mod
index f8acd1d04..81a5f8762 100644
--- a/go.mod
+++ b/go.mod
@@ -45,7 +45,7 @@ require (
github.com/suapapa/go_eddystone v1.3.1
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
- github.com/swaggo/swag v1.8.12
+ github.com/swaggo/swag v1.16.2
github.com/tbrandon/mbserver v0.0.0-20211210035124-daf3c8c4269f
github.com/thinkgos/go-iecp5 v1.2.1
github.com/urfave/cli/v2 v2.25.5
@@ -58,8 +58,8 @@ require (
go.bug.st/serial v1.5.0
go.mongodb.org/mongo-driver v1.11.6
go.uber.org/zap v1.15.0
- golang.org/x/crypto v0.14.0
- golang.org/x/sys v0.13.0
+ golang.org/x/crypto v0.15.0
+ golang.org/x/sys v0.14.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
gopkg.in/ini.v1 v1.67.0
@@ -70,22 +70,20 @@ require (
require (
github.com/KyleBanks/depth v1.2.1 // indirect
- github.com/PuerkitoBio/purell v1.1.1 // indirect
- github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
- github.com/go-openapi/jsonpointer v0.19.5 // indirect
- github.com/go-openapi/jsonreference v0.19.6 // indirect
- github.com/go-openapi/spec v0.20.4 // indirect
- github.com/go-openapi/swag v0.19.15 // indirect
+ github.com/go-openapi/jsonpointer v0.20.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/spec v0.20.9 // indirect
+ github.com/go-openapi/swag v0.22.4 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 // indirect
- github.com/mailru/easyjson v0.7.6 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
- github.com/stretchr/testify v1.8.4 // indirect
+ github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
- golang.org/x/tools v0.7.0 // indirect
+ golang.org/x/tools v0.15.0 // indirect
)
require (
@@ -160,9 +158,9 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.5.0 // indirect
golang.org/x/arch v0.3.0 // indirect
- golang.org/x/net v0.17.0 // indirect
- golang.org/x/sync v0.3.0 // indirect
- golang.org/x/text v0.13.0 // indirect
+ golang.org/x/net v0.18.0 // indirect
+ golang.org/x/sync v0.5.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
diff --git a/go.sum b/go.sum
deleted file mode 100644
index eac761085..000000000
--- a/go.sum
+++ /dev/null
@@ -1,1645 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
-cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
-cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
-cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
-cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
-cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
-cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o=
-cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE=
-cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
-cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
-cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
-cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
-cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk=
-cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc=
-cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc=
-cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04=
-cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno=
-cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak=
-cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
-cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
-cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
-cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
-cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0=
-cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc=
-cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
-cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
-cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=
-cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ=
-cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY=
-cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
-cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
-cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=
-cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=
-cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
-cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
-cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
-cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8=
-cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM=
-cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc=
-cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI=
-cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE=
-cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE=
-cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4=
-cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
-cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw=
-cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
-cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
-cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI=
-cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y=
-cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
-cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
-cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0=
-cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk=
-cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg=
-cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590=
-cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk=
-cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk=
-cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U=
-cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA=
-cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM=
-cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk=
-cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
-cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
-cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4=
-cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
-cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
-cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
-cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
-cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
-cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
-cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
-cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
-cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg=
-cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo=
-cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
-cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
-cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
-cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
-cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
-cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE=
-cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM=
-cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
-cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
-cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
-cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
-cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0=
-cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38=
-cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w=
-cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
-cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
-cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA=
-cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A=
-cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s=
-cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI=
-cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
-cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
-cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
-cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g=
-cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4=
-cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c=
-cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s=
-cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
-cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
-cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=
-cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek=
-cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0=
-cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM=
-cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q=
-cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
-cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
-cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k=
-cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4=
-cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
-cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
-cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
-cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
-cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI=
-cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8=
-cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc=
-cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw=
-cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w=
-cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI=
-cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
-cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
-cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY=
-cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08=
-cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
-cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
-cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w=
-cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM=
-cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60=
-cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo=
-cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
-cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
-cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
-cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
-cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA=
-cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI=
-cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
-cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM=
-cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
-cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
-cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc=
-cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
-cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
-cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
-cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=
-cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY=
-cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs=
-cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g=
-cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg=
-cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0=
-cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
-cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
-cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=
-cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8=
-cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
-cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
-cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
-cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
-cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
-cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
-cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
-cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
-cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
-cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
-cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA=
-cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY=
-cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
-cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
-cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8=
-cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI=
-cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk=
-cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=
-cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
-cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
-cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM=
-cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8=
-cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8=
-cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4=
-cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
-cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
-cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
-cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
-cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA=
-cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0=
-cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4=
-cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs=
-cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA=
-cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk=
-cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE=
-cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc=
-cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
-cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
-cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo=
-cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw=
-cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
-cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
-cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70=
-cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo=
-cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
-cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
-cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg=
-cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE=
-cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
-cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
-cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
-cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
-cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=
-cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE=
-cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U=
-cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
-cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
-cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
-cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
-cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs=
-cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70=
-cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
-cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
-cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA=
-cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM=
-cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA=
-cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0=
-cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU=
-cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg=
-cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
-cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
-cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc=
-cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y=
-cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do=
-cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo=
-cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
-cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
-cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk=
-cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44=
-cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
-cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4=
-cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4=
-cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
-cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
-cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
-cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q=
-cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA=
-cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
-cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
-cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk=
-cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk=
-cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU=
-cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s=
-cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
-cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
-cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4=
-cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U=
-cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco=
-cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo=
-cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E=
-cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU=
-cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4=
-cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw=
-cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
-cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
-cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0=
-cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
-cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
-cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
-cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
-cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
-cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
-cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM=
-cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA=
-cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8=
-cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4=
-cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ=
-cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg=
-cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28=
-cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y=
-cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs=
-cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg=
-cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk=
-cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw=
-cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
-cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
-cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M=
-cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU=
-cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
-cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
-cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
-cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY=
-cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E=
-cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE=
-cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g=
-cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w=
-cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8=
-cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
-cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
-cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc=
-cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A=
-cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo=
-cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ=
-cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
-cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
-cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
-cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/BeatTime/bacnet v0.2.1 h1:Bs9DzPWilcvwOPvwXuyn/tYjDuln9Mng1d+Z6wRZDGs=
-github.com/BeatTime/bacnet v0.2.1/go.mod h1:hDPKj40Is3JbYPoSgL6giQXslpQwxou64R2SixNPAos=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DrmagicE/gmqtt v0.5.0 h1:CDg1bNmkXhKHwQoRMpKamqz0vWU40BguL/vg5sD4y7s=
-github.com/DrmagicE/gmqtt v0.5.0/go.mod h1:iOdhUTFLsVbZMTX/p5uxcyQswYkBqQ4r8PZtd1C3Vs4=
-github.com/Kowiste/ProfinetServer v0.0.0-20200929093941-9c422ae1f008 h1:61oQ1jLOU2K00wKzMu4OmT5vHQyB8yI4m2JZy/gonGU=
-github.com/Kowiste/ProfinetServer v0.0.0-20200929093941-9c422ae1f008/go.mod h1:ZoTCmOIdEiUhWdG0j9OkdwJrqjtAabzcNLlbr0r8hMo=
-github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
-github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/adrianmo/go-nmea v1.8.0 h1:KE6xDTMQNoKoyCb7IQRXGOIbn4WJlUKwIuRD4LbpKlM=
-github.com/adrianmo/go-nmea v1.8.0/go.mod h1:u8bPnpKt/D/5rll/5l9f6iDfeq5WZW0+/SXdkwix6Tg=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blastrain/vitess-sqlparser v0.0.0-20201030050434-a139afbb1aba h1:hBK2BWzm0OzYZrZy9yzvZZw59C5Do4/miZ8FhEwd5P8=
-github.com/blastrain/vitess-sqlparser v0.0.0-20201030050434-a139afbb1aba/go.mod h1:FGQp+RNQwVmLzDq6HBrYCww9qJQyNwH9Qji/quTQII4=
-github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
-github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
-github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
-github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
-github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
-github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
-github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
-github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dsnet/golib/memfile v0.0.0-20190531212259-571cdbcff553/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
-github.com/dsnet/golib/memfile v0.0.0-20200723050859-c110804dfa93/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
-github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs=
-github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
-github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
-github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
-github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
-github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
-github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
-github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
-github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
-github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
-github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
-github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
-github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
-github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
-github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
-github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
-github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
-github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-ocf/go-coap/v2 v2.0.4-0.20200728125043-f38b86f047a7/go.mod h1:X9wVKcaOSx7wBxKcvrWgMQq1R2DNeA7NBLW2osIb8TM=
-github.com/go-ocf/kit v0.0.0-20200728130040-4aebdb6982bc/go.mod h1:TIsoMT/iB7t9P6ahkcOnsmvS83SIJsv9qXRfz/yLf6M=
-github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
-github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
-github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
-github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
-github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
-github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
-github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
-github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
-github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
-github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
-github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
-github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
-github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
-github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/goburrow/modbus v0.1.0 h1:DejRZY73nEM6+bt5JSP6IsFolJ9dVcqxsYbpLbeW/ro=
-github.com/goburrow/modbus v0.1.0/go.mod h1:Kx552D5rLIS8E7TyUwQ/UdHEqvX5T8tyiGBTlzMcZBg=
-github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
-github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
-github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
-github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/gopcua/opcua v0.3.15 h1:l0hS2gUTSfN5n604lbf90TyRuHjgEO/T/sq5DVWn8sE=
-github.com/gopcua/opcua v0.3.15/go.mod h1:DVDwHvR5lYgO9T4nTn+QxzGl6VQMywgaRgmOH1skBps=
-github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
-github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gosnmp/gosnmp v1.35.0 h1:EuWWNPxTCdAUx2/NbQcSa3WdNxjzpy4Phv57b4MWpJM=
-github.com/gosnmp/gosnmp v1.35.0/go.mod h1:2AvKZ3n9aEl5TJEo/fFmf/FGO4Nj4cVeEc5yuk88CYc=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
-github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
-github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
-github.com/hootrhino/go-ais v1.0.0 h1:7SSOn3XCB4I4o7SAJ8RvJ4jhqI9w3rVU379jgINCuhA=
-github.com/hootrhino/go-ais v1.0.0/go.mod h1:QsnHNr4Xcj6xEfa32MmUmhAc1i0BtJZzi4rSZKftw1M=
-github.com/hootrhino/gopher-lua v1.0.0 h1:6kYVenNQYvWdqf4sd2KXOr4XUPKyDZqrxW0PNIM061E=
-github.com/hootrhino/gopher-lua v1.0.0/go.mod h1:tY0TknOctxfkzkx60g+po3hj7K1R+YdwLYqT4OqAINc=
-github.com/hootrhino/wmi v0.0.0-20230603082700-cfa077a8cf01 h1:oPtZwF/Th9FuFZH4bv0otm6de/5ewcqfaf6pVI/Xfwc=
-github.com/hootrhino/wmi v0.0.0-20230603082700-cfa077a8cf01/go.mod h1:RmN9Gg8TiRseWz6DqrfekUqlRWzUtLJonWUwyfTdcu0=
-github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
-github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
-github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
-github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
-github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
-github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
-github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
-github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68 h1:d2hBkTvi7B89+OXY8+bBBshPlc+7JYacGrG/dFak8SQ=
-github.com/juju/errors v0.0.0-20170703010042-c7d06af17c68/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI=
-github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt3aWYjoIsUGCbt21ekbeJcTWv0=
-github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
-github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
-github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
-github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
-github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
-github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
-github.com/lestrrat-go/jwx v1.0.2/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0=
-github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8=
-github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
-github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
-github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
-github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
-github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
-github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
-github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
-github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
-github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
-github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
-github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
-github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mochi-co/mqtt/v2 v2.2.12 h1:g7JlVF3UNtkpoXOF5f3+I8gWTqByya+QhNEqZxUEjZg=
-github.com/mochi-co/mqtt/v2 v2.2.12/go.mod h1:MDMTThFgWj/LjJ6wc51bP5l4xnJG/ahpc9tR9vZVf8Q=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
-github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
-github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
-github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
-github.com/muka/go-bluetooth v0.0.0-20221213043340-85dc80edc4e1 h1:BuVRHr4HHJbk1DHyWkArJ7E8J/VA8ncCr/VLnQFazBo=
-github.com/muka/go-bluetooth v0.0.0-20221213043340-85dc80edc4e1/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4=
-github.com/nats-io/nats-server/v2 v2.9.17 h1:gFpUQ3hqIDJrnqog+Bl5vaXg+RhhYEZIElasEuRn2tw=
-github.com/nats-io/nats-server/v2 v2.9.17/go.mod h1:eQysm3xDZmIjfkjr7DuD9DjRFpnxQc2vKVxtEg0Dp6s=
-github.com/nats-io/nats.go v1.26.0 h1:fWJTYPnZ8DzxIaqIHOAMfColuznchnd5Ab5dbJpgPIE=
-github.com/nats-io/nats.go v1.26.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
-github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
-github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
-github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
-github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/panjf2000/ants/v2 v2.4.3/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
-github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
-github.com/patrikeh/go-deep v0.0.0-20230427173908-a2775168ab3d h1:uklDHZ8eaoO7TzqTu1bk/ijlkfadd8ogGfit4oIeSik=
-github.com/patrikeh/go-deep v0.0.0-20230427173908-a2775168ab3d/go.mod h1:W7GtTeZHpwautuPVtKBFp1+df69GkwlOGD2cwvYeYIE=
-github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
-github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
-github.com/pion/dtls/v2 v2.0.1-0.20200503085337-8e86b3a7d585/go.mod h1:/GahSOC8ZY/+17zkaGJIG4OUkSGAcZu/N/g3roBOCkM=
-github.com/pion/dtls/v2 v2.0.10-0.20210502094952-3dc563b9aede/go.mod h1:86wv5dgx2J/z871nUR+5fTTY9tISLUlo+C5Gm86r1Hs=
-github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
-github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
-github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
-github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
-github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
-github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
-github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
-github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
-github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
-github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/plgd-dev/go-coap/v2 v2.0.4-0.20200819112225-8eb712b901bc/go.mod h1:+tCi9Q78H/orWRtpVWyBgrr4vKFo2zYtbbxUllerBp4=
-github.com/plgd-dev/go-coap/v2 v2.4.1-0.20210517130748-95c37ac8e1fa/go.mod h1:rA7fc7ar+B/qa+Q0hRqv7yj/EMtIlmo1l7vkQGSrHPU=
-github.com/plgd-dev/go-coap/v2 v2.6.0 h1:T8tefZK4jag1ssr6gAGU+922QhVcrjk207aPhdg7i3o=
-github.com/plgd-dev/go-coap/v2 v2.6.0/go.mod h1:wm9fcL58Ky442Krix74S9Y54rCo36u59xFcYKRQaSBg=
-github.com/plgd-dev/kit v0.0.0-20200819113605-d5fcf3e94f63/go.mod h1:Yl9zisyXfPdtP9hTWlJqjJYXmgU/jtSDKttz9/CeD90=
-github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 h1:TC1HJ/UbyflJFPvaOdGmNZ5TeFGex1/dyr9urNGLy7M=
-github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90/go.mod h1:Z7oKFLSGQjdi8eInxwFCs0tSApuEM1o0qNck+sJYp4M=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
-github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
-github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI=
-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
-github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
-github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
-github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
-github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
-github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
-github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/robinson/gos7 v0.0.0-20230421131203-d20ac6ca08cd h1:UhVHgo6tdrKiuVqxTd63ja0B9YihC7D3z8hfEye6Vqo=
-github.com/robinson/gos7 v0.0.0-20230421131203-d20ac6ca08cd/go.mod h1:/gqBMUg6JbR1JKVqIK4ZKCtWDUq/cfrh+hVH8Ak/35A=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
-github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
-github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y=
-github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY=
-github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
-github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
-github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
-github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/suapapa/go_eddystone v1.3.1 h1:mfW3eNoRPpaZ0iARRZtEyudFfNFtytqeCexwXg1wIKE=
-github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8=
-github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
-github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
-github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
-github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
-github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
-github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
-github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
-github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
-github.com/tbrandon/mbserver v0.0.0-20211210035124-daf3c8c4269f h1:RSsPHbWJpo/IaGb+7S7hNIQtuLfli2kIi97clK7BW/o=
-github.com/tbrandon/mbserver v0.0.0-20211210035124-daf3c8c4269f/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw=
-github.com/thinkgos/go-iecp5 v1.2.1 h1:p5l8FGNtMpOQ2BMCmUHT+3eSG/Ley6Uv6xFt5j8MNFI=
-github.com/thinkgos/go-iecp5 v1.2.1/go.mod h1:jUgKVFgiyamwD0/eWgx3wbGcVojeiTouvJsLe6CyKmM=
-github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
-github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
-github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
-github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
-github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
-github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
-github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
-github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
-github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
-github.com/wwhai/gomodbus v0.2.4 h1:PHSzIv7YtVJh8yCAD1BPerLbHZut7QluDiKi+1gDhus=
-github.com/wwhai/gomodbus v0.2.4/go.mod h1:+ke0zV8eHAvlDSRooGJshCOB+dSEQBfOWfTBadrdi9A=
-github.com/wwhai/goserial v0.2.0 h1:kl+rJXGsSM9HMIwmZTVEyBXiJpp/gjIzQzrY/gQSnYI=
-github.com/wwhai/goserial v0.2.0/go.mod h1:Uj8aER/uh/cDyzxzkV3KGl6GzcJCmC6hkBlJtgp5PTU=
-github.com/wwhai/ntp v0.3.0 h1:pH3R36pFdoF3srjvqT3J0C4NOb/Tt++hYFmEMT9giiA=
-github.com/wwhai/ntp v0.3.0/go.mod h1:WCmadLV7QTOxPOyXlBVNttH3OcLU0xp0GqBLtl9G22U=
-github.com/wwhai/tarmserial v1.0.0 h1:vxm3PZncXfYr4I/N3yAK2ouFMdLRFxLcHHvVEh0gf0Q=
-github.com/wwhai/tarmserial v1.0.0/go.mod h1:bim1tzdGKaJlYrSE38JRRxC1Pqz55oYSZJH3TlmYR+I=
-github.com/wwhai/tinycache v0.0.0-20191004192108-46f407853014 h1:ILKCpEBNUfC1iwUqCmVB/E2Mk9dLkspPcF2+eYJIi+M=
-github.com/wwhai/tinycache v0.0.0-20191004192108-46f407853014/go.mod h1:YSnIPMAVDKDpSAMV+ju1PoTC3LN83+ZP0UnK8bIswjQ=
-github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
-github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
-github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
-github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
-github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
-github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
-github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
-github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdVI=
-github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY=
-github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
-github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
-github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
-github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
-github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
-go.bug.st/serial v1.5.0 h1:ThuUkHpOEmCVXxGEfpoExjQCS2WBVV4ZcUKVYInM9T4=
-go.bug.st/serial v1.5.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o=
-go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
-go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
-go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
-golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
-golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.0.0-20180302201248-b7ef84aaf62a/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
-golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
-google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
-google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=
-google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
-google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
-google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
-google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
-google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
-google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
-google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
-google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
-google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
-google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=
-google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
-google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
-google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU=
-google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
-gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
-gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
-gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/sqlite v1.5.1 h1:hYyrLkAWE71bcarJDPdZNTLWtr8XrSjOWyjUYI6xdL4=
-gorm.io/driver/sqlite v1.5.1/go.mod h1:7MZZ2Z8bqyfSQA1gYEV6MagQWj3cpUkJj9Z+d1HEMEQ=
-gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
-gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/main.go b/main.go
index c3f347b35..bfbe99258 100644
--- a/main.go
+++ b/main.go
@@ -3,19 +3,22 @@ package main
import (
"context"
"fmt"
- "github.com/hootrhino/rulex/engine"
- "github.com/hootrhino/rulex/ossupport"
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
- "github.com/urfave/cli/v2"
"log"
_ "net/http/pprof"
"os"
"runtime"
"time"
+
+ _ "github.com/hootrhino/rulex/component/cron_task/docs"
+ "github.com/hootrhino/rulex/engine"
+ "github.com/hootrhino/rulex/ossupport"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+ "github.com/urfave/cli/v2"
)
func init() {
+
go func() {
for {
select {
@@ -27,15 +30,37 @@ func init() {
}
}
}()
+ env := os.Getenv("ARCHSUPPORT")
+ if env == "EEKITT507" {
+ typex.DefaultVersionInfo.Product = env
+ }
+ if env == "EEKITH3" {
+ typex.DefaultVersionInfo.Product = env
+ }
+ if env == "WKYS805" {
+ typex.DefaultVersionInfo.Product = env
+ }
+ if env == "RPI4B" {
+ typex.DefaultVersionInfo.Product = env
+ }
dist, err := utils.GetOSDistribution()
if err != nil {
panic(err)
}
- typex.DefaultVersion.Dist = dist
- arch := fmt.Sprintf("%s-%s", typex.DefaultVersion.Dist, runtime.GOARCH)
- typex.DefaultVersion.Arch = arch
+ typex.DefaultVersionInfo.Dist = dist
+ arch := fmt.Sprintf("%s-%s", typex.DefaultVersionInfo.Dist, runtime.GOARCH)
+ typex.DefaultVersionInfo.Arch = arch
}
+// @title Rulex API
+// @version 1.0
+// @description Rulex Swagger API
+
+// @contact.name API Support
+// @contact.url https://github.com/hootrhino/rulex
+// @BasePath /api/v1
+//
+//go:generate bash ./gen_info.sh
func main() {
app := &cli.App{
Name: "RULEX Gateway FrameWork",
@@ -43,7 +68,7 @@ func main() {
Commands: []*cli.Command{
{
Name: "run",
- Usage: "Start rulex, Must with config: -config path/rulex.ini",
+ Usage: "Start rulex, Must with config: -config=path/rulex.ini",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "db",
@@ -57,9 +82,9 @@ func main() {
},
},
Action: func(c *cli.Context) error {
- fmt.Println(typex.Banner)
+ utils.CLog(typex.Banner)
engine.RunRulex(c.String("config"))
- log.Printf("[RULEX UPGRADE] Run rulex successfully.")
+ fmt.Printf("[RULEX UPGRADE] Run rulex successfully.")
return nil
},
},
@@ -68,41 +93,64 @@ func main() {
Hidden: true,
Usage: "! JUST FOR Upgrade FirmWare",
Flags: []cli.Flag{
- &cli.IntFlag{
- Name: "oldpid",
+ &cli.BoolFlag{
+ Name: "upgrade",
Usage: "! THIS PARAMENT IS JUST FOR Upgrade FirmWare",
- Value: -1,
+ Value: false,
},
},
Action: func(c *cli.Context) error {
- OldPid := c.Int("oldpid")
- log.Println("[RULEX UPGRADE] Updater Pid=",
- os.Getpid(), "Gid=", os.Getegid(), " OldPid:", OldPid)
- if OldPid < 0 {
- log.Printf("[RULEX UPGRADE] Invalid OldPid:%d", OldPid)
+ file, err := os.Create(ossupport.UpgradeLogPath)
+ if err != nil {
+ utils.CLog(err.Error())
+ return nil
+ }
+ defer file.Close()
+ os.Stdout = file
+ os.Stderr = file
+ // upgrade lock
+ if err := os.WriteFile(ossupport.UpgradeLockPath, []byte{48}, 0755); err != nil {
+ utils.CLog("[RULEX UPGRADE] Write Upgrade Lock File error:%s", err.Error())
return nil
}
- // Try 5 times
- killOld := true
- log.Println("[RULEX UPGRADE] Try to kill Old Process:", OldPid)
- if killOld {
- // EEKITH3 Use SystemCtl manage RULEX
- env := os.Getenv("ARCHSUPPORT")
- if runtime.GOOS == "linux" {
- log.Println("[RULEX UPGRADE] Ready to Upgrade on product:", env)
- if err := ossupport.UnzipFirmware(
- "/usr/local/upload/Firmware/Firmware.zip",
- "/usr/local"); err != nil {
- log.Println("[RULEX UPGRADE] Unzip error:", err)
- return err
- }
- if err := ossupport.Restart(); err != nil {
- log.Println("[RULEX UPGRADE] Restart rulex error", err)
- return err
- }
- log.Println("[RULEX UPGRADE] Restart rulex success, Upgrade Process Exited")
+ defer func() {
+ // upgrade lock
+ if err := os.Remove(ossupport.UpgradeLockPath); err != nil {
+ utils.CLog("[RULEX UPGRADE] Remove Upgrade Lock File error:%s", err.Error())
+ return
}
+ utils.CLog("[RULEX UPGRADE] Remove Upgrade Lock File Finished")
+ }()
+ if runtime.GOOS != "linux" {
+ utils.CLog("[RULEX UPGRADE] Only Support Linux")
+ return nil
+ }
+ if !c.Bool("upgrade") {
+ utils.CLog("[RULEX UPGRADE] Nothing todo")
+ return nil
}
+ // unzip Firmware
+ utils.CLog("[RULEX UPGRADE] Unzip Firmware")
+ if err := ossupport.UnzipFirmware(
+ ossupport.FirmwarePath, ossupport.MainWorkDir); err != nil {
+ utils.CLog("[RULEX UPGRADE] Unzip error:%s", err.Error())
+ return nil
+ }
+ utils.CLog("[RULEX UPGRADE] Unzip Firmware finished")
+ // Remove old package
+ utils.CLog("[RULEX UPGRADE] Remove Firmware")
+ if err := os.Remove(ossupport.FirmwarePath); err != nil {
+ utils.CLog("[RULEX UPGRADE] Remove Firmware error:%s", err.Error())
+ return nil
+ }
+ utils.CLog("[RULEX UPGRADE] Remove Firmware finished")
+ //
+ utils.CLog("[RULEX UPGRADE] Restart rulex")
+ if err := ossupport.RestartRulex(); err != nil {
+ utils.CLog("[RULEX UPGRADE] Restart rulex error:%s", err.Error())
+ return nil
+ }
+ utils.CLog("[RULEX UPGRADE] Restart rulex finished, Upgrade Process Exited")
os.Exit(0)
return nil
},
@@ -120,28 +168,101 @@ func main() {
},
},
Action: func(c *cli.Context) error {
+ file, err := os.Create(ossupport.RecoverLogPath)
+ if err != nil {
+ utils.CLog(err.Error())
+ return nil
+ }
+ defer file.Close()
+ os.Stdout = file
+ os.Stderr = file
+ // upgrade lock
+ if err := os.WriteFile(ossupport.UpgradeLockPath, []byte{48}, 0755); err != nil {
+ utils.CLog("[DATA RECOVER] Write Recover Lock File error:%s", err.Error())
+ return nil
+ }
+ defer func() {
+ // upgrade lock
+ if err := os.Remove(ossupport.UpgradeLockPath); err != nil {
+ utils.CLog("[DATA RECOVER] Remove Recover Lock File error:%s", err.Error())
+ return
+ }
+ utils.CLog("[DATA RECOVER] Remove Recover Lock File Finished")
+ }()
+ if runtime.GOOS != "linux" {
+ utils.CLog("[DATA RECOVER] Only Support Linux")
+ return nil
+ }
+
if !c.Bool("recover") {
+ utils.CLog("[DATA RECOVER] Nothing todo")
return nil
}
- if err := ossupport.StopRulex(); err != nil {
- log.Println("[DATA RECOVER] Stop rulex error", err)
- return err
+ utils.CLog("[DATA RECOVER] Remove Old Db File")
+ if err := os.Remove(ossupport.RunDbPath); err != nil {
+ utils.CLog("[DATA RECOVER] Remove Old Db File error:%s", err.Error())
+ return nil
}
- dir := "./upload/Backup/"
- fileName := "recovery.db"
- if err := ossupport.MoveFile(dir+fileName, "./rulex.db"); err != nil {
- log.Println("[DATA RECOVER] Move Db File error", err)
- return err
+ utils.CLog("[DATA RECOVER] Remove Old Db File Finished")
+ utils.CLog("[DATA RECOVER] Move New Db File")
+ if err := ossupport.MoveFile(ossupport.RecoveryDbPath,
+ ossupport.RunDbPath); err != nil {
+ utils.CLog("[DATA RECOVER] Move New Db File error:%s", err.Error())
+ return nil
}
- if err := ossupport.Restart(); err != nil {
- log.Println("[DATA RECOVER] Restart rulex error", err)
- return err
+ utils.CLog("[DATA RECOVER] Move New Db File Finished")
+ utils.CLog("[DATA RECOVER] Try to Restart rulex")
+ if err := ossupport.RestartRulex(); err != nil {
+ utils.CLog("[DATA RECOVER] Restart rulex error:%s", err.Error())
+ } else {
+ utils.CLog("[DATA RECOVER] Restart rulex success, Recover Process Exited")
}
- log.Println("[DATA RECOVER] Restart rulex success, Recover Process Exited")
os.Exit(0)
return nil
},
},
+ {
+ Name: "active",
+ Usage: "active -H host -U rhino -P hoot",
+ Hidden: true,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "H",
+ Usage: "active server ip",
+ },
+ &cli.StringFlag{
+ Name: "U",
+ Usage: "active admin username",
+ },
+ &cli.StringFlag{
+ Name: "P",
+ Usage: "active admin password",
+ },
+ },
+
+ Action: func(c *cli.Context) error {
+ host := c.String("H")
+ if host == "" {
+ return fmt.Errorf("[LICENCE ACTIVE]: missing host")
+ }
+ username := c.String("U")
+ if username == "" {
+ return fmt.Errorf("[LICENCE ACTIVE]: missing admin username")
+ }
+ password := c.String("P")
+ if password == "" {
+ return fmt.Errorf("[LICENCE ACTIVE]: missing admin password")
+ }
+ macAddr, err := ossupport.ReadIfaceMacAddr("eth0")
+ if err != nil {
+ return err
+ }
+ // commercial version will implement it
+ utils.CLog("[LICENCE ACTIVE]: Admin(%s,%s), mac addr:[%s] try to request license from %s\n",
+ username, password, macAddr, host)
+ return nil
+ },
+ },
// version
{
Name: "version",
@@ -154,8 +275,8 @@ func main() {
},
Action: func(*cli.Context) error {
version := fmt.Sprintf("[%v-%v-%v]",
- runtime.GOOS, runtime.GOARCH, typex.DefaultVersion.Version)
- fmt.Println("[*] Current Version: " + version)
+ runtime.GOOS, runtime.GOARCH, typex.MainVersion)
+ utils.CLog("[*] Rulex Version: " + version)
return nil
},
},
diff --git a/ossupport/linux_const_value.go b/ossupport/linux_const_value.go
new file mode 100644
index 000000000..0efe3d7c0
--- /dev/null
+++ b/ossupport/linux_const_value.go
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ossupport
+
+/*
+*
+* Linux系统下的一些和应用交互的系统级路径
+*
+ */
+const (
+ // Rulex 工作目录
+ MainWorkDir = "/usr/local"
+ // RULEX 的配置数据库
+ RunDbPath = "/usr/local/rulex.db"
+ // 固件保存路径
+ FirmwarePath = "/usr/local/upload/Firmware/Firmware.zip"
+ // 升级日志
+ UpgradeLogPath = "/usr/local/local-upgrade-log.txt"
+ // 运行时日志
+ RunningLogPath = "/usr/local/rulexlog.txt"
+ // 数据恢复日志
+ RecoverLogPath = "/usr/local/local-recover-log.txt"
+ // 备份锁
+ BackupLockPath = "/var/run/rulex-upgrade.lock"
+ // 升级锁
+ UpgradeLockPath = BackupLockPath
+ // 备份数据库
+ RecoveryDbPath = "/usr/local/upload/Backup/recovery.db"
+)
diff --git a/ossupport/rhinoh3_datetime_ctl_windows.go b/ossupport/rhinoh3_datetime_ctl_windows.go
new file mode 100644
index 000000000..6a49d8153
--- /dev/null
+++ b/ossupport/rhinoh3_datetime_ctl_windows.go
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ossupport
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/hootrhino/wmi"
+)
+
+/*
+*
+* 获取开机时间
+*
+ */
+type Win32OperatingSystem struct {
+ LastBootUpTime string
+}
+
+func GetUptime() (string, error) {
+ var result []Win32OperatingSystem
+ query := "SELECT LastBootUpTime FROM Win32_OperatingSystem"
+
+ err := wmi.Query(query, &result)
+ if err != nil {
+ return "", err
+ }
+
+ if len(result) > 0 {
+ // 20231226170151.500000+480
+ wmicTime := parseWinWmicTime(result[0].LastBootUpTime)
+ Seconds := wmicTime.Abs().Seconds()
+ hour := int(Seconds / 3600)
+ minute := int(Seconds/60) % 60
+ second := int(Seconds) % 60
+ return fmt.Sprintf("%d Hours %02d Minutes %02d Seconds", hour, minute, second), nil
+ }
+
+ return "0 Year 0 Month 0 Days 0 Hours 0 Minutes 0 Seconds",
+ fmt.Errorf("failed to retrieve system uptime")
+}
+
+/*
+*
+* 解析ISO时间戳
+*
+ */
+func parseWinWmicTime(timestamp string) time.Duration {
+ parts1 := strings.Split(timestamp, "+")
+ if len(parts1) != 2 {
+ return 0
+ }
+ //2023 12 26 17 01 51.500000+480
+ mainTimestamp, err := time.Parse("20060102150405", parts1[0])
+ if err != nil {
+ return 0
+ }
+ return time.Until(mainTimestamp)
+}
diff --git a/ossupport/rhinoh3_ubuntu18_datetime_ctl.go b/ossupport/rhinoh3_ubuntu18_datetime_ctl_linux.go
similarity index 88%
rename from ossupport/rhinoh3_ubuntu18_datetime_ctl.go
rename to ossupport/rhinoh3_ubuntu18_datetime_ctl_linux.go
index 5e95a36ce..14e8ddc9f 100644
--- a/ossupport/rhinoh3_ubuntu18_datetime_ctl.go
+++ b/ossupport/rhinoh3_ubuntu18_datetime_ctl_linux.go
@@ -17,6 +17,7 @@ package ossupport
import (
"fmt"
+ "golang.org/x/sys/unix"
"os/exec"
"strings"
"time"
@@ -161,14 +162,21 @@ func SetTimeZone(timezone string) error {
* 获取开机时间
*
*/
+
func GetUptime() (string, error) {
- shell := `
-awk '{print int($1 / 86400) " days " int(($1 % 86400) / 3600) " hours " int(($1 % 3600) / 60) " minutes"}' /proc/uptime
-`
- cmd := exec.Command("sh", "-c", shell)
- output, err := cmd.CombinedOutput()
- if err != nil {
- return "", fmt.Errorf("GetUptime error:%s,%s", string(output), err.Error())
+ var info unix.Sysinfo_t
+
+ if err := unix.Sysinfo(&info); err != nil {
+ return "0 Year 0 Month 0 Days 0 Hours 0 Minutes 0 Seconds", err
}
- return strings.Trim(string(output), "\n"), nil
+
+ return formatUptime(int64(info.Uptime)), nil
+}
+
+func formatUptime(uptime int64) string {
+ days := uptime / 86400
+ hours := (uptime % 86400) / 3600
+ minutes := (uptime % 3600) / 60
+ seconds := uptime % 60
+ return fmt.Sprintf("%d days %d Hours %02d Minutes %02d Seconds", days, hours, minutes, seconds)
}
diff --git a/ossupport/rhinoh3_ubuntu18_firmware_ctl.go b/ossupport/rhinoh3_ubuntu18_firmware_ctl.go
index c66b02dce..f70ad0e73 100644
--- a/ossupport/rhinoh3_ubuntu18_firmware_ctl.go
+++ b/ossupport/rhinoh3_ubuntu18_firmware_ctl.go
@@ -17,11 +17,10 @@ package ossupport
import (
"fmt"
- "io"
"log"
"os"
"os/exec"
- "strings"
+ "path/filepath"
)
/*
@@ -30,39 +29,30 @@ import (
*
*/
func StopRulex() error {
- cmd := exec.Command("service", "rulex", "stop")
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("%s,%s", err, string(out))
+ pid, err1 := GetEarliestProcessPID("rulex")
+ if err1 != nil {
+ return err1
+ }
+ err2 := KillProcess(pid)
+ if err2 != nil {
+ return err2
}
return nil
}
/*
*
-* 重启
+* 重启, 依赖于守护进程脚本, 因此这个不是通用的
*
*/
-func Restart() error {
- {
- cmd := exec.Command("sudo", "systemctl", "daemon-reload")
- cmd.SysProcAttr = NewSysProcAttr()
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("%s,%s", err, string(out))
- }
- log.Println("[Prepare Stage] systemctl daemon-reload:", string(out))
-
- }
- {
- cmd := exec.Command("sudo", "service", "rulex", "start")
- cmd.SysProcAttr = NewSysProcAttr()
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("%s,%s", err, string(out))
- }
- log.Println("[Prepare Stage] service start:", string(out))
-
+func RestartRulex() error {
+ cmd := exec.Command("/etc/init.d/rulex.service", "restart")
+ cmd.SysProcAttr = NewSysProcAttr()
+ cmd.Env = os.Environ()
+ err := cmd.Start()
+ if err != nil {
+ log.Println("Restart Rulex Failed:", err)
+ return err
}
return nil
}
@@ -71,27 +61,28 @@ func Restart() error {
*
* 恢复上传的DB
1 停止RULEX
-2 删除DB
-3 复制DB过去
-4 重启
+2 删除老DB
+3 复制新DB到路径
+3 删除PID,停止守护进程
+4 重启(脚本会新建PID)
- path: /usr/local/rulex, args: recover=true
*
*/
+func FileExists(filename string) bool {
+ _, err := os.Stat(filename)
+ return !os.IsNotExist(err)
+}
+
+/*
+*
+* 数据备份
+*
+ */
func StartRecoverProcess() {
- log.Printf("Start Recover Process Pid=%d, Gid=%d", os.Getpid(), os.Getegid())
- cmd := exec.Command("bash", "-c", "/usr/local/rulex -recover=true")
+ cmd := exec.Command("./rulex", "recover", "-recover=true")
cmd.SysProcAttr = NewSysProcAttr()
cmd.Env = os.Environ()
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if cmd.Process != nil {
- cmd.Process.Release() // 用来分离进程用,简直天坑参数!!!
- }
err := cmd.Start()
- if cmd.Process != nil {
- cmd.Process.Release() // 用来分离进程用,简直天坑参数!!!
- }
if err != nil {
log.Println("Start Recover Process Failed:", err)
return
@@ -104,21 +95,11 @@ func StartRecoverProcess() {
* 启用升级进程
*
*/
-func StartUpgradeProcess(path string, args []string) {
- log.Printf("Start Upgrade Process Pid=%d, Gid=%d", os.Getpid(), os.Getegid())
- cmd := exec.Command("bash", "-c", path+" "+strings.Join(args, " "))
+func StartUpgradeProcess() {
+ cmd := exec.Command("./rulex", "upgrade", "-upgrade=true")
cmd.SysProcAttr = NewSysProcAttr()
cmd.Env = os.Environ()
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if cmd.Process != nil {
- cmd.Process.Release() // 用来分离进程用,简直天坑参数!!!
- }
err := cmd.Start()
- if cmd.Process != nil {
- cmd.Process.Release() // 用来分离进程用,简直天坑参数!!!
- }
if err != nil {
log.Println("Start Upgrade Process Failed:", err)
return
@@ -161,25 +142,14 @@ func UnzipFirmware(zipFile, destDir string) error {
*
*/
func MoveFile(sourcePath, destPath string) error {
- inputFile, err := os.Open(sourcePath)
- if err != nil {
- return fmt.Errorf("couldn't open source file: %s", err)
- }
- outputFile, err := os.Create(destPath)
- if err != nil {
- inputFile.Close()
- return fmt.Errorf("couldn't open dest file: %s", err)
- }
- defer outputFile.Close()
- _, err = io.Copy(outputFile, inputFile)
- inputFile.Close()
- if err != nil {
- return fmt.Errorf("Writing to output file failed: %s", err)
+
+ destDir := filepath.Dir(destPath)
+ if err := os.MkdirAll(destDir, 0755); err != nil {
+ return fmt.Errorf("failed to create destination directory: %w", err)
}
- // The copy was successful, so now delete the original file
- err = os.Remove(sourcePath)
+ err := os.Rename(sourcePath, destPath)
if err != nil {
- return fmt.Errorf("Failed removing original file: %s", err)
+ return fmt.Errorf("failed to move file: %w", err)
}
return nil
}
diff --git a/ossupport/rhinoh3_ubuntu18_process_ctrl.go b/ossupport/rhinoh3_ubuntu18_process_ctrl.go
new file mode 100644
index 000000000..591ed5971
--- /dev/null
+++ b/ossupport/rhinoh3_ubuntu18_process_ctrl.go
@@ -0,0 +1,81 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ossupport
+
+import (
+ "fmt"
+ "os/exec"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+/*
+*
+* kill -9
+*
+ */
+func KillProcess(processID int) error {
+ cmd := exec.Command("kill", "-9", fmt.Sprintf("%d", processID))
+ err := cmd.Run()
+ return err
+}
+
+/*
+*
+* pgrep rulex -> 38506\n
+*
+ */
+func GetProcessPID(processName string) (int, error) {
+ cmd := exec.Command("pgrep", processName)
+ output, err := cmd.Output()
+ if err != nil {
+ return 0, err
+ }
+ pidString := strings.TrimSpace(string(output))
+ pid, err := strconv.Atoi(pidString)
+ if err != nil {
+ return 0, err
+ }
+ return pid, nil
+}
+
+/*
+*
+* 取最老的那个进程Id
+*
+ */
+func GetEarliestProcessPID(processName string) (int, error) {
+ cmd := exec.Command("pgrep", processName)
+ output, err := cmd.Output()
+ if err != nil {
+ return 0, err
+ }
+ pidStrings := strings.Fields(string(output))
+ var pids []int
+ for _, pidString := range pidStrings {
+ pid, err := strconv.Atoi(pidString)
+ if err == nil {
+ pids = append(pids, pid)
+ }
+ }
+ if len(pids) == 0 {
+ return 0, fmt.Errorf("No process found with name %s", processName)
+ }
+ sort.Ints(pids)
+ earliestPID := pids[0]
+ return earliestPID, nil
+}
diff --git a/ossupport/rhinoh3_ubuntu18xx_network_ctl.go b/ossupport/rhinoh3_ubuntu18xx_network_ctl.go
index 4b61684a4..7fc0a1164 100644
--- a/ossupport/rhinoh3_ubuntu18xx_network_ctl.go
+++ b/ossupport/rhinoh3_ubuntu18xx_network_ctl.go
@@ -3,6 +3,8 @@ package ossupport
import (
"encoding/json"
"fmt"
+ "os"
+ "path/filepath"
"strings"
)
@@ -68,3 +70,23 @@ func (iface *EtcNetworkConfig) GenEtcConfig() string {
configText := strings.Join(configLines, "\n")
return configText
}
+
+/*
+*
+* 获取网卡的MAC地址
+*
+ */
+func ReadIfaceMacAddr(ifaceName string) (string, error) {
+ // 构建文件路径
+ filePath := filepath.Join("/sys/class/net", ifaceName, "address")
+
+ // 读取文件内容
+ content, err := os.ReadFile(filePath)
+ if err != nil {
+ return "", err
+ }
+ if len(content) < 10 {
+ return "", fmt.Errorf("get mac address error:%s", ifaceName)
+ }
+ return string(content[:len(content)-1]), nil
+}
diff --git a/ossupport/sysattr_linux.go b/ossupport/sysattr_linux.go
index 1b8d22e8b..967cc5d1a 100644
--- a/ossupport/sysattr_linux.go
+++ b/ossupport/sysattr_linux.go
@@ -6,8 +6,6 @@ import (
func NewSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
- Setsid: true,
- Pdeathsig: syscall.SIGTERM,
- Cloneflags: syscall.CLONE_NEWUTS,
+ Setsid: true,
}
}
diff --git a/ossupport/unix_dev_tree_ctrl.go b/ossupport/unix_dev_tree_ctrl.go
new file mode 100644
index 000000000..567683352
--- /dev/null
+++ b/ossupport/unix_dev_tree_ctrl.go
@@ -0,0 +1,49 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package ossupport
+
+import (
+ "os"
+ "regexp"
+)
+
+const devFolder = "/dev"
+const regexFilter = "(ttyS|ttyHS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}"
+
+func GetPortsListUnix() ([]string, error) {
+ files, err := os.ReadDir(devFolder)
+ if err != nil {
+ return nil, err
+ }
+ ports := make([]string, 0, len(files))
+ regex, err := regexp.Compile(regexFilter)
+ if err != nil {
+ return nil, err
+ }
+ for _, f := range files {
+ // Skip folders
+ if f.IsDir() {
+ continue
+ }
+ if !regex.MatchString(f.Name()) {
+ continue
+ }
+ portName := devFolder + "/" + f.Name()
+ ports = append(ports, portName)
+ }
+
+ return ports, nil
+}
diff --git a/plugin/cs104_server/cs_104_server.go b/plugin/cs104_server/cs_104_server.go
index e834f3466..130e10178 100644
--- a/plugin/cs104_server/cs_104_server.go
+++ b/plugin/cs104_server/cs_104_server.go
@@ -96,9 +96,9 @@ func (cs *cs104Server) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/demo_plugin/demo_plugin.go b/plugin/demo_plugin/demo_plugin.go
index 015c5a645..97b188b79 100644
--- a/plugin/demo_plugin/demo_plugin.go
+++ b/plugin/demo_plugin/demo_plugin.go
@@ -33,9 +33,9 @@ func (hh *DemoPlugin) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/devguide.md b/plugin/devguide.md
index 0cf1a3a86..73260458e 100644
--- a/plugin/devguide.md
+++ b/plugin/devguide.md
@@ -81,9 +81,9 @@ func (hh *DemoPlugin) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/generic_watchdog/generic_watchdog_linux.go b/plugin/generic_watchdog/generic_watchdog_linux.go
index 90efa7be9..409db1930 100644
--- a/plugin/generic_watchdog/generic_watchdog_linux.go
+++ b/plugin/generic_watchdog/generic_watchdog_linux.go
@@ -112,9 +112,9 @@ func (hh *genericWatchDog) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/generic_watchdog/generic_watchdog_windows.go b/plugin/generic_watchdog/generic_watchdog_windows.go
index a066a10e4..a8319b8da 100644
--- a/plugin/generic_watchdog/generic_watchdog_windows.go
+++ b/plugin/generic_watchdog/generic_watchdog_windows.go
@@ -39,7 +39,7 @@ func NewGenericWatchDog() *genericWatchDog {
}
func (dog *genericWatchDog) Init(config *ini.Section) error {
- return fmt.Errorf("OS support Wdog:%s", runtime.GOOS)
+ return fmt.Errorf("OS Not Support Soft WatchDog:%s", runtime.GOOS)
}
func (dog *genericWatchDog) Start(typex.RuleX) error {
@@ -57,9 +57,9 @@ func (hh *genericWatchDog) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/http_server/apis/device_api.go b/plugin/http_server/apis/device_api.go
deleted file mode 100644
index 2509f3e52..000000000
--- a/plugin/http_server/apis/device_api.go
+++ /dev/null
@@ -1,370 +0,0 @@
-package apis
-
-import (
- "errors"
- "io"
- "strconv"
-
- common "github.com/hootrhino/rulex/plugin/http_server/common"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/server"
- "github.com/hootrhino/rulex/plugin/http_server/service"
-
- "github.com/xuri/excelize/v2"
-
- "github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
-
- "github.com/gin-gonic/gin"
- "gopkg.in/square/go-jose.v2/json"
-)
-
-type DeviceVo struct {
- UUID string `json:"uuid"`
- Gid string `json:"gid"`
- Name string `json:"name"`
- Type string `json:"type"`
- State int `json:"state"`
- Config map[string]interface{} `json:"config"`
- Description string `json:"description"`
-}
-
-/*
-*
-* 列表先读数据库,然后读内存,合并状态后输出
-*
- */
-func DeviceDetail(c *gin.Context, ruleEngine typex.RuleX) {
- uuid, _ := c.GetQuery("uuid")
- mdev, err := service.GetMDeviceWithUUID(uuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400EmptyObj(err))
- return
- }
- DeviceVo := DeviceVo{}
- DeviceVo.UUID = mdev.UUID
- DeviceVo.Name = mdev.Name
- DeviceVo.Type = mdev.Type
- DeviceVo.Description = mdev.Description
- DeviceVo.Config = mdev.GetConfig()
- //
- device := ruleEngine.GetDevice(mdev.UUID)
- if device == nil {
- DeviceVo.State = int(typex.DEV_STOP)
- } else {
- DeviceVo.State = int(device.Device.Status())
- }
- Group := service.GetVisualGroup(mdev.UUID)
- DeviceVo.Gid = Group.UUID
- c.JSON(common.HTTP_OK, common.OkWithData(DeviceVo))
-}
-
-/*
-*
-* 分组查看
-*
- */
-func ListDeviceByGroup(c *gin.Context, ruleEngine typex.RuleX) {
- Gid, _ := c.GetQuery("uuid")
- devices := []DeviceVo{}
- _, MDevices := service.FindByType(Gid, "DEVICE")
- for _, mdev := range MDevices {
- DeviceVo := DeviceVo{}
- DeviceVo.UUID = mdev.UUID
- DeviceVo.Name = mdev.Name
- DeviceVo.Type = mdev.Type
- DeviceVo.Description = mdev.Description
- DeviceVo.Config = mdev.GetConfig()
- //
- device := ruleEngine.GetDevice(mdev.UUID)
- if device == nil {
- DeviceVo.State = int(typex.DEV_STOP)
- } else {
- DeviceVo.State = int(device.Device.Status())
- }
- Group := service.GetVisualGroup(mdev.UUID)
- DeviceVo.Gid = Group.UUID
- devices = append(devices, DeviceVo)
- }
- c.JSON(common.HTTP_OK, common.OkWithData(devices))
-}
-
-// 删除设备
-func DeleteDevice(c *gin.Context, ruleEngine typex.RuleX) {
- uuid, _ := c.GetQuery("uuid")
- Mdev, err := service.GetMDeviceWithUUID(uuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // 要处理这个空字符串 ""
- if Mdev.BindRules.Len() == 1 && len(Mdev.BindRules[0]) != 0 {
- c.JSON(common.HTTP_OK, common.Error("Can't remove, Already have rule bind:"+Mdev.BindRules.String()))
- return
- }
- // 检查是否有规则被绑定了
- for _, ruleId := range Mdev.BindRules {
- if ruleId != "" {
- _, err0 := service.GetMRuleWithUUID(ruleId)
- if err0 != nil {
- c.JSON(common.HTTP_OK, common.Error400(err0))
- return
- }
- }
-
- }
-
- // 检查是否通用Modbus设备.需要同步删除点位表记录
- if Mdev.Type == "GENERIC_MODBUS_POINT_EXCEL" {
- if err := service.DeleteModbusPointAndDevice(uuid); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- } else {
- if err := service.DeleteDevice(uuid); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- }
-
- old := ruleEngine.GetDevice(uuid)
- if old != nil {
- if old.Device.Status() == typex.DEV_UP {
- old.Device.Stop()
- }
- }
-
- ruleEngine.RemoveDevice(uuid)
- c.JSON(common.HTTP_OK, common.Ok())
-
-}
-
-// 创建设备
-func CreateDevice(c *gin.Context, ruleEngine typex.RuleX) {
-
- form := DeviceVo{}
- if err := c.ShouldBindJSON(&form); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- configJson, err := json.Marshal(form.Config)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- newUUID := utils.DeviceUuid()
- MDevice := model.MDevice{
- UUID: newUUID,
- Type: form.Type,
- Name: form.Name,
- Description: form.Description,
- Config: string(configJson),
- BindRules: []string{},
- }
- if err := service.InsertDevice(&MDevice); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // 新建大屏的时候必须给一个分组
- if err := service.BindResource(form.Gid, MDevice.UUID); err != nil {
- c.JSON(common.HTTP_OK, common.Error("Group not found"))
- return
- }
- if err := server.LoadNewestDevice(newUUID, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.OkWithMsg(err.Error()))
- return
- }
- c.JSON(common.HTTP_OK, common.Ok())
-
-}
-
-// 更新设备
-func UpdateDevice(c *gin.Context, ruleEngine typex.RuleX) {
-
- form := DeviceVo{}
- if err := c.ShouldBindJSON(&form); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- configJson, err := json.Marshal(form.Config)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- if form.UUID == "" {
- c.JSON(common.HTTP_OK, common.Error("missing 'uuid' fields"))
- return
- }
- // 更新的时候从数据库往外面拿
- Device, err := service.GetMDeviceWithUUID(form.UUID)
- if err != nil {
- c.JSON(common.HTTP_OK, err)
- return
- }
- MDevice := model.MDevice{
- Type: form.Type,
- Name: form.Name,
- Description: form.Description,
- Config: string(configJson),
- }
- if err := service.UpdateDevice(Device.UUID, &MDevice); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // 取消绑定分组,删除原来旧的分组
- Group := service.GetVisualGroup(Device.UUID)
- if err := service.UnBindResource(Group.UUID, Device.UUID); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- // 重新绑定分组
- if err := service.BindResource(form.Gid, Device.UUID); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- if err := server.LoadNewestDevice(form.UUID, ruleEngine); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
-
- c.JSON(common.HTTP_OK, common.Ok())
-}
-
-// ModbusPoints 获取modbus_excel类型的点位数据
-func ModbusPoints(c *gin.Context, ruleEngine typex.RuleX) {
- deviceUuid := c.GetString("deviceUuid")
- list, err := service.AllModbusPointByDeviceUuid(deviceUuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- c.JSON(common.HTTP_OK, common.OkWithData(list))
-}
-
-// UpdateModbusPoint 更新modbus_excel类型的点位数据
-func UpdateModbusPoint(c *gin.Context, ruleEngine typex.RuleX) {
- type Form struct {
- Id uint
- DeviceUuid string `json:"deviceUuid" gorm:"not null"`
- Tag string `json:"tag" gorm:"not null"`
- Function int `json:"function" gorm:"not null"`
- SlaverId byte `json:"slaverId" gorm:"not null"`
- StartAddress uint16 `json:"startAddress" gorm:"not null"`
- Quality uint16 `json:"quality" gorm:"not null"`
- }
-
- form := Form{}
- if err := c.ShouldBindJSON(&form); err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
-
- err := service.UpdateModbusPoint(model.MModbusPointPosition{
- RulexModel: model.RulexModel{
- ID: form.Id,
- },
- DeviceUuid: form.DeviceUuid,
- Tag: form.Tag,
- Function: form.Function,
- SlaverId: form.SlaverId,
- StartAddress: form.StartAddress,
- Quality: form.Quality,
- })
-
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
-
- c.JSON(common.HTTP_OK, common.Ok())
-
-}
-
-// ModbusSheetImport 上传Excel文件
-func ModbusSheetImport(c *gin.Context, ruleEngine typex.RuleX) {
- // 解析 multipart/form-data 类型的请求体
- err := c.Request.ParseMultipartForm(32 << 20) // 限制上传文件大小为 512MB
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
-
- // 获取上传的文件
- file, header, err := c.Request.FormFile("file")
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- defer file.Close()
-
- deviceUuid := c.Request.Form.Get("deviceUuid")
-
- // 检查文件类型是否为 Excel
- contentType := header.Header.Get("Content-Type")
- if contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" &&
- contentType != "application/vnd.ms-excel" {
- c.JSON(common.HTTP_OK, common.Error("上传的文件必须是 Excel 格式"))
- return
- }
-
- // 判断文件大小是否符合要求(1MB)
- if header.Size > 1024*1024 {
- c.JSON(common.HTTP_OK, common.Error("Excel file size cannot be greater than 1MB"))
- return
- }
-
- list, err := parseModbusPointExcel(file, "Sheet1", deviceUuid)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
-
- err = service.InsertModbusPointPosition(list)
- if err != nil {
- c.JSON(common.HTTP_OK, common.Error400(err))
- return
- }
- c.JSON(common.HTTP_OK, common.Ok())
-}
-
-func parseModbusPointExcel(r io.Reader,
- sheetName string,
- deviceUuid string) (list []model.MModbusPointPosition, err error) {
- excelFile, err := excelize.OpenReader(r)
- if err != nil {
- return nil, err
- }
- defer func() {
- excelFile.Close()
- }()
- // 读取表格
- rows, err := excelFile.GetRows(sheetName)
- if err != nil {
- return nil, err
- }
- // 判断首行标头
- // |Tag|Function|SlaverId|StartAddress|Quality|
- if rows[0][0] != "Tag" || rows[0][1] != "Function" || rows[0][2] != "SlaverId" || rows[0][3] != "StartAddress" || rows[0][4] != "Quality" {
- return nil, errors.New("表头不符合要求")
- }
-
- list = make([]model.MModbusPointPosition, 0)
-
- for i := 1; i < len(rows); i++ {
- row := rows[i]
- function, _ := strconv.Atoi(row[1])
- slaverId, _ := strconv.ParseInt(row[2], 10, 8)
- address, _ := strconv.ParseUint(row[3], 10, 16)
- quantity, _ := strconv.ParseUint(row[3], 10, 16)
- model := model.MModbusPointPosition{
- DeviceUuid: deviceUuid,
- Tag: row[0],
- Function: function,
- SlaverId: byte(slaverId),
- StartAddress: uint16(address),
- Quality: uint16(quantity),
- }
- list = append(list, model)
- }
- return list, nil
-}
diff --git a/plugin/http_server/server/www/index.html b/plugin/http_server/server/www/index.html
deleted file mode 100644
index 4903f8789..000000000
--- a/plugin/http_server/server/www/index.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
- Rulex
-
-
-
-
-
-Hello,Rulex
-
-
-
\ No newline at end of file
diff --git a/plugin/http_server/service/memory_usage_linux.go b/plugin/http_server/service/memory_usage_linux.go
deleted file mode 100644
index 64d55ac59..000000000
--- a/plugin/http_server/service/memory_usage_linux.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2023 wwhai
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-package service
-
-import (
- "bufio"
- "fmt"
- "math"
- "os"
- "strconv"
- "strings"
-)
-
-// GetMemPercent 获取Linux内存使用百分比
-func GetMemPercent() (float64, error) {
- // 打开 /proc/meminfo 文件
- file, err := os.Open("/proc/meminfo")
- if err != nil {
- return 0, fmt.Errorf("Open /proc/meminfo error: %v", err)
- }
- defer file.Close()
-
- // 初始化变量用于存储内存信息
- var totalMem, freeMem int64
-
- // 逐行读取文件内容
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := scanner.Text()
- fields := strings.Fields(line)
- if len(fields) == 3 {
- // 提取字段名、值和单位
- fieldName := fields[0]
- fieldValue := fields[1]
-
- // 将值转换为整数
- value, err := parseMemInfoValue(fieldValue)
- if err != nil {
- return 0, fmt.Errorf("parse MemInfo Value error: %v", err)
- }
-
- // 根据字段名更新内存信息
- switch fieldName {
- case "MemTotal:":
- totalMem = value
- case "MemFree:":
- freeMem = value
- }
- }
- }
-
- // 计算已使用内存
- usedMem := totalMem - freeMem
-
- // 计算内存使用百分比
- memUsagePercent := float64(usedMem) / float64(totalMem) * 100
-
- return math.Round(memUsagePercent*100) / 100, nil
-}
-
-// parseMemInfoValue 解析 /proc/meminfo 文件中的内存值(以 KB 为单位)
-func parseMemInfoValue(valueStr string) (int64, error) {
- value, err := strconv.ParseInt(valueStr, 10, 64)
- if err != nil {
- return 0, err
- }
- return value * 1024, nil
-}
diff --git a/plugin/icmp_sender/icmp_sender.go b/plugin/icmp_sender/icmp_sender.go
index a2b4a62d3..0947eb0b2 100644
--- a/plugin/icmp_sender/icmp_sender.go
+++ b/plugin/icmp_sender/icmp_sender.go
@@ -40,9 +40,9 @@ func (hh *ICMPSender) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v1.0.0",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
@@ -84,16 +84,15 @@ func (icmp *ICMPSender) Service(arg typex.ServiceArg) typex.ServiceResult {
for i := 0; i < 5; i++ {
switch ip := tt[0].(type) {
case string:
- if Duration, err := pingQ(ip, 2000*time.Millisecond); err != nil {
+ if Duration, err := pingQ(ip, 1000*time.Millisecond); err != nil {
glogger.GLogger.WithFields(Fields).Info(fmt.Sprintf(
- "[Count:%d] Ping Error:%s", i,
- err.Error()))
+ "[Count:%d] Ping Error:%s", i, err.Error()))
} else {
glogger.GLogger.WithFields(Fields).Info(fmt.Sprintf(
- "[Count:%d] Ping Reply From %s: time=%v ms TTL=128", i,
- tt, Duration))
+ "[Count:%d] Ping Reply From %s: time=%v ms TTL=128", i, tt, Duration))
}
- time.Sleep(1 * time.Second)
+ // 300毫秒
+ time.Sleep(100 * time.Millisecond)
}
}
diff --git a/plugin/license_manager/cert_codec.go b/plugin/license_manager/cert_codec.go
deleted file mode 100644
index 727b9b513..000000000
--- a/plugin/license_manager/cert_codec.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package licensemanager
-
-import (
- "crypto/rsa"
- "crypto/x509"
- "encoding/pem"
- "fmt"
- "reflect"
- "unsafe"
-)
-
-func decodeCert(data []byte) (*Certificate, error) {
- // 解析证书
- block, _ := pem.Decode(data)
- if block == nil {
- return nil, fmt.Errorf("illegal certificate")
- }
- xcert, err := x509.ParseCertificate(block.Bytes)
- if err != nil {
- return nil, err
- }
-
- key, ok := xcert.PublicKey.(*rsa.PublicKey)
- if !ok {
- return nil, ErrUnsupportedPublicKey
- }
-
- var cert = &Certificate{
- Raw: b2s(data),
- Issuer: xcert.Issuer,
- Subject: xcert.Subject,
- NotBefore: xcert.NotBefore,
- NotAfter: xcert.NotAfter,
- PublicKey: key,
- }
- return cert, nil
-}
-
-func b2s(b []byte) string {
- return *(*string)(unsafe.Pointer(&b))
-}
-
-func s2b(s string) (b []byte) {
- var sh = (*reflect.StringHeader)(unsafe.Pointer(&s))
- var bh = (*reflect.SliceHeader)(unsafe.Pointer(&b))
- bh.Len = sh.Len
- bh.Cap = sh.Len
- bh.Data = sh.Data
- return
-}
diff --git a/plugin/license_manager/license_loader.go b/plugin/license_manager/license_loader.go
deleted file mode 100644
index 5f6a3b3c5..000000000
--- a/plugin/license_manager/license_loader.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package licensemanager
-
-import (
- "bytes"
- "crypto/md5"
- "crypto/rand"
- "crypto/rsa"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "runtime"
- "time"
-
- "github.com/hootrhino/rulex/glogger"
-)
-
-const defaultCertPath = "./rulex.pem"
-
-func loadAndVerifyCert(conf LicenseConfig) (*Certificate, error) {
- path := conf.LocalAddr
- if len(path) == 0 {
- path = defaultCertPath
- }
- // 加载证书
- cert, isLocal, err := loadCert(path, conf.RemoteAddr, conf.NetName)
- if err != nil {
- return nil, err
- }
-
- if !isLocal {
- // 第一次拉到证书,联网校验
- if err := onlineVerifyCert(conf.RemoteAddr, conf.NetName, cert.PublicKey); err != nil {
- return nil, err
- }
- // 校验成功,证书写入到本地
- writeToFile(path, cert.Raw)
- } else {
- // 本地已有证书,离线校验即可
- if err := offlineVerifyCert(cert, conf.NetName); err != nil {
- // 验证失败,移除证书
- os.Remove(path)
- return nil, err
- }
- }
- return cert, nil
-}
-
-func writeToFile(path string, s string) {
- file, err := os.Create(path)
- if err != nil {
- glogger.GLogger.Error("license_manager:writeToFile", err.Error())
- // fmt.Println("license_manager:writeToFile", err. common.Error())
- return
- }
- defer file.Close()
- file.Write(s2b(s))
- file.Sync()
-}
-
-func loadCert(local, remote, netName string) (*Certificate, bool, error) {
- cert, err := loadLocalCert(local)
- if err == nil {
- return cert, true, nil
- }
-
- cert, err1 := pullRemoteCert(remote, netName)
- if err1 == nil {
- // 写入到本地
- return cert, false, nil
- }
-
- return nil, false, fmt.Errorf("load cert from local '%s': %s, load cert from remote '%s': %s",
- local, err.Error(), remote, err1.Error())
-}
-
-func loadLocalCert(path string) (*Certificate, error) {
- // 没有配置路径,使用默认路径
- if len(path) == 0 {
- path = defaultCertPath
- }
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- // 读证书文件
- data, err := ioutil.ReadAll(file)
- if err != nil {
- return nil, err
- }
-
- cert, err := decodeCert(data)
-
- return cert, err
-}
-
-// DeviceInfo 硬件信息
-type DeviceInfo struct {
- MAC string `json:"mac"` // 硬件mac地址
- OS string `json:"os"`
- Arch string `json:"arch"`
-}
-
-type CertificateResponse struct {
- Ret int `json:"ret"`
- Msg string `json:"msg"`
- Data string `json:"data"`
-}
-
-// pullRemoteCert 请求远程主机生成证书并下发
-/*
-POST http://remote.site/xxx/cert
-request:
-{
- "mac": "xxx",
- "os": "linux",
- "arch": "arm"
-}
-response:
-{
- "ret":0,
- "msg": "",
- "data": "-----BEGIN CERTIFICATE----"
-}
-*/
-func pullRemoteCert(url, netName string) (*Certificate, error) {
- if len(url) == 0 {
- return nil, errors.New("empty url")
- }
- hw := deviceInfo(netName)
- if len(hw.MAC) == 0 {
- return nil, ErrUnknownHardwareAddr
- }
-
- // 构建请求
- infoBytes, _ := json.Marshal(&hw)
- body := bytes.NewReader(infoBytes)
- req, err := http.NewRequest(http.MethodPost, url, body)
- if err != nil {
- return nil, err
- }
- // 请求证书
- var cert *Certificate
- err = retryRequest(req, func(resp *http.Response) error {
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
- var cr CertificateResponse
- if err := json.Unmarshal(data, &cr); err != nil {
- return err
- }
-
- if cr.Ret != 0 {
- return fmt.Errorf("ret: %d, msg: %s", cr.Ret, cr.Msg)
- }
-
- // 解析证书
- cert, err = decodeCert(s2b(cr.Data))
- return err
- })
-
- return cert, err
-}
-
-type VerifyCertRequest struct {
- MAC string `json:"mac"`
- Data string `json:"data"`
-}
-
-type VerifyCertResponse struct {
- Ret int `json:"ret"` // 返回值
- Msg string `json:"msg"` //
-}
-
-/*
-GET http://remote.site/xxx/cert
-request:
-
- {
- "mac": "xxx", // 硬件地址,标识机器
- "data" "xxx" // 加密后的数据
- }
-
-response:
-*/
-
-var (
- ErrUnsupportedPublicKey = errors.New("unsupported public key type")
- ErrUnknownHardwareAddr = errors.New("unknown hardware addr")
- ErrMismatchedCertificate = errors.New("mismatched certificate")
-)
-
-func onlineVerifyCert(url, netName string, key *rsa.PublicKey) error {
- hw := deviceInfo(netName)
- if len(hw.MAC) == 0 {
- return ErrUnknownHardwareAddr
- }
-
- // 加密数据
- src := []byte(hw.MAC)
- dst, err := rsa.EncryptPKCS1v15(rand.Reader, key, src)
- if err != nil {
- return err
- }
-
- // 构建请求
- rc := VerifyCertRequest{
- MAC: string(src),
- Data: base64.StdEncoding.EncodeToString(dst),
- }
-
- bts, _ := json.Marshal(&rc)
- body := bytes.NewReader(bts)
- req, err := http.NewRequest(http.MethodGet, url, body)
- if err != nil {
- return err
- }
-
- // 请求验证
- err = retryRequest(req, func(resp *http.Response) error {
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
-
- var vc VerifyCertResponse
- if err := json.Unmarshal(data, &vc); err != nil {
- return err
- }
-
- // 检查响应
- if vc.Ret != 0 {
- return fmt.Errorf("ret: %d, msg: %s", vc.Ret, vc.Msg)
- }
- return nil
- })
-
- return err
-}
-
-// retryRequest 重试请求,第1次超时1.5s,每次重试增加0.75s
-func retryRequest(req *http.Request, fn func(*http.Response) error) error {
- var err error
- for i := 0; i < 3; i++ {
- client := http.Client{
- Timeout: time.Duration(1500+i*750) * time.Millisecond,
- }
- // 发起请求
- var resp *http.Response
- resp, err = client.Do(req)
- if err != nil {
- continue
- }
- // 处理响应
- if err = fn(resp); err != nil {
- continue
- }
-
- return nil
- }
-
- return err
-}
-
-func offlineVerifyCert(cert *Certificate, netName string) error {
- var mac string
- inte, err := net.InterfaceByName(netName)
- if err == nil && inte.HardwareAddr != nil {
- mac = inte.HardwareAddr.String()
- md5Byts := md5.Sum(s2b(mac))
- mac = hex.EncodeToString(md5Byts[:])
- }
- if len(mac) == 0 {
- return fmt.Errorf("unknown netName: %s", netName)
- }
-
- if mac != cert.Subject.CommonName {
- return ErrMismatchedCertificate
- }
- return nil
-}
-
-func deviceInfo(name string) DeviceInfo {
- info := DeviceInfo{
- OS: runtime.GOOS,
- Arch: runtime.GOARCH,
- }
-
- inte, err := net.InterfaceByName(name)
- if err == nil && inte.HardwareAddr != nil {
- info.MAC = inte.HardwareAddr.String()
- }
-
- return info
-}
diff --git a/plugin/license_manager/license_manager.go b/plugin/license_manager/license_manager.go
index bfb12c35f..858c14e07 100644
--- a/plugin/license_manager/license_manager.go
+++ b/plugin/license_manager/license_manager.go
@@ -6,89 +6,78 @@ package licensemanager
*/
import (
- "crypto/rsa"
- "crypto/x509/pkix"
- "sync/atomic"
- "time"
+ "fmt"
+ "os"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
- "github.com/hootrhino/rulex/utils"
"gopkg.in/ini.v1"
)
// LicenseManager 证书管理
type LicenseManager struct {
- uuid string
- cert atomic.Value // 证书信息(*Certificate)
- conf LicenseConfig
+ uuid string
+ CpuId string
+ EthMac string
+ AdminUsername string
+ AdminPassword string
}
-func NewLicenseManager() *LicenseManager {
+/*
+*
+* 开源版本默认是给一个授权证书
+*
+ */
+func NewLicenseManager(r typex.RuleX) *LicenseManager {
return &LicenseManager{
- uuid: "LicenseManager",
+ uuid: "LicenseManager",
+ AdminUsername: "rhino",
+ AdminPassword: "hoot",
+ CpuId: "o:p:e:n:r:h:i:n:o",
+ EthMac: "o:p:e:n:h:o:o:t",
}
}
-// LicenseConfig 证书配置
-type LicenseConfig struct {
- LocalAddr string `ini:"local_addr"` // 本地存放证书的路径
- RemoteAddr string `ini:"remote_addr"` // 远程拉取证书的路径
- NetName string `ini:"net_name"` // 网络名('eth0')
-}
-
-// Certificate 证书信息
-type Certificate struct {
- Raw string `json:"raw"`
- Issuer pkix.Name `json:"issuer"` // 授权方
- Subject pkix.Name `json:"subject"` // 证书授权信息
- NotAfter time.Time
- NotBefore time.Time
- PublicKey *rsa.PublicKey `json:"key"` // 加密公钥
-}
-
-// Init 初始化LicenseManager
func (l *LicenseManager) Init(section *ini.Section) error {
- // 解析配置
- _ = utils.InIMapToStruct(section, &l.conf)
-
- // 先尝试加载证书
- l.reload(false)
-
- // 加载失败,设置定时器1小时再检查
- if l.cert.Load() == nil {
- time.AfterFunc(time.Hour, func() { l.reload(true) })
+ license_path, err1 := section.GetKey("license_path")
+ errMsg := "Load License Public Cipher Failed, May be Your License IS Invalid."
+ if err1 != nil {
+ glogger.GLogger.Fatal()
+ os.Exit(0)
}
-
- return nil
-}
-
-func (l *LicenseManager) reload(quit bool) *Certificate {
- // 已有证书,直接返回
- cert, _ := l.cert.Load().(*Certificate)
- if cert != nil {
- return cert
+ license_key, err2 := section.GetKey("key_path")
+ if err2 != nil {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
}
-
- // 尝试加载证书
- cert, err := loadAndVerifyCert(l.conf)
- if err == nil {
- if !l.cert.CompareAndSwap(nil, cert) {
- cert, _ = l.cert.Load().(*Certificate)
- }
- return cert
+ lic, err1 := os.ReadFile(license_path.String())
+ if err1 != nil {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
}
-
- // 日志输出
- glogger.GLogger.Error("license_manager:", err.Error())
- // fmt.Println("license_manager:", err. common.Error())
-
- // 加载失败并退出
- if quit {
- glogger.GLogger.Fatal("need a valid certificate")
- // panic("need a valid certificate")
+ license := string(lic)
+ key, err2 := os.ReadFile(license_key.String())
+ if err2 != nil {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
}
-
+ private := string(key)
+ localMacSum := SumMd5(fmt.Sprintf("%s,%s", l.CpuId, l.EthMac))
+ localAdminSum := SumMd5(fmt.Sprintf("%s,%s", l.AdminUsername, l.AdminPassword))
+ adminSalt, err3 := DecryptAES(private, license)
+ if err3 != nil {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
+ }
+ if localAdminSum != private {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
+ }
+ if adminSalt != localMacSum {
+ glogger.GLogger.Fatal(errMsg)
+ os.Exit(0)
+ }
+ glogger.GLogger.Info("Load License Success:", license)
return nil
}
@@ -96,6 +85,9 @@ func (l *LicenseManager) reload(quit bool) *Certificate {
func (dm *LicenseManager) Start(typex.RuleX) error {
return nil
}
+func (dm *LicenseManager) Service(arg typex.ServiceArg) typex.ServiceResult {
+ return typex.ServiceResult{}
+}
// Stop 未实现
func (dm *LicenseManager) Stop() error {
@@ -107,57 +99,10 @@ func (hh *LicenseManager) PluginMetaInfo() typex.XPluginMetaInfo {
UUID: hh.uuid,
Name: "LicenseManager",
Version: "v0.0.1",
- Homepage: "https://github.com/dropliu/rulex",
- HelpLink: "https://github.com/dropliu/rulex",
- Author: "dropliu",
- Email: "13594448678@163.com",
- License: "MIT",
- }
-}
-
-/*
- *
- * 服务调用接口
- *
- */
-func (l *LicenseManager) Service(arg typex.ServiceArg) typex.ServiceResult {
- switch arg.Name {
- case "info":
- var info CertificateInfo
- if cert, _ := l.cert.Load().(*Certificate); cert != nil {
- info = Certificate2Info(cert)
- }
- return typex.ServiceResult{Out: info}
- case "verify":
- var info CertificateInfo
- if cert := l.reload(false); cert != nil {
- info = Certificate2Info(cert)
- }
- return typex.ServiceResult{Out: info}
- default:
- }
-
- return typex.ServiceResult{}
-}
-
-type CertificateInfo struct {
- Authorized bool `json:"authorized"` // 是否已授权?(当为false时,其它字段为空)
- Content string `json:"content"` // 证书原始内容
- StartDate string `json:"start_date"` // 开始日期
- EndDate string `json:"end_date"` // 结束日期
- Valid bool `json:"valid"` // 证书是否有效?
- Issuer string `json:"issuer"`
- Subject string `json:"subject"`
-}
-
-func Certificate2Info(cert *Certificate) CertificateInfo {
- return CertificateInfo{
- Authorized: true,
- Content: cert.Raw,
- StartDate: cert.NotBefore.Format(time.DateOnly),
- EndDate: cert.NotAfter.Format(time.DateOnly),
- Valid: time.Since(cert.NotAfter) < 0,
- Issuer: cert.Issuer.CommonName,
- Subject: cert.Subject.CommonName,
+ Homepage: "https://hootrhino.github.io",
+ HelpLink: "https://hootrhino.github.io",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/license_manager/license_manager.md b/plugin/license_manager/license_manager.md
index 59265d8eb..064d479c4 100644
--- a/plugin/license_manager/license_manager.md
+++ b/plugin/license_manager/license_manager.md
@@ -1,72 +1,2 @@
-# license manager: 固件证书管理器
-固件证书管理器,用来防止盗版或者破解。
-## 原理
-该插件会向一个服务器发送一个 HTTP 请求,该请求包含了:
-1. 本地计算机 MAC 地址
-1. 本地软件版本号
-1. 本地操作系统
-1. 本地硬件架构
-2. 或者其他更多
-
-最终会将上述信息用某种算法加密后上传到服务器,服务器验证后返回一个加密数字证书,该证书存放在本地路径下。每次开机启动的时候验证证书即可。
-
-注册伪代码:
-```
-license_text = request_server("*********")
-if check(license_text){
- save(license_text)
-}
-```
-
-启动认证伪代码:
-```
-license_text = load_file("/path/app.lic")
-if check(license_text){
- start()
-}else{
- stop(error-message)
-}
-```
-
-## 配置
-```ini
-[plugin.license_manager]
-local_addr = "./rulex/cert.pem"
-remote_addr = "http://remote.site/cert/"
-net_name = "eth0"
-```
-
-## 接口
-远程服务器承担了拉取、验证证书的作用,需要实现对应的接口
-
-```
-创建证书
-POST remote.site/cert
-request:
-{
- "mac": "xxx",
- "os": "linux",
- "arch": "amd64",
-}
-response:
-{
- "ret":0,
- "msg": "",
- "data": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----"
-}
-```
-
-```
-验证证书
-GET remote.site/cert
-request:
-{
- "mac": "xxx",
- "data": "xxx" // 使用证书加密mac后的数据,使用base64编码(二进制安全)
-}
-response:
-{
- "ret": 0,
- "msg": "xxx"
-}
-```
\ No newline at end of file
+# 固件证书管理器
+固件证书管理器,用来防止盗版或者破解。开源版不限制使用,商业版有单独的证书管理器。
diff --git a/plugin/license_manager/misc.go b/plugin/license_manager/misc.go
new file mode 100644
index 000000000..642d68307
--- /dev/null
+++ b/plugin/license_manager/misc.go
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package licensemanager
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/md5"
+ "crypto/rand"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// encryptAES 加密字符串
+func EncryptAES(key, text string) (string, error) {
+ block, err := aes.NewCipher([]byte(key))
+ if err != nil {
+ return "", err
+ }
+
+ // 为加密创建一个密码分组模式
+ cipherText := make([]byte, aes.BlockSize+len(text))
+ iv := cipherText[:aes.BlockSize]
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ return "", err
+ }
+
+ stream := cipher.NewCFBEncrypter(block, iv)
+ stream.XORKeyStream(cipherText[aes.BlockSize:], []byte(text))
+
+ return base64.URLEncoding.EncodeToString(cipherText), nil
+}
+
+// decryptAES 解密字符串
+func DecryptAES(key, cipherText string) (string, error) {
+ block, err := aes.NewCipher([]byte(key))
+ if err != nil {
+ return "", err
+ }
+
+ // 使用密文的前 16 个字节作为 IV
+ cipherTextBytes, err := base64.URLEncoding.DecodeString(cipherText)
+ if err != nil {
+ return "", err
+ }
+
+ if len(cipherTextBytes) < aes.BlockSize {
+ return "", errors.New("cipherText is too short")
+ }
+
+ iv := cipherTextBytes[:aes.BlockSize]
+ cipherTextBytes = cipherTextBytes[aes.BlockSize:]
+
+ // 创建一个密码分组模式
+ stream := cipher.NewCFBDecrypter(block, iv)
+
+ // 解密
+ stream.XORKeyStream(cipherTextBytes, cipherTextBytes)
+
+ return string(cipherTextBytes), nil
+}
+
+/*
+*
+* MD5
+*
+ */
+func SumMd5(inputString string) string {
+ hasher := md5.New()
+ io.WriteString(hasher, inputString)
+ hashBytes := hasher.Sum(nil)
+ md5String := fmt.Sprintf("%x", hashBytes)
+ return md5String
+}
diff --git a/plugin/modbuscrc_tools/modbuscrc_tools.go b/plugin/modbus_crc_tools/modbuscrc_tools.go
similarity index 92%
rename from plugin/modbuscrc_tools/modbuscrc_tools.go
rename to plugin/modbus_crc_tools/modbuscrc_tools.go
index b319fb36f..8550dfced 100644
--- a/plugin/modbuscrc_tools/modbuscrc_tools.go
+++ b/plugin/modbus_crc_tools/modbuscrc_tools.go
@@ -13,7 +13,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-package modbuscrctools
+package modbus_crc_tools
import (
"github.com/hootrhino/rulex/typex"
@@ -48,8 +48,8 @@ func (hh *modbusCRCCalculator) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/modbuscrc_tools/modbuscrc_tools.md b/plugin/modbus_crc_tools/modbuscrc_tools.md
similarity index 100%
rename from plugin/modbuscrc_tools/modbuscrc_tools.md
rename to plugin/modbus_crc_tools/modbuscrc_tools.md
diff --git a/plugin/modbuscrc_tools/modbuscrc_tools_service.go b/plugin/modbus_crc_tools/modbuscrc_tools_service.go
similarity index 98%
rename from plugin/modbuscrc_tools/modbuscrc_tools_service.go
rename to plugin/modbus_crc_tools/modbuscrc_tools_service.go
index b3094bef3..72fb67aa9 100644
--- a/plugin/modbuscrc_tools/modbuscrc_tools_service.go
+++ b/plugin/modbus_crc_tools/modbuscrc_tools_service.go
@@ -13,7 +13,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-package modbuscrctools
+package modbus_crc_tools
import (
"encoding/binary"
diff --git a/plugin/modbus_scanner/modbus_scanner.go b/plugin/modbus_scanner/modbus_scanner.go
index 96077d6f6..99b197e50 100644
--- a/plugin/modbus_scanner/modbus_scanner.go
+++ b/plugin/modbus_scanner/modbus_scanner.go
@@ -60,8 +60,8 @@ func (hh *modbusScanner) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/mqtt_server/mqtt_server.go b/plugin/mqtt_server/mqtt_server.go
index 491a86895..390fab576 100644
--- a/plugin/mqtt_server/mqtt_server.go
+++ b/plugin/mqtt_server/mqtt_server.go
@@ -89,11 +89,11 @@ func (s *MqttServer) PluginMetaInfo() typex.XPluginMetaInfo {
UUID: s.uuid,
Name: "Light Weight MqttServer",
Version: "v2.0.0",
- Homepage: "https://github.com/lion-brave",
- HelpLink: "https://github.com/lion-brave",
- Author: "liyong",
- Email: "liyong@gmail.com",
- License: "MIT",
+ Homepage: "https://hootrhino.github.io",
+ HelpLink: "https://hootrhino.github.io",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/net_discover/net_boardcast.go b/plugin/net_discover/net_boardcast.go
index 579ef893f..e634a8e6b 100644
--- a/plugin/net_discover/net_boardcast.go
+++ b/plugin/net_discover/net_boardcast.go
@@ -135,7 +135,7 @@ func (dm *NetDiscover) Start(typex.RuleX) error {
var m runtime.MemStats
runtime.ReadMemStats(&m)
hardWareInfo := map[string]interface{}{
- "version": typex.DefaultVersion,
+ "version": typex.MainVersion,
"diskInfo": int(diskInfo.UsedPercent),
"systemMem": bToMb(m.Sys),
"allocMem": bToMb(m.Alloc),
@@ -176,9 +176,9 @@ func (hh *NetDiscover) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/sensor_server/sensor_server.go b/plugin/sensor_server/sensor_server.go
index 00bb68f20..6892a775d 100644
--- a/plugin/sensor_server/sensor_server.go
+++ b/plugin/sensor_server/sensor_server.go
@@ -48,9 +48,9 @@ func (hh *SensorServer) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/telemetry/telemetry.go b/plugin/telemetry/telemetry.go
index 02f4f8ef2..ce5dc39a7 100644
--- a/plugin/telemetry/telemetry.go
+++ b/plugin/telemetry/telemetry.go
@@ -93,6 +93,6 @@ func (p *Telemetry) PluginMetaInfo() typex.XPluginMetaInfo {
HelpLink: "https://github.com/dropliu/rulex",
Author: "dropliu",
Email: "13594448678@163.com",
- License: "MIT",
+ License: "AGPL",
}
}
diff --git a/plugin/ttyd_terminal/ttyd_terminal.go b/plugin/ttyd_terminal/ttyd_terminal.go
index f18f30701..ae0a082f1 100644
--- a/plugin/ttyd_terminal/ttyd_terminal.go
+++ b/plugin/ttyd_terminal/ttyd_terminal.go
@@ -86,8 +86,8 @@ func (hh *WebTTYPlugin) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://github.com/tsl0922/ttyd",
HelpLink: "https://github.com/tsl0922/ttyd",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/usb_monitor/usb_mnitor_windows.go b/plugin/usb_monitor/usb_mnitor_windows.go
index 43cadfea9..349d30fc7 100644
--- a/plugin/usb_monitor/usb_mnitor_windows.go
+++ b/plugin/usb_monitor/usb_mnitor_windows.go
@@ -42,9 +42,9 @@ func (usbm *usbMonitor) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://github.com/hootrhino/rulex.git",
HelpLink: "https://github.com/hootrhino/rulex.git",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/plugin/usb_monitor/usb_monitor_linux.go b/plugin/usb_monitor/usb_monitor_linux.go
index 16b28d658..047fdd9cb 100644
--- a/plugin/usb_monitor/usb_monitor_linux.go
+++ b/plugin/usb_monitor/usb_monitor_linux.go
@@ -164,9 +164,9 @@ func (usbm *usbMonitor) PluginMetaInfo() typex.XPluginMetaInfo {
Version: "v0.0.1",
Homepage: "https://hootrhino.github.io",
HelpLink: "https://hootrhino.github.io",
- Author: "wwhai",
- Email: "cnwwhai@gmail.com",
- License: "MIT",
+ Author: "HootRhinoTeam",
+ Email: "HootRhinoTeam@hootrhino.com",
+ License: "AGPL",
}
}
diff --git a/release_pkg.sh b/release_pkg.sh
index 3a885353a..d66f98ede 100755
--- a/release_pkg.sh
+++ b/release_pkg.sh
@@ -9,7 +9,7 @@ create_pkg() {
local version="$(git describe --tags $(git rev-list --tags --max-count=1))"
local release_dir="_release"
local pkg_name="${APP}-$target-$version.zip"
- local common_files="./LICENSE ./conf/${APP}.ini ./md5.sum"
+ local common_files="./conf/license.* ./LICENSE ./conf/${APP}.ini ./md5.sum"
local files_to_include="./${APP} $common_files"
local files_to_include_exe="./${APP}.exe $common_files"
@@ -19,7 +19,7 @@ create_pkg() {
chmod +x ./${APP}
calculate_and_save_md5 ./${APP}
else
- files_to_include="$files_to_include_exe"
+ files_to_include="$files_to_include_exe ./script/*.bat"
mv ./${APP}-$target.exe ./${APP}.exe
calculate_and_save_md5 ./${APP}.exe
fi
@@ -121,30 +121,38 @@ calculate_and_save_md5() {
#
# fetch dashboard
#
+#!/bin/bash
+
fetch_dashboard() {
- local www_zip="./www.zip"
- local http_server_dir="./plugin/http_server/www"
+ local owner="hootrhino"
+ local repo="hootrhino-eekit-web"
- # 检查文件是否存在
- if [ -f "$www_zip" ]; then
- echo -e "\033[44;32m [√] File www.zip already downloaded \033[0m"
- unzip -q "$www_zip" -d "$http_server_dir"
- else
- VERSION="$(git describe --tags $(git rev-list --tags --max-count=1))"
- local URL="${RESPOSITORY}/hootrhino-eekit-web/releases/download/${VERSION}/www.zip"
- echo -e "\033[41;37m [*] Fetch www.zip from: ${URL}\033[0m"
- # 发送HEAD请求来检查URL是否存在
- response=$(curl -s --head -w %{http_code} "$URL" -o /dev/null)
-
- if [ "$response" = "200" ]; then
- echo -e "\033[40;32m [*] Unzip www.zip to:${http_server_dir} \033[0m"
- wget -q --show-progress "$URL"
- unzip -q "$www_zip" -d "$http_server_dir"
- else
- echo -e "\033[41;30m [x] Error with http code 404, check if ${URL} exists \033[0m"
- exit 1
- fi
+ # 检查当前目录是否已经存在 www.zip 文件
+ if [ -f "www.zip" ]; then
+ echo "[!] www.zip already exists. No need to download."
+ exit 0
+ fi
+
+ # 获取最新 release 的 tag 名称
+ local tag=$(curl -s "https://api.github.com/repos/$owner/$repo/releases/latest" | jq -r .tag_name)
+
+ # 获取最新 release 中的 www.zip 下载链接
+ local zip_url=$(curl -s "https://api.github.com/repos/$owner/$repo/releases/latest" | jq -r '.assets[] | select(.name == "www.zip") | .browser_download_url')
+
+ if [ -z "$zip_url" ]; then
+ echo "[x] Error: www.zip not found in the release assets."
+ exit 1
fi
+
+ # 下载 www.zip 文件
+ curl -L -o www.zip "$zip_url"
+
+ echo "[√] Download complete. Tag: $tag"
+
+ # 解压 www.zip 文件到指定目录
+ unzip -o www.zip -d /plugin/rulex_api_server/server/www/
+
+ echo "[√] Extraction complete. www.zip contents have been overwritten to /plugin/rulex_api_server/server/www/."
}
#
@@ -153,7 +161,7 @@ fetch_dashboard() {
gen_changelog() {
echo -e "[.]Version Change log:"
- log=$(git log --oneline --pretty=format:"\033[0;31m[#]\033[0m%s\n" $(git describe --abbrev=0 --tags).. | cat)
+ log=$(git log --oneline --pretty=format:" \033[0;31m[*]\033[0m%s\n" $(git describe --abbrev=0 --tags).. | cat)
echo -e $log
}
diff --git a/rulexlib/binary_lib.go b/rulexlib/binary_lib.go
index 91169e60b..804aa1505 100644
--- a/rulexlib/binary_lib.go
+++ b/rulexlib/binary_lib.go
@@ -231,7 +231,7 @@ func bitStringToBytes(s string) ([]byte, error) {
for i := 0; i < len(s); i++ {
c := s[i]
if c < '0' || c > '1' {
- return nil, errors.New("bitstring to bytes error, bit value out of range, only can be 0 or 1")
+ return nil, errors.New("bit string to bytes error, bit value out of range, only can be 0 or 1")
}
b[i>>3] |= (c - '0') << uint(7-i&7)
}
@@ -515,16 +515,82 @@ func HexToNumber(s string) (int64, error) {
func BinToFloat32(rx typex.RuleX) func(*lua.LState) int {
return func(l *lua.LState) int {
bin := l.ToString(2)
- bits := binary.BigEndian.Uint32([]byte(bin))
- l.Push(lua.LNumber(math.Float32frombits(bits)))
+ dBytes, err := hex.DecodeString(bin)
+ if err != nil {
+ l.Push(lua.LNil)
+ return 1
+ }
+ if len(dBytes) == 4 {
+ l.Push(lua.LNumber(math.Float32frombits(binary.BigEndian.Uint32(dBytes))))
+ } else {
+ l.Push(lua.LNumber(0))
+ }
return 1
}
}
+
+/*
+*
+* 二进制转浮点数
+*
+ */
func BinToFloat64(rx typex.RuleX) func(*lua.LState) int {
return func(l *lua.LState) int {
bin := l.ToString(2)
- bits := binary.BigEndian.Uint64([]byte(bin))
- l.Push(lua.LNumber(math.Float64frombits(bits)))
+ dBytes, err := hex.DecodeString(bin)
+ if err != nil {
+ l.Push(lua.LNil)
+ return 1
+ }
+ if len(dBytes) == 8 {
+ l.Push(lua.LNumber(math.Float64frombits(binary.BigEndian.Uint64(dBytes))))
+ } else {
+ l.Push(lua.LNumber(0))
+ }
+ return 1
+ }
+}
+
+/*
+*
+* 二进制转浮点小端
+*
+ */
+func BinToFloat32Little(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ bin := l.ToString(2)
+ dBytes, err := hex.DecodeString(bin)
+ if err != nil {
+ l.Push(lua.LNil)
+ return 1
+ }
+ if len(dBytes) == 4 {
+ l.Push(lua.LNumber(math.Float32frombits(binary.LittleEndian.Uint32(dBytes))))
+ } else {
+ l.Push(lua.LNumber(0))
+ }
+ return 1
+ }
+}
+
+/*
+*
+* 二进制转8字节浮点小端
+*
+ */
+func BinToFloat64Little(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ bin := l.ToString(2)
+ dBytes, err := hex.DecodeString(bin)
+ if err != nil {
+ l.Push(lua.LNil)
+ return 1
+ }
+ if len(dBytes) == 8 {
+ l.Push(lua.LNumber(math.Float64frombits(binary.LittleEndian.Uint64(dBytes))))
+ } else {
+ l.Push(lua.LNumber(0))
+ }
return 1
}
}
diff --git a/rulexlib/data_to_localdb_lib.go b/rulexlib/data_to_localdb_lib.go
index 4800a6f41..5cc4c4bf7 100644
--- a/rulexlib/data_to_localdb_lib.go
+++ b/rulexlib/data_to_localdb_lib.go
@@ -31,7 +31,7 @@ import (
func LocalDBQuery(rx typex.RuleX) func(*lua.LState) int {
return func(l *lua.LState) int {
sql := l.ToString(2)
- Map, err := datacenter.Query("INTERNAL_DATACENTER", sql)
+ Map, err := datacenter.Query("RULEX_INTERNAL_DATACENTER", sql)
if err != nil {
l.Push(lua.LNil)
l.Push(lua.LString(err.Error()))
diff --git a/rulexlib/eekith3_adda_lib.go b/rulexlib/eekith3_adda_lib.go
index d88e4c96d..1307509a2 100644
--- a/rulexlib/eekith3_adda_lib.go
+++ b/rulexlib/eekith3_adda_lib.go
@@ -115,3 +115,28 @@ func H3DI3Get(rx typex.RuleX) func(*lua.LState) int {
return 2
}
}
+
+// User Gpio operation
+// 注意:低电平亮
+func Led1On(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ e := archsupport.EEKIT_GPIOSetUserGpio(0)
+ if e != nil {
+ l.Push(lua.LString(e.Error()))
+ } else {
+ l.Push(lua.LNil)
+ }
+ return 1
+ }
+}
+func Led1Off(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ e := archsupport.EEKIT_GPIOSetUserGpio(1)
+ if e != nil {
+ l.Push(lua.LString(e.Error()))
+ } else {
+ l.Push(lua.LNil)
+ }
+ return 1
+ }
+}
diff --git a/rulexlib/hex_lib.go b/rulexlib/hex_lib.go
index 672ed5606..00343facb 100644
--- a/rulexlib/hex_lib.go
+++ b/rulexlib/hex_lib.go
@@ -83,25 +83,37 @@ func MatchUInt(rx typex.RuleX) func(*lua.LState) int {
ntb := lua.LTable{}
for _, v := range mhs {
size := len(v.Value)
-
- if size == 2 {
+ // 空
+ if size == 0 {
+ ntb.RawSetString(v.Name, lua.LNumber(0))
+ }
+ // 单字节
+ if size == 1 {
+ ntb.RawSetString(v.Name, lua.LNumber(v.ToUint8()))
+ }
+ // 长度是 2 3 字节全看成2字节
+ if size == 2 || size == 3 {
ntb.RawSetString(v.Name, lua.LNumber(v.ToUint16()))
}
- if size == 4 {
+ // 长度是 4 5 6 7 字节全看成4字节
+ if size == 4 || size == 5 || size == 6 || size == 7 {
ntb.RawSetString(v.Name, lua.LNumber(v.ToUint32()))
}
- if size == 8 {
+ // 不支持超过8位的
+ if size >= 8 {
ntb.RawSetString(v.Name, lua.LNumber(v.ToUInt64()))
}
- if size > 8 {
- ntb.RawSetString(v.Name, lua.LNumber(-0xFFFFFFFF))
- }
}
l.Push(&ntb)
return 1
}
}
+/*
+*
+* 十六进制字节表示字符串
+*
+ */
type HexSegment struct {
Name string
Value []byte
@@ -111,14 +123,35 @@ func (sgm HexSegment) ToHexString() string {
return fmt.Sprintf("%X", sgm.Value)
}
+func (sgm HexSegment) ToUint8() uint8 {
+ return uint8(sgm.Value[0])
+}
+
+/*
+*
+* 大端表示法
+*
+ */
func (sgm HexSegment) ToUint16() uint16 {
value := binary.BigEndian.Uint16(sgm.Value)
return value
}
+
+/*
+*
+* 大端表示法
+*
+ */
func (sgm HexSegment) ToUint32() uint32 {
value := binary.BigEndian.Uint32(sgm.Value)
return value
}
+
+/*
+*
+* 大端表示法
+*
+ */
func (sgm HexSegment) ToUInt64() uint64 {
value := binary.BigEndian.Uint64(sgm.Value)
return value
@@ -177,3 +210,17 @@ func extHex(hexStr string, start, end int) string {
}
return hexStr[start*2 : (end+1)*2]
}
+
+// func extHex(hexStr string, start, end int) string {
+// // 检查输入边界的有效性
+// if start < 0 || end < 0 || start > end || end*2 >= len(hexStr) {
+// return ""
+// }
+
+// // 计算起始和结束索引
+// startIdx := start * 2
+// endIdx := (end + 1) * 2
+
+// // 切片并返回结果
+// return hexStr[startIdx:endIdx]
+// }
diff --git a/rulexlib/http_api_lib.go b/rulexlib/http_api_lib.go
new file mode 100644
index 000000000..ca6ca9918
--- /dev/null
+++ b/rulexlib/http_api_lib.go
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package rulexlib
+
+import (
+ "io"
+ "net/http"
+ "strings"
+ "time"
+
+ lua "github.com/hootrhino/gopher-lua"
+ "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/typex"
+)
+
+/*
+*
+* HTTP GET
+*
+ */
+func HttpGet(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ url := l.ToString(2)
+ l.Push(lua.LString(__HttpGet(url)))
+ return 1
+ }
+}
+func HttpPost(rx typex.RuleX) func(*lua.LState) int {
+ return func(l *lua.LState) int {
+ url := l.ToString(2)
+ body := l.ToString(3)
+ l.Push(lua.LString(__HttpPost(url, body)))
+ return 1
+ }
+}
+
+/*
+*
+* GET
+*
+ */
+func __HttpGet(url string) string {
+ client := http.Client{
+ Timeout: 5 * time.Second,
+ }
+ resp, err := client.Get(url)
+ if err != nil {
+ return ""
+ }
+ defer resp.Body.Close()
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return ""
+ }
+ return string(body)
+}
+
+/*
+*
+* POST
+ */
+func __HttpPost(url string, body string) string {
+ resp, err := http.Post(url, "application/json", strings.NewReader(body))
+ if err != nil {
+ glogger.GLogger.Error(err)
+ return ""
+ }
+ defer resp.Body.Close()
+
+ responseBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ glogger.GLogger.Error(err)
+ return ""
+ }
+ return string(responseBody)
+}
diff --git a/rulexlib/modbus_lib.go b/rulexlib/modbus_lib.go
index c49cdcce4..b9b019499 100644
--- a/rulexlib/modbus_lib.go
+++ b/rulexlib/modbus_lib.go
@@ -96,27 +96,30 @@ func F5(rx typex.RuleX) func(l *lua.LState) int {
}
}
Device := rx.GetDevice(devUUID)
- if Device != nil {
- if Device.Type != typex.GENERIC_MODBUS {
- l.Push(lua.LString("Only support GENERIC_MODBUS device"))
- return 1
- }
- if Device.Device.Status() == typex.DEV_UP {
- args, _ := json.Marshal([]common.RegisterW{
- {
- Function: 5,
- SlaverId: byte(slaverId),
- Address: uint16(Address),
- Values: HexValues,
- },
- })
- _, err := Device.Device.OnWrite([]byte("F5"), args)
- if err != nil {
- l.Push(lua.LString(err.Error()))
- return 1
- }
- }
- l.Push(lua.LString("Device down:" + devUUID))
+ if Device == nil {
+ l.Push(lua.LString("Device is not exists"))
+ return 1
+ }
+
+ if Device.Type != typex.GENERIC_MODBUS {
+ l.Push(lua.LString("Only support GENERIC_MODBUS device"))
+ return 1
+ }
+ if Device.Device.Status() != typex.DEV_UP {
+ l.Push(lua.LString("device down:" + devUUID))
+ return 1
+ }
+ args, _ := json.Marshal([]common.RegisterW{
+ {
+ Function: 5,
+ SlaverId: byte(slaverId),
+ Address: uint16(Address),
+ Values: HexValues,
+ },
+ })
+ _, err0 := Device.Device.OnWrite([]byte("F5"), args)
+ if err0 != nil {
+ l.Push(lua.LString(err0.Error()))
return 1
}
l.Push(lua.LNil)
@@ -142,29 +145,32 @@ func F6(rx typex.RuleX) func(l *lua.LState) int {
return 1
}
Device := rx.GetDevice(devUUID)
- if Device != nil {
- if Device.Type != typex.GENERIC_MODBUS {
- l.Push(lua.LString("Only support GENERIC_MODBUS device"))
- return 1
- }
- if Device.Device.Status() == typex.DEV_UP {
- args, _ := json.Marshal(common.RegisterW{
- Function: 6,
- SlaverId: byte(slaverId),
- Address: uint16(Address),
- Quantity: uint16(1), //2字节
- Values: HexValues,
- })
- _, err := Device.Device.OnWrite([]byte("F6"), args)
- if err != nil {
- glogger.GLogger.Error(err)
- l.Push(lua.LString(err.Error()))
- return 1
- }
- }
+ if Device == nil {
+ l.Push(lua.LString("Device is not exists"))
+ return 1
+ }
+
+ if Device.Type != typex.GENERIC_MODBUS {
+ l.Push(lua.LString("Only support GENERIC_MODBUS device"))
+ return 1
+ }
+ if Device.Device.Status() != typex.DEV_UP {
l.Push(lua.LString("device down:" + devUUID))
return 1
}
+ args, _ := json.Marshal(common.RegisterW{
+ Function: 6,
+ SlaverId: byte(slaverId),
+ Address: uint16(Address),
+ Quantity: uint16(1), //2字节
+ Values: HexValues,
+ })
+ _, err0 := Device.Device.OnWrite([]byte("F6"), args)
+ if err0 != nil {
+ glogger.GLogger.Error(err0)
+ l.Push(lua.LString(err0.Error()))
+ return 1
+ }
l.Push(lua.LNil)
return 1
}
@@ -190,29 +196,31 @@ func F15(rx typex.RuleX) func(l *lua.LState) int {
return 1
}
Device := rx.GetDevice(devUUID)
- if Device != nil {
- if Device.Type != typex.GENERIC_MODBUS {
- l.Push(lua.LString("Only support GENERIC_MODBUS device"))
- return 1
- }
- if Device.Device.Status() == typex.DEV_UP {
- args, _ := json.Marshal(common.RegisterW{
- Function: 15,
- SlaverId: byte(slaverId),
- Address: uint16(Address),
- Quantity: uint16(Quantity),
- Values: HexValues,
- })
- _, err := Device.Device.OnWrite([]byte("F15"), args)
- if err != nil {
- glogger.GLogger.Error(err)
- l.Push(lua.LString(err.Error()))
- return 1
- }
- }
+ if Device == nil {
+ l.Push(lua.LString("Device is not exists"))
+ return 1
+ }
+ if Device.Type != typex.GENERIC_MODBUS {
+ l.Push(lua.LString("Only support GENERIC_MODBUS device"))
+ return 1
+ }
+ if Device.Device.Status() != typex.DEV_UP {
l.Push(lua.LString("device down:" + devUUID))
return 1
}
+ args, _ := json.Marshal(common.RegisterW{
+ Function: 15,
+ SlaverId: byte(slaverId),
+ Address: uint16(Address),
+ Quantity: uint16(Quantity),
+ Values: HexValues,
+ })
+ _, err0 := Device.Device.OnWrite([]byte("F15"), args)
+ if err0 != nil {
+ glogger.GLogger.Error(err0)
+ l.Push(lua.LString(err0.Error()))
+ return 1
+ }
l.Push(lua.LNil)
return 1
}
@@ -236,26 +244,30 @@ func F16(rx typex.RuleX) func(l *lua.LState) int {
return 1
}
Device := rx.GetDevice(devUUID)
- if Device != nil {
- if Device.Type != typex.GENERIC_MODBUS {
- l.Push(lua.LString("Only support GENERIC_MODBUS device"))
- return 1
- }
- if Device.Device.Status() == typex.DEV_UP {
- args, _ := json.Marshal(common.RegisterW{
- Function: 16,
- SlaverId: byte(slaverId),
- Address: uint16(Address),
- Quantity: uint16(Quantity),
- Values: HexValues,
- })
- _, err := Device.Device.OnWrite([]byte("F16"), args)
- if err != nil {
- glogger.GLogger.Error(err)
- l.Push(lua.LString(err.Error()))
- return 1
- }
- }
+ if Device == nil {
+ l.Push(lua.LString("Device is not exists"))
+ return 1
+ }
+ if Device.Type != typex.GENERIC_MODBUS {
+ l.Push(lua.LString("Only support GENERIC_MODBUS device"))
+ return 1
+ }
+ if Device.Device.Status() != typex.DEV_UP {
+ l.Push(lua.LString("device down:" + devUUID))
+ return 1
+ }
+ args, _ := json.Marshal(common.RegisterW{
+ Function: 16,
+ SlaverId: byte(slaverId),
+ Address: uint16(Address),
+ Quantity: uint16(Quantity),
+ Values: HexValues,
+ })
+ _, err0 := Device.Device.OnWrite([]byte("F16"), args)
+ if err0 != nil {
+ glogger.GLogger.Error(err0)
+ l.Push(lua.LString(err0.Error()))
+ return 1
}
l.Push(lua.LNil)
return 1
diff --git a/rulexlib/network_lib.go b/rulexlib/network_lib.go
new file mode 100644
index 000000000..382af4d1a
--- /dev/null
+++ b/rulexlib/network_lib.go
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+// if Duration, err := pingQ(ip, 2000*time.Millisecond); err != nil {
+// glogger.GLogger.WithFields(Fields).Info(fmt.Sprintf(
+// "[Count:%d] Ping Error:%s", i,
+// err.Error()))
+// } else {
+//
+// glogger.GLogger.WithFields(Fields).Info(fmt.Sprintf(
+// "[Count:%d] Ping Reply From %s: time=%v ms TTL=128", i,
+// tt, Duration))
+// }
+package rulexlib
+
+import (
+ "net"
+ "time"
+
+ lua "github.com/hootrhino/gopher-lua"
+ "github.com/hootrhino/rulex/typex"
+)
+
+/*
+*
+* Ping
+*
+ */
+func PingIp(rx typex.RuleX) func(l *lua.LState) int {
+ return func(l *lua.LState) int {
+ ip := l.ToString(2)
+ Duration, err := pingQ(ip, 5000*time.Millisecond)
+ l.Push(lua.LNumber(Duration))
+ l.Push(lua.LString(err.Error()))
+ return 2
+ }
+}
+
+// --------------------------------------------------------------------------------------------------
+// private
+// --------------------------------------------------------------------------------------------------
+func pingQ(ip string, timeout time.Duration) (time.Duration, error) {
+ const IcmpLen = 8
+ msg := [32]byte{
+ 8, 0, 0, 0, 0, 13, 0, 37,
+ }
+ check := checkSum(msg[:IcmpLen])
+ msg[2] = byte(check >> 8)
+ msg[3] = byte(check & 255)
+
+ remoteAddr, err := net.ResolveIPAddr("ip", ip)
+ if err != nil {
+ return 0, err
+ }
+ conn, err := net.DialIP("ip:icmp", nil, remoteAddr)
+ if err != nil {
+ return 0, err
+ }
+ start := time.Now()
+ if _, err := conn.Write(msg[:IcmpLen]); err != nil {
+ return 0, err
+ }
+ conn.SetReadDeadline(time.Now().Add(timeout))
+ _, err1 := conn.Read(msg[:])
+ conn.SetReadDeadline(time.Time{})
+ if err1 != nil {
+ return 0, err1
+ }
+ return time.Since(start), nil
+}
+
+func checkSum(msg []byte) uint16 {
+ sum := 0
+ for n := 0; n < len(msg); n += 2 {
+ sum += int(msg[n])<<8 + int(msg[n+1])
+ }
+ sum = (sum >> 16) + sum&0xffff
+ sum += sum >> 16
+ return uint16(^sum)
+}
diff --git a/rulexlib/rhinoh3_audio_lib.go b/rulexlib/rhinoh3_audio_lib.go
index e1ce755d0..f7df76199 100644
--- a/rulexlib/rhinoh3_audio_lib.go
+++ b/rulexlib/rhinoh3_audio_lib.go
@@ -33,7 +33,7 @@ var __mkv_playing bool = false
func __MPVPlay(filePath string, duration time.Duration) error {
if __mkv_playing {
- return fmt.Errorf("Audio output device busying now")
+ return fmt.Errorf("audio output device busying now")
}
__mkv_playing = true
cmd := exec.Command("mpv", filePath)
diff --git a/script/readme-daemon.md b/script/readme-daemon.md
new file mode 100644
index 000000000..177e3a5d8
--- /dev/null
+++ b/script/readme-daemon.md
@@ -0,0 +1,38 @@
+# Linux daemon script
+该脚本是RULEX的`通用Linux系统`操作脚本,处理RULEX的安装、启动、停止、卸载等。
+## 基础使用
+### 下载
+将安装包解压:
+```sh
+unzip rulex-arm32linux-$VERSION.zip -d rulex
+```
+### 安装
+```sh
+./rulex-daemon.sh install
+```
+
+### 卸载
+```sh
+./rulex-daemon.sh uninstall
+```
+
+### 使用
+下面的脚本一定要在root权限下执行,或者使用sudo。
+```bash
+# 启动
+./rulex-daemon.sh start
+# 停止
+./rulex-daemon.sh stop
+# 重启
+./rulex-daemon.sh restart
+# 状态
+./rulex-daemon.sh status
+```
+
+## 守护进程
+```sh
+# 打开crontab
+sudo crontab -e
+# 输入
+@reboot (export ARCHSUPPORT=EEKITH3 && /etc/init.d/rulex.service start > /var/log/rulex.log 2>&1)
+```
\ No newline at end of file
diff --git a/script/readme-openwrt.md b/script/readme-openwrt.md
new file mode 100644
index 000000000..4059c561c
--- /dev/null
+++ b/script/readme-openwrt.md
@@ -0,0 +1,38 @@
+# Openwrt daemon script
+该脚本是RULEX的`Openwrt系统`操作脚本,处理RULEX的安装、启动、停止、卸载等。
+## 基础使用
+### 下载
+将安装包解压:
+```sh
+unzip rulex-arm32linux-$VERSION.zip -d rulex
+```
+### 安装
+```sh
+./rulex-openwrt.sh install
+```
+
+### 卸载
+```sh
+./rulex-openwrt.sh uninstall
+```
+
+### 使用
+下面的脚本一定要在root权限下执行,或者使用sudo。
+```bash
+# 启动
+./rulex-openwrt.sh start
+# 停止
+./rulex-openwrt.sh stop
+# 重启
+./rulex-openwrt.sh restart
+# 状态
+./rulex-openwrt.sh status
+```
+
+## 守护进程
+```sh
+# 打开crontab
+sudo crontab -e
+# 输入
+@reboot (export ARCHSUPPORT=EEKITH3 && /etc/init.d/rulex.service start > /var/log/rulex.log 2>&1)
+```
\ No newline at end of file
diff --git a/script/readme-systemctl.md b/script/readme-systemctl.md
new file mode 100644
index 000000000..468447ed2
--- /dev/null
+++ b/script/readme-systemctl.md
@@ -0,0 +1,29 @@
+# Linux systemctl
+该脚本是RULEX的`systemctl`操作脚本,处理RULEX的安装、启动、停止、卸载等。
+## 基础使用
+将安装包解压:
+```sh
+unzip rulex-arm32linux-v0.6.2.zip -d rulex
+```
+
+下面的脚本一定要在root权限下执行,或者使用sudo。
+- 安装
+ ```sh
+ ./rulex_systemctl.sh install
+ ```
+- 启动
+ ```sh
+ ./rulex_systemctl.sh start
+ ```
+- 状态
+ ```sh
+ ./rulex_systemctl.sh status
+ ```
+- 停止
+ ```sh
+ ./rulex_systemctl.sh stop
+ ```
+- 卸载
+ ```sh
+ ./rulex_systemctl.sh uninstall
+ ```
\ No newline at end of file
diff --git a/device/tss200_device.md b/script/readme-windows.md
similarity index 87%
rename from device/tss200_device.md
rename to script/readme-windows.md
index 65f4e272d..fd37107d3 100644
--- a/device/tss200_device.md
+++ b/script/readme-windows.md
@@ -15,3 +15,8 @@
along with this program. If not, see .
-->
+# Windows 服务安装
+- 安装:rulex_install.bat
+- 卸载:rulex_uninstall.bat
+
+> 只支持windows10+
\ No newline at end of file
diff --git a/script/readme.md b/script/readme.md
deleted file mode 100644
index bc87965ae..000000000
--- a/script/readme.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Linux script
-该脚本是RULEX的系统服务操作脚本,处理RULEX的安装、启动、停止、卸载等。
-## 基础使用
-将安装包解压:
-```sh
-unzip rulex-arm32linux-v0.6.2.zip -d rulex
-```
-
-下面的脚本一定要在root权限下执行,或者使用sudo。
-- 安装
- ```sh
- ./rulexd.sh install
- ```
-- 启动
- ```sh
- ./rulexd.sh start
- ```
-- 状态
- ```sh
- ./rulexd.sh status
- ```
-- 停止
- ```sh
- ./rulexd.sh stop
- ```
-- 卸载
- ```sh
- ./rulexd.sh uninstall
- ```
-## 操作演示
-```sh
-rer@revb-h3:~/Desktop/rulex$ unzip rulex-arm32linux-v0.6.2.zip -d rulex
-Archive: rulex-arm32linux-v0.6.2.zip
- End-of-central-directory signature not found. Either this file is not
- a zipfile, or it constitutes one disk of a multi-part archive. In the
- latter case the central directory and zipfile comment will be found on
- the last disk(s) of this archive.
-unzip: cannot find zipfile directory in one of rulex-arm32linux-v0.6.2.zip or
- rulex-arm32linux-v0.6.2.zip.zip, and cannot find rulex-arm32linux-v0.6.2.zip.ZIP, period.
-rer@revb-h3:~/Desktop/rulex$ unzip rulex-arm32linux-v0.6.2.zip -d rulex
-Archive: rulex-arm32linux-v0.6.2.zip
- inflating: rulex/rulex
- inflating: rulex/LICENSE
- inflating: rulex/rulex.ini
- inflating: rulex/rulexd.sh
-rer@revb-h3:~/Desktop/rulex$ ll
-total 16540
-drwxrwxrwx 3 rer rer 4096 Sep 4 21:00 ./
-drwxrwxrwx 3 rer rer 4096 May 19 2022 ../
-drwxrwxr-x 2 rer rer 4096 Sep 4 21:00 rulex/
--rw-rw-r-- 1 rer rer 16921343 Sep 4 21:00 rulex-arm32linux-v0.6.2.zip
-rer@revb-h3:~/Desktop/rulex$ cd rulex/
-rer@revb-h3:~/Desktop/rulex/rulex$ ll
-total 45848
-drwxrwxr-x 2 rer rer 4096 Sep 4 21:00 ./
-drwxrwxrwx 3 rer rer 4096 Sep 4 21:00 ../
--rwxrwxrwx 1 rer rer 34523 Sep 4 20:51 LICENSE*
--rwxrwxrwx 1 rer rer 46891964 Sep 4 20:52 rulex*
--rwxrwxrwx 1 rer rer 2104 Sep 4 20:51 rulexd.sh*
--rwxrwxrwx 1 rer rer 2605 Sep 4 20:51 rulex.ini*
-rer@revb-h3:~/Desktop/rulex/rulex$ ./rulexd.sh install
-This script must be run as root
-rer@revb-h3:~/Desktop/rulex/rulex$ sudo ./rulexd.sh install
-Created symlink /etc/systemd/system/multi-user.target.wants/rulex.service → /etc/systemd/system/rulex.service.
-Rulex service has been created and extracted.
-rer@revb-h3:~/Desktop/rulex/rulex$ sudo ./rulexd.sh start
-RULEX started as a daemon.
-rer@revb-h3:~/Desktop/rulex/rulex$ sudo ./rulexd.sh restart
-RULEX started as a daemon.
-rer@revb-h3:~/Desktop/rulex/rulex$ sudo ./rulexd.sh stop
-Service Rulex has been stopped.
-rer@revb-h3:~/Desktop/rulex/rulex$ sudo ./rulexd.sh uninstall
-Removed /etc/systemd/system/multi-user.target.wants/rulex.service.
-Rulex has been uninstalled.
-```
\ No newline at end of file
diff --git a/script/rulex-daemon.sh b/script/rulex-daemon.sh
new file mode 100644
index 000000000..fc27b4849
--- /dev/null
+++ b/script/rulex-daemon.sh
@@ -0,0 +1,232 @@
+#!/bin/bash
+
+SERVICE_NAME="rulex"
+WORKING_DIRECTORY="/usr/local"
+EXECUTABLE_PATH="$WORKING_DIRECTORY/$SERVICE_NAME"
+CONFIG_PATH="$WORKING_DIRECTORY/$SERVICE_NAME.ini"
+
+SERVICE_FILE="/etc/init.d/$SERVICE_NAME.service"
+
+STOP_SIGNAL="/var/run/rulex-stop.sinal"
+UPGRADE_SIGNAL="/var/run/rulex-upgrade.lock"
+
+SOURCE_DIR="$PWD"
+
+
+log() {
+ local level=$1
+ shift
+ echo "[$level] $(date +'%Y-%m-%d %H:%M:%S') - $@"
+}
+
+install(){
+cat > "$SERVICE_FILE" << EOL
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: rulex
+# Required-Start: \$network \$local_fs \$remote_fs
+# Required-Stop: \$network \$local_fs \$remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Rulex Service
+# Description: Rulex Service
+### END INIT INFO
+
+#
+# Create Time: $(date +'%Y-%m-%d %H:%M:%S')
+#
+EXECUTABLE_PATH="$WORKING_DIRECTORY/rulex"
+CONFIG_PATH="$WORKING_DIRECTORY/rulex.ini"
+
+log() {
+ local level=\$1
+ shift
+ echo "[\$level] \$(date +'%Y-%m-%d %H:%M:%S') - \$@"
+}
+
+start() {
+ rm -f $UPGRADE_SIGNAL
+ rm -f $STOP_SIGNAL
+ pid=\$(pgrep -x -n -f "$EXECUTABLE_PATH run -config=$CONFIG_PATH")
+ if [ -n "\$pid" ]; then
+ log INFO "rulex is running with Pid:\${pid}"
+ exit 0
+ fi
+ cd $WORKING_DIRECTORY
+ daemon
+}
+
+stop() {
+ echo "1" > $STOP_SIGNAL
+ if pgrep -x "rulex" > /dev/null; then
+ log INFO "rulex process is running. Killing it..."
+ pkill -x "rulex"
+ log INFO "rulex process has been killed."
+ else
+ log WARNING "rulex process is not running."
+ fi
+}
+
+restart() {
+ stop
+ sleep 1
+ start
+}
+
+status() {
+ log INFO "Checking rulex status."
+ pid=\$(pgrep -x -n "rulex")
+ if [ -n "\$pid" ]; then
+ log INFO "rulex is running with Pid:\${pid}"
+ else
+ log INFO "rulex is not running."
+ fi
+}
+
+daemon() {
+ while true; do
+ if pgrep -x "rulex" > /dev/null; then
+ log INFO "rulex process exists"
+ sleep 3
+ continue
+ fi
+ if ! pgrep -x "rulex" > /dev/null; then
+ if [ -e "$UPGRADE_SIGNAL" ]; then
+ log INFO "File $UPGRADE_SIGNAL exists. May upgrade now."
+ sleep 2
+ continue
+ elif [ -e "$STOP_SIGNAL" ]; then
+ log INFO "$STOP_SIGNAL file found. Exiting."
+ exit 0
+ else
+ log WARNING "Detected that rulex process is interrupted. Restarting..."
+ cd $WORKING_DIRECTORY
+ $EXECUTABLE_PATH run -config=$CONFIG_PATH
+ log WARNING "Detected that rulex process has Restarted."
+ fi
+ fi
+ sleep 4
+ done
+}
+
+case "\$1" in
+ start)
+ start
+ ;;
+ restart)
+ restart
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status
+ ;;
+ *)
+ log ERROR "Usage: \$0 {start|restart|stop|status}"
+ exit 1
+ ;;
+esac
+
+EOL
+
+ mkdir -p $WORKING_DIRECTORY
+ chmod +x $SOURCE_DIR/rulex
+ log INFO "Copy rulex to $WORKING_DIRECTORY"
+ cp -rfp "$SOURCE_DIR/rulex" "$EXECUTABLE_PATH"
+
+ log INFO "Copy rulex.ini to $WORKING_DIRECTORY"
+ cp -rfp "$SOURCE_DIR/rulex.ini" "$CONFIG_PATH"
+
+ log INFO "Copy license.lic to $WORKING_DIRECTORY"
+ cp -rfp "$SOURCE_DIR/license.lic" "$WORKING_DIRECTORY/"
+
+ log INFO "Copy license.key to $WORKING_DIRECTORY"
+ cp -rfp "$SOURCE_DIR/license.key" "$WORKING_DIRECTORY/"
+ chmod 777 $SERVICE_FILE
+ if [ $? -eq 0 ]; then
+ log INFO "Rulex service has been created and extracted."
+ else
+ log ERROR "Failed to create the Rulex service or extract files."
+ fi
+ exit 0
+}
+
+__remove_files() {
+ local file=$1
+ log INFO "Removing $file."
+ if [ -e "$file" ]; then
+ if [ -d "$file" ]; then
+ rm -rf "$file"
+ else
+ rm "$file"
+ fi
+ log INFO "$file removed."
+ else
+ log INFO "$file not found. No need to remove."
+ fi
+}
+
+uninstall(){
+ if [ -e "$SERVICE_FILE" ]; then
+ $SERVICE_FILE stop
+ fi
+ __remove_files "$SERVICE_FILE"
+ __remove_files "$WORKING_DIRECTORY/rulex"
+ __remove_files "$WORKING_DIRECTORY/rulex.ini"
+ __remove_files "$WORKING_DIRECTORY/rulex.db"
+ __remove_files "$WORKING_DIRECTORY/license.lic"
+ __remove_files "$WORKING_DIRECTORY/license.key"
+ __remove_files "$WORKING_DIRECTORY/RULEX_INTERNAL_DATACENTER.db"
+ __remove_files "$WORKING_DIRECTORY/upload/"
+ __remove_files "$WORKING_DIRECTORY/rulexlog.txt"
+ __remove_files "$WORKING_DIRECTORY/rulex-daemon-log.txt"
+ __remove_files "$WORKING_DIRECTORY/rulex-recover-log.txt"
+ __remove_files "$WORKING_DIRECTORY/rulex-upgrade-log.txt"
+ log INFO "Rulex has been uninstalled."
+}
+
+start() {
+ $SERVICE_FILE start
+}
+
+restart() {
+ $SERVICE_FILE restart
+}
+
+stop() {
+ $SERVICE_FILE stop
+}
+
+status() {
+ $SERVICE_FILE status
+}
+
+case "$1" in
+ install)
+ install
+ ;;
+ start)
+ start
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ uninstall)
+ uninstall
+ ;;
+ status)
+ status
+ ;;
+ *)
+ log ERROR "Usage: $0 {install|start|restart|stop|uninstall|status}"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/script/rulex-openwrt.sh b/script/rulex-openwrt.sh
new file mode 100644
index 000000000..43046b67a
--- /dev/null
+++ b/script/rulex-openwrt.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+# Copyright (C) 2023 wwhai
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+WORKING_DIRECTORY="/usr/local"
+
+
+log() {
+ local level=$1
+ shift
+ echo "[$level] $(date +'%Y-%m-%d %H:%M:%S') - $@"
+}
+
+install() {
+ local source_dir="$PWD"
+ local service_file="/etc/init.d/rulex.service"
+ local executable="/usr/local/rulex"
+ local config_file="/usr/local/rulex.ini"
+ local db_file="/usr/local/rulex.db"
+
+ cat > "$service_file" << EOL
+#!/bin/sh $service_file
+
+START=180
+
+USE_PROCD=1
+
+start() {
+ procd_open_instance
+ cd $WORKING_DIRECTORY
+ procd_set_param command "$executable run -config=$config_file -db=$db_file"
+ procd_set_param respawn 0
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_close_instance
+}
+
+stop() {
+ service_stop "$executable"
+}
+
+restart() {
+ stop
+ start
+}
+
+status() {
+ log INFO "Checking rulex status."
+ pid=\$(pgrep -x -n "rulex")
+ if [ -n "\$pid" ]; then
+ log INFO "rulex is running with Pid:\${pid}"
+ else
+ log INFO "rulex is not running."
+ fi
+}
+
+EOL
+
+ mkdir -p "$WORKING_DIRECTORY/"
+ chmod +x "$source_dir/rulex"
+ cp -rfp "$source_dir/rulex" "$executable"
+ cp -rfp "$source_dir/rulex.ini" "$config_file"
+ cp -rfp "$source_dir/license.lic" "$WORKING_DIRECTORY/"
+ cp -rfp "$source_dir/license.key" "$WORKING_DIRECTORY/"
+
+ chmod 777 "$service_file"
+ "$service_file" enable
+
+ if [ $? -eq 0 ]; then
+ log INFO "Rulex service has been created and extracted."
+ else
+ log ERROR "Failed to create the Rulex service or extract files."
+ fi
+ exit 0
+}
+
+__remove_files() {
+ local file=$1
+ log INFO "Removing $file..."
+ if [ -e "$file" ]; then
+ if [ -d "$file" ]; then
+ rm -rf "$file"
+ else
+ rm "$file"
+ fi
+ log INFO "$file removed."
+ else
+ log INFO "$file not found. No need to remove."
+ fi
+}
+
+uninstall() {
+ if [ -e "$service_file" ]; then
+ $service_file stop
+ $service_file disable
+ fi
+ __remove_files $service_file
+ __remove_files "$WORKING_DIRECTORY/rulex"
+ __remove_files "$WORKING_DIRECTORY/rulex.ini"
+ __remove_files "$WORKING_DIRECTORY/rulex.db"
+ __remove_files "$WORKING_DIRECTORY/license.lic"
+ __remove_files "$WORKING_DIRECTORY/license.key"
+ __remove_files "$WORKING_DIRECTORY/RULEX_INTERNAL_DATACENTER.db"
+ __remove_files "$WORKING_DIRECTORY/LICENSE"
+ __remove_files "$WORKING_DIRECTORY/md5.sum"
+ __remove_files "$WORKING_DIRECTORY/upload/"
+ rm -f "$WORKING_DIRECTORY/*.txt"
+ rm -f "$WORKING_DIRECTORY/*.txt.gz"
+ log INFO "Rulex has been uninstalled."
+}
+
+start() {
+ $service_file start
+}
+
+restart() {
+ $service_file restart
+}
+
+stop() {
+ $service_file stop
+}
+
+status() {
+ $service_file status
+}
+
+main() {
+ case "$1" in
+ install | start | restart | stop | uninstall | create_user | status | openwrt)
+ $1
+ ;;
+ *)
+ log ERROR "Invalid command: $1"
+ echo "[?] Usage: $0 "
+ exit 1
+ ;;
+ esac
+ exit 0
+}
+
+if [ "$(id -u)" != "0" ]; then
+ log ERROR "This script must be run as root"
+ exit 1
+else
+ main "$1"
+fi
diff --git a/script/rulexd.sh b/script/rulex-systemctl.sh
similarity index 59%
rename from script/rulexd.sh
rename to script/rulex-systemctl.sh
index 7d363712c..2cebed77b 100644
--- a/script/rulexd.sh
+++ b/script/rulex-systemctl.sh
@@ -19,11 +19,10 @@ echo_yellow() {
echo -e "${YELLOW}$1${RESET}"
}
install(){
- # check_deps
local source_dir="$PWD"
local service_file="/etc/systemd/system/rulex.service"
local executable="/usr/local/rulex"
- local working_directory="/usr/local/"
+ local WORKING_DIRECTORY="/usr/local/"
local config_file="/usr/local/rulex.ini"
local db_file="/usr/local/rulex.db"
cat > "$service_file" << EOL
@@ -33,8 +32,9 @@ After=network.target
[Service]
Environment="ARCHSUPPORT=EEKITH3"
-WorkingDirectory=$working_directory
+WorkingDirectory=$WORKING_DIRECTORY
ExecStart=$executable run -config=$config_file -db=$db_file
+ConditionPathExists=!/var/run/rulex-upgrade.lock
Restart=always
User=root
Group=root
@@ -44,8 +44,14 @@ RestartSec=5
WantedBy=multi-user.target
EOL
chmod +x $source_dir/rulex
+ echo "[.] Copy $source_dir/rulex to $WORKING_DIRECTORY."
cp "$source_dir/rulex" "$executable"
+ echo "[.] Copy $source_dir/rulex.ini to $WORKING_DIRECTORY."
cp "$source_dir/rulex.ini" "$config_file"
+ echo "[.] Copy $source_dir/license.key to /usr/local/license.key."
+ cp "$source_dir/license.key" "/usr/local/license.key"
+ echo "[.] Copy $source_dir/license.lic to /usr/local/license.lic."
+ cp "$source_dir/license.lic" "/usr/local/license.lic"
systemctl daemon-reload
systemctl enable rulex
systemctl start rulex
@@ -75,63 +81,35 @@ stop(){
systemctl stop rulex
echo "[√] Service Rulex has been stopped."
}
-remove_files(){
- if ls $1 1> /dev/null 2>&1; then
- rm $1
+remove_files() {
+ if [ -e "$1" ]; then
+ if [[ $1 == *"/upload"* ]]; then
+ rm -rf "$1"
+ else
+ rm "$1"
+ fi
echo "[!] $1 files removed."
else
- echo "[#] $1 files not found. No need to remove."
+ echo "[*] $1 files not found. No need to remove."
fi
}
+
uninstall(){
- local working_directory="/usr/local"
systemctl stop rulex
systemctl disable rulex
remove_files /etc/systemd/system/rulex.service
- remove_files $working_directory/rulex
- remove_files $working_directory/rulex.ini
- remove_files $working_directory/rulex.db
- remove_files $working_directory/*.txt
- remove_files $working_directory/*.txt.gz
+ remove_files $WORKING_DIRECTORY/rulex
+ remove_files $WORKING_DIRECTORY/rulex.ini
+ remove_files $WORKING_DIRECTORY/rulex.db
+ remove_files $WORKING_DIRECTORY/upload/
+ remove_files $WORKING_DIRECTORY/license.key
+ remove_files $WORKING_DIRECTORY/license.lic
+ rm -f "$WORKING_DIRECTORY/*.txt"
+ rm -f "$WORKING_DIRECTORY/*.txt.gz"
systemctl daemon-reload
systemctl reset-failed
echo "[√] Rulex has been uninstalled."
}
-
-check_deps() {
- # Check if ffmpeg is installed
- echo_yellow "[·] Check necessary software"
- if command -v ffmpeg >/dev/null 2>&1; then
- echo "[=] ffmpeg is installed"
- else
- echo "!!! ffmpeg is not installed, Please install ffmpeg, you can go to install document: https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu"
- exit 1
- fi
- # Check if ttyd is installed
- if command -v ttyd >/dev/null 2>&1; then
- echo "[=] ttyd is installed"
- else
- echo "!!! ttyd is not installed, Please install ttyd, you can go to install document: https://github.com/tsl0922/ttyd"
- exit 1
- fi
- echo_yellow "[√] All necessary software is installed"
-}
-# create a default user
-create_user(){
- response=$(curl -X POST -H "Content-Type: application/json" -d '{
- "role": "admin",
- "username": "admin",
- "password": "admin",
- "description": "system admin"
- }' http://127.0.0.1:2580/api/v1/users -w "%{http_code}")
-
- if [ "$response" = "201" ]; then
- echo "[√] User created"
- else
- echo "[x] User creation failed"
- fi
-
-}
#
#
#
diff --git a/script/rulex_install.bat b/script/rulex_install.bat
new file mode 100644
index 000000000..f1dce5257
--- /dev/null
+++ b/script/rulex_install.bat
@@ -0,0 +1,25 @@
+rem Copyright (C) 2023 wwhai
+rem
+rem This program is free software: you can redistribute it and/or modify
+rem it under the terms of the GNU Affero General Public License as
+rem published by the Free Software Foundation, either version 3 of the
+rem License, or (at your option) any later version.
+rem
+rem This program is distributed in the hope that it will be useful,
+rem but WITHOUT ANY WARRANTY; without even the implied warranty of
+rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+rem GNU Affero General Public License for more details.
+rem
+rem You should have received a copy of the GNU Affero General Public License
+rem along with this program. If not, see .
+
+@echo off
+
+set SERVICE_NAME=RulexService
+set BINARY_PATH=%CD%\rulex.exe
+set CONFIG_PATH=%CD%\rulex.ini
+
+sc create %SERVICE_NAME% binPath= "%BINARY_PATH% run -config %CONFIG_PATH%" start= auto DisplayName= RulexService
+
+echo Service created successfully.
+
diff --git a/script/rulex_uninstall.bat b/script/rulex_uninstall.bat
new file mode 100644
index 000000000..8005723b5
--- /dev/null
+++ b/script/rulex_uninstall.bat
@@ -0,0 +1,23 @@
+rem Copyright (C) 2023 wwhai
+rem
+rem This program is free software: you can redistribute it and/or modify
+rem it under the terms of the GNU Affero General Public License as
+rem published by the Free Software Foundation, either version 3 of the
+rem License, or (at your option) any later version.
+rem
+rem This program is distributed in the hope that it will be useful,
+rem but WITHOUT ANY WARRANTY; without even the implied warranty of
+rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+rem GNU Affero General Public License for more details.
+rem
+rem You should have received a copy of the GNU Affero General Public License
+rem along with this program. If not, see .
+
+@echo off
+
+set SERVICE_NAME=RulexService
+
+sc stop %SERVICE_NAME%
+sc delete %SERVICE_NAME%
+
+echo Service deleted successfully.
diff --git a/source/internal_event_source.go b/source/internal_event_source.go
new file mode 100644
index 000000000..91f7bf63b
--- /dev/null
+++ b/source/internal_event_source.go
@@ -0,0 +1,160 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package source
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/hootrhino/rulex/component/internotify"
+ "github.com/hootrhino/rulex/typex"
+ "github.com/hootrhino/rulex/utils"
+)
+
+/*
+*
+* 用来将内部消息总线的事件推到资源脚本
+*
+ */
+type __InternalEventSourceConfig struct {
+ // - ALL: 全部事件
+ // - SOURCE: 南向事件
+ // - DEVICE: 设备事件
+ // - TARGET: 北向事件
+ // - SYSTEM: 系统事件
+ // - HARDWARE: 硬件事件
+ Type string `json:"type"`
+}
+type InternalEventSource struct {
+ typex.XStatus
+ mainConfig __InternalEventSourceConfig
+}
+
+func NewInternalEventSource(r typex.RuleX) typex.XSource {
+ s := InternalEventSource{}
+ s.mainConfig = __InternalEventSourceConfig{
+ Type: "ALL",
+ }
+ s.RuleEngine = r
+ return &s
+}
+
+func (u *InternalEventSource) Init(inEndId string, configMap map[string]interface{}) error {
+ u.PointId = inEndId
+ if err := utils.BindSourceConfig(configMap, &u.mainConfig); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (u *InternalEventSource) Start(cctx typex.CCTX) error {
+ u.Ctx = cctx.Ctx
+ u.CancelCTX = cctx.CancelCTX
+ u.startInternalEventQueue()
+ return nil
+
+}
+func (u *InternalEventSource) DataModels() []typex.XDataModel {
+ return u.XDataModels
+}
+
+func (u *InternalEventSource) Status() typex.SourceState {
+ return typex.SOURCE_UP
+}
+
+func (u *InternalEventSource) Stop() {
+ if u.CancelCTX != nil {
+ u.CancelCTX()
+ }
+}
+func (*InternalEventSource) Driver() typex.XExternalDriver {
+ return nil
+}
+
+func (u *InternalEventSource) Details() *typex.InEnd {
+ return u.RuleEngine.GetInEnd(u.PointId)
+}
+
+func (u *InternalEventSource) Test(inEndId string) bool {
+ return true
+}
+
+// 拓扑
+func (*InternalEventSource) Topology() []typex.TopologyPoint {
+ return []typex.TopologyPoint{}
+}
+
+// 来自外面的数据
+func (*InternalEventSource) DownStream([]byte) (int, error) {
+ return 0, nil
+}
+
+// 上行数据
+func (*InternalEventSource) UpStream([]byte) (int, error) {
+ return 0, nil
+}
+
+type event struct {
+ // - ALL: 全部
+ // - SOURCE: 南向事件
+ // - DEVICE: 设备事件
+ // - TARGET: 北向事件
+ // - SYSTEM: 系统内部事件
+ // - HARDWARE: 硬件事件
+ Type string `json:"type"`
+ Event string `json:"event"`
+ Ts uint64 `json:"ts"`
+ Info interface{} `json:"info"`
+}
+
+/*
+*
+* 从内部总线拿数据
+*
+ */
+func (ie *InternalEventSource) startInternalEventQueue() {
+ go func(ctx context.Context) {
+ internotify.AddSource()
+ defer internotify.RemoveSource()
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case Event := <-internotify.GetQueue():
+ if ie.mainConfig.Type == "ALL" {
+ bytes, _ := json.Marshal(event{
+ Type: Event.Type,
+ Event: Event.Event,
+ Ts: Event.Ts,
+ Info: Event.Info,
+ })
+ ie.RuleEngine.WorkInEnd(ie.RuleEngine.GetInEnd(ie.PointId), string(bytes))
+ continue
+ }
+ if ie.mainConfig.Type == Event.Type {
+ bytes, _ := json.Marshal(event{
+ Type: Event.Type,
+ Event: Event.Event,
+ Ts: Event.Ts,
+ Info: Event.Info,
+ })
+ ie.RuleEngine.WorkInEnd(ie.RuleEngine.GetInEnd(ie.PointId), string(bytes))
+ continue
+ }
+ }
+ }
+ }(ie.Ctx)
+}
diff --git a/source/internal_event_source.md b/source/internal_event_source.md
new file mode 100644
index 000000000..9f2752a8d
--- /dev/null
+++ b/source/internal_event_source.md
@@ -0,0 +1,38 @@
+# 内部消息源
+可以使用这个数据源来监控系统内部的消息,比如设备离线,资源连接失败等。
+
+## 消息类型
+- SOURCE: 南向事件
+- DEVICE: 设备事件
+- TARGET: 北向事件
+- SYSTEM: 系统内部事件
+- HARDWARE: 硬件事件
+
+## 示例
+### 设备上线:
+```json
+{
+ "type" :"DEVICE",
+ "event":"event.connected",
+ "ts":121312431432,
+ "device_info":{
+ "uuid":"UUID1234567",
+ "name":"温湿度计"
+ }
+}
+
+```
+
+### 设备离线:
+```json
+{
+ "type" :"DEVICE",
+ "event":"event.disconnected",
+ "ts":121312431432,
+ "device_info":{
+ "uuid":"UUID1234567",
+ "name":"温湿度计"
+ }
+}
+
+```
\ No newline at end of file
diff --git a/source/readme.md b/source/readme.md
index 3a3799632..5009d0ee6 100644
--- a/source/readme.md
+++ b/source/readme.md
@@ -1,44 +1,2 @@
-##
-
-### 开发模板
-资源需要实现以下接口:
-```go
-
-type XSource interface {
- Test(inEndId string) bool
- Init(inEndId string, cfg map[string]interface{}) error
- Start(CCTX) error
- DataModels() []XDataModel
- Status() SourceState
- Details() *InEnd
- Driver() XExternalDriver
- Topology() []TopologyPoint
- Stop()
-}
-
-```
-### 加载配置
-我们以一个 `COAP Server` 为例来解释,首先定义一个配置结构体:
-```go
-type coAPConfig struct {
- Port uint16 `json:"port" validate:"required" title:"端口"`
- DataModels []typex.XDataModel `json:"dataModels" title:"数据模型"`
-}
-```
-然后在Init里面解析外部配置到资源的配置结构体,相当于是加载配置:
-```go
-
-func (cc *coAPInEndSource) Init(inEndId string, cfg map[string]interface{}) error {
- cc.PointId = inEndId
- var mainConfig coAPConfig
- if err := utils.BindSourceConfig(cfg, &mainConfig); err != nil {
- return err
- }
- cc.port = mainConfig.Port
- cc.dataModels = mainConfig.DataModels
- return nil
-}
-```
-参数说明
-- inEndId:资源元数据的ID
-- cfg:配置数据的Map形式结构
\ No newline at end of file
+## 外部双向、单向资源
+主要用来和外部资源对接,比如Mqtt等。具备双向通信的功能。
\ No newline at end of file
diff --git a/source/uudp_source.go b/source/uudp_source.go
index 63ccadfef..28632a572 100644
--- a/source/uudp_source.go
+++ b/source/uudp_source.go
@@ -23,6 +23,7 @@ func NewUdpInEndSource(e typex.RuleX) typex.XSource {
u.RuleEngine = e
return &u
}
+
func (u *udpSource) Start(cctx typex.CCTX) error {
u.Ctx = cctx.Ctx
u.CancelCTX = cctx.CancelCTX
diff --git a/target/http_target.go b/target/http_target.go
index ad9e6070c..6259a8fb3 100644
--- a/target/http_target.go
+++ b/target/http_target.go
@@ -69,9 +69,10 @@ func (ht *HTTPTarget) To(data interface{}) (interface{}, error) {
func (ht *HTTPTarget) Stop() {
ht.status = typex.SOURCE_STOP
- ht.CancelCTX()
+ if ht.CancelCTX != nil {
+ ht.CancelCTX()
+ }
}
func (ht *HTTPTarget) Details() *typex.OutEnd {
return ht.RuleEngine.GetOutEnd(ht.PointId)
}
-
diff --git a/target/tdengine_target.go b/target/tdengine_target.go
index ce9fde2b5..262565eb8 100644
--- a/target/tdengine_target.go
+++ b/target/tdengine_target.go
@@ -93,19 +93,8 @@ func (td *tdEngineTarget) Start(cctx typex.CCTX) error {
td.Ctx = cctx.Ctx
td.CancelCTX = cctx.CancelCTX
//
-
- if err := execQuery(td.client, td.mainConfig.Username,
- td.mainConfig.Password, td.mainConfig.CreateDbSql, td.url()); err != nil {
- return err
- }
- if err := execQuery(td.client, td.mainConfig.Username,
- td.mainConfig.Password, td.mainConfig.CreateTableSql, td.url()); err != nil {
- return err
- } else {
- td.status = typex.SOURCE_UP
- return nil
- }
-
+ td.status = typex.SOURCE_UP
+ return nil
}
// 数据模型, 用来描述该资源支持的数据, 对应的是云平台的物模型
diff --git a/target/tdengine_target.md b/target/tdengine_target.md
new file mode 100644
index 000000000..cef04b0b6
--- /dev/null
+++ b/target/tdengine_target.md
@@ -0,0 +1,23 @@
+# Tdengine 支持
+
+## 测试
+新建一个数据库,这里用Docker:
+```sh
+docker run -d --name tdengine -p 6041:6041 tdengine/tdengine
+```
+新建测试库:
+```sql
+create database if not exists rhino
+```
+新建测试表
+```sql
+create table if not exists tb1 (ts timestamp, a int)
+```
+插入数据
+```sql
+use rhino;
+insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3);
+insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3);
+insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3);
+```
+
diff --git a/target/ttcp_target.go b/target/ttcp_target.go
index 3d089b30d..6de8ef61d 100644
--- a/target/ttcp_target.go
+++ b/target/ttcp_target.go
@@ -21,20 +21,18 @@ import (
"net"
"time"
- "github.com/hootrhino/rulex/common"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
)
-type _TcpCommonConfig struct {
- DataMode string `json:"dataMode" validate:"required"` // RAW_STRING ; HEX_STRING
- AllowPing *bool `json:"allowPing" validate:"required"` // 是否开启ping
- PingPacket string `json:"pingPacket" validate:"required"` // Ping 包内容, 必填16字符以内
-}
type _TcpMainConfig struct {
- CommonConfig _TcpCommonConfig `json:"commonConfig" validate:"required"`
- HostConfig common.HostConfig `json:"hostConfig" validate:"required"`
+ AllowPing *bool `json:"allowPing"`
+ DataMode string `json:"dataMode"`
+ Host string `json:"host"`
+ PingPacket string `json:"pingPacket"`
+ Port int `json:"port"`
+ Timeout int `json:"timeout"`
}
type TTcpTarget struct {
typex.XStatus
@@ -52,19 +50,10 @@ func NewTTcpTarget(e typex.RuleX) typex.XTarget {
ht := new(TTcpTarget)
ht.RuleEngine = e
ht.mainConfig = _TcpMainConfig{
- CommonConfig: _TcpCommonConfig{
- DataMode: "RAW_STRING",
- AllowPing: func() *bool {
- b := true
- return &b
- }(),
- PingPacket: "HR0001", // 默认每隔5秒发送PING包
- },
- HostConfig: common.HostConfig{
- Host: "127.0.0.1",
- Port: 2585,
- Timeout: 3000,
- },
+ AllowPing: func() *bool {
+ b := true
+ return &b
+ }(),
}
ht.status = typex.SOURCE_DOWN
return ht
@@ -75,6 +64,7 @@ func (ht *TTcpTarget) Init(outEndId string, configMap map[string]interface{}) er
if err := utils.BindSourceConfig(configMap, &ht.mainConfig); err != nil {
return err
}
+ ht.mainConfig.PingPacket += "\r\n"
return nil
}
@@ -82,7 +72,7 @@ func (ht *TTcpTarget) Start(cctx typex.CCTX) error {
ht.Ctx = cctx.Ctx
ht.CancelCTX = cctx.CancelCTX
var err error
- host := fmt.Sprintf("%s:%d", ht.mainConfig.HostConfig.Host, ht.mainConfig.HostConfig.Port)
+ host := fmt.Sprintf("%s:%d", ht.mainConfig.Host, ht.mainConfig.Port)
serverAddr, err := net.ResolveTCPAddr("tcp", host)
if err != nil {
return err
@@ -94,7 +84,7 @@ func (ht *TTcpTarget) Start(cctx typex.CCTX) error {
if err != nil {
return err
}
- if *ht.mainConfig.CommonConfig.AllowPing {
+ if *ht.mainConfig.AllowPing {
go func(ht *TTcpTarget) {
for {
select {
@@ -107,10 +97,10 @@ func (ht *TTcpTarget) Start(cctx typex.CCTX) error {
}
}
ht.client.SetReadDeadline(
- time.Now().Add((time.Duration(ht.mainConfig.HostConfig.Timeout) *
+ time.Now().Add((time.Duration(ht.mainConfig.Timeout) *
time.Millisecond)),
)
- _, err1 := ht.client.Write([]byte(ht.mainConfig.CommonConfig.PingPacket))
+ _, err1 := ht.client.Write([]byte(ht.mainConfig.PingPacket))
ht.client.SetReadDeadline(time.Time{})
if err1 != nil {
glogger.GLogger.Error("TTcpTarget Ping Error:", err1)
@@ -139,26 +129,27 @@ func (ht *TTcpTarget) To(data interface{}) (interface{}, error) {
if ht.client != nil {
switch s := data.(type) {
case string:
- if ht.mainConfig.CommonConfig.DataMode == "RAW_STRING" {
+ if ht.mainConfig.DataMode == "RAW_STRING" {
ht.client.SetReadDeadline(
- time.Now().Add((time.Duration(ht.mainConfig.HostConfig.Timeout) *
+ time.Now().Add((time.Duration(ht.mainConfig.Timeout) *
time.Millisecond)),
)
- _, err0 := ht.client.Write([]byte(s))
+ _, err0 := ht.client.Write([]byte(s + "\r\n"))
ht.client.SetReadDeadline(time.Time{})
if err0 != nil {
return 0, err0
}
}
- if ht.mainConfig.CommonConfig.DataMode == "HEX_STRING" {
+ if ht.mainConfig.DataMode == "HEX_STRING" {
dByte, err1 := hex.DecodeString(s)
if err1 != nil {
return 0, err1
}
ht.client.SetReadDeadline(
- time.Now().Add((time.Duration(ht.mainConfig.HostConfig.Timeout) *
+ time.Now().Add((time.Duration(ht.mainConfig.Timeout) *
time.Millisecond)),
)
+ dByte = append(dByte, []byte{'\r', '\n'}...)
_, err0 := ht.client.Write(dByte)
ht.client.SetReadDeadline(time.Time{})
if err0 != nil {
diff --git a/target/udp_target.go b/target/udp_target.go
index 037f82f00..6807f7ca9 100644
--- a/target/udp_target.go
+++ b/target/udp_target.go
@@ -90,7 +90,9 @@ func (udpt *UdpTarget) To(data interface{}) (interface{}, error) {
func (udpt *UdpTarget) Stop() {
udpt.status = typex.SOURCE_STOP
- udpt.CancelCTX()
+ if udpt.CancelCTX != nil {
+ udpt.CancelCTX()
+ }
}
func (udpt *UdpTarget) Details() *typex.OutEnd {
return udpt.RuleEngine.GetOutEnd(udpt.PointId)
diff --git a/test/Bin2F32Little_test.go b/test/Bin2F32Little_test.go
new file mode 100644
index 000000000..aae98d865
--- /dev/null
+++ b/test/Bin2F32Little_test.go
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package test
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "math"
+ "testing"
+)
+
+// go test -timeout 30s -run ^Test_Binary_to_Float github.com/hootrhino/rulex/test -v -count=1
+func Test_Binary_to_Float(t *testing.T) {
+ // Hex 40 49 0E 56 = D3.141
+ dBytes, _ := hex.DecodeString("40490E56")
+ V1 := math.Float32frombits(binary.LittleEndian.Uint32(dBytes))
+ t.Log(V1)
+ V2 := math.Float32frombits(binary.BigEndian.Uint32(dBytes))
+ t.Log(V2)
+}
diff --git a/test/apps/binary_hex_to_float32.lua b/test/apps/binary_hex_to_float32.lua
new file mode 100644
index 000000000..10cb96418
--- /dev/null
+++ b/test/apps/binary_hex_to_float32.lua
@@ -0,0 +1,21 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+function Main(arg)
+ stdlib:Debug("Bin2F32Big 410e6667: " .. binary:Bin2F32Big("410e6667"))
+ -- 8.9
+ stdlib:Debug("Bin2F32Little 410e6667: " .. binary:Bin2F32Little("410e6667"))
+ return 0
+end
diff --git a/test/apps/complex_data_push.lua b/test/apps/complex_data_push.lua
index dfa1450a2..71adf330b 100644
--- a/test/apps/complex_data_push.lua
+++ b/test/apps/complex_data_push.lua
@@ -28,7 +28,7 @@ function Main(arg)
state = string.sub(jsonT['out'], 9, 10)
}
print("udpData => ", udpData)
- local err4 = applib:DataToUdp(udpServerUUID, udpData)
+ local err4 = data:ToUdp(udpServerUUID, udpData)
print('DataToUdp success? =>', err4 == nil)
time:Sleep(1000)
until true
diff --git a/test/apps/complex_data_push1.lua b/test/apps/complex_data_push1.lua
index d657bc608..2cbd0bb1c 100644
--- a/test/apps/complex_data_push1.lua
+++ b/test/apps/complex_data_push1.lua
@@ -28,7 +28,7 @@ function Main(arg)
state = string.sub(jsonT['out'], 9, 10)
}
print("udpData => ", udpData)
- local err4 = applib:DataToUdp(udpServerUUID, udpData)
+ local err4 = data:ToUdp(udpServerUUID, udpData)
print('DataToUdp success? =>', err4 == nil)
time:Sleep(1000)
end
diff --git a/test/apps/data_to_screen_test.lua b/test/apps/data_to_screen_test.lua
index 690061ac8..767c39e0b 100644
--- a/test/apps/data_to_screen_test.lua
+++ b/test/apps/data_to_screen_test.lua
@@ -21,7 +21,7 @@ function ToScreen(In, Out, Data)
state = string.sub(jsonT['out'], 14, 14)
}
print("udpData => ", udpData)
- local err4 = applib:DataToUdp(Out, udpData)
+ local err4 = data:ToUdp(Out, udpData)
print('DataToUdp success? =>', err4 == nil)
end
diff --git a/test/apps/data_udp_server.lua b/test/apps/data_udp_server.lua
index 1c5b0c6d8..4ac1f4be8 100644
--- a/test/apps/data_udp_server.lua
+++ b/test/apps/data_udp_server.lua
@@ -3,7 +3,7 @@ AppVERSION = '0.0.1'
function Main(arg)
for i = 1, 10, 1 do
local data = { name = 'Demo', sn = 'A123456', state = '00' }
- local err = applib:DataToUdp('UdpServer', applib:T2J(data))
+ local err = data:ToUdp('UdpServer', applib:T2J(data))
applib:log('DataToUdp success? =>', err == nil)
time:Sleep(100)
end
diff --git a/test/apps/h3_loop_di10.lua b/test/apps/h3_loop_di10.lua
index 8e3dc0637..f02141b65 100644
--- a/test/apps/h3_loop_di10.lua
+++ b/test/apps/h3_loop_di10.lua
@@ -29,7 +29,7 @@ function Main(arg)
break
else
if v ~= s then
- local err0 = applib:DataToUdp('udpServerUUID', 'hello gpio10:' .. v)
+ local err0 = data:ToUdp('udpServerUUID', 'hello gpio10:' .. v)
print('DataToUdp success? =>', err0 == nil)
end
s = v
diff --git a/test/apps/h3_loop_di8.lua b/test/apps/h3_loop_di8.lua
index cad27182c..14c2fe095 100644
--- a/test/apps/h3_loop_di8.lua
+++ b/test/apps/h3_loop_di8.lua
@@ -29,7 +29,7 @@ function Main(arg)
break
else
if v ~= s then
- local err0 = applib:DataToUdp('udpServerUUID', 'hello gpio8:' .. v)
+ local err0 = data:ToUdp('udpServerUUID', 'hello gpio8:' .. v)
print('DataToUdp success? =>', err0 == nil)
end
s = v
diff --git a/test/apps/h3_loop_di9.lua b/test/apps/h3_loop_di9.lua
index 5078606ca..60acd5040 100644
--- a/test/apps/h3_loop_di9.lua
+++ b/test/apps/h3_loop_di9.lua
@@ -29,7 +29,7 @@ function Main(arg)
break
else
if v ~= s then
- local err0 = applib:DataToUdp('udpServerUUID', 'hello gpio9:' .. v)
+ local err0 = data:ToUdp('udpServerUUID', 'hello gpio9:' .. v)
print('DataToUdp success? =>', err0 == nil)
end
s = v
diff --git a/test/apps/hex_and_uint_covert_demo.lua b/test/apps/hex_and_uint_covert_demo.lua
new file mode 100644
index 000000000..fd4405580
--- /dev/null
+++ b/test/apps/hex_and_uint_covert_demo.lua
@@ -0,0 +1,31 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+
+function Main(arg)
+ -- MatchHexS 是一个Map结构,K为表达式的name,Value为十六进制字符串,
+ -- binary:MatchUInt:表示提取值转换成4字节无符号数,也就是原始字节。
+ -- "hum:[0,1];temp:[2,3]": 表示提取第 0 1 个字节,赋值给hum;提取第 2 3个字节赋值给humi
+ local MatchHexS = hex:MatchUInt("hum:[0,1];temp:[2,3]", "000102030405060708")
+ -- 大端输出温度
+ stdlib:Debug("Bin2F32Big hum: " .. binary:Bin2F32Big(MatchHexS['hum']))
+ -- 大端输出湿度
+ stdlib:Debug("Bin2F32Big temp: " .. binary:Bin2F32Big(MatchHexS['temp']))
+ -- 小端输出温度
+ stdlib:Debug("Bin2F32Little hum: " .. binary:Bin2F32Little(MatchHexS['hum']))
+ -- 小端输出湿度
+ stdlib:Debug("Bin2F32Little temp: " .. binary:Bin2F32Little(MatchHexS['temp']))
+ return 0
+end
diff --git a/test/apps/http_get_demo.lua b/test/apps/http_get_demo.lua
new file mode 100644
index 000000000..0ec953978
--- /dev/null
+++ b/test/apps/http_get_demo.lua
@@ -0,0 +1,20 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+function Main(arg)
+ local Value = http:Get("http://127.0.0.1:2580/api/v1/os/system")
+ stdlib:Debug("Http Get:" .. Value)
+ return 0
+end
diff --git a/test/apps/http_post_demo.lua b/test/apps/http_post_demo.lua
new file mode 100644
index 000000000..5d0283445
--- /dev/null
+++ b/test/apps/http_post_demo.lua
@@ -0,0 +1,43 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software= you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+function Main(arg)
+ local dataTable = {
+ device_uuid = 1,
+ recv_time = "2023-11-28T11=11=36+08=00",
+ bat_voltage = 0,
+ longitude = 0,
+ latitude = 0,
+ air_height = 0,
+ water_temp = 19.3171,
+ salinity = 754.523,
+ dissolved_oxygen = 0,
+ ph_value = 5.94821,
+ wind_speed = 1.02,
+ wind_direction = 12,
+ air_temp = 21.1,
+ air_pressure = 102.3,
+ air_humidity = 69.1,
+ noise = 42.9,
+ wave_height = 0,
+ mean_wave_period = 0,
+ peak_wave_period = 0,
+ mean_wave_direction = 0
+ }
+ local JsonString = json:T2J(dataTable)
+ local Value = http:Post("http://127.0.0.1:6003/api", JsonString)
+ stdlib:Debug("Http Post:" .. Value)
+ return 0
+end
diff --git a/test/apps/read_multi_device1.lua b/test/apps/read_multi_device1.lua
index 0fe8fec1b..c971aabe6 100644
--- a/test/apps/read_multi_device1.lua
+++ b/test/apps/read_multi_device1.lua
@@ -29,7 +29,7 @@ function Main(arg)
state = string.sub(jsonT['out'], 9, 10)
}
print("UdpData => ", udpDataJson)
- local err4 = applib:DataToUdp(udpServerUUID, udpDataJson)
+ local err4 = data:ToUdp(udpServerUUID, udpDataJson)
print('DataToUdp success? =>', err4 == nil)
time:Sleep(1000)
end
diff --git a/test/apps/s1200_parse_float.lua b/test/apps/s1200_parse_float.lua
new file mode 100644
index 000000000..32a478c8f
--- /dev/null
+++ b/test/apps/s1200_parse_float.lua
@@ -0,0 +1,23 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+-- 003e 004c 00cc 00cd
+function Main(arg)
+ local HexS = "3e4ccccd"
+ stdlib:Debug("Bin2F32Big:" .. HexS .. "->" .. binary:Bin2F32Big(HexS))
+ -- 8.9
+ stdlib:Debug("Bin2F32Little:" .. HexS .. "->" .. binary:Bin2F32Little(HexS))
+ return 0
+end
diff --git a/test/apps/user_led_ctrl.lua b/test/apps/user_led_ctrl.lua
new file mode 100644
index 000000000..d280a0f3b
--- /dev/null
+++ b/test/apps/user_led_ctrl.lua
@@ -0,0 +1,19 @@
+---@diagnostic disable: undefined-global
+--
+-- App use lua syntax, goto https://hootrhino.github.io for more document
+-- APPID: APP6b28330ff4be4b0ba2f3e9317c4e2a47
+--
+AppNAME = "LED-RGB"
+AppVERSION = "1.0.0"
+AppDESCRIPTION = ""
+--
+-- Main
+--
+function Main(arg)
+ while true do
+ rhinopi:Led1On()
+ time:Sleep(200)
+ rhinopi:Led1Off()
+ time:Sleep(200)
+ end
+end
diff --git a/test/appstack_httpapi_test.go b/test/appstack_httpapi_test.go
index bdecb1785..76f419901 100644
--- a/test/appstack_httpapi_test.go
+++ b/test/appstack_httpapi_test.go
@@ -11,7 +11,7 @@ import (
"testing"
"github.com/go-playground/assert/v2"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
/*
diff --git a/test/binary_lua_test.go b/test/binary_lua_test.go
index 223e1d9e0..0eb7efbfe 100644
--- a/test/binary_lua_test.go
+++ b/test/binary_lua_test.go
@@ -5,12 +5,12 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/engine"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/plugin/demo_plugin"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/conf/licence.key b/test/conf/licence.key
new file mode 100644
index 000000000..bb51973d3
--- /dev/null
+++ b/test/conf/licence.key
@@ -0,0 +1 @@
+914674f13f92175feceac1cd70d566cc
\ No newline at end of file
diff --git a/test/conf/licence.lic b/test/conf/licence.lic
new file mode 100644
index 000000000..eb662e1e1
--- /dev/null
+++ b/test/conf/licence.lic
@@ -0,0 +1 @@
+P2UO_dEbw5sCsXBYAF6hGgUIdTEyBi9K7x6Wai1UpymdycrtVV2Fqtwt1ssydG1U
\ No newline at end of file
diff --git a/test/conf/license.key b/test/conf/license.key
new file mode 100644
index 000000000..bb51973d3
--- /dev/null
+++ b/test/conf/license.key
@@ -0,0 +1 @@
+914674f13f92175feceac1cd70d566cc
\ No newline at end of file
diff --git a/test/conf/license.lic b/test/conf/license.lic
new file mode 100644
index 000000000..57d389d42
--- /dev/null
+++ b/test/conf/license.lic
@@ -0,0 +1 @@
+6HQVH6rUebN8D0J2KjEHHoza-P-xtVhEFh7pmB-P4Wc5RJ70lf4G8__kWtWC9SJS
\ No newline at end of file
diff --git a/test/conf/rulex.ini b/test/conf/rulex.ini
index c867ab55b..a22d819a1 100644
--- a/test/conf/rulex.ini
+++ b/test/conf/rulex.ini
@@ -36,9 +36,9 @@ log_path = rulex-log.txt
#
lua_log_path = rulex-lua-log.txt
#
-# Max data cache size, default is 20MB
+# Max data cache size
#
-max_queue_size = 204800
+max_queue_size = 102400
#
# Max store size, default is 20MB
#
@@ -181,7 +181,7 @@ enable = true
#
# Modbus CRC calculator
#
-[plugin.modbuscrc_tools]
+[plugin.modbus_crc_tools]
#
# Enable
#
@@ -193,4 +193,17 @@ enable = true
#
# Enable
#
-enable = true
\ No newline at end of file
+enable = true
+[plugin.license_manager]
+#
+# Enable
+#
+enable = true
+#
+# Enable
+#
+license_path = test/conf/license.lic
+#
+# Enable
+#
+key_path = test/conf/license.key
\ No newline at end of file
diff --git a/test/custom_tcp_server_test.go b/test/custom_tcp_server_test.go
index 86dd7f79b..3eba538c7 100644
--- a/test/custom_tcp_server_test.go
+++ b/test/custom_tcp_server_test.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/hootrhino/rulex/component/appstack"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/data/daemon.sh b/test/data/daemon.sh
new file mode 100644
index 000000000..13e9bb1bd
--- /dev/null
+++ b/test/data/daemon.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+# Create Time: 2023-11-27 14:59:06
+
+WORKING_DIRECTORY="/usr/local"
+PID_FILE="/var/run/rulex.pid"
+EXECUTABLE_PATH="$WORKING_DIRECTORY/rulex"
+CONFIG_PATH="$WORKING_DIRECTORY/rulex.ini"
+
+log() {
+ local level=$1
+ shift
+ echo "[$level] $(date +'%Y-%m-%d %H:%M:%S') - $@"
+}
+
+start() {
+ rm -f /var/run/rulex-stop.sinal
+ pid=$(pgrep -x -n -f "/usr/local/rulex run -config=/usr/local/rulex.ini")
+ if [ -n "$pid" ]; then
+ log INFO "rulex is running with Pid:${pid}"
+ exit 0
+ fi
+ daemon &
+ exit 0
+}
+
+stop() {
+ echo "1" > /var/run/rulex-stop.sinal
+ if pgrep -x "rulex" > /dev/null; then
+ log INFO "rulex process is running. Killing it..."
+ pkill -x "rulex"
+ log INFO "rulex process has been killed."
+ else
+ log WARNING "rulex process is not running."
+ fi
+}
+
+restart() {
+ stop
+ sleep 1
+ start
+}
+
+status() {
+ log INFO "Checking rulex status."
+ pid=$(pgrep -x -n "rulex")
+ if [ -n "$pid" ]; then
+ log INFO "rulex is running with Pid:${pid}"
+ else
+ log INFO "rulex is not running."
+ fi
+}
+
+daemon() {
+ while true; do
+ if pgrep -x "rulex" > /dev/null; then
+ sleep 3
+ continue
+ fi
+ if ! pgrep -x "rulex" > /dev/null; then
+ if [ -e "/var/run/rulex-upgrade.lock" ]; then
+ log INFO "File /var/run/rulex-upgrade.lock exists. May upgrade now."
+ sleep 2
+ continue
+ elif [ -e "/var/run/rulex-stop.sinal" ]; then
+ log INFO "/var/run/rulex-stop.sinal file found. Exiting."
+ exit 0
+ else
+ log WARNING "Detected that rulex process is interrupted. Restarting..."
+ /usr/local/rulex run -config=/usr/local/rulex.ini
+ log WARNING "Detected that rulex process has Restarted."
+ fi
+ fi
+ sleep 4
+ done
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ restart)
+ restart
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status
+ ;;
+ *)
+ log ERROR "Usage: $0 {start|restart|stop|status}"
+ exit 1
+ ;;
+esac
+
diff --git a/test/data/h264-stream-play.html b/test/data/h264-stream-play.html
new file mode 100644
index 000000000..d9fd5b635
--- /dev/null
+++ b/test/data/h264-stream-play.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ WebSocket FLV Stream Player
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/data/test-modbus-tcp-sheet.xlsx b/test/data/test-modbus-tcp-sheet.xlsx
new file mode 100644
index 000000000..cf899fac7
Binary files /dev/null and b/test/data/test-modbus-tcp-sheet.xlsx differ
diff --git a/test/data/test-modbus-uart-sheet.xlsx b/test/data/test-modbus-uart-sheet.xlsx
new file mode 100644
index 000000000..e3c298aa3
Binary files /dev/null and b/test/data/test-modbus-uart-sheet.xlsx differ
diff --git a/test/data/test-siemens-sheet.xlsx b/test/data/test-siemens-sheet.xlsx
new file mode 100644
index 000000000..d360c1054
Binary files /dev/null and b/test/data/test-siemens-sheet.xlsx differ
diff --git a/test/device_485ther_gw_test.go b/test/device_485ther_gw_test.go
index 07ba6dddf..96201f8d1 100644
--- a/test/device_485ther_gw_test.go
+++ b/test/device_485ther_gw_test.go
@@ -3,7 +3,7 @@ package test
import (
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
diff --git a/test/device_custom_protocol_device_test.go b/test/device_custom_protocol_device_test.go
index 9d985e195..82e3f46a6 100644
--- a/test/device_custom_protocol_device_test.go
+++ b/test/device_custom_protocol_device_test.go
@@ -9,8 +9,8 @@ import (
serial "github.com/wwhai/tarmserial"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"github.com/hootrhino/rulex/utils"
"google.golang.org/grpc"
diff --git a/test/device_daccall_test.go b/test/device_daccall_test.go
index fbf97bca8..e72d8bc16 100644
--- a/test/device_daccall_test.go
+++ b/test/device_daccall_test.go
@@ -4,8 +4,8 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
mqttserver "github.com/hootrhino/rulex/plugin/mqtt_server"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/device_g776dtu_test.go b/test/device_g776dtu_test.go
index 5dec70c89..037d222b9 100644
--- a/test/device_g776dtu_test.go
+++ b/test/device_g776dtu_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/device_generic_ais_rxtx_device_test.go b/test/device_generic_ais_rxtx_device_test.go
index ea1e73d8c..44aaf2652 100644
--- a/test/device_generic_ais_rxtx_device_test.go
+++ b/test/device_generic_ais_rxtx_device_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/adrianmo/go-nmea"
"github.com/hootrhino/rulex/typex"
@@ -35,7 +35,7 @@ func Test_AIS_SEND_PACKET(t *testing.T) {
* AIS 数据发送模拟器
*
*/
-func ais_sender_emulator_udp() {
+func Ais_sender_emulator_udp() {
// Server address
serverAddr := "localhost:6005"
@@ -74,7 +74,7 @@ func ais_sender_emulator_udp() {
fmt.Println("Response from server:", response)
}
-func ais_sender_emulator_tcp() {
+func Ais_sender_emulator_tcp() {
// Connect to the server
s1 := `!ABVDM,1,1,5,B,H69EvShlTpID@TpMUG3COOL0000,2*14`
conn, err := net.Dial("tcp", "localhost:6005")
diff --git a/test/device_generic_camera_stream_test.go b/test/device_generic_camera_stream_test.go
index 42da1a584..514990070 100644
--- a/test/device_generic_camera_stream_test.go
+++ b/test/device_generic_camera_stream_test.go
@@ -1,8 +1,8 @@
package test
import (
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"testing"
"time"
@@ -24,7 +24,7 @@ func Test_Generic_Local_camera(t *testing.T) {
hh := httpserver.NewHttpApiServer(engine)
// HttpApiServer loaded default
if err := engine.LoadPlugin("plugin.http_server", hh); err != nil {
- glogger.GLogger.Fatal("Rule load failed:", err)
+ glogger.GLogger.Fatal("http_server load failed:", err)
t.Fatal(err)
}
GENERIC_CAMERA := typex.NewDevice(typex.GENERIC_CAMERA,
@@ -66,7 +66,35 @@ func Test_Generic_RTSP_camera(t *testing.T) {
"inputMode": "RTSP",
"device": "video0",
"rtspUrl": "rtsp://192.168.0.101:554/av0_0",
- "outputMode": "JPEG_STREAM",
+ "outputMode": "H264_STREAM",
+ "outputAddr": "0.0.0.0:2599",
+ })
+ ctx, cancelF := typex.NewCCTX()
+ if err := engine.LoadDeviceWithCtx(GENERIC_CAMERA, ctx, cancelF); err != nil {
+ t.Fatal(err)
+ }
+ time.Sleep(25 * time.Second)
+ engine.Stop()
+}
+
+// go test -timeout 30s -run ^Test_Generic_LOCAL_camera github.com/hootrhino/rulex/test -v -count=1
+
+func Test_Generic_LOCAL_camera(t *testing.T) {
+ engine := RunTestEngine()
+ engine.Start()
+
+ hh := httpserver.NewHttpApiServer(engine)
+ if err := engine.LoadPlugin("plugin.http_server", hh); err != nil {
+ glogger.GLogger.Fatal("Rule load failed:", err)
+ t.Fatal(err)
+ }
+ GENERIC_CAMERA := typex.NewDevice(typex.GENERIC_CAMERA,
+ "GENERIC_CAMERA", "GENERIC_CAMERA", map[string]interface{}{
+ "maxThread": 10,
+ "inputMode": "LOCAL",
+ "device": "video0",
+ "rtspUrl": "rtsp://192.168.0.101:554/av0_0",
+ "outputMode": "H264_STREAM",
"outputAddr": "0.0.0.0:2599",
})
ctx, cancelF := typex.NewCCTX()
diff --git a/test/device_generic_modbus_device_test.go b/test/device_generic_modbus_device_test.go
index ead7af0d5..c032b1cd9 100644
--- a/test/device_generic_modbus_device_test.go
+++ b/test/device_generic_modbus_device_test.go
@@ -3,8 +3,8 @@ package test
import (
"context"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
mbserver "github.com/tbrandon/mbserver"
"testing"
diff --git a/test/device_generic_opcua_device_test.go b/test/device_generic_opcua_device_test.go
index 63cc145ca..7dc732748 100644
--- a/test/device_generic_opcua_device_test.go
+++ b/test/device_generic_opcua_device_test.go
@@ -4,9 +4,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/device"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/device_generic_snmp_device_test.go b/test/device_generic_snmp_device_test.go
index 7eb3f0987..d128f7ad1 100644
--- a/test/device_generic_snmp_device_test.go
+++ b/test/device_generic_snmp_device_test.go
@@ -1,8 +1,8 @@
package test
import (
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"testing"
"time"
diff --git a/test/device_generic_uart_device_test.go b/test/device_generic_uart_device_test.go
index ecf75bb6e..b5151a71c 100644
--- a/test/device_generic_uart_device_test.go
+++ b/test/device_generic_uart_device_test.go
@@ -3,7 +3,7 @@ package test
import (
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
diff --git a/test/device_gos7_plc_data_parse_test.go b/test/device_gos7_plc_data_parse_test.go
index aa8200032..e9f588ed3 100644
--- a/test/device_gos7_plc_data_parse_test.go
+++ b/test/device_gos7_plc_data_parse_test.go
@@ -5,9 +5,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/device_icmp_sender_test.go b/test/device_icmp_sender_test.go
index bc0b9d1f6..ac858d6fd 100644
--- a/test/device_icmp_sender_test.go
+++ b/test/device_icmp_sender_test.go
@@ -3,7 +3,7 @@ package test
import (
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
diff --git a/test/device_modbus_wite_with_app_test.go b/test/device_modbus_wite_with_app_test.go
index 325e389dc..77525148f 100644
--- a/test/device_modbus_wite_with_app_test.go
+++ b/test/device_modbus_wite_with_app_test.go
@@ -5,7 +5,7 @@ import (
"github.com/hootrhino/rulex/common"
"github.com/hootrhino/rulex/component/appstack"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
@@ -13,12 +13,12 @@ import (
)
// 读出来的字节缓冲默认大小
-const __DEFAULT_BUFFER_SIZE = 100
+// const __DEFAULT_BUFFER_SIZE = 100
// 传输形式:
// `rawtcp`, `rawudp`, `rawserial`
-const rawtcp string = "TCP"
-const rawudp string = "rawudp"
+// const rawtcp string = "TCP"
+// const rawudp string = "rawudp"
const rawserial string = "UART"
type _CPDCommonConfig struct {
diff --git a/test/device_s1200plc_test.go b/test/device_s1200plc_test.go
index 69d0d0d1f..788434b62 100644
--- a/test/device_s1200plc_test.go
+++ b/test/device_s1200plc_test.go
@@ -31,32 +31,6 @@ import (
// ]
// }
func Test_gen_config(t *testing.T) {
- port := 1800
- Rack := 0
- Slot := 1
- Timeout := 5
- IdleTimeout := 5
- ReadFrequency := 5
- c := common.S1200Config{
- Host: "127.0.0.1",
- Port: &port,
- Rack: &Rack,
- Slot: &Slot,
- Model: "S1200",
- Timeout: &Timeout,
- IdleTimeout: &IdleTimeout,
- Frequency: int64(ReadFrequency),
- Blocks: []common.S1200Block{
- {
- Tag: "V1",
- Address: 1,
- Start: 1,
- Size: 10,
- },
- },
- }
- b, _ := json.MarshalIndent(c, "", " ")
- t.Log(string(b))
}
func Test_parse_config(t *testing.T) {
@@ -92,14 +66,14 @@ func Test_parse_config(t *testing.T) {
/*
*
-* 测试RULEX加载 S1200PLC
+* 测试RULEX加载 SIEMENS_PLC
*
*/
-func Test_RULEX_WITH_S1200PLC(t *testing.T) {
+func Test_RULEX_WITH_SIEMENS_PLC(t *testing.T) {
engine := RunTestEngine()
engine.Start()
- S1200PLC := typex.NewDevice(typex.S1200PLC,
+ SIEMENS_PLC := typex.NewDevice(typex.SIEMENS_PLC,
"PLC工站系统", "PLC工站系统", map[string]interface{}{
"host": "127.0.0.1",
"port": 1800,
@@ -125,10 +99,10 @@ func Test_RULEX_WITH_S1200PLC(t *testing.T) {
},
},
)
- S1200PLC.UUID = "S1200PLC"
+ SIEMENS_PLC.UUID = "SIEMENS_PLC"
ctx, cancelF := typex.NewCCTX()
- if err := engine.LoadDeviceWithCtx(S1200PLC, ctx, cancelF); err != nil {
- t.Error("S1200PLC load failed:", err)
+ if err := engine.LoadDeviceWithCtx(SIEMENS_PLC, ctx, cancelF); err != nil {
+ t.Error("SIEMENS_PLC load failed:", err)
}
//
// 透传到内部EMQX
@@ -136,7 +110,7 @@ func Test_RULEX_WITH_S1200PLC(t *testing.T) {
EMQX_BROKER := typex.NewOutEnd(typex.MQTT_TARGET,
"内网MQTT桥接",
"内网MQTT桥接", map[string]interface{}{
- "Host": "emqx.dev.inrobot.cloud",
+ "Host": "emqx.dev.com",
"Port": 1883,
"DataTopic": "iothub/upstream/YK0801",
"ClientId": "YK0801",
diff --git a/test/device_th_485_sensor_data_parse_test.go b/test/device_th_485_sensor_data_parse_test.go
index 96f240c58..6ac7a1ec8 100644
--- a/test/device_th_485_sensor_data_parse_test.go
+++ b/test/device_th_485_sensor_data_parse_test.go
@@ -6,8 +6,8 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/device_tss200_test.go b/test/device_tss200_test.go
index 148bdbe48..f76162e55 100644
--- a/test/device_tss200_test.go
+++ b/test/device_tss200_test.go
@@ -1,8 +1,8 @@
package test
import (
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"testing"
"time"
diff --git a/test/device_yk8_with_rulex_test.go b/test/device_yk8_with_rulex_test.go
index da20ede05..b7be8a148 100644
--- a/test/device_yk8_with_rulex_test.go
+++ b/test/device_yk8_with_rulex_test.go
@@ -3,7 +3,7 @@ package test
import (
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
diff --git a/test/eventbus_test.go b/test/eventbus_test.go
new file mode 100644
index 000000000..e7b839cb1
--- /dev/null
+++ b/test/eventbus_test.go
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 wwhai
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/hootrhino/rulex/component/eventbus"
+)
+
+// @ go test -timeout 30s -run ^TestEventBus github.com/hootrhino/rulex/test -v -count=1
+func TestEventBus(t *testing.T) {
+
+ eventbus.InitEventBus()
+ eventbus.Subscribe("hello", &eventbus.Subscriber{
+ Callback: func(Topic string, Msg eventbus.EventMessage) {
+ t.Log("hello:", Msg)
+ },
+ })
+ start := time.Now()
+ for i := 0; i < 100; i++ {
+ eventbus.Publish("hello", eventbus.EventMessage{
+ Payload: fmt.Sprintf("world:%d", i),
+ })
+ }
+ duration := time.Since(start)
+ t.Log("time.Since(start):", duration)
+ time.Sleep(3 * time.Second)
+ eventbus.Flush()
+}
diff --git a/test/fully_test.go b/test/fully_test.go
index 1e6752781..bd7a2fa57 100644
--- a/test/fully_test.go
+++ b/test/fully_test.go
@@ -3,9 +3,9 @@ package test
import (
"context"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"testing"
diff --git a/test/http_api_device_snmp_curd_test.go b/test/http_api_device_snmp_curd_test.go
index 2c468ce07..007bd82d7 100644
--- a/test/http_api_device_snmp_curd_test.go
+++ b/test/http_api_device_snmp_curd_test.go
@@ -11,7 +11,7 @@ import (
"testing"
"github.com/go-playground/assert/v2"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
)
/*
diff --git a/test/init_data_test.go b/test/init_data_test.go
index 61ea2eda8..32db1312c 100644
--- a/test/init_data_test.go
+++ b/test/init_data_test.go
@@ -4,12 +4,12 @@ import (
"encoding/json"
"testing"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
+ "github.com/hootrhino/rulex/component/rulex_api_server/service"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/engine"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
- "github.com/hootrhino/rulex/plugin/http_server/model"
- "github.com/hootrhino/rulex/plugin/http_server/service"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/jq_test.go b/test/jq_test.go
index ccccedeef..c600629fe 100644
--- a/test/jq_test.go
+++ b/test/jq_test.go
@@ -5,9 +5,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/list_serial_port_test.go b/test/list_serial_port_test.go
index f383ad90c..e57b37ab7 100644
--- a/test/list_serial_port_test.go
+++ b/test/list_serial_port_test.go
@@ -1,9 +1,10 @@
package test
import (
+ "runtime"
"testing"
- "github.com/hootrhino/rulex/glogger"
+ "github.com/hootrhino/rulex/ossupport"
"go.bug.st/serial"
)
@@ -11,12 +12,11 @@ func Test_GetPortsList(t *testing.T) {
t.Log(GetUartList())
}
func GetUartList() []string {
- r := []string{}
- ports, err := serial.GetPortsList()
- if err != nil {
- glogger.GLogger.Error(err)
- return r
+ var ports []string
+ if runtime.GOOS == "windows" {
+ ports, _ = serial.GetPortsList()
+ } else {
+ ports, _ = ossupport.GetPortsListUnix()
}
- r = append(r, ports...)
- return r
+ return ports
}
diff --git a/test/lua/_exit.lua b/test/lua/_exit.lua
deleted file mode 100644
index 19a0eac8f..000000000
--- a/test/lua/_exit.lua
+++ /dev/null
@@ -1,2 +0,0 @@
-function _exit() end
-_exit()
\ No newline at end of file
diff --git a/test/lua/data_tag.lua b/test/lua/data_tag.lua
index 8b96e5f4e..d7cfe049c 100644
--- a/test/lua/data_tag.lua
+++ b/test/lua/data_tag.lua
@@ -3,9 +3,9 @@
-- {"tag":"add2", "id": "002", "value": 0x0002},
-- ]
-function ParseData(data)
+function ParseData(args)
-- data: {"in":"AA0011...","out":"AABBCDD..."}
- local DataT, err = rulexlib:J2T(data)
+ local DataT, err = rulexlib:J2T(args)
if err ~= nil then
return true, args
end
diff --git a/test/lua/parse_485_float_value.lua b/test/lua/parse_485_float_value.lua
new file mode 100644
index 000000000..f6665a272
--- /dev/null
+++ b/test/lua/parse_485_float_value.lua
@@ -0,0 +1,24 @@
+Actions = {
+ function(args)
+ local dataT, err = json:J2T(args)
+ if (err ~= nil) then
+ stdlib:Throw('parse json error:' .. err)
+ return true, args
+ end
+ for key, value in pairs(dataT) do
+ stdlib:Debug(key .. " :: " .. value['value'])
+ local MatchHexS = hex:MatchHex("humi:[0,3];temp:[4,7];pres:[8,11]", value['value'])
+ local ts = time:Time()
+ local Json = json:T2J(
+ {
+ tag = key,
+ ts = ts,
+ hum = math:TFloat(binary:Bin2F32(MatchHexS['humi'])),
+ temp = math:TFloat(binary:Bin2F32(MatchHexS['temp'])),
+ pres = math:TFloat(binary:Bin2F32(MatchHexS['pres'])),
+ })
+ stdlib:Debug(Json)
+ end
+ return true, args
+ end
+}
diff --git a/test/lua/siemens_s1200_data_parse.lua b/test/lua/siemens_s1200_data_parse.lua
new file mode 100644
index 000000000..5c00b4320
--- /dev/null
+++ b/test/lua/siemens_s1200_data_parse.lua
@@ -0,0 +1,52 @@
+-- Copyright (C) 2023 wwhai
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program. If not, see .
+
+-- Actions
+-- {
+-- "tag":"Value",
+-- "type":"DB",
+-- "frequency":0,
+-- "address":1,
+-- "start":100,
+-- "size":16,
+-- "value":"00000001000000020000000300000004"
+-- }
+Actions =
+{
+ function(args)
+ local dataT, err = json:J2T(args)
+ if (err ~= nil) then
+ stdlib:Debug('parse json error:' .. err)
+ return true, args
+ end
+ for key, value in pairs(dataT) do
+ --data: 00000001000000020000000300000004
+ local MatchHexS = hex:MatchUInt("a:[0,3];b:[4,7];c:[8,11];d:[12,15]", value['value'])
+ local ts = time:Time()
+ local Json = json:T2J(
+ {
+ tag = key,
+ ts = ts,
+ a = MatchHexS['a'],
+ b = MatchHexS['b'],
+ c = MatchHexS['c'],
+ d = MatchHexS['d'],
+ }
+ )
+ stdlib:Debug(Json)
+ end
+ return true, args
+ end
+}
diff --git a/test/modbus_master_test.go b/test/modbus_master_test.go
index 5da1081f2..d48719e2e 100644
--- a/test/modbus_master_test.go
+++ b/test/modbus_master_test.go
@@ -13,13 +13,10 @@ type registerParam struct {
}
type ModBusConfig struct {
- Mode string `json:"mode" title:"工作模式" info:"可以在 UART/TCP 两个模式之间切换"`
- Timeout int `json:"timeout" validate:"required" title:"连接超时"`
- SlaverId byte `json:"slaverId" validate:"required" title:"TCP端口"`
- //
- // Weather allow AutoRequest?
- AutoRequest bool `json:"autoRequest" title:"启动轮询"`
- // Request Frequency, default 5 second
+ Mode string `json:"mode" title:"工作模式" info:"可以在 UART/TCP 两个模式之间切换"`
+ Timeout int `json:"timeout" validate:"required" title:"连接超时"`
+ SlaverId byte `json:"slaverId" validate:"required" title:"TCP端口"`
+ AutoRequest bool `json:"autoRequest" validate:"required"`
Frequency int64 `json:"frequency" validate:"required" title:"采集频率"`
Config interface{} `json:"config" validate:"required" title:"工作模式配置"`
RegisterParams []registerParam `json:"registerParams" validate:"required" title:"寄存器配置"`
diff --git a/test/modbus_parse_test.go b/test/modbus_parse_test.go
index 02d192914..f18b0b9c6 100644
--- a/test/modbus_parse_test.go
+++ b/test/modbus_parse_test.go
@@ -8,12 +8,12 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/engine"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/plugin/demo_plugin"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/rpc_codec_test.go b/test/rpc_codec_test.go
index 94700a4ca..50bb11ce3 100644
--- a/test/rpc_codec_test.go
+++ b/test/rpc_codec_test.go
@@ -6,9 +6,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/rule_id_get_test.go b/test/rule_id_get_test.go
index 5e8363a32..3f7ea419c 100644
--- a/test/rule_id_get_test.go
+++ b/test/rule_id_get_test.go
@@ -5,9 +5,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/rulex_snapshot_dump_test.go b/test/rulex_snapshot_dump_test.go
index 768ccaecd..16ab8fbf2 100644
--- a/test/rulex_snapshot_dump_test.go
+++ b/test/rulex_snapshot_dump_test.go
@@ -8,12 +8,12 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/engine"
"github.com/hootrhino/rulex/glogger"
"github.com/hootrhino/rulex/plugin/demo_plugin"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/source_http_source_test.go b/test/source_http_source_test.go
index f59ab0994..e05362ae4 100644
--- a/test/source_http_source_test.go
+++ b/test/source_http_source_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/utils"
"github.com/hootrhino/rulex/typex"
diff --git a/test/suorce_txiothub_test.go b/test/suorce_txiothub_test.go
index 4a3fae310..903e4e03a 100644
--- a/test/suorce_txiothub_test.go
+++ b/test/suorce_txiothub_test.go
@@ -3,7 +3,7 @@ package test
import (
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"testing"
diff --git a/test/target_data_to_mongodb_test.go b/test/target_data_to_mongodb_test.go
index f57187162..07df91b4f 100644
--- a/test/target_data_to_mongodb_test.go
+++ b/test/target_data_to_mongodb_test.go
@@ -7,9 +7,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/target_data_tohttp_test.go b/test/target_data_tohttp_test.go
index 4d818bbaa..08f20c8a1 100644
--- a/test/target_data_tohttp_test.go
+++ b/test/target_data_tohttp_test.go
@@ -10,8 +10,8 @@ import (
"time"
"github.com/go-playground/assert/v2"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"github.com/hootrhino/rulex/typex"
)
diff --git a/test/target_data_toudp_test.go b/test/target_data_toudp_test.go
index 6937a0b50..baecd45f6 100644
--- a/test/target_data_toudp_test.go
+++ b/test/target_data_toudp_test.go
@@ -10,13 +10,13 @@ import (
"time"
"github.com/go-playground/assert/v2"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
- "github.com/hootrhino/rulex/plugin/http_server/model"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
+ "github.com/hootrhino/rulex/component/rulex_api_server/model"
"github.com/hootrhino/rulex/typex"
)
-var _DataToUdp_luaCase = `function Main(arg) for i = 1, 3, 1 do local err = applib:DataToUdp('UdpServer',applib:T2J({temp = 20,humi = 13.45})) applib:log('result =>',err) time:Sleep(100) end return 0 end`
+var _DataToUdp_luaCase = `function Main(arg) for i = 1, 3, 1 do local err = data:ToUdp('UdpServer',applib:T2J({temp = 20,humi = 13.45})) applib:log('result =>',err) time:Sleep(100) end return 0 end`
// go test -timeout 30s -run ^Test_DataToUdp github.com/hootrhino/rulex/test -v -count=1
diff --git a/test/target_tdengine_test.go b/test/target_tdengine_test.go
index 795564eab..344d7ffcf 100644
--- a/test/target_tdengine_test.go
+++ b/test/target_tdengine_test.go
@@ -7,8 +7,8 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/rulexrpc"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"github.com/hootrhino/rulex/typex"
"google.golang.org/grpc"
diff --git a/test/test_utils.go b/test/test_utils.go
index 6f173a4db..a3cfd771b 100644
--- a/test/test_utils.go
+++ b/test/test_utils.go
@@ -10,7 +10,7 @@ import (
"testing"
"time"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/core"
"github.com/hootrhino/rulex/engine"
diff --git a/test/trailer_test.go b/test/trailer_test.go
index 0093ece49..700def8a3 100644
--- a/test/trailer_test.go
+++ b/test/trailer_test.go
@@ -7,9 +7,9 @@ import (
"testing"
"time"
+ httpserver "github.com/hootrhino/rulex/component/rulex_api_server"
"github.com/hootrhino/rulex/component/trailer"
"github.com/hootrhino/rulex/glogger"
- httpserver "github.com/hootrhino/rulex/plugin/http_server"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
diff --git a/typex/constant.go b/typex/constant.go
index 43ea8426a..b1fd60532 100644
--- a/typex/constant.go
+++ b/typex/constant.go
@@ -47,37 +47,14 @@ func (i InEndType) String() string {
}
const (
- MQTT InEndType = "MQTT"
- HTTP InEndType = "HTTP"
- COAP InEndType = "COAP"
- GRPC InEndType = "GRPC"
- UART_MODULE InEndType = "UART_MODULE"
- //
- // MODBUS_MASTER
- //
- MODBUS_MASTER InEndType = "MODBUS_MASTER"
- //
- // MODBUS_SLAVER
- //
- MODBUS_SLAVER InEndType = "MODBUS_SLAVER"
- //
- // From snmp server provider
- //
- SNMP_SERVER InEndType = "SNMP_SERVER"
- //
- // NATS.IO SERVER
- //
- NATS_SERVER InEndType = "NATS_SERVER"
- //
- // 西门子S7客户端
- //
- SIEMENS_S7 InEndType = "SIEMENS_S7"
- //
- // RULEX UDP 自定义简单协议
- //
- RULEX_UDP InEndType = "RULEX_UDP"
- // 通用IotHUB
+ MQTT InEndType = "MQTT"
+ HTTP InEndType = "HTTP"
+ COAP InEndType = "COAP"
+ GRPC InEndType = "GRPC"
+ NATS_SERVER InEndType = "NATS_SERVER"
+ RULEX_UDP InEndType = "RULEX_UDP"
GENERIC_IOT_HUB InEndType = "GENERIC_IOT_HUB"
+ INTERNAL_EVENT InEndType = "INTERNAL_EVENT" // 内部消息
)
// TargetType
@@ -102,9 +79,7 @@ const (
PGSQL_TARGET TargetType = "PGSQL"
NATS_TARGET TargetType = "NATS"
HTTP_TARGET TargetType = "HTTP"
- //
// TDENGINE
- //
TDENGINE_TARGET TargetType = "TDENGINE"
// GRPC
GRPC_CODEC_TARGET TargetType = "GRPC_CODEC_TARGET"
diff --git a/typex/global.go b/typex/global.go
index c254d82a7..981a81dd0 100644
--- a/typex/global.go
+++ b/typex/global.go
@@ -3,7 +3,7 @@ package typex
import "context"
// Global context
-var GCTX = context.Background()
+var GCTX, GCancel = context.WithCancel(context.Background())
// child context
type CCTX struct {
diff --git a/typex/rule.go b/typex/rule.go
index cc64579d0..d3517537f 100644
--- a/typex/rule.go
+++ b/typex/rule.go
@@ -23,8 +23,8 @@ type Rule struct {
Type string `json:"type"` // 脚本类型,目前支持"lua"
Status RuleStatus `json:"status"`
Name string `json:"name"`
- FromSource []string `json:"fromSource"` // 来自数据源
- FromDevice []string `json:"fromDevice"` // 来自设备
+ FromSource string `json:"fromSource"` // 来自数据源
+ FromDevice string `json:"fromDevice"` // 来自设备
Actions string `json:"actions"`
Success string `json:"success"`
Failed string `json:"failed"`
@@ -36,8 +36,8 @@ func NewLuaRule(e RuleX,
uuid string,
name string,
description string,
- fromSource []string,
- fromDevice []string,
+ fromSource string,
+ fromDevice string,
success string,
actions string,
failed string) *Rule {
@@ -58,8 +58,8 @@ func NewRule(e RuleX,
uuid string,
name string,
description string,
- fromSource []string,
- fromDevice []string,
+ fromSource string,
+ fromDevice string,
success string,
actions string,
failed string) *Rule {
diff --git a/typex/rulex.go b/typex/rulex.go
index 5ccfdec87..d287ae105 100644
--- a/typex/rulex.go
+++ b/typex/rulex.go
@@ -122,7 +122,7 @@ type RuleX interface {
//
// 获取版本
//
- Version() Version
+ Version() VersionInfo
//
// 停止规则引擎
diff --git a/typex/version.go b/typex/version.go
index bb3463b52..79235fb4b 100644
--- a/typex/version.go
+++ b/typex/version.go
@@ -3,19 +3,21 @@
// This file is generated by go compiler, don't change it!!!
package typex
-type Version struct {
- Version string
+var MainVersion string
+
+type VersionInfo struct {
ReleaseTime string
Arch string
+ Product string
Dist string
}
-var DefaultVersion = Version{
- Version: `v0.6.4`,
- ReleaseTime: "2023-11-14 21:53:36",
+var DefaultVersionInfo = VersionInfo{
+ Product: "COMMON",
+ ReleaseTime: "2023-12-30 22:38:41",
}
var Banner = `
** Welcome to RULEX framework world <'_'>
-** Version: v0.6.4-014c2c4e2cf3603
+** Version: v0.6.5-cb68b5b9df7ae04
** Document: https://hootrhino.github.io
`
diff --git a/typex/xdevice.go b/typex/xdevice.go
index a6969ee85..955f14c3d 100644
--- a/typex/xdevice.go
+++ b/typex/xdevice.go
@@ -41,17 +41,18 @@ func (s DeviceState) String() string {
type DeviceType string
+func (d DeviceType) String() string {
+ return string(d)
+
+}
+
// 支持的设备类型
const (
- TSS200V02 DeviceType = "TSS200V02" // Multi params Sensor
- RTU485_THER DeviceType = "RTU485_THER" // RS485 Sensor
- YK08_RELAY DeviceType = "YK08_RELAY" // YK8 RS485 Relay
- S1200PLC DeviceType = "S1200PLC" // SIEMENS-S71200
+ SIEMENS_PLC DeviceType = "SIEMENS_PLC" // SIEMENS-S71200
GENERIC_MODBUS DeviceType = "GENERIC_MODBUS" // 通用Modbus
GENERIC_MODBUS_POINT_EXCEL DeviceType = "GENERIC_MODBUS_POINT_EXCEL" // 通用Modbus通过Excel表配置点位
GENERIC_UART DeviceType = "GENERIC_UART" // 通用串口
GENERIC_SNMP DeviceType = "GENERIC_SNMP" // SNMP 支持
- USER_G776 DeviceType = "USER_G776" // 有人 G776 4G模组
ICMP_SENDER DeviceType = "ICMP_SENDER" // ICMP_SENDER
GENERIC_PROTOCOL DeviceType = "GENERIC_PROTOCOL" // 通用自定义协议处理器
GENERIC_OPCUA DeviceType = "GENERIC_OPCUA" // 通用OPCUA
@@ -59,6 +60,7 @@ const (
GENERIC_AIS_RECEIVER DeviceType = "GENERIC_AIS_RECEIVER" // 通用AIS
GENERIC_BACNET_IP DeviceType = "GENERIC_BACNET_IP" // 通用BacnetIP
RHINOPI_IR DeviceType = "RHINOPI_IR" // 大犀牛PI的红外线接收器
+ GENERIC_HTTP_DEVICE DeviceType = "GENERIC_HTTP_DEVICE" // GENERIC_HTTP
)
// 设备元数据, 本质是保存在配置里面的数据的一个内存映射实例
diff --git a/typex/xstore.go b/typex/xstore.go
index 64aeaa990..0c8b72b66 100644
--- a/typex/xstore.go
+++ b/typex/xstore.go
@@ -16,5 +16,5 @@ type XStore interface {
Count() int
// 模糊查询匹配
// 支持: *AAA AAA* A*B
- FuzzyGet(k string) string
+ FuzzyGet(k string) any
}
diff --git a/utils/io_util.go b/utils/io_util.go
index 604925dbc..7ef9d65d4 100644
--- a/utils/io_util.go
+++ b/utils/io_util.go
@@ -3,6 +3,7 @@ package utils
import (
"context"
"errors"
+ "fmt"
"io"
"time"
)
@@ -147,3 +148,15 @@ func Paginate(pageNum int, pageSize int, sliceLength int) (int, int) {
return start, end
}
+
+/*
+*
+* 自定义日志
+*
+ */
+func CLog(format string, v ...interface{}) {
+ timestamp := time.Now().UTC().Format("2006/01/02 15:04:05.000000")
+ logMsg := fmt.Sprintf(format, v...)
+ logLine := fmt.Sprintf("[%s] %s\n", timestamp, logMsg)
+ fmt.Print(logLine)
+}
diff --git a/utils/os_common.go b/utils/os_common.go
index 71fa708c6..4c28e31e1 100644
--- a/utils/os_common.go
+++ b/utils/os_common.go
@@ -94,30 +94,42 @@ func GetOSDistribution() (string, error) {
}
// Linux 有很多发行版, 目前特别要识别一下Openwrt
if runtime.GOOS == "linux" {
- cmd := exec.Command("cat", "/etc/os-release")
- output, err := cmd.Output()
- if err != nil {
- return runtime.GOOS, err
- }
- osIssue := strings.ToLower(string(output))
- if strings.Contains((osIssue), "openwrt") {
- return "openwrt", nil
- }
- if strings.Contains((osIssue), "ubuntu") {
- return "ubuntu", nil
- }
- if strings.Contains((osIssue), "debian") {
- return "debian", nil
- }
- if strings.Contains((osIssue), "armbian") {
- return "armbian", nil
- }
- if strings.Contains((osIssue), "deepin") {
- return "deepin", nil
+ if PathExists("/etc/os-release") {
+ cmd := exec.Command("cat", "/etc/os-release")
+ output, err := cmd.Output()
+ if err != nil {
+ return runtime.GOOS, err
+ }
+ osIssue := strings.ToLower(string(output))
+ if strings.Contains((osIssue), "openwrt") {
+ return "openwrt", nil
+ }
+ if strings.Contains((osIssue), "ubuntu") {
+ return "ubuntu", nil
+ }
+ if strings.Contains((osIssue), "debian") {
+ return "debian", nil
+ }
+ if strings.Contains((osIssue), "armbian") {
+ return "armbian", nil
+ }
+ if strings.Contains((osIssue), "deepin") {
+ return "deepin", nil
+ }
}
}
return runtime.GOOS, nil
}
+func PathExists(path string) bool {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true
+ }
+ if os.IsNotExist(err) {
+ return false
+ }
+ return false
+}
/*
*
diff --git a/utils/os_util_linux.go b/utils/os_util_linux.go
index 35739ef30..76c43778a 100644
--- a/utils/os_util_linux.go
+++ b/utils/os_util_linux.go
@@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"strings"
+ "runtime"
)
/*
@@ -139,17 +140,23 @@ func GetSystemDevices() (SystemDevices, error) {
*/
func CatOsRelease() (map[string]string, error) {
returnMap := map[string]string{}
- cfg, err := ini.ShadowLoad("/etc/os-release")
- if err != nil {
- return nil, err
- }
- DefaultSection, err := cfg.GetSection("DEFAULT")
- if err != nil {
- return nil, err
- }
- for _, Key := range DefaultSection.KeyStrings() {
- V, _ := DefaultSection.GetKey(Key)
- returnMap[Key] = V.String()
+ if runtime.GOOS == "linux" {
+ if PathExists("/etc/os-release") {
+ cfg, err := ini.ShadowLoad("/etc/os-release")
+ if err != nil {
+ return nil, err
+ }
+ DefaultSection, err := cfg.GetSection("DEFAULT")
+ if err != nil {
+ return nil, err
+ }
+ for _, Key := range DefaultSection.KeyStrings() {
+ V, _ := DefaultSection.GetKey(Key)
+ returnMap[Key] = V.String()
+ }
+ } else {
+ returnMap["OS Version"] = "UNKNOWN"
+ }
}
return returnMap, nil
diff --git a/utils/uuid_util.go b/utils/uuid_util.go
index 9f6ebaa60..529e8a732 100644
--- a/utils/uuid_util.go
+++ b/utils/uuid_util.go
@@ -50,9 +50,23 @@ func RuleUuid() string {
return MakeUUID("RULE")
}
+// MakeUUID
+func UserLuaUuid() string {
+ return MakeUUID("USERLUA")
+}
+
+// MakeUUID
+func ModbusPointUUID() string {
+ return MakeUUID("MDTB")
+}
+// MakeUUID
+func SiemensPointUUID() string {
+ return MakeUUID("SIMTB")
+}
+
// MakeUUID
func MakeUUID(prefix string) string {
- return prefix + strings.ToUpper(shortuuid.New()[:6])
+ return prefix + strings.ToUpper(shortuuid.New()[:8])
}
func MakeLongUUID(prefix string) string {
return prefix + strings.ToUpper(shortuuid.New())