diff --git a/config/db.go b/config/db.go index 2654eef..23405f9 100644 --- a/config/db.go +++ b/config/db.go @@ -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类型 ) diff --git a/controllers/device.go b/controllers/device.go index 34fc6cb..05a0600 100644 --- a/controllers/device.go +++ b/controllers/device.go @@ -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/#" diff --git a/dao/dao.go b/dao/dao.go index de06a76..b10b060 100644 --- a/dao/dao.go +++ b/dao/dao.go @@ -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 { //打印数据库连接错误信息 diff --git a/docs/docs.go b/docs/docs.go index ada984e..1853315 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -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": "设备编号", diff --git a/docs/swagger.json b/docs/swagger.json index ae27aad..1094d9d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -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": "设备编号", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 64158b7..2f9c6bb 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -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 diff --git a/dto/sys_dto.go b/dto/sys_dto.go index d10aa8e..75e8933 100644 --- a/dto/sys_dto.go +++ b/dto/sys_dto.go @@ -2,6 +2,7 @@ package dto // SystemDeviceRequest 用户创建请求结构体 type SystemDeviceRequest struct { + ProductKey string `uri:"product_key" binding:"required"` DeviceNumber string `uri:"device_number" binding:"required"` } diff --git a/go.mod b/go.mod index 04802e3..1d860b7 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index d1b1535..5c9049b 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index af81df9..c08b436 100644 --- a/main.go +++ b/main.go @@ -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() diff --git a/middleware/device.go b/middleware/device.go new file mode 100644 index 0000000..667f660 --- /dev/null +++ b/middleware/device.go @@ -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() + } +} diff --git a/models/device.go b/models/device.go index ab28065..b4c60de 100644 --- a/models/device.go +++ b/models/device.go @@ -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 +} diff --git a/router/routers.go b/router/routers.go index 47499f8..ef019ce 100644 --- a/router/routers.go +++ b/router/routers.go @@ -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") { //开关物模型测试开始 diff --git a/runtime/log/success_2025-08-11.log b/runtime/log/success_2025-08-11.log index a3781e5..c96b260 100644 --- a/runtime/log/success_2025-08-11.log +++ b/runtime/log/success_2025-08-11.log @@ -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" " diff --git a/runtime/log/success_2025-08-12.log b/runtime/log/success_2025-08-12.log new file mode 100644 index 0000000..e69de29 diff --git a/runtime/log/success_2025-08-13.log b/runtime/log/success_2025-08-13.log new file mode 100644 index 0000000..17d3e48 --- /dev/null +++ b/runtime/log/success_2025-08-13.log @@ -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" "