增加设备请求中间件

This commit is contained in:
2025-08-13 19:42:30 +08:00
parent a6b12a50ec
commit e3eee46349
16 changed files with 362 additions and 29 deletions

View File

@@ -1,5 +1,5 @@
package config
const (
Mysqldb = "device_management:12345678@tcp(124.220.236.38:3306)/device_management?charset=utf8"
Mysqldb = "device_management:12345678@tcp(124.220.236.38:3306)/device_management?charset=utf8mb4&parseTime=true" //在MySQL连接字符串中添加parseTime=true参数会让MySQL驱动自动将日期时间字符串转换为time.Time类型
)

View File

@@ -120,9 +120,9 @@ func (d *DeviceController) HandleMQTTMessage(topic string, payload []byte) {
deviceID := parts[1]
action := parts[2]
deviceModel := models.Device{}
// 检查设备是否存在
// 获取设备信息
d.mu.RLock()
deviceInfo, err := deviceModel.CheckDevice(deviceID)
deviceInfo, err := deviceModel.GetDeviceInfo(deviceID)
d.mu.RUnlock()
if err != nil || deviceInfo.ID <= 0 {
return
@@ -270,25 +270,27 @@ func (d *DeviceController) handleDisconnected(deviceID uint, deviceKey, orgId, t
// @Tags 设备管理
// @Accept json
// @Produce json
// @Param product_key path string true "设备类型key"
// @Param device_number path string true "设备编号"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 409 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /devices/{device_number}/onoff [get]
// @Router /devices/{product_key}/{device_number}/onoff [get]
func (d *DeviceController) DeviceOnOff(c *gin.Context) {
var req dto.SystemDeviceRequest
if err := c.ShouldBindUri(&req); err != nil {
ReturnError(c, 400, "请求参数错误: "+err.Error())
return
}
//查询设备是否存在
// 检查设备是否已存在
deviceModel := models.Device{}
/*deviceModel := models.Device{}
deviceInfo, err := deviceModel.CheckDevice(req.DeviceNumber)
if err != nil || deviceInfo.ID <= 0 {
ReturnError(c, 400, "设备不存在")
return
}
}*/
//订阅设备泛型
//topic := "sys/" + req.DeviceNumber + "/connected"
topic := "sys/#"

View File

@@ -3,6 +3,7 @@ package dao
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
gormlog "gorm.io/gorm/logger"
"iot-mqtt-gin/config"
"iot-mqtt-gin/pkg/logger"
"time"
@@ -15,7 +16,10 @@ var (
func init() {
Db, err = gorm.Open(mysql.Open(config.Mysqldb), &gorm.Config{
//Logger: gormlog.Default.LogMode(gormlog.Info),//打印数据库sql
Logger: gormlog.Default.LogMode(gormlog.Info), //打印数据库sql
NowFunc: func() time.Time {
return time.Now().Local() // 使用本地时间
},
})
if err != nil {
//打印数据库连接错误信息

View File

@@ -15,7 +15,7 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/devices/{device_number}/onoff": {
"/devices/{product_key}/{device_number}/onoff": {
"get": {
"description": "订阅设备上下线",
"consumes": [
@@ -29,6 +29,13 @@ const docTemplate = `{
],
"summary": "订阅设备上下线",
"parameters": [
{
"type": "string",
"description": "设备类型key",
"name": "product_key",
"in": "path",
"required": true
},
{
"type": "string",
"description": "设备编号",

View File

@@ -13,7 +13,7 @@
"host": "127.0.0.1:9999",
"basePath": "/api/v1",
"paths": {
"/devices/{device_number}/onoff": {
"/devices/{product_key}/{device_number}/onoff": {
"get": {
"description": "订阅设备上下线",
"consumes": [
@@ -27,6 +27,13 @@
],
"summary": "订阅设备上下线",
"parameters": [
{
"type": "string",
"description": "设备类型key",
"name": "product_key",
"in": "path",
"required": true
},
{
"type": "string",
"description": "设备编号",

View File

@@ -25,12 +25,17 @@ info:
title: 物联网MQTT API
version: "1.0"
paths:
/devices/{device_number}/onoff:
/devices/{product_key}/{device_number}/onoff:
get:
consumes:
- application/json
description: 订阅设备上下线
parameters:
- description: 设备类型key
in: path
name: product_key
required: true
type: string
- description: 设备编号
in: path
name: device_number

View File

@@ -2,6 +2,7 @@ package dto
// SystemDeviceRequest 用户创建请求结构体
type SystemDeviceRequest struct {
ProductKey string `uri:"product_key" binding:"required"`
DeviceNumber string `uri:"device_number" binding:"required"`
}

2
go.mod
View File

@@ -5,6 +5,7 @@ go 1.23.7
require (
github.com/eclipse/paho.mqtt.golang v1.5.0
github.com/gin-gonic/gin v1.10.1
github.com/goburrow/modbus v0.1.0
github.com/sirupsen/logrus v1.9.3
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
@@ -36,6 +37,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goburrow/serial v0.1.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect

4
go.sum
View File

@@ -53,6 +53,10 @@ github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHO
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=

View File

@@ -3,7 +3,6 @@ package main
import (
"iot-mqtt-gin/config"
"iot-mqtt-gin/controllers"
db "iot-mqtt-gin/dao"
_ "iot-mqtt-gin/docs"
"iot-mqtt-gin/mqtt"
"iot-mqtt-gin/router"
@@ -39,11 +38,11 @@ func main() {
log.Println("成功连接到MQTT服务器")
// 初始化TDengine连接
if err := db.InitTDengine(); err != nil {
/*if err := db.InitTDengine(); err != nil {
log.Fatalf("初始化TDengine失败: %v", err)
return // 确保不执行后续DB操作
}
defer db.Close()
defer db.Close()*/
// 初始化MQ服务
mqService := service.NewMQService()
defer mqService.Close()

49
middleware/device.go Normal file
View File

@@ -0,0 +1,49 @@
package middleware
import (
"github.com/gin-gonic/gin"
"iot-mqtt-gin/controllers"
"iot-mqtt-gin/models"
"net/http"
"strings"
)
func DeviceRequired() gin.HandlerFunc {
return func(c *gin.Context) {
fullPath := c.Request.URL.Path
// 定义基础路径前缀
basePrefix := "/api/v1/devices/"
// 检查路径是否包含设备前缀
if !strings.HasPrefix(fullPath, basePrefix) {
c.Next()
return
}
// 提取devices后面的部分
devicePath := strings.TrimPrefix(fullPath, basePrefix)
// 按/拆分路径
parts := strings.Split(devicePath, "/")
// 打印拆分后的parts
//log.Printf("路径拆分结果: %v", parts) // 打印parts内容
// 检查是否有足够的路径部分
if len(parts) < 1 {
controllers.ReturnError(c, http.StatusBadRequest, "无效的请求路径")
c.Abort()
return
}
productKey := parts[0]
deviceNumber := parts[1]
deviceModel := models.Device{}
deviceInfo, err := deviceModel.CheckDevice(productKey, deviceNumber)
if err != nil || deviceInfo.ID <= 0 {
controllers.ReturnError(c, http.StatusBadRequest, "设备不存在")
c.Abort()
return
}
// 将拆分的路径部分存入上下文,供后续处理使用
//c.Set("deviceInfo", deviceInfo)
// 继续处理请求
c.Next()
}
}

View File

@@ -1,24 +1,21 @@
package models
import (
"errors"
"iot-mqtt-gin/dao"
"strconv"
"sync"
"time"
)
type Device struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `json:"name" gorm:"not null"` // 名称不为空
DeviceKey string `json:"device_key" gorm:"unique;not null"` // 设备号唯一不为空
OrgId string `json:"org_id" gorm:"not null"` // 组织不为空
}
// DeviceData 设备数据模型
type DeviceData struct {
OrgID string `json:"org_id" binding:"required"` // 组织ID
DeviceID string `json:"device_id" binding:"required"` // 设备ID
Value float64 `json:"value"` // 设备数值(如温度、湿度等)
Status bool `json:"status"` // 设备状态(开关等)
Timestamp time.Time `json:"timestamp"` // 时间戳,可选,默认使用当前时间
ID uint `gorm:"primaryKey" json:"id"`
Name string `json:"name" gorm:"not null"` // 名称不为空
DeviceKey string `json:"device_key" gorm:"unique;not null"` // 设备号唯一不为空
OrgId string `json:"org_id" gorm:"not null"` // 组织不为空
Enabled string `json:"enabled"` // 设备状态: online, offline, error
CreatedAt time.Time `json:"created_at" gorm:"type:datetime"` // 创建时间
UpdatedAt time.Time `json:"updated_at" gorm:"type:datetime"` // 更新时间
}
func (Device) TableName() string {
@@ -44,8 +41,194 @@ type Event struct {
}
// Exists 检查设备是否存在
func (d *Device) CheckDevice(device_key string) (Device, error) {
func (d *Device) CheckDevice(product_key, device_key string) (Device, error) {
var device Device
err := dao.Db.Where("product_id = ? and device_key = ?", product_key, device_key).First(&device).Error
return device, err
}
// 获取设备信息
func (d *Device) GetDeviceInfo(device_key string) (Device, error) {
var device Device
err := dao.Db.Where("device_key = ?", device_key).First(&device).Error
return device, err
}
// DeviceData 设备数据模型
type DeviceData struct {
ID string `json:"id"` // 数据ID
DeviceID string `json:"device_id"` // 设备ID
Data map[string]interface{} `json:"data"` // 设备数据
Protocol string `json:"protocol"` // 数据传输协议
Timestamp time.Time `json:"timestamp"` // 数据时间戳
Value uint64 `json:"value"`
Status bool `json:"status"`
OrgID string `json:"org_id"`
}
// Command 设备命令模型
type Command struct {
ID string `json:"id"` // 命令ID
DeviceID string `json:"device_id"` // 设备ID
Command string `json:"command"` // 命令名称
Params map[string]interface{} `json:"params"` // 命令参数
Status string `json:"status"` // 命令状态pending, sent, executed, failed
Result interface{} `json:"result"` // 命令执行结果
CreatedAt time.Time `json:"created_at"` // 创建时间
SentAt time.Time `json:"sent_at"` // 发送时间
ExecutedAt time.Time `json:"executed_at"` // 执行时间
}
// Rule 规则模型,用于边缘计算规则
type Rule struct {
ID string `json:"id"` // 规则ID
Name string `json:"name"` // 规则名称
Description string `json:"description"` // 规则描述
DeviceID string `json:"device_id"` // 关联设备ID空表示适用于所有设备
Condition string `json:"condition"` // 触发条件
Action string `json:"action"` // 执行动作
Enabled bool `json:"enabled"` // 是否启用
CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` // 更新时间
}
// Alert 告警模型
type Alert struct {
ID string `json:"id"` // 告警ID
DeviceID string `json:"device_id"` // 设备ID
RuleID string `json:"rule_id"` // 关联规则ID
Level string `json:"level"` // 告警级别info, warning, error, critical
Message string `json:"message"` // 告警信息
Status string `json:"status"` // 告警状态active, resolved
CreatedAt time.Time `json:"created_at"` // 创建时间
ResolvedAt time.Time `json:"resolved_at"` // 解决时间
}
// DeviceStore 设备存储接口
type DeviceStore struct {
devices map[string]Device
mu sync.RWMutex
}
// NewDeviceStore 创建设备存储
func NewDeviceStore() *DeviceStore {
return &DeviceStore{
devices: make(map[string]Device),
}
}
// Save 保存设备
func (s *DeviceStore) Save(device Device) {
s.mu.Lock()
defer s.mu.Unlock()
device.UpdatedAt = time.Now()
if device.CreatedAt.IsZero() {
device.CreatedAt = device.UpdatedAt
}
s.devices[strconv.FormatUint(uint64(device.ID), 10)] = device
}
// Get 获取设备
func (s *DeviceStore) Get(id string) (Device, error) {
s.mu.RLock()
defer s.mu.RUnlock()
device, exists := s.devices[id]
if !exists {
return Device{}, errors.New("device not found")
}
return device, nil
}
// List 列出所有设备
func (s *DeviceStore) List() []Device {
s.mu.RLock()
defer s.mu.RUnlock()
devices := make([]Device, 0, len(s.devices))
for _, device := range s.devices {
devices = append(devices, device)
}
return devices
}
// Delete 删除设备
func (s *DeviceStore) Delete(id string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, exists := s.devices[id]; !exists {
return errors.New("device not found")
}
delete(s.devices, id)
return nil
}
// DataStore 数据存储接口
type DataStore struct {
data map[string][]DeviceData // deviceID -> data list
mu sync.RWMutex
maxEntries int
}
// NewDataStore 创建数据存储
func NewDataStore() *DataStore {
return &DataStore{
data: make(map[string][]DeviceData),
maxEntries: 10000, // 每个设备最大数据条目
}
}
// Save 保存设备数据
func (s *DataStore) Save(data DeviceData) {
s.mu.Lock()
defer s.mu.Unlock()
if _, exists := s.data[data.DeviceID]; !exists {
s.data[data.DeviceID] = make([]DeviceData, 0)
}
// 限制数据量,防止内存溢出
if len(s.data[data.DeviceID]) >= s.maxEntries {
// 移除最旧的条目
s.data[data.DeviceID] = s.data[data.DeviceID][1:]
}
s.data[data.DeviceID] = append(s.data[data.DeviceID], data)
}
// GetLatest 获取设备最新数据
func (s *DataStore) GetLatest(deviceID string, limit int) []DeviceData {
s.mu.RLock()
defer s.mu.RUnlock()
dataList, exists := s.data[deviceID]
if !exists || len(dataList) == 0 {
return []DeviceData{}
}
// 确保limit有效
if limit <= 0 || limit > len(dataList) {
limit = len(dataList)
}
// 返回最新的limit条数据
return dataList[len(dataList)-limit:]
}
// GetHistorical 获取设备历史数据
func (s *DataStore) GetHistorical(deviceID string, start, end time.Time) []DeviceData {
s.mu.RLock()
defer s.mu.RUnlock()
result := []DeviceData{}
dataList, exists := s.data[deviceID]
if !exists {
return result
}
for _, data := range dataList {
if data.Timestamp.After(start) && data.Timestamp.Before(end) {
result = append(result, data)
}
}
return result
}

View File

@@ -5,6 +5,7 @@ import (
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"iot-mqtt-gin/controllers"
"iot-mqtt-gin/middleware"
"iot-mqtt-gin/pkg/logger"
)
@@ -26,9 +27,10 @@ func SetupRouter(
}
// 设备相关路由
devices := v1.Group("/devices")
devices.Use(middleware.DeviceRequired())
{
devices.GET("/:device_number/onoff", deviceCtrl.DeviceOnOff) // 设备上下线
devices.GET("/tdengine/test", deviceCtrl.TDengineTest) // TDengine测试
devices.GET("/:product_key/:device_number/onoff", deviceCtrl.DeviceOnOff) // 设备上下线
devices.GET("/tdengine/test", deviceCtrl.TDengineTest) // TDengine测试
switchs := devices.Group("/switch")
{
//开关物模型测试开始

View File

@@ -15,3 +15,5 @@
2025-08-11 19:44:24 - 127.0.0.1 "PUT /api/v1/devices/switch/turnOn HTTP/1.1 404 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
2025-08-11 19:44:40 - 127.0.0.1 "POST /api/v1/devices/switch/turnOn HTTP/1.1 200 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
2025-08-11 19:44:46 - 127.0.0.1 "POST /api/v1/devices/switch/turnOff HTTP/1.1 200 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
2025-08-11 19:46:28 - 127.0.0.1 "POST /api/v1/devices/switch/toggle HTTP/1.1 200 0s "PostmanRuntime-ApipostRuntime/1.1.0" "
2025-08-11 19:46:31 - 127.0.0.1 "POST /api/v1/devices/switch/toggle HTTP/1.1 200 0s "PostmanRuntime-ApipostRuntime/1.1.0" "

View File

View File

@@ -0,0 +1,66 @@
2025-08-13 10:51:18 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 525.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:51:18 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 529.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:51:18 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.1426ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:51:18 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.2324ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:51:19 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:51:31 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 1.3426ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:58:53 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 1.5551ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 524.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0849ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 547.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 2.7763ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:01 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:09 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 560.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 10:59:18 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:04:46 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 76.5365ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:17:00 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 74.3632ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:25:39 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 70.1653ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:26:10 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 70.758ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:28:33 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 76.0298ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:42:17 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 78.9669ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:42:55 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 400 73.4376ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 11:44:30 - 127.0.0.1 "GET /api/v1/devices/test001/onoff HTTP/1.1 200 113.0583ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:26 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 516.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:26 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 784.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:26 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.3683ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:26 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 5.453ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:27 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:09:27 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 516.4µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 521.7µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.2175ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 4.3197ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:28 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:11:44 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 400 594.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 514.9µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 525.3µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.2887ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 5.0589ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:13:55 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:14:07 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 400 523.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:15:46 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 523.1µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:15:46 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0798ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:15:46 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 2.3699ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:15:46 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 5.0981ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:15:46 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:16:01 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 400 520.6µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:39 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 527.8µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:39 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 526.2µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:39 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0423ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:39 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 3.1414ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:39 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:17:50 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 200 103.7766ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/index.html HTTP/1.1 200 999.2µs "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/swagger-ui.css HTTP/1.1 200 1.0697ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/swagger-ui-standalone-preset.js HTTP/1.1 200 1.0657ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/swagger-ui-bundle.js HTTP/1.1 200 5.6651ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/doc.json HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:33 - 127.0.0.1 "GET /swagger/favicon-32x32.png HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:19:45 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 200 107.0438ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:20:48 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 401 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:32:43 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 200 120.2295ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:45:14 - 127.0.0.1 "GET /api/v1/devices/13/test001/onoff HTTP/1.1 400 71.4915ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "
2025-08-13 18:45:18 - 127.0.0.1 "GET /api/v1/devices/1/test001/onoff HTTP/1.1 200 109.5059ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36" "