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())