339 lines
8.8 KiB
Go
339 lines
8.8 KiB
Go
package ys7
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/os/gctx"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/tidwall/gjson"
|
|
"github.com/tiger1103/gfast/v3/internal/app/system/dao"
|
|
"github.com/tiger1103/gfast/v3/internal/app/system/model/do"
|
|
"github.com/tiger1103/gfast/v3/internal/app/system/model/entity"
|
|
)
|
|
|
|
type Ys72 struct {
|
|
FnLogin func() (string, error) // 登录回调
|
|
Devices map[string]Ys7Device // 设备列表
|
|
Total int // 总页数 和 当前页数
|
|
}
|
|
|
|
var Ys72Instance = NewYs72()
|
|
|
|
// NewYs72 初始化
|
|
func NewYs72() *Ys72 {
|
|
ys7 := &Ys72{
|
|
FnLogin: DefaultLogin,
|
|
Devices: make(map[string]Ys7Device),
|
|
Total: 999, // 第一次获取设备列表时,会刷新其实际值
|
|
}
|
|
|
|
// 刷新 ys7.go 中的 AccessToken
|
|
_ = ys7.GetAccessToken()
|
|
|
|
// 初始化设备列表
|
|
//if err := ys7.Init(); err != nil {
|
|
// panic(err)
|
|
//}
|
|
|
|
// 定时任务更新设备列表
|
|
go func() {
|
|
for range time.Tick(3 * time.Minute) {
|
|
if err := ys7.Init(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := ys7.GetAllDeviceList(); err != nil {
|
|
log.Println("更新设备列表失败:", err)
|
|
}
|
|
|
|
// 获取设备截图并存入 Redis
|
|
ys7.CaptureAllDevice()
|
|
}
|
|
}()
|
|
|
|
return ys7
|
|
}
|
|
|
|
// Init 第一次初始化时从数据库获取设备列表存入 Devices
|
|
func (ys *Ys72) Init() error {
|
|
device := []entity.Ys7Devices{}
|
|
|
|
// 获取设备列表
|
|
err := dao.Ys7Devices.Ctx(context.Background()).Scan(&device)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 将设备列表存入 Devices
|
|
for _, v := range device {
|
|
ys.Devices[v.DeviceSerial] = Ys7Device{
|
|
DeviceSerial: v.DeviceSerial,
|
|
DeviceName: v.DeviceName,
|
|
DeviceType: v.DeviceType,
|
|
Status: v.Status,
|
|
Defence: int(v.Defence),
|
|
DeviceVersion: v.DeviceVersion,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 遍历 ys.Devices 调用 Capture 获取设备截图并存入Redis
|
|
func (ys *Ys72) CaptureAllDevice() {
|
|
for _, device := range ys.Devices {
|
|
// 获取设备截图
|
|
resp, err := device.Capture()
|
|
if err != nil {
|
|
log.Println("获取设备截图失败:", err)
|
|
return
|
|
}
|
|
|
|
// 存入 Redis
|
|
ctx := context.Background()
|
|
g.Redis().Set(ctx, "ys7:capture:"+device.DeviceSerial, resp.Data.PicUrl)
|
|
// 两小时后过期
|
|
g.Redis().Expire(ctx, "ys7:capture:"+device.DeviceSerial, 2*60*60)
|
|
}
|
|
}
|
|
|
|
// 由 Redis 管理 AccessToken 的生命周期,如果获取失败则重新刷新
|
|
func (ys *Ys72) GetAccessToken() string {
|
|
ctx := context.Background()
|
|
// 从 Redis 获取 AccessToken
|
|
accessToken, err := g.Redis().Get(ctx, "ys7:accessToken")
|
|
|
|
// 兼容原本获取 AccessToken 的逻辑
|
|
SetAccessToken(accessToken.String())
|
|
|
|
if err == nil && accessToken.String() != "" {
|
|
return accessToken.String()
|
|
}
|
|
|
|
// 未获取到 AccessToken 则重新登录
|
|
if accessToken.String() == "" {
|
|
// 如果登录失败则递归重试
|
|
if err := ys.login(); err != nil {
|
|
return ys.GetAccessToken()
|
|
}
|
|
|
|
accessToken, err = g.Redis().Get(ctx, "ys7:accessToken")
|
|
if err != nil {
|
|
return accessToken.String()
|
|
}
|
|
}
|
|
|
|
return accessToken.String()
|
|
}
|
|
|
|
// 传入 deviceSerial 获取设备信息
|
|
func (ys *Ys72) GetDevice(deviceSerial string) (Ys7Device, error) {
|
|
if device, ok := ys.Devices[deviceSerial]; ok {
|
|
return device, nil
|
|
}
|
|
|
|
return Ys7Device{}, fmt.Errorf("设备不存在: %s", deviceSerial)
|
|
}
|
|
|
|
// 获取单页设备列表并存入 Devices
|
|
func (ys *Ys72) getDeviceList(page int, deviceMapping map[string]Ys7Device) error {
|
|
accessToken := ys.GetAccessToken()
|
|
|
|
req := struct {
|
|
AccessToken string `json:"accessToken"`
|
|
PageStart int `json:"pageStart"`
|
|
PageSize int `json:"pageSize"`
|
|
}{AccessToken: accessToken, PageSize: 50, PageStart: page}
|
|
|
|
resp, err := Post("https://open.ys7.com/api/lapp/device/list", req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
body := string(resp)
|
|
|
|
// 解析返回数据
|
|
if gjson.Get(body, "code").Int() != 200 {
|
|
return fmt.Errorf("获取设备列表失败: %s", gjson.Get(body, "msg").String())
|
|
}
|
|
|
|
// 解析设备列表
|
|
gjson.Get(body, "data").ForEach(func(_, value gjson.Result) bool {
|
|
deviceSerial := value.Get("deviceSerial").String() // 设备序列号
|
|
deviceName := value.Get("deviceName").String() // 设备名称
|
|
deviceType := value.Get("deviceType").String() // 设备类型
|
|
deviceStatus := int(value.Get("status").Int()) // 设备状态 1:在线 2:离线
|
|
defence := int(value.Get("defence").Int()) // 是否布撤防 0:未布防 1:布防 不用管
|
|
deviceVersion := value.Get("deviceVersion").String() // 设备版本
|
|
|
|
deviceMapping[deviceSerial] = Ys7Device{
|
|
DeviceSerial: deviceSerial,
|
|
DeviceName: deviceName,
|
|
DeviceType: deviceType,
|
|
Status: deviceStatus,
|
|
Defence: defence,
|
|
DeviceVersion: deviceVersion,
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
ys.Total = int(gjson.Get(body, "page.total").Int())
|
|
|
|
return nil
|
|
}
|
|
|
|
// 依次调用 getDeviceList 获取所有设备列表
|
|
func (ys *Ys72) GetAllDeviceList() error {
|
|
// 创建一个设备map
|
|
deviceMapping := make(map[string]Ys7Device)
|
|
|
|
// 遍历页码获取所有设备列表
|
|
for page := 0; page <= ys.Total; page++ {
|
|
if err := ys.getDeviceList(page, deviceMapping); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
newDevices := []do.Ys7Devices{} // 新设备列表
|
|
updateDevices := []do.Ys7Devices{} // 更新设备列表
|
|
|
|
// 遍历 deviceMapping 对比 ys.Devices 筛选出增量或更新设备列表
|
|
for deviceSerial, device := range deviceMapping {
|
|
// 如果设备不存在则为新增设备
|
|
if _, ok := ys.Devices[deviceSerial]; !ok {
|
|
newDevices = append(newDevices, do.Ys7Devices{
|
|
DeviceSerial: device.DeviceSerial,
|
|
DeviceName: device.DeviceName,
|
|
DeviceType: device.DeviceType,
|
|
Status: device.Status,
|
|
Defence: device.Defence,
|
|
DeviceVersion: device.DeviceVersion,
|
|
})
|
|
} else {
|
|
// 如果设备存在则对比设备状态
|
|
if ok, newDevice := ys.CompareDevice(ys.Devices[deviceSerial], device); !ok {
|
|
// 如果设备状态不同则更新设备
|
|
updateDevices = append(updateDevices, do.Ys7Devices{
|
|
DeviceSerial: newDevice.DeviceSerial,
|
|
DeviceName: newDevice.DeviceName,
|
|
DeviceType: newDevice.DeviceType,
|
|
Status: newDevice.Status,
|
|
Defence: newDevice.Defence,
|
|
DeviceVersion: newDevice.DeviceVersion,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// 新旧状态替换
|
|
ys.Devices = deviceMapping
|
|
|
|
// 新设备列表入库
|
|
if len(newDevices) > 0 {
|
|
_, err := dao.Ys7Devices.Ctx(context.Background()).Data(newDevices).Batch(20).Insert()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 更新设备列表
|
|
if len(updateDevices) > 0 {
|
|
for _, device := range updateDevices {
|
|
_, err := dao.Ys7Devices.Ctx(context.Background()).Data(device).
|
|
Where(dao.Ys7Devices.Columns().DeviceSerial, device.DeviceSerial).Update()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// _, err := dao.Ys7Devices.Ctx(context.Background()).Data(updateDevices).Batch(20).Update()
|
|
// if err != nil {
|
|
// log.Println("更新错误:", err)
|
|
// return err
|
|
// }
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 登录并将 AccessToken 存入 Redis 由 Redis 管理其有效期
|
|
// 当获取失败则重新登录后刷新周期
|
|
func (ys *Ys72) login() error {
|
|
accessToken, err := ys.FnLogin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
g.Redis().Set(ctx, "ys7:accessToken", accessToken)
|
|
// 设置 key 将在 5 天后过期
|
|
g.Redis().Expire(ctx, "ys7:accessToken", 5*24*60*60)
|
|
return nil
|
|
}
|
|
|
|
// CompareDevice 对比传递的两个设备结构体是否相同
|
|
// 如果不同则返回 false 并构造出新的设备结构体
|
|
func (ys *Ys72) CompareDevice(old, new Ys7Device) (bool, Ys7Device) {
|
|
// 对比old 和 new 结构体的所有字段,是否相同
|
|
if old == new {
|
|
return true, Ys7Device{}
|
|
}
|
|
|
|
// 构造出新的ys7Device
|
|
return false, Ys7Device{
|
|
DeviceSerial: new.DeviceSerial,
|
|
DeviceName: new.DeviceName,
|
|
DeviceType: new.DeviceType,
|
|
Status: new.Status,
|
|
Defence: new.Defence,
|
|
DeviceVersion: new.DeviceVersion,
|
|
}
|
|
}
|
|
|
|
type defalutFn func() (string, error)
|
|
|
|
var DefaultLogin = defalutFn(func() (string, error) {
|
|
appKey := "3acf9f1a43dc4209841e0893003db0a2"
|
|
appSecret := "4bbf3e9394f55d3af6e3af27b2d3db36"
|
|
|
|
req := struct {
|
|
AppKey string `json:"appKey"`
|
|
AppSecret string `json:"appSecret"`
|
|
}{AppKey: appKey, AppSecret: appSecret}
|
|
|
|
// 登录
|
|
resp, err := Post("https://open.ys7.com/api/lapp/token/get", req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
body := string(resp)
|
|
|
|
// 解析返回数据
|
|
if gjson.Get(body, "code").Int() != 200 {
|
|
return "", fmt.Errorf("登录失败: %s", gjson.Get(body, "msg").String())
|
|
}
|
|
|
|
// 返回 AccessToken
|
|
return gjson.Get(body, "data.accessToken").String(), nil
|
|
})
|
|
|
|
func StartAI(device Ys7Device) {
|
|
capture, err := device.Capture()
|
|
if err != nil {
|
|
fmt.Println("定时快照错误!", err)
|
|
} else {
|
|
get, err := g.Client().Get(gctx.New(), capture.Data.PicUrl)
|
|
if err != nil {
|
|
} else {
|
|
os.WriteFile("", get.ReadAll(), 0o666)
|
|
}
|
|
}
|
|
}
|