bzd_lkh 1 mēnesi atpakaļ
revīzija
ab9f0b826c
70 mainītis faili ar 8535 papildinājumiem un 0 dzēšanām
  1. 3 0
      .gitignore
  2. BIN
      Cold_mqtt
  3. 208 0
      MqttServer/MqttClients.go
  4. 193 0
      MqttServer/MqttControllers.go
  5. 31 0
      MqttServer/MqttPolling.go
  6. 305 0
      MqttServer/MqttServer.go
  7. 133 0
      MqttServer/MqttStruct.go
  8. 331 0
      MqttServer/V3MqttControllers.go
  9. 978 0
      MqttServer/V3MqttHandle.go
  10. 672 0
      MqttServer/WarningNotice/WarningNotice.go
  11. 36 0
      MqttServer/WarningNotice/WarningNoticeTruct.go
  12. 211 0
      Nats/Nats.go
  13. 36 0
      Nats/NatsServer/AddWarning.go
  14. 80 0
      Nats/NatsServer/Hw_VoiceNotice.go
  15. 91 0
      Nats/NatsServer/NatsAliM.go
  16. 72 0
      Nats/NatsServer/NatsAppWarn.go
  17. 14 0
      Nats/NatsServer/NatsMqtt.go
  18. 91 0
      Nats/NatsServer/NatsSbM.go
  19. 92 0
      Nats/NatsServer/NatsSbV.go
  20. 108 0
      Nats/NatsServer/NatsWx.go
  21. 56 0
      Plugins/ContinuousPushOfOfflineDeviceWarning.go
  22. 35 0
      README.md
  23. 38 0
      TimeTask/OrmLog.go
  24. 6 0
      TimeTask/TimeTask.go
  25. 9 0
      Z_Build.bat
  26. 39 0
      conf/app.conf
  27. 39 0
      conf/config.go
  28. 108 0
      controllers/default.go
  29. 49 0
      go.mod
  30. 337 0
      go.sum
  31. 61 0
      lib/Aes.go
  32. 86 0
      lib/MinuteToDataTime.go
  33. 486 0
      lib/lib.go
  34. 111 0
      logs/LogPrintln.go
  35. 7 0
      logs/nohup.sh
  36. 45 0
      main.go
  37. 128 0
      models/Account/Admin.go
  38. 85 0
      models/Account/Company.go
  39. 62 0
      models/Account/Tokey.go
  40. 48 0
      models/Company/CompanyNotice.go
  41. 199 0
      models/Device/Device.go
  42. 189 0
      models/Device/DeviceData.go
  43. 105 0
      models/Device/DeviceDataOld.go
  44. 186 0
      models/Device/DeviceParameter.go
  45. 191 0
      models/Device/DeviceSensor.go
  46. 246 0
      models/Device/DeviceSensorParameter.go
  47. 31 0
      models/Device/DeviceSnOld.go
  48. 80 0
      models/Device/DeviceTask.go
  49. 33 0
      models/GTime.go
  50. 76 0
      models/Product/ProductType.go
  51. 36 0
      models/Product/ProductUpgrade.go
  52. 33 0
      models/System/Logs.go
  53. 429 0
      models/Warning/Warning.go
  54. 105 0
      models/Warning/WarningBack.go
  55. 154 0
      models/Warning/WarningDB.go
  56. 82 0
      models/Warning/WarningSand.go
  57. 94 0
      models/Warning/WarningType.go
  58. 10 0
      routers/router.go
  59. 12 0
      run.sh
  60. 1 0
      static/js/reload.min.js
  61. 147 0
      tests/Merge.go
  62. 39 0
      tests/conf/app.conf
  63. 35 0
      tests/default_test.go
  64. 120 0
      tests/device/MqttServer.go
  65. 1 0
      tests/fa_test.go
  66. 216 0
      tests/logrizhi_test.go
  67. 60 0
      tests/period_test.go
  68. 89 0
      tests/periodw_test.go
  69. 10 0
      tests/warming_test.go
  70. 6 0
      views/index.tpl

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+*.log
+*.tmp
+/.idea

BIN
Cold_mqtt


+ 208 - 0
MqttServer/MqttClients.go

@@ -0,0 +1,208 @@
+package MqttServer
+
+import (
+	"Cold_mqtt/MqttServer/WarningNotice"
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Warning"
+	"encoding/json"
+	"strings"
+	"time"
+)
+
+// 设备连接
+func MessageConnected(topicName string, message []byte) {
+
+	logs.Println("============= MessageConnected Mqtt JSON =============")
+	logs.Println("topic:", topicName, "  message:", string(message))
+
+	//logs.Println("=>", a)
+	var Ms_project Ms_Project_Connected
+	err := json.Unmarshal(message, &Ms_project)
+	if err != nil {
+		logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms_Project]", string(message))
+		return
+	}
+
+	Clientid_list := strings.Split(Ms_project.Clientid+"_", "_")
+	Ms_project.Clientid = Clientid_list[0]
+	// 过滤
+	if len(Ms_project.Clientid) < 5 {
+		return
+	}
+
+	r_Device, err := Device.Read_Device_ByT_sn(Ms_project.Clientid)
+	if err != nil {
+		logs.Println("MessageDisconnected 没有该设备:", Ms_project.Clientid)
+		return
+	}
+	logs.Println("Clientid:", Ms_project.Clientid)
+	logs.Println("Username:", Ms_project.Username)
+
+	Reason := ""
+	if strings.Contains(Ms_project.Username, "_s") {
+		r_Device.T_online_s = 1
+		Reason = "备用网络|IP:" + Ms_project.Ipaddress
+	} else {
+		r_Device.T_online = 1
+		Reason = "主网络|IP:" + Ms_project.Ipaddress
+	}
+	Warning.Add_DeviceLogs(103, r_Device, Reason)
+
+	r_Device.T_mqttid = conf.MqttServer_id
+
+	// 同步参数
+	Device.Update_Device(r_Device, "T_mqttid", "T_online", "T_online_s")
+	Device.Update_Device_To_DeviceSensor(r_Device) // 设备同步参数
+
+	// 监查 设备参数
+	go func() {
+		time.Sleep(time.Second * 10)
+		logs.Println("上线检测 待设置 设备参数:", r_Device.T_sn)
+		DeviceParameter_list := Device.Read_DeviceParameter_SN_T_SendState_0(r_Device.T_sn)
+		if len(DeviceParameter_list) > 0 {
+			logs.Println("Pu_DeviceParameter->:", DeviceParameter_list[0].Id, DeviceParameter_list[0].T_sn)
+			Pu_DeviceParameter(DeviceParameter_list[0])
+		}
+
+		logs.Println("上线检测 待设置 传感器参数:", r_Device.T_sn)
+		DeviceSensorParameter_list := Device.Read_DeviceSensorParameter_SN_T_SendState_0_sql(r_Device.T_sn)
+		for i, v := range DeviceSensorParameter_list {
+			sn := v[0].(string)
+			t_id := v[1].(string)
+			logs.Println("DeviceSensorParameter_list:", i, sn, t_id)
+			r_DeviceSensorParameter_list := Device.Read_DeviceSensorParameter_SN_T_id(sn, lib.To_int(t_id))
+			if len(r_DeviceSensorParameter_list) > 0 {
+				if r_DeviceSensorParameter_list[0].T_SendState == 0 {
+					logs.Println("Pu_DeviceParameter_Sensor->:", i, sn, t_id)
+					Pu_DeviceParameter_Sensor(r_DeviceSensorParameter_list[0])
+				}
+			}
+		}
+
+	}()
+
+	logs.Println("============= MessageConnected Mqtt JSON AND =============", topicName)
+}
+func ToReason(str string) (Reason string) {
+	switch str {
+	case "normal":
+		Reason = "客户端主动断开"
+		break
+	case "kicked":
+		Reason = "设备异常掉线,服务端踢出,通过 REST API"
+		break
+	case "keepalive_timeout":
+		Reason = "设备异常掉线,keepalive 超时"
+		break
+	case "not_authorized":
+		Reason = "设备异常掉线,认证失败"
+		break
+	case "tcp_closed":
+		Reason = "设备异常掉线,对端关闭了网络连接"
+		break
+	case "internal_error":
+		Reason = "设备异常掉线,畸形报文或其他未知错误"
+		break
+	}
+
+	return Reason
+}
+
+// 设备断开
+func MessageDisconnected(topicName string, message []byte) {
+
+	logs.Println("============= MessageDisconnected Mqtt JSON =============")
+	logs.Println("topic:", topicName, "  message:", string(message))
+
+	//logs.Println("=>", a)
+	var Ms_project Ms_Project_Disconnected
+	err := json.Unmarshal(message, &Ms_project)
+	if err != nil {
+		logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms_Project]", string(message))
+		return
+	}
+
+	logs.Println("Clientid:", Ms_project.Clientid)
+	logs.Println("Username:", Ms_project.Username)
+
+	Clientid_list := strings.Split(Ms_project.Clientid+"_s", "_")
+	Ms_project.Clientid = Clientid_list[0]
+	// 过滤
+	if len(Ms_project.Clientid) < 5 {
+		return
+	}
+
+	r_Device, err := Device.Read_Device_ByT_sn(Ms_project.Clientid)
+	if err != nil {
+		logs.Println("MessageDisconnected 没有该设备:", Ms_project.Clientid)
+		return
+	}
+
+	Reason := ToReason(Ms_project.Reason)
+	if strings.Contains(Ms_project.Username, "_s") {
+		r_Device.T_online_s = 2
+		Reason = "备用网络|" + Reason
+	} else {
+		r_Device.T_online = 2
+		Reason = "主网络|" + Reason
+	}
+
+	if !strings.Contains(Reason, "主动断开") && r_Device.T_model == "BX100W" {
+		Reason = "可能市电断电,请注意查看"
+	}
+
+	Warning_r := Warning.Add_DeviceLogs(1001, r_Device, Reason)
+	Warning_r.T_fUt = time.Now()
+	if !strings.Contains(Reason, "主动断开") { // 异常断开
+		//WarningNotice.WarningToAdminId(&Warning_r, []int{}) // 发送给 绑定公司管理员
+		//// 报警处理
+		//Warning.Add_Warning_Log(&Warning_r, "====== 报警策略 ===== \n")
+		WarningNotice.WarningCompanyNotice(&Warning_r, 0, 0)
+		//  移动端 监控状态同步问题(2小时后自动拉为未监控状态)
+		if strings.Contains(r_Device.T_model, "BW100") {
+			go func(sn string) {
+				time_s := time.Now()
+				time.Sleep(2 * time.Hour)
+				r_Device_, err := Device.Read_Device_ByT_sn(sn)
+				if err != nil {
+					logs.Println("MessageDisconnected 没有该设备:", Ms_project.Clientid)
+					return
+				}
+				if r_Device_.T_online != 1 {
+					r_Device_.T_monitor = 0
+					// 同步参数
+					Device.Update_Device(r_Device, "T_monitor")
+					Device.Update_Device_To_DeviceSensor(r_Device) // 设备同步参数
+					logs.Println(sn + ",在" + time_s.Format("2006-01-02 15:04:05") + "异常下线, 在两个小时后 未能上线,将其监控状态强制拉为未监控!")
+				} else {
+					logs.Println(sn + ",在" + time_s.Format("2006-01-02 15:04:05") + "异常下线, 在两个小时后 成功上线!")
+				}
+			}(r_Device.T_sn)
+
+		}
+
+	} else { // 正常关机
+		r_Device.T_monitor = 0
+		if !strings.Contains(r_Device.T_model, "BW100") {
+			DeviceSensorList := Device.Read_DeviceSensor_ByTsn(r_Device.T_sn)
+			for _, v := range DeviceSensorList {
+				DeviceData_t, is := Device.RedisDeviceData_Get(r_Device.T_sn+"_Redis", v.T_id)
+				if is {
+					Device.Add_DeviceData(r_Device.T_sn, v.T_id, DeviceData_t, true)
+					Device.RedisDeviceData_Del(r_Device.T_sn+"_Redis", v.T_id) // 清除缓存标志
+				}
+			}
+
+		}
+	}
+
+	// 同步参数
+	Device.Update_Device(r_Device, "T_online", "T_online_s", "T_monitor")
+	Device.Update_Device_To_DeviceSensor(r_Device) // 设备同步参数
+
+	logs.Println("============= MessageDisconnected Mqtt JSON AND =============", topicName)
+
+}

+ 193 - 0
MqttServer/MqttControllers.go

@@ -0,0 +1,193 @@
+package MqttServer
+
+import (
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Product"
+
+	"time"
+)
+
+func Read_DeviceParameter(T_sn string) {
+
+	r_Device, err := Device.Read_Device_ByT_sn(T_sn)
+	if err != nil {
+		logs.Println("Read_DeviceParameter 没有该设备:", T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Read_DeviceParameter 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Read_DeviceParameter3(r_Device.T_sn)
+	default:
+		Read_DeviceParameter3(r_Device.T_sn)
+	}
+
+	return
+}
+
+func Read_DeviceSensorParameter(T_sn string) {
+	time.Sleep(time.Second * 3)
+	r_Device, err := Device.Read_Device_ByT_sn(T_sn)
+	if err != nil {
+		logs.Println("Read_DeviceSensorParameter 没有该设备:", T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Read_DeviceSensorParameter 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Read_DeviceSensorParameter3(r_Device.T_sn)
+	default:
+		Read_DeviceSensorParameter3(r_Device.T_sn)
+	}
+
+	return
+
+}
+
+// 修改设备参数
+func Pu_DeviceParameter(Rt_DeviceParameter Device.DeviceParameter) {
+	r_Device, err := Device.Read_Device_ByT_sn(Rt_DeviceParameter.T_sn)
+	if err != nil {
+		logs.Println("Pu_DeviceParameter 没有该设备:", Rt_DeviceParameter.T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Pu_DeviceParameter 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Pu_DeviceParameter3(Rt_DeviceParameter)
+	default:
+		Pu_DeviceParameter3(Rt_DeviceParameter)
+	}
+
+	return
+}
+
+// 修改传感器参数
+func Pu_DeviceParameter_Sensor(Rt_parameter_sensor Device.DeviceSensorParameter) {
+	r_Device, err := Device.Read_Device_ByT_sn(Rt_parameter_sensor.T_sn)
+	if err != nil {
+		logs.Println("Pu_DeviceParameter_Sensor 没有该设备:", Rt_parameter_sensor.T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Pu_DeviceParameter_Sensor 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Pu_DeviceParameter_Sensor3(Rt_parameter_sensor)
+	default:
+		Pu_DeviceParameter_Sensor3(Rt_parameter_sensor)
+	}
+
+	return
+}
+
+func Set_DeviceTask(v Device.Device_task) {
+	r_Device, err := Device.Read_Device_ByT_sn(v.T_sn)
+	if err != nil {
+		logs.Println("Set_DeviceTask 没有该设备:", v.T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Set_DeviceTask 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Set_DeviceTask3(v)
+	default:
+		Set_DeviceTask3(v)
+	}
+
+	return
+
+}
+func Set_RestartShutdown(v Device.Device_task) {
+	r_Device, err := Device.Read_Device_ByT_sn(v.T_sn)
+	if err != nil {
+		logs.Println("Set_DeviceTask 没有该设备:", v.T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Set_RestartShutdown 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Set_DeviceRestart3(v)
+	default:
+		Set_DeviceRestart3(v)
+	}
+
+	return
+
+}
+
+func Get_Device_Realtime(T_sn string) {
+
+	r_Device, err := Device.Read_Device_ByT_sn(T_sn)
+	if err != nil {
+		logs.Println("Get_Device_Realtime 没有该设备:", T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Get_Device_Realtime 设备不在线:", T_sn)
+		return
+	}
+	if DeviceParameter, is := Device.Read_DeviceParameter(r_Device.T_sn); is {
+		if DeviceParameter.T_dormancy == 1 {
+			logs.Println("Get_Device_Realtime 设备进入休眠!!!", T_sn)
+			return
+		}
+	}
+
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Get_Device_Realtime3(r_Device.T_sn)
+	default:
+		Get_Device_Realtime3(r_Device.T_sn)
+	}
+
+	return
+
+}
+
+func Up_ProductUpgrade(v Product.ProductUpgrade_T) {
+	r_Device, err := Device.Read_Device_ByT_sn(v.T_sn)
+	if err != nil {
+		logs.Println("Up_ProductUpgrade 没有该设备:", v.T_sn)
+		return
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		logs.Println("Up_ProductUpgrade 设备不在线:", r_Device.T_sn)
+		return
+	}
+	// 协议区分
+	switch r_Device.T_protocol {
+	case 3:
+		Up_DeviceProductUpgrade(v)
+	default:
+		Up_DeviceProductUpgrade(v)
+	}
+
+	return
+
+}

+ 31 - 0
MqttServer/MqttPolling.go

@@ -0,0 +1,31 @@
+package MqttServer
+
+//func MqttPolling() {
+//
+//	var DevicePollingMap map[string]int /*创建集合 */
+//	DevicePollingMap = make(map[string]int)
+//
+//	for true {
+//
+//		for k, _ := range DevicePollingMap {
+//			Dpm, ok := DevicePollingMap[k]
+//			if ok {
+//				r_Device, err := Device.Read_Device_ByT_sn(k)
+//				if err != nil {
+//					delete(DevicePollingMap, k)
+//					continue
+//				}
+//				Read_DeviceSensorParameter(r_Device.T_sn)
+//
+//				DevicePollingMap[k] = Dpm - 1
+//				if Dpm <= 1 {
+//					delete(DevicePollingMap, k)
+//				}
+//
+//			}
+//			time.Sleep(10 * time.Millisecond)
+//		}
+//
+//		time.Sleep(10 * time.Second)
+//	}
+//}

+ 305 - 0
MqttServer/MqttServer.go

@@ -0,0 +1,305 @@
+package MqttServer
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Warning"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	beego "github.com/beego/beego/v2/server/web"
+	"github.com/yosssi/gmq/mqtt"
+	"github.com/yosssi/gmq/mqtt/client"
+	"math"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	cli       *client.Client
+	DeviceSn  map[string]Device.Device /*创建集合 */
+	test      = true
+	Task_Time int64 // 执行时间
+)
+
+type DeviceMqttMap_Struct struct {
+	T_sn    string
+	T_data  string
+	T_num   int
+	T_tonum int
+}
+
+var redisCache_Mid cache.Cache
+var DeviceMqttMap map[string]DeviceMqttMap_Struct /*创建集合 */
+var lock sync.Mutex
+
+func init() {
+
+	Task_Time = time.Now().UnixMilli()
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redisCache_Mid", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println("", config)
+	var err error
+	redisCache_Mid, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Mid == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+		panic(any(errMsg))
+	}
+	//Task_Etection_Time
+	//Task_Etection_x
+
+	DeviceMqttMap = make(map[string]DeviceMqttMap_Struct)
+
+}
+
+// 缓存数据发送-确保设备在休眠后 能收到数据
+func DeviceMqttMap_go() {
+	logs.Println("=====================DeviceMqttMap_go GO===============")
+	time.Sleep(time.Second * 10)
+	for true {
+		for k, v := range DeviceMqttMap {
+			data, ok := DeviceMqttMapGet(k)
+			if !ok {
+				logs.Println("DeviceMqttMapGet:", k, " !!不存在,跳过!! ")
+				continue
+			}
+			v = data
+			logs.Println("DeviceMqttMap:", k, " T_sn:", v.T_sn, " T_data:", v.T_data, " T_num:", v.T_num, " T_tonum:", v.T_tonum)
+			if v.T_tonum >= 1 {
+				Mqtt_publish(v.T_sn, v.T_data)
+			}
+			v.T_tonum += 1
+			lock.Lock()
+			DeviceMqttMap[k] = v
+			if v.T_num <= v.T_tonum {
+				delete(DeviceMqttMap, k)
+			}
+			lock.Unlock()
+			time.Sleep(time.Millisecond * 100)
+		}
+		time.Sleep(time.Second * 3)
+	}
+}
+func DeviceMqttMapGet(key string) (DeviceMqttMap_Struct, bool) {
+	lock.Lock()
+	defer lock.Unlock()
+	data, ok := DeviceMqttMap[key]
+
+	return data, ok
+}
+func DeviceMqttMapAdd(key string, data DeviceMqttMap_Struct) {
+	lock.Lock()
+	defer lock.Unlock()
+	DeviceMqttMap[key] = data // 缓存数据
+	logs.Println("DeviceMqttMapAdd:", key, " T_sn:", data.T_sn, " T_data:", data.T_data, " T_num:", data.T_num, " T_tonum:", data.T_tonum)
+}
+
+// 获取GetMid缓存
+func GetMid(key string, Type int) bool {
+	if !redisCache_Mid.IsExist(key) {
+		//redisCache_Mid.Put(key, "", time.Minute*4)  // 24-3-26  = 1
+		if Type == 2 {
+			redisCache_Mid.Put(key, "", time.Minute*10)
+		} else {
+			redisCache_Mid.Put(key, "", time.Minute*1)
+		}
+		return false
+	}
+	return true
+}
+
+func Run_MqttServer() {
+	time.Sleep(3 * time.Second)
+	logs.Println("============Run_MqttServer=============", "")
+	HTTPPort, _ := beego.AppConfig.String("HTTPPort")
+	// Create an MQTT Client.
+	cli = client.New(&client.Options{
+		// Define the processing of the error handler.
+		ErrorHandler: func(err error) {
+			logs.PrintlnError("err!!!!!! Run_MqttServer:", err.Error())
+
+			time.Sleep(3 * time.Second)
+			go Run_MqttServer() // MQTT 通讯
+
+			return
+		},
+	})
+
+	// Terminate the Client.
+	defer cli.Terminate()
+
+	c := client.ConnectOptions{
+		Network:  "tcp",
+		Address:  conf.MqttServer_Url,
+		ClientID: []byte(conf.MqttServer_ClientID + HTTPPort),
+		UserName: []byte(conf.MqttServer_Username),
+		Password: []byte(conf.MqttServer_Password),
+	}
+	logs.Println("Address:", c.Address)
+	logs.Println("ClientID:", string(c.ClientID))
+
+	// Connect to the MQTT Server.
+	err := cli.Connect(&c)
+
+	if err != nil {
+		logs.Println("MqttServer", "连接MQTT失败 [cli.Connect]", "")
+		logs.Println("err!!!!!! Run_MqttServer:", "连接MQTT失败:", err)
+		fmt.Println("err!!!!!! 连接MQTT失败:", err)
+		cli.Terminate()
+		time.Sleep(3 * time.Second)
+		go Run_MqttServer() // MQTT 通讯
+
+		return
+	}
+
+	// Subscribe to topics.
+	err = cli.Subscribe(&client.SubscribeOptions{
+		SubReqs: []*client.SubReq{
+			&client.SubReq{
+				TopicFilter: []byte("/sub/#"),
+				QoS:         mqtt.QoS0,
+				// Define the processing of the message handler.
+				Handler: func(topicName, message []byte) {
+					logs.PrintlnMqtt("<-" + string(topicName) + " " + string(message))
+					messagePubHandlerV3(string(topicName), message)
+				},
+			},
+			&client.SubReq{ // 设备断开
+				TopicFilter: []byte("$SYS/brokers/+/clients/+/disconnected"),
+				QoS:         mqtt.QoS0,
+				// Define the processing of the message handler.
+				Handler: func(topicName, message []byte) {
+					logs.PrintlnMqtt("<-" + string(topicName) + " " + string(message))
+					MessageDisconnected(string(topicName), message)
+				},
+			},
+			&client.SubReq{ // 设备上线
+				TopicFilter: []byte("$SYS/brokers/+/clients/+/connected"),
+				QoS:         mqtt.QoS0,
+				// Define the processing of the message handler.
+				Handler: func(topicName, message []byte) {
+					logs.PrintlnMqtt("<-" + string(topicName) + " " + string(message))
+					MessageConnected(string(topicName), message)
+				},
+			},
+		},
+	})
+	if err != nil {
+		logs.Println("MqttServer", "订阅消息 [cli.Subscribe]", "")
+		logs.Println("err!!!!!! Run_MqttServer:", "连接MQTT失败:", err)
+
+	}
+
+	fmt.Println("MQTT ok!")
+}
+
+// 冷链 3.0 协议
+func messagePubHandlerV3(topicName string, message []byte) {
+	// 本地测试
+	logs.Println("=", "============= Mqtt2 JSON =============")
+	logs.Println("<-"+topicName, string(message))
+	start := time.Now()
+	var Ms2_project Ms2m_Project
+	err := json.Unmarshal(message, &Ms2_project)
+	if err != nil {
+		logs.Println("MqttServer", "JSON反序列化失败[Ms_Project]", string(message), err.Error())
+		return
+	}
+
+	// 过滤
+	topicNameS := strings.Split(topicName, "/")
+	if len(topicNameS) != 3 {
+		logs.Println("MqttServer", "订阅地址错误 len(topicNameS) != 3", strconv.Itoa(len(topicNameS)))
+		return
+	}
+	Sn := topicNameS[2]
+	if len(Sn) < 4 {
+		logs.Println("MqttServer", "订阅地址错误 len(Sn) < 4", Sn)
+		return
+	}
+	// 过滤重复数据
+	SnMid := fmt.Sprintf("%s|%d", Sn, Ms2_project.Msid)
+	if GetMid(SnMid, Ms2_project.Type) {
+		logs.Println("MqttServer", "SnMid 重复发送!!", SnMid)
+		ms2_Return := Ms2_Return{
+			Type:   Ms2_project.Type,
+			Msid:   Ms2_project.Msid,
+			Status: 1,
+		}
+		// 回复
+		jsonStu, err := json.Marshal(ms2_Return)
+		if err != nil {
+			logs.Println("回复失败 [Ms_project_0],err=", err)
+			return
+		}
+		Mqtt_publish(Sn, string(jsonStu))
+		return
+	}
+
+	r_Device, err := Device.Read_Device_ByT_sn(Sn)
+	if err != nil {
+		Device.Add_DeviceSnOld(Sn)
+		logs.Println("没有该设备:", Sn)
+		return
+	}
+
+	//误差 10秒
+	if math.Abs(float64(Ms2_project.Dut-time.Now().Unix())) > 10 && Ms2_project.Dut != 0 {
+		logs.Println(fmt.Sprintf("%s 时间相差:%d", r_Device.T_sn, int(math.Abs(float64(Ms2_project.Dut-time.Now().Unix())))))
+		Warning.Add_DeviceLogs(103, r_Device, fmt.Sprintf("时间相差:%d", int(math.Abs(float64(Ms2_project.Dut-time.Now().Unix())))))
+		RTC_TimeStr(r_Device.T_sn, time.Now().Unix(), 99)
+	}
+	logs.Println("1r_Device:", r_Device.T_sn, "T_devName:", r_Device.T_devName, "T_Dattery:", r_Device.T_Dattery, "T_Site:", r_Device.T_Site, "T_monitor:", r_Device.T_monitor, "T_online:", r_Device.T_online, "T_online_s:", r_Device.T_online_s)
+
+	//// 更新 状态 为了防止中间有变化,重新获取最新数据
+	//r_Device_new, err := Device.Read_Device_ByT_sn(r_Device.T_sn)
+
+	if r_Device.T_online_s == 0 { // 如果 备用网络没有启动
+		r_Device.T_online = 1
+		Device.Update_Device(r_Device, "T_online", "T_online_s")
+	}
+	if r_Device.T_online == 0 { // 如果 备用网络没有启动
+		r_Device.T_online_s = 1
+		Device.Update_Device(r_Device, "T_online", "T_online_s")
+	}
+	if r_Device.T_online != 1 && r_Device.T_online_s != 1 {
+		r_Device.T_online = 1
+		Device.Update_Device(r_Device, "T_online", "T_online_s")
+	}
+
+	// 开始出来
+	AsyncFuncV3(&r_Device, Ms2_project, message) // 开始处理
+
+	logs.Println("========= END Mqtt2 JSON========== ", " mid:", Ms2_project.Msid, " RunTime :", time.Since(start))
+
+	go func() {
+		time.Sleep(time.Second)
+		// 清除数据缓存 数据
+		logs.Println("清除数据缓存:", r_Device.T_sn+"-"+strconv.FormatInt(Ms2_project.Msid, 10))
+		lock.Lock()
+		delete(DeviceMqttMap, r_Device.T_sn+"-"+strconv.FormatInt(Ms2_project.Msid, 10))
+		lock.Unlock()
+	}()
+
+}
+
+// 发送数据
+func Mqtt_publish(topic string, text string) {
+
+	// Publish a message.
+	err := cli.Publish(&client.PublishOptions{
+		QoS:       mqtt.QoS0,
+		TopicName: []byte("/pub/" + topic),
+		Message:   []byte(text),
+	})
+
+	logs.PrintlnMqtt("-> /pub/" + topic + " " + text)
+	if err != nil {
+		logs.PrintlnError("MqttServer", "发送消息失败 [cli.Publish]", text)
+	}
+
+}

+ 133 - 0
MqttServer/MqttStruct.go

@@ -0,0 +1,133 @@
+package MqttServer
+
+//////////////////////////
+
+// 实体类
+type Ms2_Project struct {
+	//Sn   string                 `json:"sn"`
+	Type int                    `json:"type"`
+	Msid int64                  `json:"mid"`
+	Data map[string]interface{} `json:"data"`
+}
+
+// 实体类
+type Ms2m_Project struct {
+	//Sn   string `json:"sn"`
+	Type int   `json:"type"`
+	Msid int64 `json:"mid"`
+	Dut  int64 `json:"dut"`
+}
+
+// 实体类-数值
+type Ms2_Project_list struct {
+	Type int                      `json:"type"`
+	Msid int64                    `json:"mid"`
+	Data []map[string]interface{} `json:"data"`
+	Dut  int64                    `json:"dut"`
+}
+
+// 反馈消息
+type Ms2_Return struct {
+	Type   int   `json:"type"`
+	Msid   int64 `json:"mid"`
+	Status int   `json:"status"` // 1代表处理成功 0处理异常
+}
+
+type Ms_Project2_Device_Parameter struct {
+	//Sn   string                           `json:"sn"`
+	Type int                              `json:"type"` //  4代表参数设置
+	Msid int64                            `json:"mid"`  //消息识别ID,随机生成,回复时ID不变
+	Data Ms_Project_Device_Parameter_Data `json:"data"` //查询基本参数编号,每次参数改变都会有新的编号,若不带此参数,则返回所有参数
+}
+type Ms_Project_Device_Parameter_Data struct {
+	T_name string `json:"name"`
+
+	T_saveT int `json:"saveT"`
+	T_overA int `json:"overA"`
+	T_outA  int `json:"outA"`
+	T_lostA int `json:"lostA"`
+	T_bat   int `json:"bat"`
+	T_scan  int `json:"scan"`
+
+	T_warn  int `json:"warn"`
+	T_warnD int `json:"warnD"`
+
+	T_dormancy int `json:"dormancy"` // 是否进入休眠  0:关闭 1:开启    默认:0
+	T_snum     int `json:"snum"`     // 是否进入休眠  0:关闭 1:开启    默认:0
+
+	T_prt    string `json:"prt"` //公司名称      (根据 设备 PID 获取)
+	T_btname string `json:"btname"`
+	T_btsid  int    `json:"btsid"`
+	T_btchar int    `json:"btchar"`
+}
+
+type Ms_Project2_Sensor_Parameter struct {
+	//Sn   string                             `json:"sn"`
+	Type int                                `json:"type"` //  4代表参数设置
+	Msid int64                              `json:"mid"`  //消息识别ID,随机生成,回复时ID不变
+	Data []Ms_Project_Sensor_Parameter_Data `json:"data"` //查询基本参数编号,每次参数改变都会有新的编号,若不带此参数,则返回所有参数
+}
+type Ms_Project_Sensor_Parameter_Data struct {
+	T_id      int     `json:"id"`
+	T_name    string  `json:"name"`
+	T_Tlower  float32 `json:"tl"`
+	T_Tupper  float32 `json:"tu"`
+	T_RHlower float32 `json:"hl"`
+	T_RHupper float32 `json:"hu"`
+	T_en      int     `json:"en"`
+
+	T_enprel     int     `json:"enprel"`
+	T_tprel      float32 `json:"tprel"`
+	T_tpreu      float32 `json:"tpreu"`
+	T_hprel      float32 `json:"hprel"`
+	T_hpreu      float32 `json:"hpreu"`
+	T_enprelnote int     `json:"enprelnote"`
+
+	T_free int `json:"free"`
+	//T_speed int `json:"speed"`
+	//T_sense int `json:"sense"`
+}
+
+type Ms_Read_DeviceSensorParameter struct {
+	Type int   `json:"type"`
+	Msid int64 `json:"mid"`
+	Data []int `json:"data"`
+}
+
+/////////////////////////////
+
+// /  -   连接 实体
+type Ms_Project_Connected struct {
+	Clientid  string `json:"clientid"`
+	Username  string `json:"username"`
+	Ipaddress string `json:"ipaddress"`
+	Reason    string `json:"reason"` // sha
+
+}
+
+// /  -   离线断开 实体
+type Ms_Project_Disconnected struct {
+	Clientid  string `json:"clientid"`
+	Username  string `json:"username"`
+	Ipaddress string `json:"ipaddress"`
+	Reason    string `json:"reason"`
+	//终端连接断开原因:
+	//normal:客户端主动断开
+	//kicked:服务端踢出,通过 REST API
+	//keepalive_timeout: keepalive 超时
+	//not_authorized: 认证失败,或者 acl_nomatch = disconnect 时没有权限的 Pub/Sub 会主动断开客户端
+	//tcp_closed: 对端关闭了网络连接
+	//internal_error: 畸形报文或其他未知错误
+}
+
+// /  -   发送 实体
+type Rt_Project_Data_Sensor struct {
+	Id     int `json:"id"`
+	Status int `json:"status"`
+}
+type Rt_Project_Data struct {
+	//Sn     string                   `json:"sn"`
+	Type   int                      `json:"type"`
+	Msid   int64                    `json:"msid"`
+	Sensor []Rt_Project_Data_Sensor `json:"sensor"`
+}

+ 331 - 0
MqttServer/V3MqttControllers.go

@@ -0,0 +1,331 @@
+package MqttServer
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Product"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"math/rand"
+
+	"strconv"
+	"time"
+)
+
+var redis_V3MqttControllers cache.Cache
+
+func init() {
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_V3MqttControllers", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redis_V3MqttControllers, err = cache.NewCache("redis", config)
+	if err != nil || redis_V3MqttControllers == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+}
+
+func Read_DeviceParameter3(T_sn string) {
+
+	logs.Println("Read_DeviceParameter:", T_sn)
+	Rt_parameter := Ms2_Project{
+		Type: 3,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+	}
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "参数请求 [Rt_Parameter]", "base")
+		logs.Println("回复失败 [Rt_Parameter],err=", err)
+	}
+	logs.Println(string(jsonStu))
+
+	// 缓存数据
+	DeviceMqttMapAdd(T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: T_sn, T_data: string(jsonStu), T_num: 10})
+
+	Mqtt_publish(T_sn, string(jsonStu))
+	//lib.CountryMsidMap[(Msid*10)+1] = T_uuid   // Mqtt 消息透传
+
+	//go func() {
+	//	time.Sleep(3 * time.Second)
+	//	Read_DeviceSensorParameter3(T_sn)
+	//}()
+
+	return
+
+}
+
+func Read_DeviceSensorParameter3(T_sn string) {
+	time.Sleep(1 * time.Second)
+	logs.Println("Read_DeviceSensorParameter:", T_sn)
+
+	//  base  读取基本参数
+	Rt_parameter := Ms_Read_DeviceSensorParameter{
+		Type: 5,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+		Data: []int{},
+	}
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "参数请求 [Rt_Parameter]", "sensor")
+		logs.Println("回复失败 [Rt_Parameter],err=", err)
+	}
+	logs.Println(string(jsonStu))
+	// 缓存数据
+	DeviceMqttMapAdd(T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: T_sn, T_data: string(jsonStu), T_num: 10})
+
+	Mqtt_publish(T_sn, string(jsonStu))
+	//lib.CountryMsidMap[(Msid*10)+2] = T_uuid   // Mqtt 消息透传
+
+	return
+
+}
+
+// 修改设备参数
+func Pu_DeviceParameter3(Rt_DeviceParameter Device.DeviceParameter) {
+
+	Rt_parameter := Ms_Project2_Device_Parameter{
+		//Sn:   Rt_DeviceParameter.T_sn,
+		Type: 4,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+	}
+	T_prt := "未知公司"
+	r_Device, err := Device.Read_Device_ByT_sn(Rt_DeviceParameter.T_sn)
+	if err == nil {
+		_, Company_r := Account.Read_Company_id(r_Device.T_pid)
+		T_prt = Company_r.T_name
+	}
+	//T_prt, err = iconvx.ConvertString(T_prt, "utf-8", "GBK")
+	//T_prt = hex.EncodeToString( []byte(T_prt))
+	//
+	T_prt_b, err := lib.Utf8ToGbk([]byte(T_prt))
+	//T_prt = string(T_prt_b)
+	T_prt = hex.EncodeToString(T_prt_b)
+
+	Rt_parameter.Data = Ms_Project_Device_Parameter_Data{
+		T_name:     Rt_DeviceParameter.T_name,
+		T_saveT:    Rt_DeviceParameter.T_saveT,
+		T_overA:    Rt_DeviceParameter.T_overA,
+		T_outA:     Rt_DeviceParameter.T_outA,
+		T_lostA:    Rt_DeviceParameter.T_lostA,
+		T_scan:     Rt_DeviceParameter.T_scan,
+		T_bat:      Rt_DeviceParameter.T_bat,
+		T_warn:     Rt_DeviceParameter.T_warn,
+		T_warnD:    Rt_DeviceParameter.T_warnD,
+		T_dormancy: Rt_DeviceParameter.T_dormancy,
+		T_snum:     Rt_DeviceParameter.T_snum,
+		T_prt:      T_prt,
+		T_btname:   Rt_DeviceParameter.T_btname,
+		T_btsid:    Rt_DeviceParameter.T_btsid,
+		T_btchar:   Rt_DeviceParameter.T_btchar,
+	}
+
+	// 更新 消息ID
+	Rt_DeviceParameter.T_Msid = Rt_parameter.Msid
+	Device.Update_DeviceParameter(Rt_DeviceParameter, "T_Msid")
+
+	// 发布数据
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("设备配置", "MQTT回复失败 [Pu_DeviceParameter]", string(jsonStu))
+		logs.Println("设备配置 MQTT回复失败 [Pu_DeviceParameter],err=", err)
+		return
+	}
+
+	logs.Println(string(jsonStu))
+	// 缓存数据
+	DeviceMqttMapAdd(Rt_DeviceParameter.T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: Rt_DeviceParameter.T_sn, T_data: string(jsonStu), T_num: 10})
+
+	Mqtt_publish(Rt_DeviceParameter.T_sn, string(jsonStu))
+
+	return
+}
+
+// 修改传感器参数
+func Pu_DeviceParameter_Sensor3(Rt_parameter_sensor Device.DeviceSensorParameter) {
+
+	Rt_parameter := Ms_Project2_Sensor_Parameter{
+		//Sn:   Rt_parameter_sensor.T_sn,
+		Type: 6,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+	}
+	// 强制修改 是否启用预警记录数据  1:启用  0:关闭  默认:1
+	if Rt_parameter_sensor.T_enprelnote != 0 {
+		Rt_parameter_sensor.T_enprelnote = 1
+	}
+	Rt_parameter.Data = append(Rt_parameter.Data, Ms_Project_Sensor_Parameter_Data{
+		T_id:         Rt_parameter_sensor.T_id,
+		T_name:       Rt_parameter_sensor.T_name,
+		T_Tlower:     Rt_parameter_sensor.T_Tlower,
+		T_Tupper:     Rt_parameter_sensor.T_Tupper,
+		T_RHlower:    Rt_parameter_sensor.T_RHlower,
+		T_RHupper:    Rt_parameter_sensor.T_RHupper,
+		T_en:         Rt_parameter_sensor.T_en,
+		T_free:       Rt_parameter_sensor.T_free,
+		T_enprel:     Rt_parameter_sensor.T_enprel,
+		T_tprel:      Rt_parameter_sensor.T_tprel,
+		T_tpreu:      Rt_parameter_sensor.T_tpreu,
+		T_hprel:      Rt_parameter_sensor.T_hprel,
+		T_hpreu:      Rt_parameter_sensor.T_hpreu,
+		T_enprelnote: Rt_parameter_sensor.T_enprelnote,
+		//T_speed:   Rt_parameter_sensor.T_speed,
+		//T_sense:   Rt_parameter_sensor.T_sense,
+	})
+
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("设备配置", "MQTT回复失败 [Pu_DeviceParameter_Sensor]", string(jsonStu))
+		logs.Println("设备配置 MQTT回复失败 [Pu_DeviceParameter_Sensor2],err=", err)
+	}
+
+	// 更新 消息ID
+	Rt_parameter_sensor.T_Msid = Rt_parameter.Msid
+	// 缓存数据
+	DeviceMqttMapAdd(Rt_parameter_sensor.T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: Rt_parameter_sensor.T_sn, T_data: string(jsonStu), T_num: 10})
+
+	Device.Update_DeviceSensorParameter(Rt_parameter_sensor, "T_Msid")
+
+	logs.Println(string(jsonStu))
+	Mqtt_publish(Rt_parameter_sensor.T_sn, string(jsonStu))
+
+	return
+}
+
+func Set_DeviceTask3(v Device.Device_task) {
+
+	Rt_parameter := Ms2_Project{
+		Type: 11,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+		Data: map[string]interface{}{},
+	}
+	if v.T_task == "start" {
+		Rt_parameter.Data["task"] = 1
+	} else {
+		Rt_parameter.Data["task"] = 0
+	}
+
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "参数请求 [Rt_Parameter]", "base")
+		logs.Println("回复失败 [Rt_Parameter],err=", err)
+	}
+	logs.Println(v.T_sn, "==>", string(jsonStu))
+	// 缓存数据
+	DeviceMqttMapAdd(v.T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: v.T_sn, T_data: string(jsonStu), T_num: 30})
+
+	Mqtt_publish(v.T_sn, string(jsonStu))
+
+	return
+
+}
+
+func Set_DeviceRestart3(v Device.Device_task) {
+
+	Rt_parameter := Ms2_Project{
+		Type: 97,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+		Data: map[string]interface{}{},
+	}
+	if v.T_task == "1" {
+		Rt_parameter.Data["task"] = 1
+	} else {
+		Rt_parameter.Data["task"] = 0
+	}
+
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "参数请求 [Rt_Parameter]", "base")
+		logs.Println("回复失败 [Rt_Parameter],err=", err)
+	}
+	logs.Println(v.T_sn, "==>", string(jsonStu))
+	// 缓存数据
+	DeviceMqttMapAdd(v.T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: v.T_sn, T_data: string(jsonStu), T_num: 30})
+	Mqtt_publish(v.T_sn, string(jsonStu))
+
+	return
+
+}
+
+func Get_Device_Realtime3(T_sn string) {
+
+	Rt_parameter := Ms2_Project{
+		Type: 0,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+	}
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "实时数据 [Rt_realtime]", T_sn)
+		logs.Println("回复失败 [Rt_realtime],err=", err)
+	}
+	logs.Println(string(jsonStu))
+	Mqtt_publish(T_sn, string(jsonStu))
+
+	return
+
+}
+
+func Up_DeviceProductUpgrade(v Product.ProductUpgrade_T) {
+
+	Rt_parameter := Ms2_Project{
+		Type: 14,
+		Msid: ((time.Now().Unix() - 1600000000) * 10) + int64(rand.Intn(10)),
+		Data: map[string]interface{}{},
+	}
+
+	Rt_parameter.Data["version"] = v.T_version
+	Rt_parameter.Data["bin"] = v.T_file
+
+	// 回复
+	jsonStu, err := json.Marshal(Rt_parameter)
+	if err != nil {
+		logs.Println("MqttServer", "参数请求 [Rt_Parameter]", "base")
+		logs.Println("回复失败 [Rt_Parameter],err=", err)
+	}
+	logs.Println(v.T_sn, "==>", string(jsonStu))
+	// 缓存数据
+	DeviceMqttMapAdd(v.T_sn+"-"+strconv.FormatInt(Rt_parameter.Msid, 10), DeviceMqttMap_Struct{T_sn: v.T_sn, T_data: string(jsonStu), T_num: 30})
+	Mqtt_publish(v.T_sn, string(jsonStu))
+
+	return
+
+}
+
+func RTC_TimeStr(T_sn string, Msid int64, Type int) {
+	if Type == 99 {
+		if redis_V3MqttControllers.IsExist(T_sn + "RTC_TimeStr") {
+			return
+		}
+		redis_V3MqttControllers.Put(T_sn+"RTC_TimeStr", "1", 2*time.Minute)
+	}
+	Time := time.Now()
+	Rt_Project_Data_r := Ms2_Project{
+		Type: Type,
+		Msid: Msid,
+	}
+
+	var Data map[string]interface{}
+	Data = make(map[string]interface{})
+	Data["ut"] = Time.Unix()
+	Data["date"] = Time.Format("2006-01-02 15:04:05")
+	Rt_Project_Data_r.Data = Data
+
+	// 回复
+	jsonStu, err := json.Marshal(Rt_Project_Data_r)
+	if err != nil {
+		return
+	}
+	Mqtt_publish(T_sn, string(jsonStu))
+	return
+}

+ 978 - 0
MqttServer/V3MqttHandle.go

@@ -0,0 +1,978 @@
+package MqttServer
+
+import (
+	"Cold_mqtt/MqttServer/WarningNotice"
+	"Cold_mqtt/Nats/NatsServer"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Product"
+	"Cold_mqtt/models/Warning"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"math"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func AsyncFuncV3(r_Device *Device.Device, Ms_project Ms2m_Project, msg []byte) {
+	ms2_Return := Ms2_Return{
+		Type:   Ms_project.Type,
+		Msid:   Ms_project.Msid,
+		Status: 1,
+	}
+
+	switch Ms_project.Type {
+	case 0: // 0:实时数据
+		var ms2_Project_list Ms2_Project_list
+
+		err := json.Unmarshal(msg, &ms2_Project_list)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_project_0]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_project_0],err=", err)
+			return
+		}
+
+		for _, v := range ms2_Project_list.Data {
+			// 是否存在传感器  不存在 跳过
+			DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(r_Device.T_sn, lib.To_int(v["id"]))
+			if !is {
+				logs.Println("MqttServer", "实时数据 传感器不存在 跳过处理", r_Device.T_sn+lib.To_string(v["id"]))
+				continue
+			}
+			// lost 字段为传感器离线状态(1代表离线,在线不传此字段),如果离线 温湿度时间数据为空
+			if _, lost_is := v["lost"]; lost_is {
+				DeviceSensor_r.T_link = 0
+				Device.Update_DeviceSensor(DeviceSensor_r, "T_link")
+				Warning.Add_DeviceLogs(103, *r_Device, fmt.Sprintf("[%d]%s: 传感器异常断开!", DeviceSensor_r.T_id, DeviceSensor_r.T_name))
+				//  发现 lost 此字段 代表 传感器异常!!!!!
+				continue
+			}
+			int64_, _ := strconv.ParseInt(lib.To_string(v["ut"]), 10, 64)
+			UT := time.Unix(int64_, 0)
+			logs.Println(UT.Format("2006-01-02 15:04:05"))
+
+			DeviceSensorParameter_r, is := Device.Read_DeviceSensorParameter(r_Device.T_sn, DeviceSensor_r.T_id)
+			if !is {
+				logs.Println("MqttServer", "记录数据 传感器参数不存在 跳过处理", r_Device.T_sn+lib.To_string(v["id"]))
+				continue
+			}
+
+			//// 更新记录 - 缓存
+			DeviceData_t := Device.DeviceData_R{
+				T_t:    float32(math.Ceil(float64(lib.To_float32(v["t"])*10)) / 10),
+				T_rh:   float32(math.Ceil(float64(lib.To_float32(v["h"])*10)) / 10),
+				T_Site: lib.To_string(v["s"]),
+				T_time: UT,
+				T_sp:   DeviceSensorParameter_r.Id,
+			}
+
+			Device.RedisDeviceData_Set(r_Device.T_sn, DeviceSensor_r.T_id, DeviceData_t)
+			r_Device.T_Site = DeviceData_t.T_Site
+
+			// 传感器属于 连接状态
+			if DeviceSensor_r.T_link != 1 {
+				DeviceSensor_r.T_link = 1
+				Device.Update_DeviceSensor(DeviceSensor_r, "T_link")
+			}
+
+		}
+		Device.Update_Device(*r_Device, "T_Site")
+		// 反馈透传
+		NatsServer.Mqtt_DeviceReal(r_Device.T_sn, msg)
+
+		return
+	case 1: // 1:记录数据
+		var ms2_Project_list Ms2_Project_list
+
+		err := json.Unmarshal(msg, &ms2_Project_list)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_project_1]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_project_1],err=", err)
+			return
+		}
+
+		// 监控状态 数据有效
+		if math.Abs(float64(time.Now().Unix()-ms2_Project_list.Dut)) < 30 {
+			r_Device.T_monitor = 1
+			Device.Update_Device(*r_Device, "T_monitor")
+		}
+
+		// 回复
+		ms2_Return.Status = 1
+		for _, v := range ms2_Project_list.Data {
+
+			int64_, _ := strconv.ParseInt(lib.To_string(v["ut"]), 10, 64)
+			UT := time.Unix(int64_, 0)
+
+			//时间异常 大于10s,直接丢掉数据
+			if (UT.Unix() - time.Now().Unix()) > 60 {
+				ms2_Return.Status = 3
+				continue
+			}
+
+			// 是否存在传感器  不存在 跳过
+			DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(r_Device.T_sn, lib.To_int(v["id"]))
+			if !is {
+				logs.Println("MqttServer", "记录数据 传感器不存在 跳过处理", r_Device.T_sn+lib.To_string(v["id"]))
+				continue
+			}
+
+			// 获取传感器参数
+			DeviceSensorParameter_r, is := Device.Read_DeviceSensorParameter(r_Device.T_sn, DeviceSensor_r.T_id)
+			if !is {
+				logs.Println("MqttServer", "记录数据 传感器参数不存在 跳过处理", r_Device.T_sn+lib.To_string(v["id"]))
+				continue
+			}
+
+			//// 更新记录 - 缓存
+			DeviceData_t := Device.DeviceData_R{
+				T_t:    float32(math.Ceil(float64(lib.To_float32(v["t"])*10)) / 10),
+				T_rh:   float32(math.Ceil(float64(lib.To_float32(v["h"])*10)) / 10),
+				T_Site: lib.To_string(v["s"]),
+				T_time: UT,
+				T_sp:   DeviceSensorParameter_r.Id,
+			}
+
+			// 验证 数据是否安装规定上传间隔
+			//DeviceData_Previous_r, is := Device.RedisDeviceData_Get(r_Device.T_sn+"_Node", DeviceSensor_r.T_id)
+			//if is {
+			//	//最新的时间 要大
+			//	if DeviceData_t.T_time.Unix() > DeviceData_Previous_r.T_time.Unix() {
+			//		DeviceParameter_r, is := Device.Read_DeviceParameter(r_Device.T_sn)
+			//		if is {
+			//			// 计算时间差
+			//			duration := DeviceData_Previous_r.T_time.Sub(DeviceData_t.T_time)
+			//			// 将时间差表示为秒
+			//			seconds := int(math.Abs(duration.Seconds()))
+			//			// 记录间隔 超过 保存间隔(+-10s)范围
+			//			//difference := int(math.Abs(float64(seconds - DeviceParameter_r.T_overA)))
+			//			if seconds < (DeviceParameter_r.T_saveT - 10) {
+			//				logs.Println(r_Device.T_sn, " 没有达到数据保存间隔,间隔:", seconds, " < ", DeviceParameter_r.T_saveT, "- 10", " 最新记录数据时间:", DeviceData_Previous_r.T_time)
+			//				if F_T_Exceeding(r_Device, DeviceData_t, DeviceSensorParameter_r) && F_H_Exceeding(r_Device, DeviceData_t, DeviceSensorParameter_r) {
+			//					logs.Println("!!! 跳过记录数据", "["+r_Device.T_sn+"]", string(msg))
+			//					continue
+			//				}
+			//			}
+			//		}
+			//	}
+			//}
+
+			DeviceParameter_r, is := Device.Read_DeviceParameter(r_Device.T_sn)
+			if !is {
+				DeviceParameter_r.T_saveT = 1
+			}
+
+			// 判断是否超标 fsfa
+			//if F_T_Exceeding(r_Device, DeviceData_t, DeviceSensorParameter_r) && F_H_Exceeding(r_Device, DeviceData_t, DeviceSensorParameter_r) {
+			//	// 数据超标
+			//	logs.Println("数据超标")
+			//
+			//}else {
+			//
+			//}
+
+			// 补丁,  判断 开机时间 ,根据  上电推送信息
+			_, is = Device.RedisDeviceData_Get(r_Device.T_sn+"_Redis", DeviceSensor_r.T_id)
+			Device.RedisDeviceData_Set(r_Device.T_sn+"_Redis", DeviceSensor_r.T_id, DeviceData_t) // 写缓存
+			if is {
+				// 计算周期时间
+				DeviceData_t.T_time = lib.PeriodTime(DeviceData_t.T_time, DeviceParameter_r.T_saveT/60)
+				logs.Println("计算周期时间:", UT.Format("2006-01-02 15:04:05"), "=>", DeviceData_t.T_time.Format("2006-01-02 15:04:05"), "保存间隔:", DeviceParameter_r.T_saveT)
+			}
+
+			if r_, _ := Device.Add_DeviceData(r_Device.T_sn, DeviceSensor_r.T_id, DeviceData_t, true); r_ {
+				// 被替换
+				Device.Add_DeviceDataOld(Device.DeviceDataOld{
+					T_sn:        r_Device.T_sn,
+					T_id:        DeviceSensor_r.T_id,
+					T_t:         DeviceData_t.T_t,
+					T_rh:        DeviceData_t.T_rh,
+					T_Site:      DeviceData_t.T_Site,
+					T_time:      UT,
+					T_operation: 4, // 4:忽略
+					T_uuid:      "",
+				})
+			}
+			r_Device.T_Site = DeviceData_t.T_Site // GPS
+
+			// 传感器属于 连接状态
+			if DeviceSensor_r.T_link != 1 {
+				DeviceSensor_r.T_link = 1
+				Device.Update_DeviceSensor(DeviceSensor_r, "T_link")
+			}
+
+		}
+		Device.Update_Device(*r_Device, "T_Site")
+		NatsServer.Mqtt_DeviceReal(r_Device.T_sn, msg)
+
+		break
+	case 2: // 2:设备报警
+		var ms2_Project Ms2_Project_list
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "设备报警", string(msg))
+			break
+		}
+		// 缓存报警ID,防止重复发送
+		if Warning.Redis_Warning_Repeat_T_sn_Msid(r_Device.T_sn + "|" + strconv.FormatInt(ms2_Project.Msid, 10)) {
+			logs.Println("MqttServer", " 存在报警 跳过:", string(msg))
+			break
+		}
+		for _, v := range ms2_Project.Data {
+
+			var Warning_r Warning.Warning
+			Warning_r.T_pid = r_Device.T_pid
+			Warning_r.T_tp = lib.To_int(v["tp"]) // 报警类型
+			Warning_r.T_sn = r_Device.T_sn
+			Warning_r.T_id = lib.To_int(v["id"])
+			Warning_r.T_Msid = Ms_project.Msid
+			Warning_r.T_D_name = r_Device.T_devName
+			Warning_r.T_State = 1
+
+			// 获取 传感器 参数
+			DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(r_Device.T_sn, Warning_r.T_id)
+			if !is {
+				logs.Println("MqttServer", " 传感器ID 不存在", string(msg))
+				break
+			}
+			Warning_r.T_DS_name = DeviceSensor_r.T_name
+
+			// 获取 传感器 配置参数
+			DeviceSensorParameter_r, is := Device.Read_DeviceSensorParameter(Warning_r.T_sn, Warning_r.T_id)
+			if !is {
+				logs.Println("MqttServer", " 传感器ID 配置参数不存在", string(msg))
+				break
+			}
+
+			// 温度湿度 封装
+			T := float32(math.Ceil(float64(lib.To_float32(v["t"])*10)) / 10)
+			RH := float32(math.Ceil(float64(lib.To_float32(v["h"])*10)) / 10)
+
+			// 持续时间计算
+			time_int64_, _ := strconv.ParseInt(lib.To_string(v["ut"]), 10, 64)
+			time_int64_f, _ := strconv.ParseInt(lib.To_string(v["fut"]), 10, 64)
+			if time_int64_f == 0 {
+				time_int64_f = time_int64_
+			}
+
+			Warning_r.T_fUt = time.Unix(time_int64_f, 0)
+			Warning_r.T_Ut = time.Unix(time_int64_, 0)
+			// 如果记录时间大于当前时间
+			//if time.Now().Unix() < time_int64_  {
+			//	Warning_r.T_Ut = time.Now()
+			//	Warning_r.T_Remark += "设备时间异常!"
+			//	// 推送同步 时间
+			//	go func() {
+			//		time.Sleep(time.Second * 3)
+			//		logs.Println(fmt.Sprintf("%s  时间相差:%d", r_Device.T_sn, int(math.Abs(float64(time_int64_-time.Now().Unix())))))
+			//		Warning.Add_DeviceLogs(103, *r_Device, fmt.Sprintf("时间相差:%d", int(math.Abs(float64(time_int64_-time.Now().Unix())))))
+			//		RTC_TimeStr(r_Device.T_sn, time.Now().Unix(), 99)
+			//	}()
+			//}
+
+			//时间异常 大于10s,直接丢掉数据
+			//if (time_int64_ - time.Now().Unix()) > 10 {
+			//	ms2_Return.Status = 3
+			//	logs.Println("时间异常 大于10s,直接丢掉数据", Warning_r.T_sn)
+			//	continue
+			//}
+
+			// 查看 sn 是否有归属
+			if Warning_r.T_pid == 0 { //  寻找报警归属
+				if len(Warning_r.T_sn) > 10 {
+					//r_Device, err := Device.Read_Device_ByT_sn(Warning_r.T_sn)
+					//if err != nil {
+					//	logs.Println("MessageDisconnected 没有该设备:", Warning_r.T_sn)
+					//}
+					Warning_r.T_pid = r_Device.T_pid
+				}
+			}
+
+			// 报警数据没有按照 设置间隔推送  (!!! 临时解决问题)
+			//if Warning_r.T_tp == 1 || Warning_r.T_tp == 2 || Warning_r.T_tp == 4 || Warning_r.T_tp == 5 {
+			//	DeviceParameter_r, is := Device.Read_DeviceParameter(r_Device.T_sn)
+			//	if !is {
+			//		logs.Println(r_Device.T_sn + " 没有设备参数,跳过")
+			//		continue
+			//	}
+			//	Warningr := Warning.Read_T_snT_idT_tp(Warning_r.T_sn, Warning_r.T_id, Warning_r.T_tp)
+			//	if Warningr.Id != 0 {
+			//		// 计算时间差
+			//		duration := time.Now().Sub(Warningr.T_Ut)
+			//
+			//		// 将时间差表示为秒
+			//		seconds := int(duration.Seconds())
+			//		if seconds < (DeviceParameter_r.T_overA - 3) {
+			//			logs.Println(r_Device.T_sn+" 没有达到设置报警间隔,跳过,间隔:", DeviceParameter_r.T_overA, " 时间差", seconds, "  New Warningr.Id ", Warningr.Id)
+			//			logs.Println("!!! 跳过没有达到设置报警间隔", "["+r_Device.T_sn+"]", string(msg))
+			//			continue
+			//		}
+			//	}
+			//
+			//}
+
+			// 时间周期补丁 --------------------
+
+			//tp	报警类型
+			// 1	温度超上限报警
+			// 2	温度超下限报警
+			// 3	温度恢复正常
+			// 4	湿度超上限报警
+			// 5	湿度超下限报警
+			// 6	湿度恢复正常
+			// 7	温度超上限预警
+			// 8	温度超下限预警
+			// 9	温度预警恢复正常
+			// 10	湿度超上限预警
+			// 11	湿度超下限预警
+			// 12	湿度预警恢复正常
+
+
+			// 首次时间缓存,防止乱传数据
+			Warning_r.T_fUt = Warning.Redis_WarningStart(Warning_r)
+
+			// 补丁
+			if Warning_r.T_tp == 3 || Warning_r.T_tp == 6 || Warning_r.T_tp == 9 || Warning_r.T_tp == 12 { // 恢复
+				Ws_time,Ws_is := Warning.Redis_WarningStart_DelK(Warning_r)
+				if !Ws_is {
+					logs.Println("之前没有报警信息,跳过保存报警!")
+					continue
+				}else {
+					logs.Println("之前发送报警信息,首次报警时间:",Ws_time.Format("2006-01-02 15:04:05"))
+				}
+			} else {
+				logs_s := "报警时间首次时间:" + Warning_r.T_fUt.Format("2006-01-02 15:04:05") + ",报警时间:" + Warning_r.T_Ut.Format("2006-01-02 15:04:05")
+				Warning_r.T_Ut = lib.PeriodWTime(Warning_r.T_fUt,Warning_r.T_Ut,2)
+				logs.Println("报警时间计算处理:", logs_s,"=>",Warning_r.T_Ut.Format("2006-01-02 15:04:05"))
+				if Warning.Redis_WarningTimeListIs(Warning_r) {
+					logs.Println("报警时间已经存在,跳过!:",Warning_r.T_Ut.Format("2006-01-02 15:04:05"))
+					continue
+				}
+			}
+
+			//当期湿度:88.0%(↓23.0~↑73.0),已持续1小时33秒
+			time_int64_f_x := (int(Warning_r.T_Ut.Unix()) - int(Warning_r.T_fUt.Unix()))
+			logs.Println("持续时间:", time_int64_f_x)
+			if time_int64_f_x > 0 {
+				Warning_r.T_Remark += "已持续 " + lib.MinuteToDataTime(time_int64_f_x)
+			}
+
+			//// 报警处理
+			WarningNotice.WarningCompanyNotice(&Warning_r, T, RH)
+
+			//封装具体信息
+			switch {
+			case Warning_r.T_tp == 1, Warning_r.T_tp == 2, Warning_r.T_tp == 3:
+				Warning_r.T_Remark = "当前温度:" + strconv.FormatFloat(float64(T), 'f', -1, 32) + " (↓" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_Tlower), 'f', -1, 32) + "~↑" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_Tupper), 'f', -1, 32) + ")," + Warning_r.T_Remark
+				break
+			case Warning_r.T_tp == 4, Warning_r.T_tp == 5, Warning_r.T_tp == 6:
+				Warning_r.T_Remark = "当前湿度:" + strconv.FormatFloat(float64(RH), 'f', -1, 32) + " (↓" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_RHlower), 'f', -1, 32) + "~↑" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_RHupper), 'f', -1, 32) + ")," + Warning_r.T_Remark
+				break
+			case Warning_r.T_tp == 7, Warning_r.T_tp == 8, Warning_r.T_tp == 9:
+				Warning_r.T_Remark = "当前温度:" + strconv.FormatFloat(float64(T), 'f', -1, 32) + " (↓" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_tprel), 'f', -1, 32) + "~↑" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_tpreu), 'f', -1, 32) + ")," + Warning_r.T_Remark
+				break
+			case Warning_r.T_tp == 10, Warning_r.T_tp == 11, Warning_r.T_tp == 12:
+				Warning_r.T_Remark = "当前湿度:" + strconv.FormatFloat(float64(RH), 'f', -1, 32) + " (↓" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_hprel), 'f', -1, 32) + "~↑" + strconv.FormatFloat(float64(DeviceSensorParameter_r.T_hpreu), 'f', -1, 32) + ")," + Warning_r.T_Remark
+				break
+			}
+			//记录温湿度
+			Warning_r.T_t = T
+			Warning_r.T_rh = RH
+			Warning_r.T_sp = DeviceSensorParameter_r.Id
+
+			// 添加报警
+			_, err = Warning.Add_Warning(Warning_r)
+
+			//// 不能 同时增加 记录数据,因为缺失GPS数据
+		}
+		NatsServer.Mqtt_DeviceReal(r_Device.T_sn, msg)
+
+		break
+	case 3: // 3:读取主机参数
+		var ms2_Project Ms2_Project
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms_Parameter]", string(msg))
+			return
+		}
+		T_name := lib.To_string(ms2_Project.Data["name"])
+		if len(T_name) == 0 {
+			return
+		}
+		if T_name == "*" {
+			T_name = r_Device.T_devName
+		}
+		if r_Device.T_devName == "未知类型" {
+			T_name = Product.Read_ProductType_Get(r_Device.T_model)
+		}
+		if T_name == r_Device.T_sn {
+			T_name = Product.Read_ProductType_Get(r_Device.T_model)
+		}
+		r_Device.T_devName = T_name
+
+		Deviceparameter := Device.DeviceParameter{
+			T_sn:       r_Device.T_sn,
+			T_name:     T_name,
+			T_saveT:    lib.To_int(ms2_Project.Data["saveT"]),     //数据保存间隔 (s)*最小值60
+			T_overA:    lib.To_int(ms2_Project.Data["overA"]),     //超限报警触发间隔 (s)*最小值60
+			T_outA:     lib.To_int(ms2_Project.Data["outA"]),      //断电报警触发间隔  (s)*最小值60
+			T_lostA:    lib.To_int(ms2_Project.Data["lostA"]),     //传感器掉线报警触发间隔 (s)*最小值60
+			T_warnD:    lib.To_int(ms2_Project.Data["warnD"]),     // 超限预警延时  (s)*
+			T_scan:     lib.To_int(ms2_Project.Data["scan"]),      // 显示轮播间隔   s(1~240) 默认:5 *
+			T_warn:     lib.To_int(ms2_Project.Data["warn"]),      // 超限预警触发间隔 (s)*为0时预警只触发一次 *最小值60
+			T_bat:      lib.To_int(ms2_Project.Data["bat"]),       //电池电量下限   (%)*
+			T_dormancy: lib.To_int(ms2_Project.Data["dormancy"]),  //是否进入休眠  0:关闭 1:开启    默认:0
+			T_snum:     lib.To_int(ms2_Project.Data["snum"]),      // 【管理主机】 - 传感器数量  (范围0~255)
+			T_btname:   lib.To_string(ms2_Project.Data["btname"]), //--蓝牙打印机名称 用于移动端连接蓝牙打印机
+			T_btsid:    lib.To_int(ms2_Project.Data["btsid"]),     //--打印机服务号 用于移动端连接蓝牙打印机
+			T_btchar:   lib.To_int(ms2_Project.Data["btchar"]),    //--蓝牙特征码	用于移动端连接蓝牙打印机
+			//T_speed:     lib.To_int(ms2_Project.Data["speed"]),     //--蓝牙特征码	用于移动端连接蓝牙打印机
+			T_State:     1,
+			T_SendState: 1,
+		}
+
+		if Device.Add_DeviceParameter_fun(Deviceparameter) {
+			Warning.Add_DeviceLogs(102, *r_Device, "修改主机参数")
+		}
+
+		Device.Update_Device(*r_Device, "T_devName")
+		// 获取所有传感器
+		//Read_DeviceSensorParameter(r_Device.T_sn)
+		// 不用反馈
+		return
+	case 4: // 4:设置主机参数 - 反馈
+		var ms2_Return_r Ms2_Return
+
+		err := json.Unmarshal(msg, &ms2_Return_r)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "JSON反序列化失败[4:设置主机参数]", string(msg), err.Error())
+			return
+		}
+
+		DeviceParameter_r, err := Device.Read_DeviceParameter_SN_Msid(r_Device.T_sn, ms2_Return_r.Msid)
+		if err != nil {
+			logs.PrintlnError("MqttServer", " SN_Msid Err![4:设置主机参数]", string(msg), err.Error())
+			return
+		}
+		DeviceParameter_r.T_SendState = 2
+		// 判断参数   //1设置成功,0设置失败
+		if ms2_Return_r.Status == 1 {
+			DeviceParameter_r.T_SendState = 1
+			r_Device.T_devName = DeviceParameter_r.T_name
+			Device.Update_Device(*r_Device, "T_devName")
+		}
+
+		// 状态更新
+		Device.Update_DeviceParameter_(DeviceParameter_r)
+
+		// 同步参数
+		DeviceParameter_r.T_State = 1
+		Device.Add_DeviceParameter_fun(DeviceParameter_r)
+		Warning.Add_DeviceLogs(102, *r_Device, "修改主机参数-反馈成功")
+
+		// 取消掉 其他参数
+		Device.UPDATE_DeviceParameter_SN_T_SendState_2_sql(r_Device.T_sn)
+
+		return
+	case 5: // 读取传感器参数
+		var ms2_Project Ms2_Project_list
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_Parameter]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_Parameter],err=", err)
+			return
+		}
+
+		for _, v := range ms2_Project.Data {
+			DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(r_Device.T_sn, lib.To_int(v["id"]))
+
+			T_name := lib.To_string(v["name"])
+			if len(T_name) == 0 {
+				continue
+			}
+			if T_name == "*" {
+				T_name = r_Device.T_sn + "-" + lib.To_string(v["id"])
+				if is {
+					T_name = DeviceSensor_r.T_name
+				}
+			}
+
+			// 不存在
+			if !is {
+				DeviceSensor_r.T_sn = r_Device.T_sn
+				DeviceSensor_r.T_pid = r_Device.T_pid
+				DeviceSensor_r.T_id = lib.To_int(v["id"])
+				DeviceSensor_r.T_name = T_name
+				DeviceSensor_r.T_datashow = 1
+				DeviceSensor_r.T_sort = 1
+				DeviceSensor_r.T_monitor = r_Device.T_monitor
+				// 设备同步参数
+				DeviceSensor_r.T_Dattery = r_Device.T_Dattery
+				DeviceSensor_r.T_Site = r_Device.T_Site
+				DeviceSensor_r.T_State = r_Device.T_State
+				DeviceSensor_r.T_online = r_Device.T_online
+
+				DeviceSensor_r.Id = Device.Add_DeviceSensor(*r_Device, DeviceSensor_r)
+			}
+
+			// 记录传感器
+			Devicesensorparameter := Device.DeviceSensorParameter{
+				T_sn:   r_Device.T_sn,
+				T_id:   lib.To_int(v["id"]),
+				T_name: T_name,
+				// 报警
+				T_Tlower:  lib.To_float32(v["tl"]),
+				T_Tupper:  lib.To_float32(v["tu"]),
+				T_RHlower: lib.To_float32(v["hl"]),
+				T_RHupper: lib.To_float32(v["hu"]),
+				// 预警
+				T_enprel:     lib.To_int(v["enprel"]),
+				T_tprel:      lib.To_float32(v["tprel"]),
+				T_tpreu:      lib.To_float32(v["tpreu"]),
+				T_hprel:      lib.To_float32(v["hprel"]),
+				T_hpreu:      lib.To_float32(v["hpreu"]),
+				T_enprelnote: lib.To_int(v["enprelnote"]),
+
+				//T_speed: lib.To_int(v["speed"]),
+				//T_sense: lib.To_int(v["sense"]),
+				T_en:   lib.To_int(v["en"]),
+				T_free: lib.To_int(v["free"]),
+
+				T_Msid:      ms2_Project.Msid, // 消息识别ID
+				T_SendState: 1,                // 发送状态  0 待发送   1 发送成功  2 失败  3 覆盖
+				T_State:     1,                //  1 系统获取   2 用户提交
+			}
+			// 强制修改 是否启用预警记录数据  1:启用  0:关闭  默认:1
+			if Devicesensorparameter.T_enprelnote != 0 {
+				Devicesensorparameter.T_enprelnote = 1
+			}
+
+			// 过滤参数 相同
+			id := Device.Add_DeviceSensorParameter_fun(Devicesensorparameter)
+			Warning.Add_DeviceLogs(102, *r_Device, "修改设备参数")
+
+			logs.Println("DeviceSensorParameter ID:", id)
+			// 对 传感器 的状态更新
+			DeviceSensor_r.T_name = T_name
+			DeviceSensor_r.T_sp = id
+			Device.Update_DeviceSensor(DeviceSensor_r, "T_name", "T_sp")
+
+		}
+
+		go func() {
+			time.Sleep(time.Second * 3)
+			Get_Device_Realtime(r_Device.T_sn)
+		}()
+		// 不用反馈
+		return
+	case 6: // 设置传感器参数
+		var ms2_Return_r Ms2_Return
+
+		err := json.Unmarshal(msg, &ms2_Return_r)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_Parameter]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_Parameter],err=", err)
+			return
+		}
+
+		r, is := Device.Read_DeviceSensorParameter_SN_T_id_Msid(r_Device.T_sn, ms2_Return_r.Msid)
+		if !is {
+			logs.Println("设备配置", "设置设备参数  SN_Msid Err! [Ms_Parameter_r]", string(msg))
+			return
+		}
+		r.T_SendState = 2
+
+		// 判断参数   //1设置成功,0设置失败
+		if ms2_Return_r.Status == 1 {
+			r.T_SendState = 1
+
+			DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(r.T_sn, r.T_id)
+			if !is {
+				logs.Println("设备配置", "设置传感器参数  r_DeviceSensor Err! [Ms_Parameter_r]", r.T_sn+"|"+strconv.Itoa(r.T_id))
+				logs.Println("设置传感器参数  r_DeviceSensor Err![Ms_Parameter_r],err=", r.T_sn+"|"+strconv.Itoa(r.T_id))
+				logs.Println("MqttServer", r_Device.T_sn+"["+lib.To_string(r.T_id)+"] 没有找到传感器:", string(msg))
+				break
+			}
+
+			// 添加一份最新的
+			r.T_State = 0
+			id := Device.Add_DeviceSensorParameter_fun(r)
+			Warning.Add_DeviceLogs(102, *r_Device, "修改设备参数")
+			logs.Println("DeviceSensorParameter ID:", id)
+
+			// 对 传感器 的状态更新
+			DeviceSensor_r.T_name = r.T_name
+			DeviceSensor_r.T_sp = id
+			Device.Update_DeviceSensor(DeviceSensor_r, "T_name", "T_sp")
+
+		}
+
+		// 状态更新
+		Device.Update_DeviceSensorParameter(r, "T_SendState")
+
+		// 取消掉 其他参数
+		Device.UPDATE_DeviceSensorParamete_SN_T_SendState_2_sql(r_Device.T_sn, strconv.Itoa(r.T_id))
+
+		//// 更新主机参数
+		//go func() {
+		//	time.Sleep(time.Second * 1)
+		//	Read_DeviceSensorParameter3(r_Device.T_sn)
+		//}()
+		return
+	case 12:
+		var ms2_Project Ms2_Project_list
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms_project_2]", string(msg), err.Error())
+			break
+		}
+
+		// 缓存报警ID,防止重复发送
+		if Warning.Redis_Warning_Repeat_T_sn_Msid(r_Device.T_sn + "|" + strconv.FormatInt(ms2_Project.Msid, 10)) {
+			logs.Println("MqttServer", " 存在报警 跳过:", string(msg))
+			break
+		}
+
+		//	{
+		//		"sn": "YD0123456789",
+		//		"type": 12,
+		//		"msid": 125456001,
+		//		"data": [
+		//	{
+		//	"tp":7, // 对照 报警类型对应表
+		//	"msg": "报警信息或故障信息", //如:1号传感器故障 (非必填,但是 要带字段)
+		//	"ut": 1626857518 //产生时间
+		//	},
+		//	..........
+		//]
+		//}
+		for _, v := range ms2_Project.Data {
+			var Warning_r Warning.Warning
+			Warning_r.T_tp = lib.To_int(v["tp"]) + 100 // 报警类型
+			Warning_r.T_sn = r_Device.T_sn
+			Warning_r.T_id = -1
+			Warning_r.T_Msid = Ms_project.Msid
+			Warning_r.T_D_name = r_Device.T_devName
+			Warning_r.T_DS_name = r_Device.T_devName
+			time_int64_, _ := strconv.ParseInt(lib.To_string(v["ut"]), 10, 64)
+			Warning_r.T_Ut = time.Unix(time_int64_, 0)
+			Warning_r.T_Remark = lib.To_string(v["msg"])
+			Warning_r.T_State = 1
+
+			//时间异常 大于10s,直接丢掉数据
+			if (time_int64_ - time.Now().Unix()) > 10 {
+				ms2_Return.Status = 3
+				continue
+			}
+
+			// 持续时间计算
+			time_int64_f, _ := strconv.ParseInt(lib.To_string(v["fut"]), 10, 64)
+			if time_int64_f == 0 {
+				time_int64_f = time_int64_
+			}
+			Warning_r.T_fUt = time.Unix(time_int64_f, 0)
+			time_int64_f_x := (int(time_int64_) - int(time_int64_f))
+			logs.Println(strconv.FormatInt(Warning_r.T_Msid, 10)+"持续时间:", time_int64_f_x)
+			if time_int64_f_x > 0 {
+				Warning_r.T_Remark += " 已持续" + lib.MinuteToDataTime(time_int64_f_x)
+			}
+
+			// 查看 sn 是否有归属
+			if Warning_r.T_pid == 0 { //  寻找报警归属
+				if len(Warning_r.T_sn) > 10 {
+					r_Device, err := Device.Read_Device_ByT_sn(Warning_r.T_sn)
+					if err != nil {
+						logs.Println("MessageDisconnected 没有该设备:", Warning_r.T_sn)
+					}
+					Warning_r.T_pid = r_Device.T_pid
+				}
+			}
+
+			switch Warning_r.T_tp {
+			case 101: // 主机报警
+				Warning_r.T_State = 3
+				WarningNotice.WarningToAdminId(&Warning_r, []int{}) // 发送给 绑定公司管理员
+				break
+
+			case 115:
+				Warning_r.T_Remark = "开始监测任务"
+				//  1   start:开始监测任务,
+				// 防止没有结束命令
+				DeviceTask_r, ok := Device.Read_DeviceTask_All_Limit_1(r_Device.T_sn)
+				logs.Println("DeviceTask_r.id:", DeviceTask_r.Id)
+				if ok {
+					if DeviceTask_r.T_Ut_end.IsZero() {
+						logs.Println("没有结束 强制 更新上一条")
+						DeviceTask_r.T_Ut_end = Warning_r.T_Ut
+						Device.Update_DeviceTask(DeviceTask_r)
+					}
+				}
+				logs.Println("插入新任务")
+				// 插入开始 任务
+				dt := Device.DeviceTask{
+					T_sn:       r_Device.T_sn,
+					T_Ut_start: Warning_r.T_Ut,
+					T_Ut:       Warning_r.T_Ut,
+					T_State:    1,
+				}
+				Device.Add_DeviceTask(dt)
+				r_Device.T_monitor = 1
+				break
+			case 116:
+				Warning_r.T_Remark = "中途打印任务"
+
+				DeviceTask_r, ok := Device.Read_DeviceTask_All_Limit_1(r_Device.T_sn)
+				if ok {
+					if DeviceTask_r.T_Ut_end.IsZero() {
+						DeviceTask_r.T_Ut_end = Warning_r.T_Ut
+						DeviceTask_r.T_State = 3
+						Device.Update_DeviceTask(DeviceTask_r)
+						// 插入开始 任务
+						dt := Device.DeviceTask{
+							T_sn:       r_Device.T_sn,
+							T_Ut_start: DeviceTask_r.T_Ut_start,
+							T_Ut:       Warning_r.T_Ut,
+							T_State:    1,
+						}
+						logs.Println("插入新任务")
+						Device.Add_DeviceTask(dt)
+					}
+				}
+				r_Device.T_monitor = 1
+				break
+			case 117:
+				Warning_r.T_Remark = "结束监测任务"
+
+				// 防止没有结束命令
+				DeviceTask_r, ok := Device.Read_DeviceTask_All_Limit_1(r_Device.T_sn)
+				if ok {
+					if DeviceTask_r.T_Ut_end.IsZero() {
+
+						DeviceTask_r.T_Ut_end = Warning_r.T_Ut
+						DeviceTask_r.T_State = 2
+						Device.Update_DeviceTask(DeviceTask_r)
+
+					}
+				}
+				// 遍历所有探头,停止记录后 将最后一条数据写入关机数据
+				DeviceSensorList := Device.Read_DeviceSensor_ByTsn(r_Device.T_sn)
+				for _, vd := range DeviceSensorList {
+					DeviceData_t, is := Device.RedisDeviceData_Get(r_Device.T_sn+"_Redis", vd.T_id)
+					if is {
+						Device.Add_DeviceData(r_Device.T_sn, vd.T_id, DeviceData_t, true)
+						Device.RedisDeviceData_Del(r_Device.T_sn+"_Redis", vd.T_id) // 清除缓存标志
+					}
+				}
+
+				r_Device.T_monitor = 0
+				break
+			}
+			Device.Update_Device(*r_Device, "T_monitor")
+			//// 推送运维信息
+			WarningNotice.WarningCompanyNotice(&Warning_r, 0, 0)
+
+			// 添加报警
+			_, err = Warning.Add_Warning(Warning_r)
+
+		}
+
+		break
+	case 13: // 上电推送信息
+		var ms2_Project Ms2_Project
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_Parameter]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_Parameter],err=", err)
+			return
+		}
+
+		var Warning_r Warning.Warning
+		Warning_r.T_tp = 106 // 报警类型
+		Warning_r.T_sn = r_Device.T_sn
+		Warning_r.T_D_name = r_Device.T_devName
+		Warning_r.T_id = 0
+		Warning_r.T_DS_name = ""
+		Warning_r.T_Msid = Ms_project.Msid
+
+		r_Device.T_model = lib.To_string(ms2_Project.Data["model"])
+		r_Device.T_sver = lib.To_string(ms2_Project.Data["sver"])
+		r_Device.T_hver = lib.To_string(ms2_Project.Data["hver"])
+		r_Device.T_imei = lib.To_string(ms2_Project.Data["imei"])
+		r_Device.T_iccid = lib.To_string(ms2_Project.Data["iccid"])
+		Device.Update_Device(*r_Device, "T_model", "T_sver", "T_hver", "T_imei", "T_iccid")
+		//  加入日志
+		//	  "rtc" : 1671173184,//设备时间
+		//    "rtl" : 3,//设备开机累计运行秒数
+		//    "st" : 1671173177,//设备开机时间
+		//    "lst" : 0//设备上次关机时间
+		UT_rtc := time.Unix(int64(lib.To_int64(ms2_Project.Data["rtc"])), 0).Format("2006-01-02 15:04:05")
+		UT_st := time.Unix(int64(lib.To_int64(ms2_Project.Data["st"])), 0).Format("2006-01-02 15:04:05")
+		UT_lst := time.Unix(int64(lib.To_int64(ms2_Project.Data["lst"])), 0).Format("2006-01-02 15:04:05")
+		UT_rtl := lib.To_string(ms2_Project.Data["rtl"])
+
+		str := "----- 设备信息 -----\n"
+		str += "设备型号:" + r_Device.T_model + " |\n"
+		str += "软件版本:" + r_Device.T_sver + " |\n"
+		str += "硬件版本:" + r_Device.T_hver + " |\n"
+		str += "模组imei:" + r_Device.T_imei + " |\n"
+		str += "sim卡号:" + r_Device.T_iccid + " |\n"
+		str += "设备时间:" + UT_rtc + " |\n"
+		str += "设备开机时间:" + UT_st + " |\n"
+		str += "设备上次关机时间:" + UT_lst + " |\n"
+		str += "设备开机累计运行秒数:" + UT_rtl + " |\n"
+		Warning_r.T_Remark = str
+		Warning_r.T_Ut = time.Now()
+		Warning_r.T_State = 1
+		// 添加报警
+		_, err = Warning.Add_Warning(Warning_r)
+
+		return
+	case 15: // 网络延时测试
+		Rt_Project_Data_r := Ms2_Project{
+			Type: Ms_project.Type,
+			Msid: Ms_project.Msid,
+		}
+
+		var Data map[string]interface{}
+		Data = make(map[string]interface{})
+		Data["ut"] = Ms_project.Dut
+		Rt_Project_Data_r.Data = Data
+
+		// 回复
+		jsonStu, err := json.Marshal(Rt_Project_Data_r)
+		if err != nil {
+			return
+		}
+		Mqtt_publish(r_Device.T_sn, string(jsonStu))
+		return
+	case 88: // 临时授权
+		var ms2_Project Ms2_Project
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms_Parameter]", string(msg))
+			return
+		}
+
+		url := "192.168.0.33:3991/CreateAuth"
+		method := "POST"
+
+		payload := strings.NewReader("model=" + lib.To_string(ms2_Project.Data["model"]) + "&sver=" + lib.To_string(ms2_Project.Data["sver"]) + "&hver=" + lib.To_string(ms2_Project.Data["hver"]) + "&imei=" + lib.To_string(ms2_Project.Data["imei"]) + "&iccid=" + lib.To_string(ms2_Project.Data["iccid"]) + "&sn=" + lib.To_string(ms2_Project.Data["sn"]))
+
+		client := &http.Client{}
+		req, err := http.NewRequest(method, url, payload)
+
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+
+		res, err := client.Do(req)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+		defer res.Body.Close()
+
+		body, err := ioutil.ReadAll(res.Body)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+		fmt.Println(string(body))
+
+		// 实体类
+		type Ms2_Projectccc struct {
+			Pass   string `json:"psaa"`
+			Status string `json:"status"`
+		}
+
+		var ms2_Projectccc Ms2_Projectccc
+
+		err = json.Unmarshal(body, &ms2_Projectccc)
+		if err != nil {
+			logs.PrintlnError("MqttServer", "JSON反序列化失败[Ms2_Projectccc]", string(msg))
+			return
+		}
+
+		Rt_Project_Data_r := Ms2_Project{
+			Type: Ms_project.Type,
+			Msid: Ms_project.Msid,
+		}
+
+		var Data map[string]interface{}
+		Data = make(map[string]interface{})
+		Data["Pass"] = ms2_Projectccc.Pass
+		Data["Status"] = ms2_Projectccc.Status
+		Rt_Project_Data_r.Data = Data
+
+		// 回复
+		jsonStu, err := json.Marshal(Rt_Project_Data_r)
+		if err != nil {
+			return
+		}
+		Mqtt_publish(r_Device.T_sn, string(jsonStu))
+		return
+	case 98: // 时间同步
+		RTC_TimeStr(r_Device.T_sn, Ms_project.Msid, 98)
+		return
+	case 100: // 设备实时状态推送
+		var ms2_Project Ms2_Project
+
+		err := json.Unmarshal(msg, &ms2_Project)
+		if err != nil {
+			logs.Println("MqttServer", "JSON反序列化失败[Ms_project_]", string(msg))
+			logs.Println("JSON反序列化失败[Ms_project],err=", err)
+			return
+		}
+
+		r_Device.T_Dattery = lib.To_int(ms2_Project.Data["power"]) //#设备剩余电量百分比
+		r_Device.T_rssi = lib.To_string(ms2_Project.Data["rssi"])  //#信号强度
+		Device.Update_Device(*r_Device, "T_Dattery", "T_rssi")
+		// 反馈透传
+		NatsServer.Mqtt_DeviceReal(r_Device.T_sn, msg)
+		Device.Update_Device(*r_Device, "T_Dattery")
+		return
+	default:
+		return
+	}
+
+	// 回复
+	jsonStu, err := json.Marshal(ms2_Return)
+	if err != nil {
+		logs.PrintlnError("MqttServer", "json.Marshal ", string(msg))
+		logs.Println("回复失败 [Ms_project_0],err=", err)
+		return
+	}
+	Mqtt_publish(r_Device.T_sn, string(jsonStu))
+
+}
+
+func F_T_Exceeding(r_Device *Device.Device, DeviceData_t Device.DeviceData_R, DeviceSensorParameter_r Device.DeviceSensorParameter) bool {
+	if r_Device.T_ist == 1 { // 温度   1开启   2关闭
+		if !(DeviceSensorParameter_r.T_Tupper < DeviceData_t.T_t || DeviceData_t.T_t < DeviceSensorParameter_r.T_Tlower) {
+			logs.Println(r_Device.T_sn+" 温度没有超标,且不在保存间隔范围 :", DeviceSensorParameter_r.T_Tupper, DeviceData_t.T_t, DeviceSensorParameter_r.T_Tlower)
+			return true
+		}
+	}
+	return false
+}
+func F_H_Exceeding(r_Device *Device.Device, DeviceData_t Device.DeviceData_R, DeviceSensorParameter_r Device.DeviceSensorParameter) bool {
+	if r_Device.T_ish == 1 { // 湿度   1开启   2关闭
+		if !(DeviceSensorParameter_r.T_RHupper < DeviceData_t.T_rh || DeviceData_t.T_rh < DeviceSensorParameter_r.T_RHlower) {
+			logs.Println(r_Device.T_sn+" 湿度没有超标,且不在保存间隔范围 :", DeviceSensorParameter_r.T_RHupper, DeviceData_t.T_rh, DeviceSensorParameter_r.T_RHlower)
+			return true
+		}
+	}
+	return false
+}

+ 672 - 0
MqttServer/WarningNotice/WarningNotice.go

@@ -0,0 +1,672 @@
+package WarningNotice
+
+import (
+	"Cold_mqtt/Nats/NatsServer"
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Company"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Warning"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"strconv"
+	"strings"
+)
+
+// 报警策略通知       fut 持续时间间隔 秒
+func WarningCompanyNotice(Warning_r *Warning.Warning, T, HR float32) {
+
+	defer func() {
+		if Warning_r.Id > 0 {
+			Warning.Update_Warning(*Warning_r, "T_Log")
+		}
+	}()
+
+	if Warning_r.T_State == 0 {
+		logs.Println("Warning_r.T_State == 0", Warning_r.T_State)
+		return
+	}
+	logs.Println("==>WarningCompanyNotice  Msid", Warning_r.T_Msid)
+
+	CompanyNotice_List := Company.Read_CompanyNotice_List(Warning_r.T_pid)
+	CompanyNotice_List_is := true
+	logs.Println("==>WarningCompanyNotice CompanyNotice_List.len", len(CompanyNotice_List))
+	// 通知策略
+	for _, CompanyNotice_v := range CompanyNotice_List {
+
+		if Warning_r.T_id == -1 {
+			// 主机 通知
+			if !strings.Contains(CompanyNotice_v.T_Notice_bind, Warning_r.T_sn) {
+				logs.Println("==>WarningCompanyNotice 不匹配!", CompanyNotice_v.T_Notice_bind, Warning_r.T_sn)
+				continue
+			}
+		} else {
+			// 传感器 通知
+			if !strings.Contains(CompanyNotice_v.T_Notice_bind, Warning_r.T_sn+","+strconv.Itoa(Warning_r.T_id)) {
+				logs.Println("==>WarningCompanyNotice 不匹配!", CompanyNotice_v.T_Notice_bind, Warning_r.T_sn+","+strconv.Itoa(Warning_r.T_id))
+				continue
+			}
+		}
+		CompanyNotice_List_is = false
+		WarningCompanyNotice_CompanyNotice(Warning_r, CompanyNotice_v, T, HR)
+	}
+
+	if CompanyNotice_List_is {
+		// 策略 有问题
+		Warning.Add_Warning_Log(Warning_r, "!!!没有报警策略,请及时去添加!!!\n")
+
+		return
+	}
+
+}
+
+// 处理分类
+func WarningCompanyNotice_CompanyNotice(Warning_r *Warning.Warning, CompanyNotice_r Company.CompanyNotice, T, HR float32) {
+	logs.Println("==>WarningCompanyNotice_CompanyNotice", "Msid", Warning_r.T_Msid, "  CompanyNotice:", CompanyNotice_r.Id, "  T_name:", CompanyNotice_r.T_name, "  T_Notice_mechanism:", CompanyNotice_r.T_Notice_mechanism)
+	cx_time := time.Now().Unix() - Warning_r.T_fUt.Unix() // 持续时间 s
+	Warning.Add_Warning_Log(Warning_r, "执行通知策略 ->["+CompanyNotice_r.T_name+"] ["+time.Now().Format("15:04:05")+"]\n")
+
+	// 遍历 所有绑定策略
+	// W报警编号,记录,处理,w启用,数量,上限,~|
+	// W15,1,1,0,1,0,9999,0,0,0,0,0,0,0,0,0|
+	T_Notice_mechanism_list := strings.Split(CompanyNotice_r.T_Notice_mechanism, "|")
+	if len(T_Notice_mechanism_list) == 0 {
+		// 策略 有问题 !!!!
+		Warning.Add_Warning_Log(Warning_r, " !!!策略规则错误!!!\n"+CompanyNotice_r.T_Notice_mechanism+" , 内容:"+Warning_r.T_Remark)
+	}
+
+	for _, v := range T_Notice_mechanism_list {
+		if len(v) == 0 {
+			continue
+		}
+		//T_nm_list   W报警编号-0,处理-1,w启用-2,持续秒-3,间隔秒-4,发送条数-5,d启用-6,持续秒-7,间隔秒-8,发送条数-9,p启用-10,持续秒-11,间隔秒-12,发送条数-13|
+
+		// 解析策略
+		T_nm_list := strings.Split(v, ",")
+		//if len(T_nm_list) != 14 {
+		//	logs.Println("==>WarningCompanyNotice_CompanyNotice 策略 ->", v, " 策略下标长度 != 15 ->", len(T_nm_list))
+		//	continue
+		//}
+
+		// 判断符合 报警类型
+		T_tp_str := Warning.Read_WarningType_Get(Warning_r.T_tp)
+		if !strings.Contains(T_tp_str, T_nm_list[0]) {
+			logs.Println("==>WarningCompanyNotice_CompanyNotice  不符合 报警类型!!", T_tp_str, T_nm_list[0])
+			continue
+		}
+
+		logs.Println("执行策略 ->", v)
+		// 是否绑定 SN,ID
+		if Warning_r.T_id == -1 {
+			if !strings.Contains(CompanyNotice_r.T_Notice_bind, Warning_r.T_sn) {
+				logs.Println("==>WarningCompanyNotice_CompanyNotice !!!没有绑定设备,跳过", CompanyNotice_r.T_Notice_bind, Warning_r.T_sn+",")
+				Warning.Add_Warning_Log(Warning_r, "   !!!没有绑定设备,跳过\n")
+				continue
+			}
+		} else {
+			if !strings.Contains(CompanyNotice_r.T_Notice_bind, Warning_r.T_sn+","+strconv.Itoa(Warning_r.T_id)+"|") {
+				logs.Println("!!!没有绑定设备,跳过", CompanyNotice_r.T_Notice_bind, Warning_r.T_sn+","+strconv.Itoa(Warning_r.T_id)+"|")
+				Warning.Add_Warning_Log(Warning_r, "   !!!没有绑定设备传感器,跳过\n")
+				continue
+			}
+		}
+
+		// 是否处理
+		if T_nm_list[1] == "1" {
+			logs.Println("==>WarningCompanyNotice_CompanyNotice  策略结果 ->", "要 处理")
+			Warning_r.T_State = 3
+		}
+
+		WarningCompanyNotice_CompanyNotice_Send(Warning_r, CompanyNotice_r, T_nm_list, T, HR, cx_time)
+
+		return
+	}
+}
+
+// 发送报警信息
+func WarningCompanyNotice_CompanyNotice_Send(Warning_r *Warning.Warning, CompanyNotice_r Company.CompanyNotice, T_nm_list []string, T, HR float32, cx_time int64) {
+	logs.Println("==>WarningDeviceHandle_CompanyNotice_Send ", "T_Msid", Warning_r.T_Msid, " T_nm_list:", T_nm_list)
+	//T_nm_list   W报警编号-0,处理-1,w启用-2,持续秒-3,间隔秒-4,发送条数-5,d启用-6,持续秒-7,间隔秒-8,发送条数-9,p启用-10,持续秒-11,间隔秒-12,发送条数-13|
+	_, Company_r := Account.Read_Company_id(Warning_r.T_pid)
+	Company_Charging_r := Company_r
+	//记账扣费
+	if Company_r.T_Charging > 0 {
+		_, Company_Charging_r = Account.Read_Company_id(Company_r.T_Charging)
+	}
+
+	//  微信
+	N_index := 2
+	if T_nm_list[N_index] == "1" { // 是否开启
+		Warning_num, Warning_sUt := Warning.Redis_Warning_Num(Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_id) + "|" + strconv.Itoa(Warning_r.T_tp) + "|wx")
+		Warning.Add_Warning_Log(Warning_r, fmt.Sprintf("[微信]持续时间:%d秒, 间隔时间:%s秒, 内发送次数:%d次, 间隔还剩时间:%d秒\n", cx_time, T_nm_list[N_index+2], Warning_num, Warning_sUt))
+
+		if int_x, _ := strconv.Atoi(T_nm_list[N_index+1]); int64(int_x) <= cx_time { // 是否满足 持续时间 秒
+			if int_u, _ := strconv.Atoi(T_nm_list[N_index+3]); (int64(int_u) > Warning_num) || int_u == 0 { //是否 间隔时间内 超过发送条数
+				Warning.Redis_Warning_Num_W(Warning_r.T_sn+"|"+strconv.Itoa(Warning_r.T_id)+"|"+strconv.Itoa(Warning_r.T_tp)+"|wx", T_nm_list[N_index+2])
+				// 开始推送
+				logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", CompanyNotice_r.T_Notice_wx)
+				//reqdata_wx := "{\"first\":{\"value\":\"" + Warning_r.T_Ut.Format("2006-01-02 15:04:05") + "\", \"color\":\"#ff450a\"}, \"keyword1\":{\"value\":\"" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\", \"color\":\"#ff450a\"}, \"keyword2\":{\"value\":\"" + Warning_r.T_D_name + "[" + Warning_r.T_DS_name + "] " + "\", \"color\":\"#056cff\"}, \"remark\":{\"value\":\"" + Warning_r.T_Remark + "\", \"color\":\"#ffcb0a\"}}"
+				arr_c := strings.Split(CompanyNotice_r.T_Notice_wx, "|")
+				for _, v_c := range arr_c {
+					if len(v_c) == 0 {
+						continue
+					}
+
+					v_c_l := strings.Split(v_c, "/")
+					if len(v_c_l) != 2 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l) != 2")
+						continue
+					}
+					if len(v_c_l[0]) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l[0]) == 0")
+						continue
+					}
+
+					admin_r, err := Account.Read_Admin_ByUuid(v_c_l[0])
+					if err != nil {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " Read_Admin_ByUuid!!")
+						continue
+					}
+					if len(admin_r.T_wx) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " admin_r.T_wx!!")
+						continue
+					}
+					// 用户下 多个微信
+					arr_b := strings.Split(admin_r.T_wx, "|")
+					for _, v_b := range arr_b {
+						v_b_l := strings.Split(v_b, "/")
+						if len(v_b_l) != 2 {
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_b, " len(v_b_l) != 2")
+							continue
+						}
+						if len(v_b_l[0]) == 0 {
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_b, " len(v_b_l[0]) == 0")
+							continue
+						}
+						//  微信
+						Warning.Add_Warning_Log(Warning_r, "----> "+v_b)
+
+						var data_json_str []byte
+						switch Warning_r.T_tp {
+						case 1, 2, 3, 7, 8, 9: //温度
+							data_json_t := Wx_t_struct{
+								Corporate: Wx_struct{Value: lib.Limit_len(Company_r.T_name, 20)},
+								Type:      Wx_struct{Value: Warning.Read_WarningType_Get(Warning_r.T_tp)},
+								Name:      Wx_struct{Value: lib.Limit_len(Warning_r.T_DS_name, 20)},
+								Time:      Wx_struct{Value: Warning_r.T_Ut.Format("2006-01-02 15:04:05")},
+								//T:         Wx_struct{Value: fmt.Sprintf("%.1f℃", T), Color: "#ff0099"},
+								Remark: Wx_struct{Value: lib.Limit_len("当前:"+fmt.Sprintf("%.1f℃", T)+Warning_r.T_Remark, 20)},
+							}
+							data_json_str, _ = json.Marshal(data_json_t)
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   通知内容:", string(data_json_str))
+							NatsServer.Wx_MessageTemplate(Company_Charging_r, Warning_r, v_b_l[0], "8bPXmJwtd7RnbI2iVNatxez9Vby_0Y-69ggN57_-Sh8", string(data_json_str))
+
+							break
+						case 4, 5, 6, 10, 11, 12: //湿度
+							data_json_t := Wx_h_struct{
+								Corporate: Wx_struct{Value: lib.Limit_len(Company_r.T_name, 20)},
+								Type:      Wx_struct{Value: Warning.Read_WarningType_Get(Warning_r.T_tp)},
+								Name:      Wx_struct{Value: lib.Limit_len(Warning_r.T_DS_name, 20)},
+								Time:      Wx_struct{Value: Warning_r.T_Ut.Format("2006-01-02 15:04:05")},
+								//H:         Wx_struct{Value: },
+								Remark: Wx_struct{Value: lib.Limit_len("当前:"+fmt.Sprintf("%.1f", HR)+"%"+Warning_r.T_Remark, 20)},
+							}
+							data_json_str, _ = json.Marshal(data_json_t)
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   通知内容:", string(data_json_str))
+							NatsServer.Wx_MessageTemplate(Company_Charging_r, Warning_r, v_b_l[0], "833Kd1-zaQJ5sOJ9F2I1RzSNS249KU_t3L7610cbgaw", string(data_json_str))
+
+							break
+						default:
+							data_json_t := Wx_d_struct{
+								Corporate: Wx_struct{Value: lib.Limit_len(Company_r.T_name, 20)},
+								Type:      Wx_struct{Value: Warning.Read_WarningType_Get(Warning_r.T_tp)},
+								Name:      Wx_struct{Value: lib.Limit_len(Warning_r.T_D_name, 20)},
+								Time:      Wx_struct{Value: Warning_r.T_Ut.Format("2006-01-02 15:04:05")},
+								Remark:    Wx_struct{Value: lib.Limit_len(Warning_r.T_Remark, 20)},
+							}
+							data_json_str, _ = json.Marshal(data_json_t)
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   通知内容:", string(data_json_str))
+							NatsServer.Wx_MessageTemplate(Company_Charging_r, Warning_r, v_b_l[0], "gnpERD6ilOQK6zvp1W_PmY5SEDOcSxiHh_KJbCdeAqw", string(data_json_str))
+
+						}
+
+					}
+				}
+			} else { // 间隔时间内 超过发送条数
+				int_x, _ := strconv.Atoi(T_nm_list[N_index+3])
+				Warning.Add_Warning_Log(Warning_r, "!微信-> 间隔时间内 超过发送条数,当期值:"+strconv.Itoa(int_x)+"\n")
+			}
+		} else { // 未达到 持续时间 秒
+			int_x, _ := strconv.Atoi(T_nm_list[N_index+1])
+			Warning.Add_Warning_Log(Warning_r, "!微信-> 未达到持续时间:"+strconv.Itoa(int_x)+"\n")
+		}
+	}
+
+	// 短信
+	N_index = 6
+	if T_nm_list[N_index] == "1" { // 是否开启
+		Warning_num, Warning_sUt := Warning.Redis_Warning_Num(Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_id) + "|" + strconv.Itoa(Warning_r.T_tp) + "|dx")
+		Warning.Add_Warning_Log(Warning_r, fmt.Sprintf("[短信]持续时间:%d秒, 间隔时间:%s秒, 内发送次数:%d次, 间隔还剩时间:%d秒\n", cx_time, T_nm_list[N_index+2], Warning_num, Warning_sUt))
+
+		if int_x, _ := strconv.Atoi(T_nm_list[N_index+1]); int64(int_x) <= cx_time { // 是否满足 持续时间 秒
+			if int_u, _ := strconv.Atoi(T_nm_list[N_index+3]); (int64(int_u) > Warning_num) || int_u == 0 { //是否 间隔时间内 超过发送条数
+				Warning.Redis_Warning_Num_W(Warning_r.T_sn+"|"+strconv.Itoa(Warning_r.T_id)+"|"+strconv.Itoa(Warning_r.T_tp)+"|dx", T_nm_list[N_index+2])
+				logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_message:", CompanyNotice_r.T_Notice_message)
+				arr_c := strings.Split(CompanyNotice_r.T_Notice_message, "|")
+				for _, v_c := range arr_c {
+					if len(v_c) == 0 {
+						continue
+					}
+					v_c_l := strings.Split(v_c, "/")
+					if len(v_c_l) != 2 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l) != 2")
+						continue
+					}
+					if len(v_c_l[0]) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l[0]) == 0")
+						continue
+					}
+
+					admin_r, err := Account.Read_Admin_ByUuid(v_c_l[0])
+					if err != nil {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " Read_Admin_ByUuid!!")
+						continue
+					}
+					if len(admin_r.T_phone) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " admin_r.T_wx!!")
+						continue
+					}
+					v_c_l[0] = admin_r.T_phone
+
+					if len("18777951277") != len(v_c_l[0]) {
+						Warning.Add_Warning_Log(Warning_r, "----> "+v_c_l[0]+" 号码异常! \n")
+						continue
+					}
+					//电话
+					Warning.Add_Warning_Log(Warning_r, "----> "+v_c_l[0])
+					// 查看余额是否有钱
+					if Company_Charging_r.T_money <= 0 {
+						Warning.Add_Warning_Log(Warning_r, "["+Company_Charging_r.T_name+"]余额不足,请及时联系管理员充值!!!\n")
+						continue
+					}
+					// 发送模板
+					switch Warning_r.T_tp {
+					case 1, 2, 3, 7, 8, 9: //温度
+						reqdata_Message := "{\"name1\":\"" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\",\"name2\":\"" + Warning_r.T_Ut.Format("2006-01-02 15:04:05") + "\",\"name3\":\"" + Warning_r.T_DS_name + "\",\"name4\":\"" + Warning_r.T_Remark + "\",\"T_T\":\"" + strconv.FormatFloat(float64(T), 'f', -1, 32) + "\"}"
+						NatsServer.Sb_Message(Company_Charging_r, Warning_r, v_c_l[0], "dks8a", reqdata_Message)
+						break
+					case 4, 5, 6, 10, 11, 12: //湿度
+						reqdata_Message := "{\"name1\":\"" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\",\"name2\":\"" + Warning_r.T_Ut.Format("2006-01-02 15:04:05") + "\",\"name3\":\"" + Warning_r.T_DS_name + "\",\"name4\":\"" + Warning_r.T_Remark + "\",\"T_RH\":\"" + strconv.FormatFloat(float64(HR), 'f', -1, 32) + "\"}"
+						NatsServer.Sb_Message(Company_Charging_r, Warning_r, v_c_l[0], "JDHNV4", reqdata_Message)
+						break
+					default: // 通知
+						reqdata_Message := "{\"name1\":\"" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\",\"name2\":\"" + Warning_r.T_Ut.Format("2006-01-02 15:04:05") + "\",\"name3\":\"" + Warning_r.T_DS_name + "\",\"name4\":\"" + Warning_r.T_Remark + "\"}"
+						NatsServer.Sb_Message(Company_Charging_r, Warning_r, v_c_l[0], "HeWYR3", reqdata_Message)
+					}
+				}
+
+			} else { // 间隔时间内 超过发送条数
+				int_x, _ := strconv.Atoi(T_nm_list[N_index+3])
+				Warning.Add_Warning_Log(Warning_r, "--!短信-> 间隔时间内 超过发送条数,当期值:"+strconv.Itoa(int_x)+"\n")
+			}
+		} else { // 未达到 持续时间 秒
+			int_x, _ := strconv.Atoi(T_nm_list[N_index+1])
+			Warning.Add_Warning_Log(Warning_r, "--!短信-> 未达到持续时间:"+strconv.Itoa(int_x)+"\n")
+		}
+	}
+
+	// 电话
+	N_index = 10
+	if T_nm_list[N_index] == "1" { // 是否开启
+		Warning_num, Warning_sUt := Warning.Redis_Warning_Num(Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_id) + "|" + strconv.Itoa(Warning_r.T_tp) + "|dh")
+		Warning.Add_Warning_Log(Warning_r, fmt.Sprintf("[电话]持续时间:%d秒, 间隔时间:%s秒, 内发送次数:%d次, 间隔还剩时间:%d秒\n", cx_time, T_nm_list[N_index+2], Warning_num, Warning_sUt))
+
+		if int_x, _ := strconv.Atoi(T_nm_list[N_index+1]); int64(int_x) <= cx_time { // 是否满足 持续时间 秒
+			if int_u, _ := strconv.Atoi(T_nm_list[N_index+3]); (int64(int_u) > Warning_num) || int_u == 0 { //是否 间隔时间内 超过发送条数
+				Warning.Redis_Warning_Num_W(Warning_r.T_sn+"|"+strconv.Itoa(Warning_r.T_id)+"|"+strconv.Itoa(Warning_r.T_tp)+"|dh", T_nm_list[N_index+2])
+				logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_phone:", CompanyNotice_r.T_Notice_phone)
+				arr_c := strings.Split(CompanyNotice_r.T_Notice_phone, "|")
+				for _, v_c := range arr_c {
+					if len(v_c) == 0 {
+						continue
+					}
+					v_c_l := strings.Split(v_c, "/")
+					if len(v_c_l) != 2 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l) != 2")
+						continue
+					}
+					if len(v_c_l[0]) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " len(v_c_l[0]) == 0")
+						continue
+					}
+
+					admin_r, err := Account.Read_Admin_ByUuid(v_c_l[0])
+					if err != nil {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " Read_Admin_ByUuid!!")
+						continue
+					}
+					if len(admin_r.T_phone) == 0 {
+						logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_wx:", v_c, " admin_r.T_wx!!")
+						continue
+					}
+					v_c_l[0] = admin_r.T_phone
+
+					if len("18777951277") != len(v_c_l[0]) {
+						Warning.Add_Warning_Log(Warning_r, "----> "+v_c_l[0]+" 号码异常! \n")
+						continue
+					}
+					//电话
+					Warning.Add_Warning_Log(Warning_r, "---->"+v_c_l[0])
+					// 查看余额是否有钱
+					if Company_Charging_r.T_money <= 0 {
+						Warning.Add_Warning_Log(Warning_r, "["+Company_Charging_r.T_name+"] 余额不足,请及时联系管理员充值!!!\n")
+						continue
+					}
+					// 语音实体
+					Hw_VoiceNotice_is := NatsServer.Hw_VoiceNotice(Company_Charging_r, Warning_r, v_c_l[0], []byte(fmt.Sprintf(`{"displayNbr":"+862022509874","calleeNbr":"%s","playInfoList":[{"templateId":"03bf51a5d8094db8ab1001aaa426ac60","templateParas":["%s","%s","%s"]}],"userData":"testUserData"}`, v_c_l[0], lib.Limit_len(Company_r.T_name, 29), lib.Limit_len(Warning_r.T_DS_name, 29), Warning.Read_WarningType_Get(Warning_r.T_tp))))
+					if Hw_VoiceNotice_is {
+						type V_struct struct {
+							Corporate string `json:"company"`
+							Device    string `json:"device"`
+							Name      string `json:"name"`
+						}
+						Warning.Add_Warning_Log(Warning_r, "--!电话-> 华为云 通知失败,切换为 赛邮云"+"\n")
+						V_struct_r := V_struct{Corporate: Company_r.T_name, Device: Warning_r.T_DS_name, Name: Warning.Read_WarningType_Get(Warning_r.T_tp)}
+						V_struct_str, _ := json.Marshal(V_struct_r)
+						NatsServer.Submail_Voice(Company_Charging_r, Warning_r, v_c_l[0], "i2ixE1", string(V_struct_str))
+					}
+
+				}
+
+			} else { // 间隔时间内 超过发送条数
+				int_x, _ := strconv.Atoi(T_nm_list[N_index+3])
+				Warning.Add_Warning_Log(Warning_r, "--!电话-> 间隔时间内 超过发送条数,当期值:"+strconv.Itoa(int_x)+"\n")
+			}
+		} else { // 未达到 持续时间 秒
+			int_x, _ := strconv.Atoi(T_nm_list[N_index+1])
+			Warning.Add_Warning_Log(Warning_r, "--!电话-> 未达到持续时间:"+strconv.Itoa(int_x)+"\n")
+		}
+	}
+
+	// APP
+	N_index = 14
+	if len(T_nm_list) > 14 { // 兼容后加的APP ,防止数组 溢出
+		if T_nm_list[N_index] == "1" { // 是否开启
+			Warning_num, Warning_sUt := Warning.Redis_Warning_Num(Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_id) + "|" + strconv.Itoa(Warning_r.T_tp) + "|app")
+			Warning.Add_Warning_Log(Warning_r, fmt.Sprintf("[APP]持续时间:%d秒, 间隔时间:%s秒, 内发送次数:%d次, 间隔还剩时间:%d秒\n", cx_time, T_nm_list[N_index+2], Warning_num, Warning_sUt))
+
+			if int_x, _ := strconv.Atoi(T_nm_list[N_index+1]); int64(int_x) <= cx_time { // 是否满足 持续时间 秒
+				if int_u, _ := strconv.Atoi(T_nm_list[N_index+3]); (int64(int_u) > Warning_num) || int_u == 0 { //是否 间隔时间内 超过发送条数
+					Warning.Redis_Warning_Num_W(Warning_r.T_sn+"|"+strconv.Itoa(Warning_r.T_id)+"|"+strconv.Itoa(Warning_r.T_tp)+"|app", T_nm_list[N_index+2])
+					logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_APP:", CompanyNotice_r.T_Notice_app)
+					arr_c := strings.Split(CompanyNotice_r.T_Notice_app, "|")
+					for _, v_c := range arr_c {
+						if len(v_c) == 0 {
+							continue
+						}
+						v_c_l := strings.Split(v_c, "/")
+						if len(v_c_l) != 2 {
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_app:", v_c, " len(v_c_l) != 2")
+							continue
+						}
+						if len(v_c_l[0]) == 0 {
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_app:", v_c, " len(v_c_l[0]) == 0")
+							continue
+						}
+
+						admin_r, err := Account.Read_Admin_ByUuid(v_c_l[0])
+						if err != nil {
+							logs.Println("==> WarningCompanyNotice_CompanyNotice_Send   推送 T_Notice_app:", v_c, " Read_Admin_ByUuid!!")
+							continue
+						}
+
+						//APP
+						Warning.Add_Warning_Log(Warning_r, "---->"+admin_r.T_uuid+"/"+admin_r.T_name)
+
+						reqdata_Message := "{\"WarningType\":\"" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\",\"T_Ut\":\"" + Warning_r.T_Ut.Format("2006-01-02 15:04:05") + "\",\"T_DS_name\":\"" + Warning_r.T_DS_name + "\",\"T_Remark\":\"" + Warning_r.T_Remark + "\",\"T_T\":\"" + strconv.FormatFloat(float64(T), 'f', -1, 32) + "\"}"
+
+						NatsServer.App_Warn(Company_Charging_r, Warning_r, admin_r.T_uuid, reqdata_Message)
+
+					}
+
+				} else { // 间隔时间内 超过发送条数
+					int_x, _ := strconv.Atoi(T_nm_list[N_index+3])
+					Warning.Add_Warning_Log(Warning_r, "--!APP-> 间隔时间内 超过发送条数,当期值:"+strconv.Itoa(int_x)+"\n")
+				}
+			} else { // 未达到 持续时间 秒
+				int_x, _ := strconv.Atoi(T_nm_list[N_index+1])
+				Warning.Add_Warning_Log(Warning_r, "--!APP-> 未达到持续时间:"+strconv.Itoa(int_x)+"\n")
+			}
+		}
+
+	}
+
+}
+
+// 报警发送给用户表
+func WarningToAdminId(Warning_r *Warning.Warning, T_Admin_list []int) {
+	logs.Println("==>WarningToAdmin  ", "Msid", Warning_r.T_Msid, "  T_Admin_list:", T_Admin_list)
+
+	// 查看 sn 是否有归属
+	if Warning_r.T_pid == 0 { //  寻找报警归属
+		if len(Warning_r.T_sn) > 10 {
+			r_Device, err := Device.Read_Device_ByT_sn(Warning_r.T_sn)
+			if err != nil {
+				logs.Println("==> WarningToAdminId   MessageDisconnected 没有该设备:", Warning_r.T_sn)
+			}
+			Warning_r.T_pid = r_Device.T_pid
+			Warning_r.T_D_name = r_Device.T_devName
+
+		}
+	}
+	// 补齐公司归属
+	var Company_r Account.Company
+	if Warning_r.T_pid != 0 {
+		_, Company_r = Account.Read_Company_id(Warning_r.T_pid)
+	}
+	// 如果 还是为空
+	if len(Company_r.T_name) == 0 {
+		Company_r.T_name = "未知公司"
+	}
+
+	// 补齐传感器名称
+	if len(Warning_r.T_DS_name) == 0 {
+		DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(Warning_r.T_sn, Warning_r.T_id)
+		if is {
+			Warning_r.T_DS_name = DeviceSensor_r.T_name
+		}
+	}
+	// 如果 还是为空 就等于 SN
+	if len(Warning_r.T_DS_name) == 0 {
+		Warning_r.T_DS_name = Warning_r.T_sn
+	}
+
+	// 选择相关 管理员
+	if len(T_Admin_list) == 0 {
+		if Warning_r.T_pid > 0 {
+			// 有绑定公司的 管理员
+			Warning.Add_Warning_Log(Warning_r, "有绑定公司ID -> ["+strconv.Itoa(Warning_r.T_pid)+"]\n")
+			Tpids := Account.Read_Admin_Tpids_List(Warning_r.T_pid)
+			for _, v := range Tpids {
+				T_Admin_list = append(T_Admin_list, v.Id)
+			}
+		} else {
+			// 默认发给管理员
+			Warning.Add_Warning_Log(Warning_r, "默认发给管理员 -> [conf.EWarningAdmin]\n")
+			T_Admin_list = conf.EWarningAdmin
+		}
+	}
+
+	if len(T_Admin_list) == 0 {
+		Warning.Add_Warning_Log(Warning_r, "!!!没有要通知的管理员!!!\n")
+		return
+	}
+
+	T_Admin_list_str := ""
+	for _, T_Admin_list_r := range T_Admin_list {
+		T_Admin_list_str += strconv.Itoa(T_Admin_list_r) + "|"
+	}
+	Warning.Add_Warning_Log(Warning_r, "通知 管理员ID列表 -> ["+T_Admin_list_str+"]\n")
+	// 通知策略
+	for _, T_Admin_list_r := range T_Admin_list {
+
+		logs.Println("==> WarningToAdminId   T_Admin_id:", T_Admin_list_r)
+		Admin_r, err := Account.Read_Admin_Byid(T_Admin_list_r)
+		if err != nil {
+			logs.Println("==> WarningToAdminId   没找到 T_Admin_id:", err)
+			Warning.Add_Warning_Log(Warning_r, "没找到 T_Admin_id:-> "+strconv.Itoa(T_Admin_list_r)+"\n")
+			continue
+		}
+		Warning.Add_Warning_Log(Warning_r, "通知-> ["+strconv.Itoa(Admin_r.Id)+"|"+Admin_r.T_name+"]\n")
+		// 累积报警记录数
+		key_Warning_num := "A" + strconv.Itoa(Admin_r.Id) + "|" + Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_tp) // 用户+报警类型
+		if !Warning.Redis_WarningToAdmin_Num(key_Warning_num) {
+			Warning.Add_Warning_Log(Warning_r, "-> "+Admin_r.T_name+"["+strconv.Itoa(Admin_r.Id)+"]"+"相同报警(30分钟内),跳过该用户通知\n")
+			continue
+		}
+
+		// 微信通知
+		if len(Admin_r.T_wx) > 3 {
+
+			data_json_t := Wx_d_struct{
+				Corporate: Wx_struct{Value: lib.Limit_len(Company_r.T_name, 20)},
+				Type:      Wx_struct{Value: Warning.Read_WarningType_Get(Warning_r.T_tp)},
+				Name:      Wx_struct{Value: lib.Limit_len(Warning_r.T_DS_name, 20)},
+				Time:      Wx_struct{Value: Warning_r.T_Ut.Format("2006-01-02 15:04:05")},
+				Remark:    Wx_struct{Value: lib.Limit_len(Warning_r.T_Remark, 20)},
+			}
+			data_json_str, _ := json.Marshal(data_json_t)
+			logs.Println("==> WarningToAdminId  通知内容:", string(data_json_str))
+
+			// 用户下 多个微信
+			arr_b := strings.Split(Admin_r.T_wx, "|")
+			for _, v_b := range arr_b {
+				v_b_l := strings.Split(v_b, "/")
+				if len(v_b_l) != 2 {
+					logs.Println("==> WarningToAdminId  推送 T_Notice_wx:", v_b, " len(v_b_l) != 2")
+					continue
+				}
+				if len(v_b_l[0]) == 0 {
+					logs.Println("==> WarningToAdminId  推送 T_Notice_wx:", v_b, " len(v_b_l[0]) == 0")
+					continue
+				}
+				NatsServer.Wx_MessageTemplate(Account.Company{Id: Warning_r.T_pid}, Warning_r, v_b_l[0], "gnpERD6ilOQK6zvp1W_PmY5SEDOcSxiHh_KJbCdeAqw", string(data_json_str))
+
+			}
+
+			//Warning.Add_Warning_Log(Warning_r, "微信-> "+Admin_r.T_name+"\n")
+		}
+
+		// 短信通知
+		//if len(Admin_r.T_phone) > 3 {
+		//	reqdata_Message := ""
+		//	NatsServer.Ali_Message(Warning_r, Admin_r.T_phone, "SMS_232170337", reqdata_Message)
+		//	Warning.Add_Warning_Log(Warning_r, "微信-> "+Admin_r.T_name+"\n")
+		//}
+
+	}
+
+	defer func() {
+		if Warning_r.Id > 0 {
+			Warning.Update_Warning(*Warning_r, "T_Log")
+		}
+	}()
+
+}
+
+// 报警发送给用户表
+func WarningToUser(Warning_r *Warning.Warning, T_pid int) {
+	logs.Println("==>WarningToUserId  ", "Msid", Warning_r.T_Msid, "  T_pid:", T_pid)
+
+	Warning_r.T_pid = T_pid
+	var T_Admin_list []int
+
+	Tpid := Account.Read_Admin_Tpid_List(Warning_r.T_pid)
+	for _, v := range Tpid {
+		T_Admin_list = append(T_Admin_list, v.Id)
+	}
+
+	if len(T_Admin_list) == 0 {
+		Warning.Add_Warning_Log(Warning_r, "!!!没有要通知的用户!!!\n")
+		return
+	}
+
+	T_Admin_list_str := ""
+	for _, T_Admin_list_r := range T_Admin_list {
+		T_Admin_list_str += strconv.Itoa(T_Admin_list_r) + "|"
+	}
+	Warning.Add_Warning_Log(Warning_r, "通知 用户ID列表 -> ["+T_Admin_list_str+"]\n")
+	// 通知策略
+	for _, T_Admin_list_r := range T_Admin_list {
+
+		logs.Println("==> WarningToAdminId  T_Admin_id:", T_Admin_list_r)
+		Admin_r, err := Account.Read_Admin_Byid(T_Admin_list_r)
+		if err != nil {
+			logs.Println("==> WarningToAdminId  没找到 T_Admin_id:", err)
+			Warning.Add_Warning_Log(Warning_r, "没找到 T_Admin_id:-> "+strconv.Itoa(T_Admin_list_r)+"\n")
+			continue
+		}
+		Warning.Add_Warning_Log(Warning_r, "通知-> ["+strconv.Itoa(Admin_r.Id)+"|"+Admin_r.T_name+"]\n")
+		// 累积报警记录数
+		key_Warning_num := "A" + strconv.Itoa(Admin_r.Id) + "|" + Warning_r.T_sn + "|" + strconv.Itoa(Warning_r.T_tp) // 用户+报警类型
+		if !Warning.Redis_WarningToAdmin_Num(key_Warning_num) {
+			Warning.Add_Warning_Log(Warning_r, "-> "+Admin_r.T_name+"["+strconv.Itoa(Admin_r.Id)+"]"+"相同报警(30分钟内),跳过该用户通知\n")
+			continue
+		}
+		// 获取公司名称
+		_, Company_r := Account.Read_Company_id(Warning_r.T_pid)
+		if Company_r.Id == 0 {
+			Company_r.T_name = "冷链系统"
+		}
+		// 微信通知
+		if len(Admin_r.T_wx) > 3 {
+
+			data_json_t := Wx_d_struct{
+				Corporate: Wx_struct{Value: lib.Limit_len(Company_r.T_name, 20)},
+				Type:      Wx_struct{Value: Warning.Read_WarningType_Get(Warning_r.T_tp)},
+				Name:      Wx_struct{Value: lib.Limit_len(Warning_r.T_D_name, 20)},
+				Time:      Wx_struct{Value: Warning_r.T_Ut.Format("2006-01-02 15:04:05")},
+				Remark:    Wx_struct{Value: lib.Limit_len(Warning_r.T_Remark, 20)},
+			}
+			data_json_str, _ := json.Marshal(data_json_t)
+			logs.Println("==> WarningToAdminId  通知内容:", string(data_json_str))
+
+			// 用户下 多个微信
+			arr_b := strings.Split(Admin_r.T_wx, "|")
+			for _, v_b := range arr_b {
+				v_b_l := strings.Split(v_b, "/")
+				if len(v_b_l) != 2 {
+					logs.Println("==> WarningToAdminId  推送 T_Notice_wx:", v_b, " len(v_b_l) != 2")
+					continue
+				}
+				if len(v_b_l[0]) == 0 {
+					logs.Println("==> WarningToAdminId   推送 T_Notice_wx:", v_b, " len(v_b_l[0]) == 0")
+					continue
+				}
+				NatsServer.Wx_MessageTemplate(Account.Company{Id: Warning_r.T_pid}, Warning_r, v_b_l[0], "gnpERD6ilOQK6zvp1W_PmY5SEDOcSxiHh_KJbCdeAqw", string(data_json_str))
+
+			}
+
+			//Warning.Add_Warning_Log(Warning_r, "微信-> "+Admin_r.T_name+"\n")
+		}
+
+		// 短信通知
+		//if len(Admin_r.T_phone) > 3 {
+		//	reqdata_Message := ""
+		//	NatsServer.Ali_Message(Warning_r, Admin_r.T_phone, "SMS_232170337", reqdata_Message)
+		//	Warning.Add_Warning_Log(Warning_r, "微信-> "+Admin_r.T_name+"\n")
+		//}
+
+	}
+
+	defer func() {
+		if Warning_r.Id > 0 {
+			Warning.Update_Warning(*Warning_r, "T_Log")
+		}
+	}()
+
+}

+ 36 - 0
MqttServer/WarningNotice/WarningNoticeTruct.go

@@ -0,0 +1,36 @@
+package WarningNotice
+
+//设备温度异常通知
+
+type Wx_struct struct {
+	Value string `json:"value"`
+}
+
+// 微信_设备温度异常通知
+type Wx_t_struct struct {
+	Corporate Wx_struct `json:"thing17"`
+	Type      Wx_struct `json:"thing7"`
+	Name      Wx_struct `json:"thing2"`
+	Time      Wx_struct `json:"time8"`
+	//T         Wx_struct `json:"character_string3"`
+	Remark Wx_struct `json:"thing13"`
+}
+
+// 微信_设备湿度异常通知
+type Wx_h_struct struct {
+	Corporate Wx_struct `json:"thing15"`
+	Type      Wx_struct `json:"thing4"`
+	Name      Wx_struct `json:"thing2"`
+	Time      Wx_struct `json:"time5"`
+	//H         Wx_struct `json:"short_thing7"`
+	Remark Wx_struct `json:"thing13"`
+}
+
+// 微信_主机报警通知
+type Wx_d_struct struct {
+	Corporate Wx_struct `json:"thing34"`
+	Type      Wx_struct `json:"thing38"`
+	Name      Wx_struct `json:"thing2"`
+	Time      Wx_struct `json:"time4"`
+	Remark    Wx_struct `json:"thing5"`
+}

+ 211 - 0
Nats/Nats.go

@@ -0,0 +1,211 @@
+package Nats
+
+import (
+	"Cold_mqtt/MqttServer"
+	"Cold_mqtt/MqttServer/WarningNotice"
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Product"
+	"Cold_mqtt/models/Warning"
+	"github.com/nats-io/nats.go"
+	"github.com/vmihailenco/msgpack/v5"
+	"time"
+)
+
+func NatsInit() {
+	time.Sleep(time.Second * 3)
+	var err error
+	// 连接Nats服务器
+	lib.Nats, err = nats.Connect("nats://"+conf.NatsServer_Url, nats.MaxReconnects(10), nats.ReconnectWait(10*time.Second),
+		nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
+			// handle disconnect error event
+			logs.PrintlnError("nats.DisconnectErrHandler 断开 ", err)
+		}),
+		nats.ReconnectHandler(func(nc *nats.Conn) {
+			// handle reconnect event
+			logs.PrintlnError("nats.ReconnectHandler, 已经重新连接 ")
+		}))
+	if err != nil {
+		logs.Println("nats 连接失败!")
+		panic(any("nats 连接失败!" + "nats://" + conf.NatsServer_Url))
+	}
+
+	if err != nil {
+		logs.Println("nats 连接失败!")
+		panic(any(err))
+	}
+	logs.Println("nats OK!")
+
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	//msg, err := lib.Nats.Request("Wx_GenerateQR", []byte("T_uuid"), 3*time.Second)
+	//if err != nil {
+	//	logs.PrintlnError("Wx_GenerateQR :",err)
+	//} else {
+	//	logs.Println("Wx_GenerateQR : %s\n", string(msg.Data))
+	//
+	//}
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Read_DeviceParameter"+conf.MqttServer_id, func(m *nats.Msg) {
+		sn := string(m.Data)
+		logs.Println("Nats Read_DeviceParameter: %s\n", sn)
+
+		MqttServer.Read_DeviceParameter(sn)
+
+	})
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Read_DeviceSensorParameter"+conf.MqttServer_id, func(m *nats.Msg) {
+		sn := string(m.Data)
+		logs.Println("Nats Read_DeviceSensorParameter: %s\n", sn)
+
+		MqttServer.Read_DeviceSensorParameter(sn)
+
+	})
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Get_Device_Realtime"+conf.MqttServer_id, func(m *nats.Msg) {
+		sn := string(m.Data)
+		logs.Println("Nats Get_Device_Realtime: 【%s】 ", sn)
+		if len(sn) <= 6 {
+			return
+		}
+		go MqttServer.Get_Device_Realtime(sn)
+
+	})
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Pu_DeviceParameter"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats Pu_DeviceParameter: %s\n", string(m.Data))
+
+		var item Device.DeviceParameter
+		err = msgpack.Unmarshal(m.Data, &item)
+		if err != nil {
+			logs.Println("Pu_DeviceParameter", err)
+			return
+		}
+		MqttServer.Pu_DeviceParameter(item)
+
+	})
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Pu_DeviceParameter_Sensor"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats Pu_DeviceParameter_Sensor: %s\n", string(m.Data))
+
+		var item Device.DeviceSensorParameter
+		err = msgpack.Unmarshal(m.Data, &item)
+		if err != nil {
+			logs.Println("Pu_DeviceParameter_Sensor", err)
+			return
+		}
+		MqttServer.Pu_DeviceParameter_Sensor(item)
+
+	})
+
+	// 发布-订阅 模式,异步订阅 test1
+	_, _ = lib.Nats.Subscribe("Set_DeviceTask"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats Set_DeviceTask: %s\n", string(m.Data))
+
+		var item Device.Device_task
+		err = msgpack.Unmarshal(m.Data, &item)
+		if err != nil {
+			logs.Println("Set_DeviceTask", err)
+			return
+		}
+		MqttServer.Set_DeviceTask(item)
+
+	})
+
+	// 重启/关机 命令       //1:重启,0:关机;
+	_, _ = lib.Nats.Subscribe("Set_RestartShutdown"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats Set_RestartShutdown: %s\n", string(m.Data))
+
+		var item Device.Device_task
+		err = msgpack.Unmarshal(m.Data, &item)
+		if err != nil {
+			logs.Println("Set_RestartShutdown", err)
+			return
+		}
+		MqttServer.Set_RestartShutdown(item)
+
+	})
+
+	// 版本升级
+	_, _ = lib.Nats.Subscribe("Up_ProductUpgrade"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats Up_ProductUpgrade: %s\n", string(m.Data))
+
+		var item Product.ProductUpgrade_T
+		err = msgpack.Unmarshal(m.Data, &item)
+		if err != nil {
+			logs.Println("Up_ProductUpgrade", err)
+			return
+		}
+
+		MqttServer.Up_ProductUpgrade(item)
+
+	})
+
+	//===========================================================================
+	// 请求-响应, 响应 test3 消息。
+	_, _ = lib.Nats.Subscribe("AddWarning"+conf.MqttServer_id, func(m *nats.Msg) {
+		logs.Println("Nats AddWarning: %s\n", string(m.Data))
+
+		type T_Warning struct {
+			T_tp      int       `xml:"T_tp"`      // 报警类型   ->WarningList
+			T_sn      string    `xml:"T_sn"`      // 设备序列号
+			T_D_name  string    `xml:"T_D_name"`  // 设备名称
+			T_id      int       `xml:"T_id"`      // 传感器 ID
+			T_DS_name string    `xml:"T_DS_name"` // 传感器名称
+			T_Remark  string    `xml:"T_Remark"`  // 采集内容
+			T_Ut      time.Time `xml:"T_Ut"`      // 采集时间
+			T_ToAdmin []int     `xml:"T_ToAdmin"` // 发送给谁 Admin.Id
+			T_State   int       `xml:"T_State"`   // 0 删除   1 不处理   2 已处理   3 未处理
+		}
+
+		type T_R struct {
+			Code int16  `xml:"Code"`
+			Msg  string `xml:"Msg"`
+		}
+
+		var t_R T_R
+
+		var T_Warning_r T_Warning
+		err = msgpack.Unmarshal(m.Data, &T_Warning_r)
+		if err != nil {
+			println("AddWarning:", err)
+			return
+		}
+
+		var Warning_r Warning.Warning
+		Warning_r.T_pid = 0
+		Warning_r.T_tp = T_Warning_r.T_tp
+		Warning_r.T_sn = T_Warning_r.T_sn
+		Warning_r.T_D_name = T_Warning_r.T_D_name
+		Warning_r.T_id = T_Warning_r.T_id
+		Warning_r.T_DS_name = T_Warning_r.T_DS_name
+		Warning_r.T_Remark = T_Warning_r.T_Remark
+		Warning_r.T_Ut = T_Warning_r.T_Ut
+		Warning_r.T_State = T_Warning_r.T_State
+
+		if Warning_r.T_tp == 1012 {
+			Warning_r.T_State = 3
+		}
+		WarningNotice.WarningToAdminId(&Warning_r, T_Warning_r.T_ToAdmin)
+
+		t_R.Code = 200
+		t_R.Msg = "ok"
+
+		// 添加报警
+		_, err = Warning.Add_Warning(Warning_r)
+		if err != nil {
+			t_R.Code = 202
+			t_R.Msg = "Err"
+		}
+
+		b, _ := msgpack.Marshal(&t_R)
+		_ = lib.Nats.Publish(m.Reply, b)
+	})
+
+}

+ 36 - 0
Nats/NatsServer/AddWarning.go

@@ -0,0 +1,36 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"github.com/vmihailenco/msgpack/v5"
+	"time"
+)
+
+type T_Warning struct {
+	T_tp      int       `xml:"T_tp"`      // 报警类型   ->WarningList
+	T_sn      string    `xml:"T_sn"`      // 设备序列号
+	T_D_name  string    `xml:"T_D_name"`  // 设备名称
+	T_id      int       `xml:"T_id"`      // 传感器 ID
+	T_DS_name string    `xml:"T_DS_name"` // 传感器名称
+	T_Remark  string    `xml:"T_Remark"`  // 采集内容
+	T_Ut      time.Time `xml:"T_Ut"`      // 采集时间
+	T_ToAdmin []int     `xml:"T_ToAdmin"` // 发送给谁 Admin.Id
+}
+
+func AddWarning(r T_Warning) bool {
+	b, err := msgpack.Marshal(&r)
+	if err != nil {
+		logs.PrintlnError("AddWarning", err)
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("AddWarning", b, 3*time.Second)
+	if err != nil {
+		logs.PrintlnError("AddWarning", err)
+	} else {
+		logs.Println("AddWarning : %s\n", string(msg.Data))
+		return true
+	}
+
+	return false
+}

+ 80 - 0
Nats/NatsServer/Hw_VoiceNotice.go

@@ -0,0 +1,80 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 发送 DEMO
+//NatsServer.Hw_VoiceNotice([]byte(fmt.Sprintf(`{"displayNbr":"+8675536362854","calleeNbr":"%s","playInfoList":[{"templateId":"f70c83b230dd4794860a383c38abdefa","templateParas":["%s"]}],"userData":"testUserData"}`,"18777951277","超过上线报警") ) )
+
+// 华为云 电话通知报警
+func Hw_VoiceNotice(Company_Charging_r Account.Company, Warning_r *Warning.Warning, PhoneNumbers string, b []byte) bool {
+	logs.Println(" => Nats", lib.FuncName(), PhoneNumbers, string(b))
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "华为语音服务通讯"
+		Warning_r_e.T_Remark = "语音服务通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("电话通知", "华为语音服务通讯 异常!")
+		// 达到数量上线跳出
+		return false
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("Hw_VoiceNotice", b, 3*time.Second)
+	if err != nil {
+		logs.PrintlnError("Hw_VoiceNotice %s", err.Error())
+		Warning.Add_Warning_Log(Warning_r, "---->语音服务通讯 异常!Nats Err!尝试发送 "+strconv.Itoa(cun_x)+" 次\n")
+		Data = "语音服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("Hw_VoiceNotice => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		Warning.Add_Warning_Log(Warning_r, "---->语音信服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次\n")
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+"\n")
+
+	// ============== 计费 ==============
+
+	Warning.Add_WarningSand(Warning.WarningSand{
+		T_pid:    Company_Charging_r.Id,
+		T_Spid:   Warning_r.T_pid,
+		T_tp:     Warning_r.T_tp,
+		T_Notice: PhoneNumbers,
+		T_Ntype:  2,
+		T_Remark: "WID:" + strconv.FormatInt(Warning_r.Id, 10) + "\n" +
+			"类型:" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\n" +
+			"SN:" + Warning_r.T_sn + "\n" +
+			"主机:" + Warning_r.T_D_name + "\n" +
+			"设备:" + Warning_r.T_DS_name + "\n" +
+			"备注:" + Warning_r.T_Remark + "\n" +
+			"通知方式:电话通知\n" +
+			"通知对象:" + PhoneNumbers,
+	})
+
+	return true
+
+}

+ 91 - 0
Nats/NatsServer/NatsAliM.go

@@ -0,0 +1,91 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"github.com/vmihailenco/msgpack/v5"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func Ali_Message(Company_Charging_r Account.Company, Warning_r *Warning.Warning, PhoneNumbers string, TemplateCode string, TemplateParam string) {
+	logs.Println("Nats =>", lib.FuncName(), PhoneNumbers, TemplateCode, TemplateParam)
+
+	type Ali_Message struct {
+		PhoneNumbers  string
+		TemplateCode  string
+		TemplateParam string
+	}
+
+	b, err := msgpack.Marshal(&Ali_Message{
+		PhoneNumbers:  PhoneNumbers,
+		TemplateCode:  TemplateCode,
+		TemplateParam: TemplateParam,
+	})
+	if err != nil {
+		logs.Println("Ali_Message", err)
+		return
+	}
+
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "短信服务通讯"
+		Warning_r_e.T_Remark = "短信服务通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("短信通知", "短信服务通讯 异常!")
+		// 达到数量上线跳出
+		return
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("Ali_Message", b, 3*time.Second)
+	if err != nil {
+		logs.PrintlnError("Ali_Message %s", err.Error())
+		Warning.Add_Warning_Log(Warning_r, "---->短信服务通讯 异常!Nats Err!尝试发送 "+strconv.Itoa(cun_x)+" 次\n")
+		Data = "短信服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("Ali_Message => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		Warning.Add_Warning_Log(Warning_r, "---->短信服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次\n")
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+"\n")
+
+	// ============== 计费 ==============
+	Warning.Add_WarningSand(Warning.WarningSand{
+		T_pid:    Company_Charging_r.Id,
+		T_Spid:   Warning_r.T_pid,
+		T_tp:     Warning_r.T_tp,
+		T_Notice: PhoneNumbers,
+		T_Ntype:  1,
+		T_Remark: "WID:" + strconv.FormatInt(Warning_r.Id, 10) + "\n" +
+			"类型:" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\n" +
+			"SN:" + Warning_r.T_sn + "\n" +
+			"主机:" + Warning_r.T_D_name + "\n" +
+			"设备:" + Warning_r.T_DS_name + "\n" +
+			"备注:" + Warning_r.T_Remark + "\n" +
+			"通知方式:短信通知\n" +
+			"通知对象:" + PhoneNumbers,
+	})
+
+}

+ 72 - 0
Nats/NatsServer/NatsAppWarn.go

@@ -0,0 +1,72 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"github.com/vmihailenco/msgpack/v5"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func App_Warn(Company_Charging_r Account.Company, Warning_r *Warning.Warning, Uuid string, Dataj string) {
+	logs.Println("Nats =>", lib.FuncName(), Uuid, Dataj)
+
+	type Ali_Message struct {
+		Uuid string
+		Data string
+	}
+
+	b, err := msgpack.Marshal(&Ali_Message{
+		Uuid: Uuid,
+		Data: Dataj,
+	})
+	if err != nil {
+		logs.Println("App_Warn", err)
+		return
+	}
+
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "App_Warn通讯"
+		Warning_r_e.T_Remark = "App_Warn通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("App通知", "App_Warn通讯 异常!")
+		// 达到数量上线跳出
+		return
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("App_Warn", b, 60*time.Second)
+	if err != nil {
+		logs.PrintlnError("App_Warn %s", err.Error())
+		Warning.Add_Warning_Log(Warning_r, "---->App_Warn服务发送超时 异常!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		Data = "App_Warn服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("App_Warn => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		Warning.Add_Warning_Log(Warning_r, "---->App_Warn服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+"\n")
+
+}

+ 14 - 0
Nats/NatsServer/NatsMqtt.go

@@ -0,0 +1,14 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+)
+
+func Mqtt_DeviceReal(T_sn string, msg []byte) {
+	logs.Println("Nats =>", lib.FuncName(), T_sn+"|+|"+string(msg))
+
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	lib.Nats.Publish("Mqtt_DeviceReal", []byte(T_sn+"|+|"+string(msg)))
+
+}

+ 91 - 0
Nats/NatsServer/NatsSbM.go

@@ -0,0 +1,91 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"github.com/vmihailenco/msgpack/v5"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func Sb_Message(Company_Charging_r Account.Company, Warning_r *Warning.Warning, PhoneNumbers string, TemplateCode string, TemplateParam string) {
+	logs.Println("Nats =>", lib.FuncName(), PhoneNumbers, TemplateCode, TemplateParam)
+
+	type Ali_Message struct {
+		PhoneNumbers  string
+		TemplateCode  string
+		TemplateParam string
+	}
+
+	b, err := msgpack.Marshal(&Ali_Message{
+		PhoneNumbers:  PhoneNumbers,
+		TemplateCode:  TemplateCode,
+		TemplateParam: TemplateParam,
+	})
+	if err != nil {
+		logs.Println("Submail_Message", err)
+		return
+	}
+
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "短信服务通讯"
+		Warning_r_e.T_Remark = "短信服务通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("短信通知", "短信服务通讯 异常!")
+		// 达到数量上线跳出
+		return
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("Submail_Message", b, 30*time.Second)
+	if err != nil {
+		logs.PrintlnError("Submail_Message %s", err.Error())
+		Warning.Add_Warning_Log(Warning_r, "---->短信服务发送超时 异常!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		Data = "短信服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("Submail_Message => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		Warning.Add_Warning_Log(Warning_r, "---->短信服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+" "+time.Now().Format("15:04:05")+"\n")
+
+	// ============== 计费 ==============
+	Warning.Add_WarningSand(Warning.WarningSand{
+		T_pid:    Company_Charging_r.Id,
+		T_Spid:   Warning_r.T_pid,
+		T_tp:     Warning_r.T_tp,
+		T_Notice: PhoneNumbers,
+		T_Ntype:  1,
+		T_Remark: "WID:" + strconv.FormatInt(Warning_r.Id, 10) + "\n" +
+			"类型:" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\n" +
+			"SN:" + Warning_r.T_sn + "\n" +
+			"主机:" + Warning_r.T_D_name + "\n" +
+			"设备:" + Warning_r.T_DS_name + "\n" +
+			"备注:" + Warning_r.T_Remark + "\n" +
+			"通知方式:短信通知\n" +
+			"通知对象:" + PhoneNumbers,
+	})
+
+}

+ 92 - 0
Nats/NatsServer/NatsSbV.go

@@ -0,0 +1,92 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"github.com/vmihailenco/msgpack/v5"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 赛邮
+func Submail_Voice(Company_Charging_r Account.Company, Warning_r *Warning.Warning, PhoneNumbers string, TemplateCode string, TemplateParam string) {
+	logs.Println("Nats =>", lib.FuncName(), PhoneNumbers, TemplateCode, TemplateParam)
+
+	type Ali_Message struct {
+		PhoneNumbers  string
+		TemplateCode  string
+		TemplateParam string
+	}
+
+	b, err := msgpack.Marshal(&Ali_Message{
+		PhoneNumbers:  PhoneNumbers,
+		TemplateCode:  TemplateCode,
+		TemplateParam: TemplateParam,
+	})
+	if err != nil {
+		logs.Println("Submail_Voice", err)
+		return
+	}
+
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "赛邮语音服务通讯"
+		Warning_r_e.T_Remark = "语音服务通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("电话通知", "赛邮语音服务通讯 异常!")
+		// 达到数量上线跳出
+		return
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("Submail_Voice", b, 60*time.Second)
+	if err != nil {
+		logs.PrintlnError("Submail_Voice %s", err.Error())
+		Warning.Add_Warning_Log(Warning_r, "---->语音服务发送超时 异常!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		Data = "语音服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("Submail_Voice => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		Warning.Add_Warning_Log(Warning_r, "---->语音服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+"\n")
+
+	// ============== 计费 ==============
+	Warning.Add_WarningSand(Warning.WarningSand{
+		T_pid:    Company_Charging_r.Id,
+		T_Spid:   Warning_r.T_pid,
+		T_tp:     Warning_r.T_tp,
+		T_Notice: PhoneNumbers,
+		T_Ntype:  2,
+		T_Remark: "WID:" + strconv.FormatInt(Warning_r.Id, 10) + "\n" +
+			"类型:" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\n" +
+			"SN:" + Warning_r.T_sn + "\n" +
+			"主机:" + Warning_r.T_D_name + "\n" +
+			"设备:" + Warning_r.T_DS_name + "\n" +
+			"备注:" + Warning_r.T_Remark + "\n" +
+			"通知方式:语音通知\n" +
+			"通知对象:" + PhoneNumbers,
+	})
+
+}

+ 108 - 0
Nats/NatsServer/NatsWx.go

@@ -0,0 +1,108 @@
+package NatsServer
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Account"
+	"Cold_mqtt/models/Warning"
+	"github.com/vmihailenco/msgpack/v5"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func Wx_MessageTemplate(Company_Charging_r Account.Company, Warning_r *Warning.Warning, openid string, templateid string, reqdata string) {
+	logs.Println(" => Nats", lib.FuncName(), openid, templateid, reqdata)
+
+	type Wx_MessageTemplate struct {
+		Openid     string
+		Templateid string
+		Parameter  string
+		Reqdata    string
+		Ttime      int64 // 发布时间
+	}
+
+	b, err := msgpack.Marshal(&Wx_MessageTemplate{
+		Openid:     openid,
+		Templateid: templateid,
+		Parameter:  "index?wid=" + strconv.Itoa(Warning_r.T_id),
+		Reqdata:    reqdata,
+		Ttime:      time.Now().Unix(),
+	})
+	if err != nil {
+		logs.Println("Wx_MessageTemplate", err)
+		return
+	}
+	cun_x := 0
+	Data := ""
+breakHere:
+	cun_x++
+	// 控制发送
+	if cun_x > 3 {
+		// 添加
+		Warning_r_e := Warning.Warning{}
+		Warning_r_e.T_pid = 0
+		Warning_r_e.T_tp = 1014
+		Warning_r_e.T_D_name = "微信服务通讯"
+		Warning_r_e.T_Remark = "微信服务通讯 异常!"
+		Warning_r_e.T_Log = Data
+		Warning_r_e.T_State = 3
+		Warning_r_e.T_Ut = time.Now()
+		//WarningNotice.WarningToAdminId(&Warning_r_e, []int{1}) // 发送给 绑定公司管理员
+		// 添加报警
+		Warning.Add_Warning(Warning_r_e)
+
+		logs.PrintlnError("微信通知", "微信服务通讯 异常!", reqdata, Data)
+		// 达到数量上线跳出
+		return
+	}
+	// 请求-响应, 向 test3 发布一个 `help me` 请求数据,设置超时间3秒,如果有多个响应,只接收第一个收到的消息
+	msg, err := lib.Nats.Request("Wx_MessageTemplate", b, 30*time.Second) // 30 秒, 订阅端 超时 30秒 将丢弃消息
+	if err != nil {
+		logs.PrintlnError("Wx_MessageTemplate %s", err.Error(), reqdata)
+		Warning.Add_Warning_Log(Warning_r, "---->微信服务发送超时 异常!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+		Data = "微信服务通讯 异常!Nats Err!"
+		time.Sleep(time.Second * time.Duration(cun_x))
+		goto breakHere // 跳转到标签
+	}
+	Data = string(msg.Data)
+	logs.Println("Wx_MessageTemplate => : %s\n", Data)
+	if !strings.Contains(Data, "成功") {
+		if strings.Contains(Data, ":43004") { //未关注微
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!用户未关注公众")
+		} else if strings.Contains(Data, ":43101") {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!用户拒绝接受消息,取消了订阅注公众")
+		} else if strings.Contains(Data, ":40001") {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!token过期")
+		} else if strings.Contains(Data, ":40013") {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!用户APPID错误,请重新绑定!")
+		} else if strings.Contains(Data, ":40037") {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!模板id不正确")
+		} else if strings.Contains(Data, ":40073") {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!openid不存在")
+		} else {
+			Warning.Add_Warning_Log(Warning_r, "---->微信服务发送失败!["+Data+"]!尝试发送 "+strconv.Itoa(cun_x)+" 次 ["+time.Now().Format("15:04:05")+"]\n")
+			time.Sleep(time.Second * time.Duration(cun_x))
+			goto breakHere // 跳转到标签
+		}
+	}
+	Warning.Add_Warning_Log(Warning_r, "->"+Data+" "+time.Now().Format("15:04:05")+"\n")
+
+	// ============== 计费 ==============
+	Warning.Add_WarningSand(Warning.WarningSand{
+		T_pid:    Company_Charging_r.Id,
+		T_Spid:   Warning_r.T_pid,
+		T_tp:     Warning_r.T_tp,
+		T_Notice: openid,
+		T_Ntype:  0,
+		T_Remark: "WID:" + strconv.FormatInt(Warning_r.Id, 10) + "\n" +
+			"类型:" + Warning.Read_WarningType_Get(Warning_r.T_tp) + "\n" +
+			"SN:" + Warning_r.T_sn + "\n" +
+			"主机:" + Warning_r.T_D_name + "\n" +
+			"设备:" + Warning_r.T_DS_name + "\n" +
+			"备注:" + Warning_r.T_Remark + "\n" +
+			"通知方式:微信通知\n" +
+			"通知对象:" + openid,
+	})
+
+}

+ 56 - 0
Plugins/ContinuousPushOfOfflineDeviceWarning.go

@@ -0,0 +1,56 @@
+package Plugins
+
+import (
+	"Cold_mqtt/MqttServer/WarningNotice"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"Cold_mqtt/models/Warning"
+	"time"
+)
+
+// 设备离线报警连续推送  冰箱采集器(wifi版)	BX100W
+func ContinuousPushOfOfflineDeviceWarning() {
+ContinuousPushOfOfflineDeviceWarningTOGO:
+	DeviceList := Device.Read_获取BX100W离线设备()
+	logs.Println("======设备离线报警连续推送========")
+	for _, r_Device := range DeviceList {
+		DeviceParameter_r, is := Device.Read_DeviceParameter(r_Device.T_sn)
+		if !is {
+			logs.Println(r_Device.T_sn + " 没有设备参数,跳过")
+			continue
+		}
+
+		Warningr_new := Warning.Read_T_snT_tp(r_Device.T_sn, 1001)
+		if Warningr_new.Id == 0 {
+			logs.Println(r_Device.T_sn + " 没有触发过离线,跳过")
+			continue
+		}
+
+		duration := time.Now().Sub(Warningr_new.T_Ut)
+
+		// 将时间差表示为秒
+		seconds := int(duration.Seconds())
+
+		if DeviceParameter_r.T_outA > seconds {
+			logs.Println(r_Device.T_sn+" 未满足触发条件,跳过,已经持续", seconds, "秒,配置", DeviceParameter_r.T_outA)
+			continue
+		}
+
+		Warningr_fUt := Warning.Read_T_snT_tpT_Msid(r_Device.T_sn, 1001, 0)
+		if Warningr_fUt.Id == 0 {
+			logs.Println(r_Device.T_sn + " 没有触发过初次离线,跳过")
+			continue
+		}
+
+		Warning_r := Warning.Add_DeviceLogs(1001, r_Device, "设备离线报警,已经持续 "+lib.TimeSinceToString(Warningr_fUt.T_Ut, time.Now()))
+		Warning_r.T_fUt = Warningr_fUt.T_Ut
+		Warning_r.T_Msid = 1
+		Warning.Update_Warning(Warning_r, "T_Msid", "T_fUt")
+		WarningNotice.WarningCompanyNotice(&Warning_r, 0, 0)
+
+	}
+
+	time.Sleep(time.Second * 30)
+	goto ContinuousPushOfOfflineDeviceWarningTOGO
+}

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+
+
+#20240924
+**Warning 表 新增 字段**  
+T_t       float32   `orm:"size(256);"`            // 温度
+T_rh      float32   `orm:"size(256);"`            // 湿度
+T_site    string    `orm:"size(256);"`            // GPS
+T_sp      int       `orm:"size(11);"`             // 传感器参数id
+
+**Warning 表 设置主键**
+T_tp 、T_sn 、T_id 、T_Ut
+
+
+
+
+开机数据方案:
+方案1:通过上电时间推算第一条数据
+    获取开机时间:
+        1、MQTT上线事件(在网络异常时不准)
+        2、获取设备上电推送信息(设备推送顺序无法确定)
+    第一条数据:
+        1、缓存最近X分钟数据,排序数据时间,获取最接近开机时间一条数据(优点:相对准确,缺点:需要等待X时间后才会显示,还会出现有整点数据后 才有开机数据)
+        2、取第一包数据,排序数据时间,获取最接近开机时间一条数据(优点:立即显示,缺点:不一定是真实的开机第一条数据)
+
+方案2:根据上传的第一包中最小时间数据,查询缓存最新数据时间,判断是否大于保存周期,如果大于视为开机时间
+    优点:防止设备异常、网络异常 重启后多数据
+    缺点:设备推送顺序不确定时,在可能在两个周期时间期间插入开机数据
+
+方案选择:暂选选择第2方案,在获取开机第一条数据时 不确定因素相对可控
+
+3.1.2
+
+sdfsdf
+asdfasdfasdfasdf
+asdfasdf

+ 38 - 0
TimeTask/OrmLog.go

@@ -0,0 +1,38 @@
+package TimeTask
+
+//import (
+//	"fmt"
+//	"io/ioutil"
+//	"os"
+//	"time"
+//)
+//
+//var OrmLogOrmLog *os.File
+//// ORM 日志清理
+//func OrmLog()  {
+//	crontab := cron.New(cron.WithSeconds())
+//	ss := "1 1 1 * * *"
+//	_, err := crontab.AddFunc(ss, OrmLog_MonitorScanStatus)
+//	if err != nil {
+//		fmt.Printf("err: %v\n", err)
+//		fmt.Println("初始化成功")
+//	}
+//	crontab.Start()
+//	defer crontab.Stop()
+//	select {}
+//}
+//func OrmLog_MonitorScanStatus()  {
+//
+//	fmt.Println("------- ORM 日志清理 --------")
+//	data, err := ioutil.ReadFile(OrmLogOrmLog.Name())
+//	if err != nil {
+//		fmt.Printf("文件打开失败=%v\n", err)
+//		return
+//	}
+//	err = ioutil.WriteFile("logs/orm/logx_"+time.Now().Format("2006-01-02") +".log", data, 0666)
+//	if err != nil {
+//		fmt.Printf("文件打开失败=%v\n", err)
+//	}
+//	fmt.Println("清空:",os.Truncate(OrmLogOrmLog.Name(), 0))
+//
+//}

+ 6 - 0
TimeTask/TimeTask.go

@@ -0,0 +1,6 @@
+package TimeTask
+
+func Init() {
+	//go OrmLog()
+
+}

+ 9 - 0
Z_Build.bat

@@ -0,0 +1,9 @@
+cd %~dp0
+set GOARCH=amd64
+set GOOS=linux
+set GOPATH=C:\Users\SIKED\go
+set GO111MODULE=auto
+set GOROOT=C:\Program Files\Go
+
+
+go build -o Cold_mqtt main.go

+ 39 - 0
conf/app.conf

@@ -0,0 +1,39 @@
+appname = Cold_mqtt
+httpport = 6202
+runmode = dev
+
+
+# Nats
+NatsServer_Url = "203.34.49.130:4222"
+
+
+# Mysql
+
+MysqlServer_UrlPort = "203.34.49.130:3306"
+MysqlServer_Database = "cold"
+MysqlServer_Username = "cold"
+MysqlServer_Password = "yjwyEckZS7rE5H"
+MysqlServer_MaxIdleConnections = 100
+MysqlServer_MaxOpenConnections = 200
+
+
+
+# Redis
+Redis_address = "203.34.49.130:6379"
+Redis_password = "redis_GGRi8f"
+Redis_dbNum = "1"
+
+# Mqtt
+# 192.168.0.7
+# Mqtt
+# 192.168.0.7
+ # mqttjxit 140.246.233.197   mqttlodr 203.195.71.200  mqttyuht 203.57.71.139
+MqttServer_id = "text_cold_mqtt"
+MqttServer_Url = "182.43.247.65:1883"
+MqttServer_ClientID = "text_cold_mqtt"
+MqttServer_Username = "admin"
+MqttServer_Password = "emqx_M4dimF"
+
+
+
+

+ 39 - 0
conf/config.go

@@ -0,0 +1,39 @@
+package conf
+
+import (
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+func init() {
+	beego.LoadAppConfig("ini", "a/conf/app.conf")
+}
+
+var HTTPPort, _ = beego.AppConfig.Int("HTTPPort")
+var AppName, _ = beego.AppConfig.String("appname")
+
+// Redis
+var Redis_address, _ = beego.AppConfig.String("Redis_address")
+var Redis_password, _ = beego.AppConfig.String("Redis_password")
+var Redis_dbNum, _ = beego.AppConfig.String("Redis_dbNum")
+
+// Mysql
+
+var MysqlServer_UrlPort, _ = beego.AppConfig.String("MysqlServer_UrlPort")
+var MysqlServer_Database, _ = beego.AppConfig.String("MysqlServer_Database")
+var MysqlServer_Username, _ = beego.AppConfig.String("MysqlServer_Username")
+var MysqlServer_Password, _ = beego.AppConfig.String("MysqlServer_Password")
+var MysqlServer_MaxIdleConnections, _ = beego.AppConfig.Int("MysqlServer_MaxIdleConnections")
+var MysqlServer_MaxOpenConnections, _ = beego.AppConfig.Int("MysqlServer_MaxOpenConnections")
+
+// Mqtt
+var MqttServer_id, _ = beego.AppConfig.String("MqttServer_id")
+var MqttServer_Url, _ = beego.AppConfig.String("MqttServer_Url")
+var MqttServer_Username, _ = beego.AppConfig.String("MqttServer_Username")
+var MqttServer_Password, _ = beego.AppConfig.String("MqttServer_Password")
+var MqttServer_ClientID, _ = beego.AppConfig.String("MqttServer_ClientID")
+
+// Nats
+var NatsServer_Url, _ = beego.AppConfig.String("NatsServer_Url")
+
+var EWarningAdmin = []int{1} // 管理
+var Page_size = 10

+ 108 - 0
controllers/default.go

@@ -0,0 +1,108 @@
+package controllers
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	beego "github.com/beego/beego/v2/server/web"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type MainController struct {
+	beego.Controller
+}
+
+func (c *MainController) Get() {
+	c.Data["Website"] = "beego.me"
+	c.Data["Email"] = "astaxie@gmail.com"
+	c.TplName = "index.tpl"
+}
+
+// 列表 - 接口
+func (c *MainController) Device_Sensor_Data_More() {
+	// 验证登录
+	b_, admin_r := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !b_ {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"}
+		c.ServeJSON()
+		return
+	}
+	T_sn := c.GetString("T_sn")
+	T_id, _ := c.GetInt("T_id")
+	T_Data := c.GetString("T_Data") // 模板: 温度?湿度?GPS?采集时间|   7.80?53.40?026.6441335,106.7994091?2019-01-08 13:50:30|
+
+	// 过滤
+	if len(T_sn) < 4 {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "T_sn Err!"}
+		c.ServeJSON()
+		return
+	}
+	Device_r, err := Device.Read_Device_ByT_sn(T_sn)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "T_sn Err!"}
+		c.ServeJSON()
+		return
+	}
+	// 是否存在传感器  不存在 跳过
+	DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(T_sn, T_id)
+	if !is {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "T_sn T_id Err!"}
+		c.ServeJSON()
+		return
+	}
+	// 获取传感器参数
+	DeviceSensorParameter_r, is := Device.Read_DeviceSensorParameter(Device_r.T_sn, DeviceSensor_r.T_id)
+	if !is {
+
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "记录数据 传感器参数不存在 跳过处理"}
+		c.ServeJSON()
+		return
+	}
+
+	T_Data_list := strings.Split(T_Data, "|")
+	logs.Println("len(T_Data_list)", len(T_Data_list))
+	for _, v := range T_Data_list {
+		// 7.80?53.40?026.6441335,106.7994091?2019-01-08 13:50:30|
+		if len(v) < 5 {
+			logs.Println(v, "len(v) < 5")
+			continue
+		}
+		v_list := strings.Split(v, "?")
+		if len(v_list) != 4 {
+			logs.Println(v, "len(v_list) != 4")
+			continue
+		}
+
+		T_t, _ := strconv.ParseFloat(v_list[0], 32)
+		T_rh, _ := strconv.ParseFloat(v_list[1], 32)
+		stamp, _ := time.ParseInLocation("2006-01-02 15:04:05", v_list[3], time.Local) //使用parseInLocation将字符串格式化返回本地时区时间
+		//// 更新记录 - 缓存
+		DeviceData_t := Device.DeviceData_R{
+			T_t:    float32(T_t),
+			T_rh:   float32(T_rh),
+			T_Site: v_list[4],
+			T_time: stamp,
+			T_sp:   DeviceSensorParameter_r.Id,
+		}
+
+		if r_, DeviceData_old_r := Device.Add_DeviceData(T_sn, T_id, DeviceData_t, true); r_ {
+			// 被替换
+			Device.Add_DeviceDataOld(Device.DeviceDataOld{
+				T_sn:        T_sn,
+				T_id:        T_id,
+				T_t:         DeviceData_old_r.T_t,
+				T_rh:        DeviceData_old_r.T_rh,
+				T_Site:      DeviceData_old_r.T_site,
+				T_time:      DeviceData_t.T_time,
+				T_operation: 2,
+				T_uuid:      admin_r.T_uuid,
+			})
+		}
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+	return
+}

+ 49 - 0
go.mod

@@ -0,0 +1,49 @@
+module Cold_mqtt
+
+go 1.19
+
+require github.com/beego/beego/v2 v2.0.1
+
+require (
+	github.com/astaxie/beego v1.12.3
+	github.com/go-sql-driver/mysql v1.7.0
+	github.com/jinzhu/gorm v1.9.16
+	github.com/nats-io/nats.go v1.16.0
+	github.com/satori/go.uuid v1.2.0
+	github.com/thinkeridea/go-extend v1.3.2
+	github.com/vmihailenco/msgpack/v5 v5.3.5
+	github.com/yosssi/gmq v0.0.1
+	golang.org/x/text v0.3.6
+	gorm.io/driver/mysql v1.5.6
+	gorm.io/gorm v1.25.9
+)
+
+require (
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cespare/xxhash/v2 v2.1.1 // indirect
+	github.com/golang/protobuf v1.4.2 // indirect
+	github.com/gomodule/redigo v2.0.0+incompatible // indirect
+	github.com/hashicorp/golang-lru v0.5.4 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+	github.com/mitchellh/mapstructure v1.3.3 // indirect
+	github.com/nats-io/nats-server/v2 v2.8.4 // indirect
+	github.com/nats-io/nkeys v0.3.0 // indirect
+	github.com/nats-io/nuid v1.0.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/prometheus/client_golang v1.7.0 // indirect
+	github.com/prometheus/client_model v0.2.0 // indirect
+	github.com/prometheus/common v0.10.0 // indirect
+	github.com/prometheus/procfs v0.1.3 // indirect
+	github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
+	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+	golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
+	golang.org/x/mod v0.3.0 // indirect
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
+	golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
+	golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
+	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+	google.golang.org/protobuf v1.23.0 // indirect
+	gopkg.in/yaml.v2 v2.2.8 // indirect
+)

+ 337 - 0
go.sum

@@ -0,0 +1,337 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+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/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
+github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
+github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
+github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
+github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
+github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
+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/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
+github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
+github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
+github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
+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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
+github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
+github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
+github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
+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-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+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.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.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+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.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+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/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
+github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
+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.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+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.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
+github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
+github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+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/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
+github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
+github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=
+github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
+github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
+github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
+github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
+github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
+github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
+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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
+github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
+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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U=
+github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
+github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
+github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
+github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
+github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
+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/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
+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/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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/thinkeridea/go-extend v1.3.2 h1:0ZImRXpJc+wBNIrNEMbTuKwIvJ6eFoeuNAewvzONrI0=
+github.com/thinkeridea/go-extend v1.3.2/go.mod h1:xqN1e3y1PdVSij1VZp6iPKlO8I4jLbS8CUuTySj981g=
+github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
+github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
+github.com/yosssi/gmq v0.0.1 h1:GhlDVaAQoi3Mvjul/qJXXGfL4JBeE0GQwbWp3eIsja8=
+github.com/yosssi/gmq v0.0.1/go.mod h1:mReykazh0U1JabvuWh1PEbzzJftqOQWsjr0Lwg5jL1Y=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
+go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190325154230-a5d413f7728c/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
+golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+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-20190313153728-d0100b6bd8b3/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-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+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-20180906233101-161cd47e91fd/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-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-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-20190923162816-aa69164e4478/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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-20180909124046-d0be0721c37e/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-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-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/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-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
+golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/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.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
+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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+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-20191119224855-298f0cb1881e/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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag=
+golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+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.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+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.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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
+gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
+gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/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.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=

+ 61 - 0
lib/Aes.go

@@ -0,0 +1,61 @@
+package lib
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+)
+
+func AesEncryptCBC(orig string, key string) string {
+	// 转成字节数组
+	origData := []byte(orig)
+	k := []byte(key)
+	// 分组秘钥
+	// NewCipher该函数限制了输入k的长度必须为16, 24或者32
+	block, _ := aes.NewCipher(k)
+	// 获取秘钥块的长度
+	blockSize := block.BlockSize()
+	// 补全码
+	origData = PKCS7Padding(origData, blockSize)
+	// 加密模式
+	blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
+	// 创建数组
+	cryted := make([]byte, len(origData))
+	// 加密
+	blockMode.CryptBlocks(cryted, origData)
+	return base64.StdEncoding.EncodeToString(cryted)
+}
+func AesDecryptCBC(cryted string, key string) string {
+	// 转成字节数组
+	crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
+	k := []byte(key)
+	// 分组秘钥
+	block, _ := aes.NewCipher(k)
+	// 获取秘钥块的长度
+	blockSize := block.BlockSize()
+	// 加密模式
+	blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
+	// 创建数组
+	orig := make([]byte, len(crytedByte))
+	// 解密
+	blockMode.CryptBlocks(orig, crytedByte)
+	// 去补全码
+	orig = PKCS7UnPadding(orig)
+	return string(orig)
+}
+
+// 补码
+// AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
+func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
+	padding := blocksize - len(ciphertext)%blocksize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+// 去码
+func PKCS7UnPadding(origData []byte) []byte {
+	length := len(origData)
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}

+ 86 - 0
lib/MinuteToDataTime.go

@@ -0,0 +1,86 @@
+package lib
+
+import (
+	"strconv"
+	"time"
+)
+
+/*
+时间常量
+*/
+const (
+	//定义每分钟的秒数
+	SecondsPerMinute = 60
+	//定义每小时的秒数
+	SecondsPerHour = SecondsPerMinute * 60
+	//定义每天的秒数
+	SecondsPerDay = SecondsPerHour * 24
+)
+
+/*
+时间转换函数
+*/
+func resolveTime(seconds int) (day int, hour int, minute int, second int) {
+	//秒
+	second = seconds % 60
+
+	//天
+	day = seconds / SecondsPerDay
+	seconds -= day * SecondsPerDay
+
+	//时
+	hour = seconds / SecondsPerHour
+	seconds -= hour * SecondsPerHour
+
+	//分
+	minute = seconds / SecondsPerMinute
+	seconds -= minute * SecondsPerHour
+
+	return
+}
+
+func MinuteToDataTime(t int) string {
+	str := ""
+	day, hour, minute, second := resolveTime(t)
+	if day > 0 {
+		str += strconv.Itoa(day) + "天 "
+	}
+	if hour > 0 {
+		str += strconv.Itoa(hour) + "小时 "
+	}
+	if minute > 0 {
+		str += strconv.Itoa(minute) + "分钟 "
+	}
+	if second > 0 {
+		str += strconv.Itoa(second) + "秒"
+	}
+
+	return str
+}
+
+func TimeSinceToString(startTime, endTime time.Time) string {
+	str := ""
+	// 计算时间差
+	duration := endTime.Sub(startTime)
+	// 将时间差表示为天、小时和分钟
+	days := int(duration.Hours() / 24)
+	hours := int(duration.Hours()) % 24
+	minutes := int(duration.Minutes()) % 60
+	seconds := int(duration.Seconds()) % 60
+
+	//fmt.Printf("时间差为 %d 天 %d 小时 %d 分钟\n", days, hours, minutes)
+	if days > 0 {
+		str += strconv.Itoa(days) + "天 "
+	}
+	if hours > 0 {
+		str += strconv.Itoa(hours) + "小时 "
+	}
+	if minutes > 0 {
+		str += strconv.Itoa(minutes) + "分钟 "
+	}
+	if seconds > 0 {
+		str += strconv.Itoa(seconds) + "秒 "
+	}
+
+	return str
+}

+ 486 - 0
lib/lib.go

@@ -0,0 +1,486 @@
+package lib
+
+import (
+	"Cold_mqtt/models/Account"
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/nats-io/nats.go"
+	"github.com/thinkeridea/go-extend/exunicode/exutf8"
+	"golang.org/x/text/encoding/simplifiedchinese"
+	"golang.org/x/text/transform"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+	"unicode/utf8"
+)
+
+type Cl_ struct {
+	Uuid_list map[string]string // 泛型
+}
+
+var Nats *nats.Conn
+
+func init() {
+
+}
+
+// 登录验证
+func Verification(GetCookie string, GetString string) (bool, Account.Admin) {
+
+	// 自适应 参数
+	User_tokey := GetCookie
+	if len(User_tokey) == 0 {
+		User_tokey = GetString
+	}
+	if len(User_tokey) == 0 {
+		return false, Account.Admin{}
+	}
+	tokey, is := Account.Redis_Tokey_Get(User_tokey)
+	if !is {
+		return false, Account.Admin{}
+	}
+	admin_r, err := Account.Read_Admin_ByUuid(tokey)
+	if err != nil {
+		return false, Account.Admin{}
+	}
+	log.Println("登录 Admin_name 为:", admin_r.T_name)
+	return true, admin_r
+}
+
+type JSONS struct {
+	//必须的大写开头
+	Code int16
+	Msg  string
+	Data interface{} // 泛型
+}
+
+// func_page 分页   [{3 1} {4 2} {4 3} {4 4} {4 5} {4 6} {4 7} {4 8} {4 9} {5 2}]-
+type Page_T struct {
+	A int
+	V int64
+}
+
+func Func_page(Page int64, Page_size int64) (page_t_list []Page_T) {
+	if Page > 1 {
+		page_t_list = append(page_t_list, Page_T{A: 1, V: Page - 1})
+	}
+	i := int64(0)
+	for aa := int64(1); aa < 5; aa++ {
+		if Page-aa <= 0 {
+			break
+		}
+		page_t_list = append(page_t_list, Page_T{A: 2, V: Page - aa})
+		i++
+	}
+	page_t_list = append(page_t_list, Page_T{A: 3, V: Page})
+
+	for aa := int64(1); aa < 10-i; aa++ {
+		if Page_size < Page+aa {
+			break
+		}
+		page_t_list = append(page_t_list, Page_T{A: 4, V: Page + aa})
+	}
+	sort.Slice(page_t_list, func(i, j int) bool {
+		if page_t_list[i].V < page_t_list[j].V {
+			return true
+		}
+		return false
+	})
+	sort.Slice(page_t_list, func(i, j int) bool {
+		if page_t_list[i].A < page_t_list[j].A {
+			return true
+		}
+		return false
+
+	})
+	if Page < Page_size {
+		page_t_list = append(page_t_list, Page_T{A: 5, V: Page + 1})
+	}
+
+	return page_t_list
+}
+
+func Strval(value interface{}) string {
+	var key string
+	if value == nil {
+		return key
+	}
+
+	switch value.(type) {
+	case float64:
+		ft := value.(float64)
+		key = strconv.FormatFloat(ft, 'f', -1, 64)
+	case float32:
+		ft := value.(float32)
+		key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
+	case int:
+		it := value.(int)
+		key = strconv.Itoa(it)
+	case uint:
+		it := value.(uint)
+		key = strconv.Itoa(int(it))
+	case int8:
+		it := value.(int8)
+		key = strconv.Itoa(int(it))
+	case uint8:
+		it := value.(uint8)
+		key = strconv.Itoa(int(it))
+	case int16:
+		it := value.(int16)
+		key = strconv.Itoa(int(it))
+	case uint16:
+		it := value.(uint16)
+		key = strconv.Itoa(int(it))
+	case int32:
+		it := value.(int32)
+		key = strconv.Itoa(int(it))
+	case uint32:
+		it := value.(uint32)
+		key = strconv.Itoa(int(it))
+	case int64:
+		it := value.(int64)
+		key = strconv.FormatInt(it, 10)
+	case uint64:
+		it := value.(uint64)
+		key = strconv.FormatUint(it, 10)
+	case string:
+		key = value.(string)
+	case []byte:
+		key = string(value.([]byte))
+	default:
+		newValue, _ := json.Marshal(value)
+		key = string(newValue)
+	}
+
+	return key
+}
+
+func To_int(value interface{}) int {
+	var key int
+	if value == nil {
+		return key
+	}
+	switch value.(type) {
+	case float64:
+		key = int(value.(float64))
+	case float32:
+		key = int(value.(float32))
+	case int:
+		key = int(value.(int))
+	case uint:
+		key = int(value.(uint))
+	case int8:
+		key = int(value.(int8))
+	case uint8:
+		key = int(value.(uint8))
+	case int16:
+		key = int(value.(int16))
+	case uint16:
+		key = int(value.(uint16))
+	case int32:
+		key = int(value.(int32))
+	case uint32:
+		key = int(value.(uint32))
+	case int64:
+		key = int(value.(int64))
+	case uint64:
+		key = int(value.(uint64))
+	case string:
+		key, _ = strconv.Atoi(value.(string))
+	case []byte:
+		key, _ = strconv.Atoi(string(value.([]byte)))
+	default:
+		newValue, _ := json.Marshal(value)
+		key, _ = strconv.Atoi(string(newValue))
+	}
+	return key
+}
+func To_int64(value interface{}) int64 {
+	var key int64
+	if value == nil {
+		return key
+	}
+	switch value.(type) {
+	case float64:
+		key = int64(value.(float64))
+	case float32:
+		key = int64(value.(float32))
+	case int:
+		key = int64(value.(int))
+	case uint:
+		key = int64(value.(uint))
+	case int8:
+		key = int64(value.(int8))
+	case uint8:
+		key = int64(value.(uint8))
+	case int16:
+		key = int64(value.(int16))
+	case uint16:
+		key = int64(value.(uint16))
+	case int32:
+		key = int64(value.(int32))
+	case uint32:
+		key = int64(value.(uint32))
+	case int64:
+		key = int64(value.(int64))
+	case uint64:
+		key = int64(value.(uint64))
+	case string:
+		key, _ = strconv.ParseInt(value.(string), 10, 64)
+	case []byte:
+		key, _ = strconv.ParseInt(string(value.([]byte)), 10, 64)
+	default:
+		newValue, _ := json.Marshal(value)
+		key, _ = strconv.ParseInt(string(newValue), 10, 64)
+	}
+	return key
+}
+
+func To_float32(value interface{}) float32 {
+	var key float32
+	if value == nil {
+		return key
+	}
+
+	switch value.(type) {
+	case float64:
+		key = float32(value.(float64))
+	case float32:
+		key = float32(value.(float32))
+	case int:
+		key = float32(value.(int))
+	case uint:
+		key = float32(value.(uint))
+	case int8:
+		key = float32(value.(int8))
+	case uint8:
+		key = float32(value.(uint8))
+	case int16:
+		key = float32(value.(int16))
+	case uint16:
+		key = float32(value.(uint16))
+	case int32:
+		key = float32(value.(int32))
+	case uint32:
+		key = float32(value.(uint32))
+	case int64:
+		key = float32(value.(int64))
+	case uint64:
+		key = float32(value.(uint64))
+	case string:
+		key_float64, _ := strconv.ParseFloat(value.(string), 32/64)
+		key = float32(key_float64)
+	case []byte:
+		key_float64, _ := strconv.ParseFloat(string(value.([]byte)), 32/64)
+		key = float32(key_float64)
+	default:
+		newValue, _ := json.Marshal(value)
+		key_float64, _ := strconv.ParseFloat(string(newValue), 32/64)
+		key = float32(key_float64)
+	}
+
+	key_float64, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", key), 32/64)
+	key = float32(key_float64)
+
+	return key
+}
+
+func To_string(value interface{}) string {
+	var key string
+	if value == nil {
+		return key
+	}
+
+	switch value.(type) {
+	case float64:
+		ft := value.(float64)
+		key = strconv.FormatFloat(ft, 'f', -1, 64)
+	case float32:
+		ft := value.(float32)
+		key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
+	case int:
+		it := value.(int)
+		key = strconv.Itoa(it)
+	case uint:
+		it := value.(uint)
+		key = strconv.Itoa(int(it))
+	case int8:
+		it := value.(int8)
+		key = strconv.Itoa(int(it))
+	case uint8:
+		it := value.(uint8)
+		key = strconv.Itoa(int(it))
+	case int16:
+		it := value.(int16)
+		key = strconv.Itoa(int(it))
+	case uint16:
+		it := value.(uint16)
+		key = strconv.Itoa(int(it))
+	case int32:
+		it := value.(int32)
+		key = strconv.Itoa(int(it))
+	case uint32:
+		it := value.(uint32)
+		key = strconv.Itoa(int(it))
+	case int64:
+		it := value.(int64)
+		key = strconv.FormatInt(it, 10)
+	case uint64:
+		it := value.(uint64)
+		key = strconv.FormatUint(it, 10)
+	case string:
+		key = value.(string)
+	case []byte:
+		key = string(value.([]byte))
+	default:
+		newValue, _ := json.Marshal(value)
+		key = string(newValue)
+	}
+	return key
+}
+
+func Random(min, max int) int {
+	rand.Seed(time.Now().Unix()) //Seed生成的随机数
+	return rand.Intn(max-min) + min
+}
+
+// 取文本(字符串)中间
+func GetBetweenStr(str, start, end string) string {
+	n := strings.Index(str, start)
+	if n == -1 {
+		n = 0
+	} else {
+		n = n + len(start) // 增加了else,不加的会把start带上
+	}
+	str = string([]byte(str)[n:])
+	m := strings.Index(str, end)
+	if m == -1 {
+		m = len(str)
+	}
+	str = string([]byte(str)[:m])
+	return str
+}
+
+// getYearMonthToDay 查询指定年份指定月份有多少天
+// @params year int 指定年份
+// @params month int 指定月份
+func GetYearMonthToDay(year int, month int) int {
+	// 有31天的月份
+	day31 := map[int]bool{
+		1:  true,
+		3:  true,
+		5:  true,
+		7:  true,
+		8:  true,
+		10: true,
+		12: true,
+	}
+	if day31[month] == true {
+		return 31
+	}
+	// 有30天的月份
+	day30 := map[int]bool{
+		4:  true,
+		6:  true,
+		9:  true,
+		11: true,
+	}
+	if day30[month] == true {
+		return 30
+	}
+	// 计算是平年还是闰年
+	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
+		// 得出2月的天数
+		return 29
+	}
+	// 得出2月的天数
+	return 28
+}
+
+func Decimal(value float64) float64 {
+	value, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", value), 64)
+	return value
+}
+
+func Strconv_Atoi(string string) int {
+	int, _ := strconv.Atoi(string)
+	return int
+}
+
+// 获取正在运行的函数名
+func FuncName() string {
+	pc := make([]uintptr, 1)
+	runtime.Callers(2, pc)
+	f := runtime.FuncForPC(pc[0])
+	return f.Name()
+}
+
+func Limit_len(str string, lenx int) string {
+	if utf8.RuneCountInString(str) > lenx {
+		return exutf8.RuneSubString(str, 0, lenx-3) + "..."
+	}
+	return exutf8.RuneSubString(str, 0, lenx)
+}
+
+func Utf8ToGbk(s []byte) ([]byte, error) {
+	reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
+	d, e := ioutil.ReadAll(reader)
+	if e != nil {
+		return nil, e
+	}
+	return d, nil
+}
+
+// timea 与当前时间,在  正负 SecondsNun 秒
+func IsWithinMinutesRange(timea time.Time, SecondsNun float64) bool {
+	currentTime := time.Now()
+	timeDifference := currentTime.Sub(timea)
+
+	if timeDifference.Seconds() <= SecondsNun && timeDifference.Seconds() >= -SecondsNun {
+		return true
+	} else {
+		return false
+	}
+}
+
+// 计算周期时间  t 时间 T_saveT 周期
+func PeriodTime(t time.Time, T_saveT int) time.Time {
+	period := time.Minute * time.Duration(T_saveT)
+	// 解析时间字符串
+	//t, err := time.Parse("2006-01-02 15:04:05", time_s)
+	//if err != nil {
+	//	fmt.Println("时间解析错误:", err)
+	//	return time.Now(), errors.New("时间解析错误!")
+	//}
+	// 计算归一化的分钟
+	minutes := t.Minute()
+	normalizedMinutes := (minutes / int(period.Minutes())) * int(period.Minutes())
+	// 创建新的时间对象,使用归一化后的分钟
+	normalizedTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), normalizedMinutes, 0, 0, t.Location())
+	// 输出结果
+	//fmt.Println(normalizedTime.Format("2006-01-02 15:04:05"))
+	return normalizedTime
+}
+// ConvertTime 根据基准时间和间隔将输入时间转换为指定格式
+func PeriodWTime(baseTime time.Time, inputTime time.Time, intervalMinutes int) time.Time {
+	// 计算输入时间距离基准时间的总分钟数
+	minutesDiff := int(inputTime.Sub(baseTime).Minutes())
+
+	// 计算离基准时间最近的间隔
+	if minutesDiff < 0 {
+		// 如果输入时间早于基准时间,返回基准时间
+		return baseTime
+	}
+
+	// 计算最近的间隔
+	closestInterval := (minutesDiff / intervalMinutes) * intervalMinutes
+
+	// 返回基准时间加上最近的间隔
+	return baseTime.Add(time.Duration(closestInterval) * time.Minute)
+}

+ 111 - 0
logs/LogPrintln.go

@@ -0,0 +1,111 @@
+package logs
+
+import (
+	"fmt"
+	"github.com/astaxie/beego/logs"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	"runtime"
+)
+
+var Test = true
+var logx *logs.BeeLogger
+var logxE *logs.BeeLogger
+var logxWData *logs.BeeLogger
+var logxMqtt *logs.BeeLogger
+var logxData *logs.BeeLogger
+var LogxOrm *logs.BeeLogger
+
+func init() {
+
+	if runtime.GOOS == "windows" {
+		Test = true
+	} else {
+		Test = false
+	}
+
+	logx = logs.NewLogger()
+	logx.SetLogger(logs.AdapterFile, `{"filename":"logs/logx/logx.log","MaxDays":30}`)
+
+	logxE = logs.NewLogger()
+	logxE.SetLogger(logs.AdapterFile, `{"filename":"logs/logxE/logx.log","MaxDays":180}`)
+
+	logxMqtt = logs.NewLogger()
+	logxMqtt.SetLogger(logs.AdapterFile, `{"filename":"logs/Mqtt/logx.log","MaxDays":30}`)
+
+	logxData = logs.NewLogger()
+	logxData.SetLogger(logs.AdapterFile, `{"filename":"logs/Data/logx.log","MaxDays":7}`)
+
+	logxWData = logs.NewLogger()
+	logxWData.SetLogger(logs.AdapterFile, `{"filename":"logs/logxWData/logx.log","MaxDays":300}`)
+
+	LogxOrm = logs.NewLogger()
+	LogxOrm.SetLogger(logs.AdapterFile, `{"filename":"logs/Orm/logx.log","MaxDays":7}`)
+	if !Test {
+		orm2.DebugLog = orm2.NewLog(LogxOrm)
+	}
+	//
+
+	// 测试 日志分割
+	//go func() {
+	//	t := 1
+	//	orm2.DebugLog.Println("==========DebugLog=========")
+	//	for true {
+	//		t +=1
+	//		orm2.DebugLog.Println("DebugLog:",t)
+	//		time.Sleep(time.Second * 1)
+	//	}
+	//}()
+	//go TimeTask.OrmLog_MonitorScanStatus()
+}
+
+func Println(format string, v ...interface{}) {
+	if Test {
+		fmt.Println(format, v)
+	}
+	logx.Info(format, v)
+
+}
+
+func PrintlnError(format string, v ...interface{}) {
+	if Test {
+		fmt.Println(format, v)
+	}
+	logxE.Error(format, v)
+	logx.Info(format, v)
+}
+
+func PrintlnMqtt(str string) {
+	if Test {
+		fmt.Println(str)
+	}
+	logxMqtt.Info(str)
+
+}
+
+func PrintlnWData(format string, v ...interface{}) {
+	logx.Info(format, v)
+
+}
+
+func PrintlnData(str string) {
+	if Test {
+		fmt.Println(str)
+	}
+	logxData.Info(str)
+}
+
+//func Println(a ...interface{}) {
+//	if test {
+//		fmt.Printf("=>", a)
+//	}
+//	logx.Info(">", a)
+//}
+
+// 日志
+// logs.Println(time.Now().Format("2006-01-02 15:04:05"), "=>", a)
+
+// 重要
+// logs.Println("MqttServer", " 存在报警 跳过:", string(msg))
+
+// 严重
+// logs.Println("MqttServer", " 存在报警 跳过:", string(msg))

+ 7 - 0
logs/nohup.sh

@@ -0,0 +1,7 @@
+#!/bin/sh 
+#获取前一天的日期 
+date=`date -d "yesterday" +%Y_%m_%d` 
+#复制原始nohup.out到备份目录,并以前一天的日期进行命名 
+cp /bzd/project/ColdVerify_server/log/nohup.out /bzd/project/ColdVerify_server/log/$date.out 
+#清空原始nohup.out中的内容
+cat /dev/null > /bzd/project/ColdVerify_server/log/nohup.out

+ 45 - 0
main.go

@@ -0,0 +1,45 @@
+package main
+
+import (
+	"Cold_mqtt/MqttServer"
+	"Cold_mqtt/Nats"
+	"Cold_mqtt/Plugins"
+	"Cold_mqtt/TimeTask"
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	_ "Cold_mqtt/routers"
+	"github.com/beego/beego/v2/adapter/orm"
+	beego "github.com/beego/beego/v2/server/web"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+)
+
+func init() {
+	println("--------------- go --------------")
+	orm.RegisterDriver("mysql", orm.DRMySQL)
+	orm.RegisterDataBase("default", "mysql",
+		conf.MysqlServer_Username+":"+conf.MysqlServer_Password+"@tcp("+conf.MysqlServer_UrlPort+")/"+conf.MysqlServer_Database+"?charset=utf8mb4&loc=Local&parseTime=True",
+		conf.MysqlServer_MaxIdleConnections, conf.MysqlServer_MaxOpenConnections)
+	orm.RunSyncdb("default", false, true) // 创建数据库
+
+	//logs.Println("MysqlServer:" + conf.MysqlServer_Username + ":" + conf.MysqlServer_Password + "@tcp(" + conf.MysqlServer_UrlPort + ")/" + conf.MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True")
+
+}
+
+func main() {
+
+	beego.BConfig.AppName = conf.AppName + "_" + strconv.Itoa(conf.HTTPPort)    // 项目名
+	beego.BConfig.ServerName = conf.AppName + "_" + strconv.Itoa(conf.HTTPPort) //server  名称
+	beego.BConfig.RunMode = "dev"                                               //  应用的运行模式
+	beego.BConfig.Listen.HTTPPort = conf.HTTPPort                               //监听端口  本地:8518  线上:8528
+
+	logs.Println("MqttServer", " ======= 项目启动 ========")
+	go MqttServer.Run_MqttServer() // MQTT 通讯
+	////go MqttServer.MqttPolling() // MQTT 设备轮询
+	go MqttServer.DeviceMqttMap_go()                  //// 缓存数据发送-确保设备在休眠后 能收到数据
+	go Nats.NatsInit()                                // Nats 通讯
+	go TimeTask.Init()                                // 时间任务
+	go Plugins.ContinuousPushOfOfflineDeviceWarning() //设备离线报警连续推送
+
+	beego.Run()
+}

+ 128 - 0
models/Account/Admin.go

@@ -0,0 +1,128 @@
+package Account
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+type Admin struct {
+	Id      int    `orm:"column(ID);size(11);auto;pk"`
+	T_uuid  string `orm:"size(256);"`         //  用户编号
+	T_pid   int    `orm:"size(200);"`         //  绑定公司 ( 只有创建公司用户时添加,内部人员 为0)
+	T_pids  string `orm:"size(256);"`         //   绑定公司管理  Pid|  如:P1|P2|
+	T_power int    `orm:"size(2);default(0)"` // 权限 (关联权限表)
+	T_user  string `orm:"size(256);"`         // 用户名 (唯一)
+	T_pass  string `orm:"size(256);"`         // MD5
+
+	T_name  string `orm:"size(256);"` // 姓名
+	T_phone string `orm:"size(256);"` // 电话
+	T_mail  string `orm:"size(200);"` // 邮箱
+	T_wx    string `orm:"type(text)"` // 微信
+
+	T_State    int       `orm:"size(200);default(1)"`                             //  0删除  1 正常
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now 每次 model 保存时都会对时间自动更新
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now_add 第一次保存时才设置时间
+}
+
+func (t *Admin) TableName() string {
+	return "admin" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_Admin cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(Admin))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_User_Admin", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	var err error
+	redisCache_Admin, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Admin == nil {
+		errMsg := "failed to init redis"
+		logs.PrintlnError(errMsg, err)
+	}
+
+}
+
+// if r,is :=Redis_Get(T_sn);is{
+// return r,nil
+// }
+func Redis_Admin_Get(key string) (r Admin, is bool) {
+	if redisCache_Admin.IsExist(key) {
+		//println("找到key:",key)
+		v := redisCache_Admin.Get(key)
+
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+	//println("没有 找到key:",key)
+	return Admin{}, false
+}
+
+// 获取 ById
+func Read_Admin_Byid(Id int) (r Admin, err error) {
+
+	o := orm.NewOrm()
+	r = Admin{Id: Id, T_State: 1}
+	err = o.Read(&r, "Id", "T_State") // o.Read(&r,"Tokey") 如果不是 主键 就得指定字段名
+	if err != nil {
+		logs.Println("Read_Admin_Byid", err)
+	}
+	return r, err
+}
+
+// 获取列表
+func Read_Admin_Tpids_List(CompanyID int) (maps []Admin) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Admin))
+
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_State", 1).And("T_pids__icontains", CompanyID)
+
+	qs.SetCond((*orm2.Condition)(cond1)).OrderBy("-Id").All(&maps)
+
+	return maps
+}
+
+// 获取列表
+func Read_Admin_Tpid_List(CompanyID int) (maps []Admin) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Admin))
+
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_State", 1).And("T_pid", CompanyID)
+
+	qs.SetCond((*orm2.Condition)(cond1)).OrderBy("-Id").All(&maps)
+
+	return maps
+}
+
+// 获取 ByT_uuid
+func Read_Admin_ByUuid(T_uuid string) (r Admin, err error) {
+	if r, is := Redis_Admin_Get(T_uuid); is {
+		//println("Redis_Get  OK")
+		return r, nil
+	}
+	o := orm.NewOrm()
+	r = Admin{T_uuid: T_uuid, T_State: 1}
+	err = o.Read(&r, "T_uuid", "T_State") // o.Read(&r,"Tokey") 如果不是 主键 就得指定字段名
+	if err != nil {
+		logs.PrintlnError("Read_Admin_ByUuid", err)
+	}
+	return r, err
+}

+ 85 - 0
models/Account/Company.go

@@ -0,0 +1,85 @@
+package Account
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"time"
+)
+
+type Company struct {
+	Id        int    `orm:"column(ID);size(11);auto;pk"`
+	T_mid     int    `orm:"size(200);"`          //  上一级 ID
+	T_name    string `orm:"size(256);"`          // 公司名称
+	T_key     string `orm:"size(256);index;"`    // 公司密钥
+	T_warning int    `orm:"size(20);default(1)"` //  是否处理报警信息 1处理 2不处理
+
+	T_money    float32   `orm:"digits(12);decimals(2)"`                           //  余额
+	T_Charging int       `orm:"size(11);default(0)"`                              //  记账扣费 公司ID  默认为:0 (自己)
+	T_State    int       `orm:"size(200);"`                                       //  0删除  1 正常
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now 每次 model 保存时都会对时间自动更新
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now_add 第一次保存时才设置时间
+}
+
+func (t *Company) TableName() string {
+	return "company" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_Company cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(Company))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_User_Company", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redisCache_Company, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Company == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+
+}
+
+// if r,is :=Redis_Get(T_sn);is{
+// return r,nil
+// }
+func Redis_Company_Get(key string) (r Company, is bool) {
+	if redisCache_Company.IsExist(key) {
+		//println("找到key:",key)
+		v := redisCache_Company.Get(key)
+
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+	//println("没有 找到key:",key)
+	return Company{}, false
+}
+
+func Redis_Company_DelK(key string) (err error) {
+	err = redisCache_Company.Delete(key)
+	return
+}
+
+// ---------------- 特殊方法 -------------------
+
+// 获取 ById
+func Read_Company_id(Id int) (e error, r Company) {
+	if r, is := Redis_Company_Get(strconv.Itoa(Id)); is {
+		//println("Redis_Get  OK")
+		return nil, r
+	}
+
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(Company))
+	e = qs.Filter("Id", Id).Filter("T_State", 1).One(&r)
+	return e, r
+}

+ 62 - 0
models/Account/Tokey.go

@@ -0,0 +1,62 @@
+package Account
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	uuid "github.com/satori/go.uuid"
+	"time"
+)
+
+var redisCache_Tokey cache.Cache
+
+func init() {
+	//注册模型
+	//orm.RegisterModel(new(Tokey))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_User_Tokey", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	var err error
+	redisCache_Tokey, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Tokey == nil {
+		errMsg := "failed to init redis"
+		logs.PrintlnError(errMsg, err)
+	}
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func Add_Tokey_Set(Uuid string) string {
+	var Tokey string
+	for true {
+		Tokey = uuid.NewV4().String()
+		if !redisCache_Tokey.IsExist(Tokey) {
+			break
+		}
+		fmt.Print("申请 TOKEY 重复!重新生成。", Tokey)
+	}
+	redisCache_Tokey.Put(Tokey, Uuid, 2*time.Hour)
+
+	return Tokey
+}
+
+// if r,is :=Redis_Get(T_sn);is{
+// return r,nil
+// }
+func Redis_Tokey_Get(Tokey string) (string, bool) {
+	if len(Tokey) < 10 {
+		return "", false
+	}
+	if redisCache_Tokey.IsExist(Tokey) {
+		//println("找到key:",key)
+		v := redisCache_Tokey.Get(Tokey)
+		value := string(v.([]byte)) //这里的转换很重要,Get返回的是interface
+
+		redisCache_Tokey.Put(Tokey, value, 2*time.Hour) //  重新计次
+		return value, true
+	}
+	//println("没有 找到key:",key)
+	return "", false
+}

+ 48 - 0
models/Company/CompanyNotice.go

@@ -0,0 +1,48 @@
+package Company
+
+import (
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+type CompanyNotice struct {
+	Id                 int    `orm:"column(ID);size(11);auto;pk"`
+	T_pid              int    `orm:"index;size(256);"` // Account.Company 绑定公司
+	T_name             string `orm:"size(256);"`       // 分类
+	T_Notice_wx        string `orm:"type(text);"`      //w微信公众号  uuid/名字|
+	T_Notice_phone     string `orm:"type(text);"`      //p手机  uuid/名字|
+	T_Notice_message   string `orm:"type(text);"`      //m短信   uuid/名字|
+	T_Notice_mailbox   string `orm:"type(text);"`      //e邮箱    uuid/名字|
+	T_Notice_app       string `orm:"type(text);"`      //eAPP    uuid/名字|
+	T_Notice_mechanism string `orm:"type(text);"`      // 报警机制
+	T_Notice_bind      string `orm:"type(text);"`      // 绑定T_sn,Tid|    862289056463538,1|8622546456433,1|
+	// W报警编号,处理,w启用,持续秒,间隔秒,发送条数,d启用,持续秒,间隔秒,发送条数,p启用,持续秒,间隔秒,发送条数|
+	// W15,1,0,1,0,9999,0,0,0,0,0,0,0,0,0|
+
+	T_State    int       `orm:"size(2);default(1)"`                               // 0 删除   1 正常
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+func (t *CompanyNotice) TableName() string {
+	return "company_notice" // 数据库名称   // ************** 替换 DesignDeviceNotice **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(CompanyNotice))
+
+}
+
+// 获取列表
+func Read_CompanyNotice_List(T_pid int) (r []CompanyNotice) {
+
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(CompanyNotice))
+
+	qs.Filter("T_pid", T_pid).Filter("T_State", 1).OrderBy("Id").All(&r)
+
+	return r
+}

+ 199 - 0
models/Device/Device.go

@@ -0,0 +1,199 @@
+package Device
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 模板
+type Device struct {
+	T_sn              string    `orm:"pk;size(256);"`            // 设备序列号
+	T_pid             int       `orm:"index;size(256);"`         // Account.Company 绑定公司
+	T_devName         string    `orm:"size(256);"`               // 设备名称  20字
+	T_protocol        int       `orm:"size(2);default(3)"`       // 冷链通讯协议 1 :1.0协议   2 :2.0协议    3 :3.0协议
+	T_mqttid          string    `orm:"size(256);"`               // MQTT 服务ID
+	T_VerifyTime      time.Time `orm:"type(timestamp);"`         // 验证时间
+	T_CalibrationTime time.Time `orm:"type(timestamp);"`         // 校准时间
+	T_PatrolTime      time.Time `orm:"type(timestamp);"`         // 巡检时间
+	T_ist             int       `orm:"size(2);default(1)"`       // 温度   1开启   2关闭
+	T_ish             int       `orm:"size(2);default(1)"`       // 湿度   1开启   2关闭
+	T_State           int       `orm:"index;size(2);default(1)"` // 0 屏蔽   1 正常  (屏蔽后 只有内部管理员才能看到,用户 输入SN\名称 搜索时 也能看到)
+
+	// 设备同步参数
+	T_Dattery  int    `orm:"size(4);"`                 // 电量       :设备实时状态推送
+	T_Site     string `orm:"size(200);"`               // GPS       :实时数据  记录数据
+	T_monitor  int    `orm:"index;size(2);"`           // 监控状态 0 未监控 1 监控  停止记录     :记录数据   上下线(=0)
+	T_online   int    `orm:"index;size(2);default(0)"` // 在线状态 0 未启用  1 在线  2 离线     :上下线  消息
+	T_online_s int    `orm:"index;size(2);default(0)"` // 在线状态-备用  0 未启用  1 在线  2 离线     :上下线
+
+	// 硬件信息
+	T_model string `orm:"size(200);"` // KF200BG  设备型号
+	T_sver  string `orm:"size(200);"` // "1.0.0",//软件版本
+	T_hver  string `orm:"size(200);"` // "1.0.0",//硬件版本
+	T_imei  string `orm:"size(200);"` // "867387060327718",//模组imei
+	T_iccid string `orm:"size(200);"` // "89860477102170049750",//sim卡号
+	T_rssi  string `orm:"size(200);"` // "80",//信号强度           :设备实时状态推送
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+type Device_task struct {
+	T_sn   string
+	T_task string
+}
+
+func (t *Device) TableName() string {
+	return "device" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_Device cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(Device))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_Device", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redisCache_Device, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Device == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+		panic(any(errMsg))
+	}
+
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func Redis_Set(key string, r Device) (err error) {
+	//json序列化
+	str, err := json.Marshal(r)
+	if err != nil {
+		logs.PrintlnError("Redis_Set", err)
+		return
+	}
+
+	err = redisCache_Device.Put(key, str, 24*time.Hour)
+	if err != nil {
+		logs.Println("set key:", key, ",value:", str, err)
+	}
+	return
+}
+
+// if r,is :=Redis_Get(T_sn);is{
+// return r,nil
+// }
+func Redis_Get(key string) (r Device, is bool) {
+	if redisCache_Device.IsExist(key) {
+		//println("找到key:",key)
+		v := redisCache_Device.Get(key)
+		if v == nil {
+			return Device{}, false
+		}
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+	//println("没有 找到key:",key)
+	return Device{}, false
+}
+
+// ---------------- 特殊方法 -------------------
+
+// 获取 ById
+func Read_Device_ByT_sn(T_sn string) (r Device, err error) {
+	if r, is := Redis_Get(T_sn); is {
+		//println("Redis_Get  OK")
+		if r.T_State != 1 {
+			return r, errors.New("设备被弃用")
+		}
+		return r, nil
+	}
+	//println("没有 Redis_Get SN")
+	o := orm.NewOrm()
+	r = Device{T_sn: T_sn, T_State: 1}
+	err = o.Read(&r, "T_sn", "T_State") // o.Read(&r,"Tokey") 如果不是 主键 就得指定字段名
+	if err != nil {
+		logs.Println("Read_Device_ByT_sn", err)
+		return r, err
+	}
+	Redis_Set(r.T_sn, r) // Redis 更新缓存
+	if r.T_State != 1 {
+		return r, errors.New("设备被弃用")
+	}
+	return r, err
+}
+
+// 修改
+func Update_Device(r Device, cols ...string) bool {
+	o := orm.NewOrm()
+	if _, err := o.Update(&r, cols...); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+		Update_Device_To_DeviceSensor(r) // 设备同步参数
+		Redis_Set(r.T_sn, r)             // Redis 更新缓存
+
+		return true
+	}
+	return false
+}
+
+//// 修改
+//func Update_Device_new(r Device, cols ...string) {
+//	// 更新 状态 为了防止中间有变化,重新获取最新数据
+//	r_Device_new, _ := Read_Device_ByT_sn(r.T_sn)
+//	for _, v := range cols {
+//		switch v {
+//		case "T_devName":
+//			r_Device_new.T_devName = r.T_devName
+//			break
+//		case "T_Dattery":
+//			r_Device_new.T_Dattery = r.T_Dattery
+//			break
+//		case "T_Site":
+//			r_Device_new.T_Site = r.T_Site
+//			break
+//		case "T_monitor":
+//			r_Device_new.T_monitor = r.T_monitor
+//			break
+//		case "T_online":
+//			r_Device_new.T_online = r.T_online
+//			break
+//		case "T_online_s":
+//			r_Device_new.T_online_s = r.T_online_s
+//			break
+//		case "T_protocol":
+//			r_Device_new.T_protocol = r.T_protocol
+//			break
+//		}
+//	}
+//
+//	Update_Device(r_Device_new, "T_devName", "T_Dattery", "T_Site", "T_monitor", "T_online", "T_online_s", "T_protocol")
+//	Update_Device_To_DeviceSensor(r_Device_new) // 设备同步参数
+//
+//	return
+//}
+
+// 获取离线设备
+func Read_获取BX100W离线设备() []Device {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Device))
+	var r []Device
+	//qs.Filter("T_model", "BX100W").Filter("T_State", 1).Filter("T_online", 2).All(&r)
+	qs.Filter("T_mqttid", conf.MqttServer_id).Filter("T_model", "BX100W").Filter("T_State", 1).Filter("T_online", 2).All(&r)
+	//qs.Filter("T_pid", 242).Filter("T_State", 1).Exclude("T_online", 1).All(&r)
+
+	return r
+}

+ 189 - 0
models/Device/DeviceData.go

@@ -0,0 +1,189 @@
+package Device
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 模板
+type DeviceData_R struct {
+	T_t    float32   // 温度
+	T_rh   float32   // 湿度
+	T_Site string    // GPS
+	T_time time.Time // 采集时间
+	T_sp   int       // 传感器参数ID
+	//create_time
+}
+type DeviceData_old struct {
+	T_t    float32
+	T_rh   float32
+	T_site string
+	T_sp   int
+}
+
+var redis_DeviceData cache.Cache
+
+func init() {
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_DeviceData", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redis_DeviceData, err = cache.NewCache("redis", config)
+	if err != nil || redis_DeviceData == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func RedisDeviceData_Set(T_sn string, T_id int, r DeviceData_R) (err error) {
+	key := T_sn + "|" + strconv.Itoa(T_id)
+
+	if redis_DeviceData.IsExist(key) {
+		var t DeviceData_R
+		v := redis_DeviceData.Get(key)
+		json.Unmarshal(v.([]byte), &t)
+		// 防止时间溢出
+		if time.Now().Unix() <= r.T_time.Unix() {
+			r.T_time = time.Now()
+		}
+		// 时间在当前时间的正负10分钟范围内
+		if !lib.IsWithinMinutesRange(r.T_time, 600) {
+			// 时间有问题,或者差距太大
+			return
+		}
+		// 提前最新数据
+		//if t.T_time.Unix() > r.T_time.Unix() {
+		//	if lib.IsWithinMinutesRange( t.T_time,600){
+		//		// 储存的 是最新数据
+		//		return
+		//	}
+		//}
+	}
+
+	//json序列化
+	str, err := json.Marshal(r)
+	if err != nil {
+		logs.PrintlnError("RedisDeviceData_Set", err)
+		return
+	}
+	err = redis_DeviceData.Put(key, str, 24*time.Hour)
+	if err != nil {
+		logs.Println("set key:", key, ",value:", str, err)
+	}
+	return
+}
+
+func RedisDeviceData_Get(T_sn string, T_id int) (r DeviceData_R, is bool) {
+	key := T_sn + "|" + strconv.Itoa(T_id)
+	if redis_DeviceData.IsExist(key) {
+		v := redis_DeviceData.Get(key)
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+
+	return DeviceData_R{}, false
+}
+func RedisDeviceData_Del(T_sn string, T_id int) {
+	key := T_sn + "|" + strconv.Itoa(T_id)
+	if redis_DeviceData.IsExist(key) {
+		redis_DeviceData.Delete(key)
+	}
+}
+
+// 添加 数据   返回   真:替换   假:第一条无替换   ignore 重复是否忽略
+func Add_DeviceData(T_sn string, T_id int, v DeviceData_R, ignore bool) (bool, DeviceData_old) {
+	o := orm.NewOrm()
+
+	// 开始插入数据
+	sql := "INSERT INTO z_device_data_" + T_sn + " (`t_id`, `t_t`, `t_rh`, `t_site`, `t_time`, `t_sp`) " +
+		"VALUES (" + strconv.Itoa(T_id) + " , " + lib.To_string(v.T_t) + ", " + lib.To_string(v.T_rh) + ", '" + v.T_Site + "', '" + v.T_time.Format("2006-01-02 15:04:05") + "', " + lib.To_string(v.T_sp) + ")"
+	//  这里有时间优化  用于一次 prepare 多次 exec,以提高批量执行的速度
+	logs.Println(sql)
+	_, err := o.Raw(sql).Exec()
+	if err != nil {
+		//Error 1146: Table 'cold.z_device_data_202335objgv2pzk' doesn't exist
+		//Error 1062: Duplicate entry '1111-11-11 00:00:00-1' for key 'z_device_data_202335objgv2pzk.PRIMARY'
+
+		if strings.Contains(err.Error(), "Error 1062") {
+			if ignore { //  重复是否忽略
+				logs.Println("重复忽略数据:", T_sn, T_id, v.T_time.Format("2006-01-02 15:04:05"))
+				return true, DeviceData_old{}
+			}
+			var DeviceData_r DeviceData_old
+			err = o.Raw("SELECT * FROM z_device_data_" + T_sn + " WHERE `t_id` = " + strconv.Itoa(T_id) + " AND `t_time` = Cast('" + v.T_time.Format("2006-01-02 15:04:05") + "' AS Binary(19))").QueryRow(&DeviceData_r)
+			if err != nil {
+				logs.PrintlnError("SELECT_DeviceData:" + err.Error())
+				return false, DeviceData_old{}
+			}
+
+			// 重复添加
+			sql = "UPDATE z_device_data_" + T_sn + " SET `t_t` = " + lib.To_string(v.T_t) + ", `t_rh` = " + lib.To_string(v.T_rh) + ", `t_site` = '" + v.T_Site + "' WHERE `t_id` = " + strconv.Itoa(T_id) + " AND `t_time` = Cast('" + v.T_time.Format("2006-01-02 15:04:05") + "' AS Binary(19))"
+			//  这里有时间优化  用于一次 prepare 多次 exec,以提高批量执行的速度
+			logs.Println(sql)
+			_, err = o.Raw(sql).Exec()
+			if err != nil {
+				logs.PrintlnError("UPDATE_DeviceData:" + sql + err.Error())
+
+			}
+			logs.PrintlnData(sql)
+			return true, DeviceData_r
+		} else {
+			logs.PrintlnError("Add_DeviceData:" + sql + err.Error())
+			return false, DeviceData_old{}
+		}
+
+	} else {
+		logs.PrintlnData(sql)
+	}
+	//res.RowsAffected()
+
+	// 添加缓存 sn id 最新数据
+	RedisDeviceData_Set(T_sn, T_id, v)
+	//RedisDeviceData_Set(T_sn+"_Node", T_id, v)
+
+	return false, DeviceData_old{}
+}
+
+func Ttt() {
+	time.Sleep(time.Second)
+
+	t1 := "2019-01-08 13:50:30"            //外部传入的时间字符串
+	timeTemplate1 := "2006-01-02 15:04:05" //常规类型
+
+	stamp, _ := time.ParseInLocation(timeTemplate1, t1, time.Local) //使用parseInLocation将字符串格式化返回本地时区时间
+
+	//// 更新记录 - 缓存
+	DeviceData_t := DeviceData_R{
+		T_t:    88,
+		T_rh:   88,
+		T_Site: "88",
+		T_time: stamp,
+	}
+
+	if r_, DeviceData_old_r := Add_DeviceData("202335objgv2pzk1", 1, DeviceData_t, true); r_ {
+		// 被替换
+		Add_DeviceDataOld(DeviceDataOld{
+			T_sn:        "ffffff",
+			T_id:        111,
+			T_t:         DeviceData_old_r.T_t,
+			T_rh:        DeviceData_old_r.T_rh,
+			T_Site:      DeviceData_old_r.T_site,
+			T_time:      DeviceData_t.T_time,
+			T_operation: 2,
+			T_uuid:      "",
+		})
+	}
+}

+ 105 - 0
models/Device/DeviceDataOld.go

@@ -0,0 +1,105 @@
+package Device
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"github.com/beego/beego/v2/adapter/orm"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 设备记录数据 操作历史记录(只存放留存下来的数据,最近记录直接写到 记录数据中)
+type DeviceDataOld struct {
+	Id int `orm:"column(ID);size(11);auto;pk"`
+
+	T_sn string `orm:"index;size(256);"` // 主键 设备序列号
+	T_id int    `orm:"index;size(12);"`  // 主键 传感器编号
+
+	T_t    float32   `orm:"size(20);digits(6);decimals(1)"` // 温度
+	T_rh   float32   `orm:"size(20);digits(6);decimals(1)"` // 湿度
+	T_Site string    `orm:"size(100);"`                     // GPS
+	T_time time.Time `orm:"index;type(timestamp);"`         // 主键 采集时间
+
+	T_operation int `orm:"size(12);"` // 操作类型  1:插入   2:替换   3:删除   4:忽略
+
+	T_uuid     string    `orm:"size(256);"`                                       // 操作人  Admin->T_uuid , 如果为空字符串 则是设备本身上传的,标记为(设备)
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+type DeviceDataOld_R struct {
+	T_t         float32   // 温度
+	T_rh        float32   // 湿度
+	T_Site      string    // GPS
+	T_time      time.Time // 采集时间
+	T_operation int       // 操作类型  1:插入   2:替换   3:删除
+	T_uuid      string    // 操作人  Admin->T_uuid , 如果为空字符串 则是设备本身上传的
+	CreateTime  time.Time //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+func (t *DeviceDataOld) TableName() string {
+	return "device_data_old" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceDataOld))
+}
+
+func DeviceDataOldToDeviceDataOld_R(r DeviceDataOld) (t DeviceDataOld_R) {
+	t.T_t = r.T_t
+	t.T_rh = r.T_rh
+	t.T_Site = r.T_Site
+	t.T_time = r.T_time
+	t.T_operation = r.T_operation
+	t.T_uuid = r.T_uuid
+	t.CreateTime = r.CreateTime
+
+	return
+}
+
+// ---------------- 特殊方法 -------------------
+
+// 添加
+func Add_DeviceDataOld(m DeviceDataOld) (id int64, err error) {
+	o := orm.NewOrm()
+
+	id, err = o.Insert(&m)
+	if err != nil {
+		logs.Println("Add_DeviceDataOld", err)
+	}
+	return id, err
+}
+
+// 获取列表
+func Read_DeviceDataOld_List(T_sn string, T_id int, T_time string, page int, page_z int) (r []DeviceDataOld_R, cnt int64) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(DeviceDataOld))
+	var offset int64
+	if page_z == 0 {
+		page_z = conf.Page_size
+	}
+
+	if page <= 1 {
+		offset = 0
+	} else {
+		offset = int64((page - 1) * page_z)
+	}
+
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_sn", T_sn).And("T_id", T_id).And("T_time", T_time)
+
+	var rx []DeviceDataOld
+
+	qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond1)).OrderBy("-Id").All(&rx)
+	cnt, _ = qs.SetCond((*orm2.Condition)(cond1)).Count()
+
+	for _, v := range rx {
+		r = append(r, DeviceDataOldToDeviceDataOld_R(v))
+	}
+
+	return r, cnt
+}

+ 186 - 0
models/Device/DeviceParameter.go

@@ -0,0 +1,186 @@
+package Device
+
+import (
+	"Cold_mqtt/logs"
+	"github.com/beego/beego/v2/adapter/orm"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 模板
+type DeviceParameter struct {
+	Id int `orm:"column(ID);size(11);auto;pk"`
+
+	T_sn string `orm:"size(256);"` // 设备序列号 KF开头,环境监测主机。 YD开头,温途监测主机
+
+	T_name     string `orm:"size(256);"` //设备名称 (传 * 代表托管给平台处理) *
+	T_saveT    int    `orm:"size(5);"`   //数据保存间隔 s(60~3600) 默认:60 *
+	T_overA    int    `orm:"size(5);"`   //数据保存间隔 超限报警触发间隔 s(60~600) 默认:60  *
+	T_outA     int    `orm:"size(5);"`   //超限报警触发间隔 s(0,60~600)  为0时只触发一次 默认:60 *
+	T_lostA    int    `orm:"size(5);"`   //传感器掉线报警触发间隔  s(0,60~600) 为0时只触发一次 默认:60  *
+	T_bat      int    `orm:"size(5);"`   // 电池电量下限   %(0~30) 默认:20 *
+	T_warn     int    `orm:"size(5);"`   // 超限预警触发间隔 s(0,60~600)为0时只触发一次 默认:60  *
+	T_warnD    int    `orm:"size(5);"`   // 超限预警延时   s(0~600) 默认:0 *
+	T_scan     int    `orm:"size(5);"`   // 显示轮播间隔   s(1~240) 默认:5 *
+	T_dormancy int    `orm:"size(5);"`   // 是否进入休眠  0:关闭 1:开启    默认:0
+	T_snum     int    `orm:"size(5);"`   // 【管理主机】 - 传感器数量  (范围0~255)
+	T_btname   string `orm:"size(256);"` //蓝牙打印机名称
+	T_btsid    int    `orm:"size(256);"` //打印机服务号
+	T_btchar   int    `orm:"size(256);"` //蓝牙特征码
+
+	T_uuid      string `orm:"size(256);"`          //处理 人员
+	T_Msid      int64  `orm:"size(50);default(0)"` // 消息识别ID
+	T_SendState int    `orm:"size(2);default(1)"`  // 发送状态  0 待发送   1 发送成功  2 发送失败  3 丢弃
+	T_State     int    `orm:"size(2);default(1)"`  //  1 系统获取   2 用户提交
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+func (t *DeviceParameter) TableName() string {
+	return "device_parameter" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceParameter))
+	orm.Debug = true
+}
+
+// ---------------- 特殊方法 -------------------
+// 添加
+func Add_DeviceParameter_fun(Deviceparameter DeviceParameter) bool {
+	if DeviceParameter_r, is := Read_DeviceParameter(Deviceparameter.T_sn); is {
+		if Deviceparameter.T_name != DeviceParameter_r.T_name ||
+			Deviceparameter.T_saveT != DeviceParameter_r.T_saveT ||
+			Deviceparameter.T_overA != DeviceParameter_r.T_overA ||
+			Deviceparameter.T_outA != DeviceParameter_r.T_outA ||
+			Deviceparameter.T_lostA != DeviceParameter_r.T_lostA ||
+			Deviceparameter.T_bat != DeviceParameter_r.T_bat ||
+			Deviceparameter.T_scan != DeviceParameter_r.T_scan ||
+			Deviceparameter.T_warn != DeviceParameter_r.T_warn ||
+			Deviceparameter.T_warnD != DeviceParameter_r.T_warnD ||
+			Deviceparameter.T_dormancy != DeviceParameter_r.T_dormancy ||
+			Deviceparameter.T_snum != DeviceParameter_r.T_snum ||
+			Deviceparameter.T_btname != DeviceParameter_r.T_btname ||
+			//Deviceparameter.T_speed != DeviceParameter_r.T_speed ||
+			Deviceparameter.T_btsid != DeviceParameter_r.T_btsid ||
+			Deviceparameter.T_btchar != DeviceParameter_r.T_btchar {
+			if _, is := Add_DeviceParameter(Deviceparameter); !is {
+				logs.PrintlnError("Add_DeviceParameter_fun", "插入失败 [DeviceParameter]")
+			}
+			return true
+		}
+	} else {
+		if _, is := Add_DeviceParameter(Deviceparameter); !is {
+			logs.PrintlnError("Add_DeviceParameter_fun", "插入失败 [DeviceParameter]")
+		}
+		return true
+	}
+	return false
+}
+
+// 添加
+func Add_DeviceParameter(m DeviceParameter) (DeviceParameter, bool) {
+	o := orm.NewOrm()
+	m.Id = 0
+	id, err := o.Insert(&m)
+	if err != nil {
+		logs.Println("Add_DeviceParameter", err)
+		return m, false
+	}
+	m.Id = int(id)
+	return m, true
+}
+
+// 获取设备参数
+func Read_DeviceParameter(T_sn string) (t DeviceParameter, bool bool) {
+
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(DeviceParameter))
+
+	cond := orm.NewCondition()
+	cond = cond.And("T_sn", T_sn).And("T_State", 1)
+
+	qs.SetCond((*orm2.Condition)(cond)).OrderBy("-UpdateTime").One(&t)
+	// 判断是否有数据
+	if t.Id == 0 {
+		return t, false
+	}
+
+	return t, true
+}
+
+// 修改
+func Update_DeviceParameter(r DeviceParameter, cols ...string) bool {
+	o := orm.NewOrm()
+	if _, err := o.Update(&r, cols...); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+		return true
+	}
+	return false
+}
+
+// 获取列表
+func Read_DeviceParameter_SN_Msid(T_sn string, T_Msid int64) (r DeviceParameter, err error) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(DeviceParameter))
+
+	err = qs.Filter("T_sn", T_sn).Filter("T_Msid", T_Msid).One(&r)
+	if err != nil {
+		logs.Println("Read_DeviceParameter_SN_Msid:", err)
+	}
+	return r, err
+}
+
+// 修改
+func Update_DeviceParameter_(m DeviceParameter) (err error) {
+	o := orm.NewOrm()
+	if _, err := o.Update(&m, "T_SendState"); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+	}
+
+	return err
+}
+
+// 获取列表
+func Read_DeviceParameter_SN_T_State_1(T_sn string) (r []DeviceParameter) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(DeviceParameter))
+
+	qs.Limit(3, 0).Filter("T_sn", T_sn).Filter("T_State", 2).OrderBy("-Id").All(&r)
+
+	return r
+}
+
+// 获取列表
+func Read_DeviceParameter_SN_T_SendState_0(T_sn string) (r []DeviceParameter) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(DeviceParameter))
+
+	qs.Limit(1, 0).Filter("T_sn", T_sn).Filter("T_State", 2).Filter("T_SendState", 0).OrderBy("-Id").All(&r)
+
+	return r
+}
+
+// 获取最新数据
+func UPDATE_DeviceParameter_SN_T_SendState_2_sql(T_sn string) {
+	o := orm.NewOrm()
+
+	res, err := o.Raw("UPDATE device_parameter SET `t__send_state` = 3 WHERE `t_sn` LIKE '%" + T_sn + "%' AND `t__send_state` = '0' AND `t__state` = '2'").Exec()
+	if err == nil {
+		num, _ := res.RowsAffected()
+		logs.Println("mysql row affected nums: ", num)
+	}
+
+}

+ 191 - 0
models/Device/DeviceSensor.go

@@ -0,0 +1,191 @@
+package Device
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"time"
+)
+
+// 模板
+type DeviceSensor struct {
+	Id   int    `orm:"column(ID);size(11);auto;pk"`
+	T_sn string `orm:"index;size(256);"` // 设备序列号 KF开头,环境监测主机。 YD开头,温途监测主机
+	T_id int    `orm:"index;size(11);"`  // 传感器编号
+	T_sp int    `orm:"size(11);null"`    // 传感器参数id
+
+	T_name string `orm:"size(256);"` // 标题
+
+	T_pid      int    `orm:"index;size(256);"`           // Account.Company 绑定公司
+	T_Class    string `orm:"size(256);"`                 // Device.DeviceClass.Id 设备分类  C1|C2|
+	T_Notice   string `orm:"size(256);"`                 // 通知绑定    N1|N2|N3|
+	T_datashow int    `orm:"index;size(2);default(1)"`   // 0 屏蔽数据展示  1 正常数据展示   (屏蔽后 数据展示无法看到,设备管理中 不受影响)
+	T_sort     int    `orm:"index;size(200);default(1)"` // 排序
+	T_3dview   string `orm:"size(256);"`                 //3D 视图ID
+	T_type     int    `orm:"index;size(4);"`             // 0:未知类型   1库房   2移动
+	T_link     int    `orm:"index;size(4);default(1)"`   // 0:断开/故障   1连接     :实时数据
+	T_State    int    `orm:"index;size(2);default(1)"`   // 0 屏蔽   1 正常  (屏蔽后 只有内部管理员才能看到,用户 输入SN\名称 搜索时 也能看到)
+
+	// 设备同步参数
+	T_Dattery  int    `orm:"size(4);"`                 // 电量
+	T_Site     string `orm:"size(200);"`               // GPS                         :实时数据
+	T_monitor  int    `orm:"index;size(2);"`           // 监控状态 0 未监控 1 监控       :记录数据(=0)   下线(=0)
+	T_online   int    `orm:"index;size(2);default(1)"` // 在线状态 0 未启用  1 在线  2 离线    :上下线
+	T_online_s int    `orm:"index;size(2);default(0)"` // 在线状态-备用  0 未启用  1 在线  2 离线   :上下线
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+type DeviceSensor_Del struct {
+	T_sn string
+	T_id int
+}
+
+func (t *DeviceSensor) TableName() string {
+	return "device_sensor" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_DeviceSensor cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceSensor))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_DeviceSensor", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redisCache_DeviceSensor, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_DeviceSensor == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+		panic(any(errMsg))
+	}
+
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func Redis_DeviceSensor_Set(r DeviceSensor) (err error) {
+	key := r.T_sn + "|" + strconv.Itoa(r.T_id)
+
+	//json序列化
+	str, err := json.Marshal(r)
+	if err != nil {
+		logs.PrintlnError("Redis_DeviceSensor_Set", err)
+		return
+	}
+
+	err = redisCache_DeviceSensor.Put(key, str, 24*time.Hour)
+	if err != nil {
+		logs.Println("set key:", key, ",value:", str, err)
+	}
+	return
+}
+func Redis_DeviceSensor_Get(key string) (r DeviceSensor, is bool) {
+	if redisCache_DeviceSensor.IsExist(key) {
+		//println("找到key:",key)
+		v := redisCache_DeviceSensor.Get(key)
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+	//println("没有 找到key:",key)
+	return DeviceSensor{}, false
+}
+func Redis_DeviceSensor_DelK(r DeviceSensor) (err error) {
+	key := r.T_sn + "|" + strconv.Itoa(r.T_id)
+	err = redisCache_DeviceSensor.Delete(key)
+	return
+}
+
+// ---------------- 特殊方法 -------------------
+
+// 添加
+func Add_DeviceSensor(Device_r Device, Devicesensor_r DeviceSensor) int {
+	//if(conf.Test_server) {return }
+
+	o := orm.NewOrm()
+
+	// 设备同步参数
+	Devicesensor_r.T_Dattery = Device_r.T_Dattery   // 电量
+	Devicesensor_r.T_Site = Device_r.T_Site         // GPS
+	Devicesensor_r.T_monitor = Device_r.T_monitor   // 监控状态 0 未监控 1 监控
+	Devicesensor_r.T_online = Device_r.T_online     // 在线状态 0 未启用  1 在线  2 离线
+	Devicesensor_r.T_online_s = Device_r.T_online_s // 在线状态-备用  0 未启用  1 在线  2 离线
+
+	// 三个返回参数依次为:是否新创建的,对象 Id 值,错误
+	_, id, err := o.ReadOrCreate(&Devicesensor_r, "T_sn", "T_id")
+	if err != nil {
+		return 0
+	}
+
+	Redis_DeviceSensor_Set(Devicesensor_r)
+	return int(id)
+}
+
+// 修改
+func Update_DeviceSensor(r DeviceSensor, cols ...string) bool {
+	o := orm.NewOrm()
+	if _, err := o.Update(&r, cols...); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+		Redis_DeviceSensor_Set(r)
+		return true
+	}
+
+	return false
+}
+
+// 获取
+func Read_DeviceSensor_ByT_sn(T_sn string, T_id int) (r DeviceSensor, is bool) {
+	key := T_sn + "|" + strconv.Itoa(T_id)
+	if r, is := Redis_DeviceSensor_Get(key); is {
+		return r, true
+	}
+	o := orm.NewOrm()
+	o.Using("default")
+	r = DeviceSensor{T_sn: T_sn, T_id: T_id}
+	err := o.Read(&r, "T_sn", "T_id") // o.Read(&r,"Tokey") 如果不是 主键 就得指定字段名
+	if err != nil {
+		return r, false
+	}
+
+	Redis_DeviceSensor_Set(r)
+	return r, true
+}
+
+// 获取列表
+func Read_DeviceSensor_ByTsn(T_sn string) []DeviceSensor {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+	qs := o.QueryTable(new(DeviceSensor))
+	var r []DeviceSensor
+	qs.Filter("T_sn", T_sn).OrderBy("T_id").All(&r)
+
+	return r
+}
+
+// 设备同步参数
+func Update_Device_To_DeviceSensor(r_Device Device) bool {
+	DeviceSensor_list := Read_DeviceSensor_ByTsn(r_Device.T_sn)
+	for _, v := range DeviceSensor_list {
+
+		// 设备同步参数
+		v.T_Dattery = r_Device.T_Dattery   // 电量
+		v.T_Site = r_Device.T_Site         // GPS
+		v.T_monitor = r_Device.T_monitor   // 监控状态 0 未监控 1 监控
+		v.T_online = r_Device.T_online     // 在线状态 0 未启用  1 在线  2 离线
+		v.T_online_s = r_Device.T_online_s // 在线状态-备用  0 未启用  1 在线  2 离线
+
+		Update_DeviceSensor(v, "T_Dattery", "T_Site", "T_monitor", "T_online", "T_online_s")
+	}
+
+	return true
+}

+ 246 - 0
models/Device/DeviceSensorParameter.go

@@ -0,0 +1,246 @@
+package Device
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"time"
+)
+
+// 传感器 参数
+type DeviceSensorParameter struct {
+	Id     int    `orm:"column(ID);size(11);auto;pk"`
+	T_sn   string `orm:"index;size(256);"` // 设备序列号 KF开头,环境监测主机。 YD开头,温途监测主机
+	T_id   int    `orm:"index;size(11);"`  // 传感器编号
+	T_name string `orm:"size(256);"`       //传感器名称
+
+	// 报警
+
+	T_Tlower  float32 `orm:"size(200);"` //  温度下限
+	T_Tupper  float32 `orm:"size(200);"` //  温度上限
+	T_RHlower float32 `orm:"size(200);"` //  湿度下限
+	T_RHupper float32 `orm:"size(200);"` //  湿度上限
+
+	// 预警
+	T_enprel     int     `orm:"size(2);default(1)"` // 是否启用预警
+	T_tprel      float32 `orm:"size(200);"`         //  温度预警下限
+	T_tpreu      float32 `orm:"size(200);"`         //  温度预警上限
+	T_hprel      float32 `orm:"size(200);"`         //  湿度预警下限
+	T_hpreu      float32 `orm:"size(200);"`         //  温度预警上限
+	T_enprelnote int     `orm:"size(2);default(1)"` // 是否启用预警记录
+
+	//T_speed int `orm:"size(5);"`           // 传感器采样率   s(1~240) 默认:15 *
+	//T_sense int `orm:"size(5);"`           // 传感器灵敏度   s(0~10) 默认:5
+	T_en   int `orm:"size(2);default(1)"` // en:是否启用传感器,   0 停用   1 启用
+	T_free int `orm:"size(2);default(1)"` //   是否为闲置状态    1:启用(正常 实时、记录, 不 报警、预警)
+	//0:关闭(正常 实时、记录、报警、预警)  默认:0
+
+	// 逻辑字段
+	T_uuid      string `orm:"size(256);"`          //处理 人员
+	T_Msid      int64  `orm:"size(50);default(0)"` // 消息识别ID
+	T_SendState int    `orm:"size(2);default(0)"`  // 发送状态  0 待发送   1 发送成功  2 失败  3 覆盖
+	T_State     int    `orm:"size(2);default(1)"`  //  1 系统获取   2 用户提交
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+func (t *DeviceSensorParameter) TableName() string {
+	return "device_sensor_parameter" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_DeviceSensorParameter cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceSensorParameter))
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_DeviceSensorParameter", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redisCache_DeviceSensorParameter, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_DeviceSensorParameter == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func Redis_DeviceSensorParameter_Set(key string, r DeviceSensorParameter) (err error) {
+
+	//json序列化
+	str, err := json.Marshal(r)
+	if err != nil {
+		logs.PrintlnError("Redis_DeviceSensorParameter_Set", err)
+		return
+	}
+
+	err = redisCache_DeviceSensorParameter.Put(key, str, 8*time.Hour)
+	if err != nil {
+		logs.Println("set key:", key, ",value:", str, err)
+	}
+	return
+}
+
+// if r,is :=Redis_Get(T_sn);is{
+// return r,nil
+// }
+func Redis_DeviceSensorParameter_Get(key string) (r DeviceSensorParameter, is bool) {
+	if redisCache_DeviceSensorParameter.IsExist(key) {
+		//println("找到key:",key)
+		v := redisCache_DeviceSensorParameter.Get(key)
+		json.Unmarshal(v.([]byte), &r)
+		return r, true
+	}
+	//println("没有 找到key:",key)
+	return DeviceSensorParameter{}, false
+}
+
+// ---------------- 特殊方法 -------------------
+func Add_DeviceSensorParameter_fun(Devicesensorparameter DeviceSensorParameter) int {
+	Devicesensorparameter_r, is := Read_DeviceSensorParameter(Devicesensorparameter.T_sn, Devicesensorparameter.T_id)
+	if is {
+		if Devicesensorparameter_r.T_name != Devicesensorparameter.T_name ||
+			Devicesensorparameter_r.T_Tlower != Devicesensorparameter.T_Tlower ||
+			Devicesensorparameter_r.T_Tupper != Devicesensorparameter.T_Tupper ||
+			Devicesensorparameter_r.T_RHlower != Devicesensorparameter.T_RHlower ||
+			Devicesensorparameter_r.T_RHupper != Devicesensorparameter.T_RHupper ||
+			Devicesensorparameter_r.T_enprel != Devicesensorparameter.T_enprel ||
+			Devicesensorparameter_r.T_tprel != Devicesensorparameter.T_tprel ||
+			Devicesensorparameter_r.T_tpreu != Devicesensorparameter.T_tpreu ||
+			Devicesensorparameter_r.T_hprel != Devicesensorparameter.T_hprel ||
+			Devicesensorparameter_r.T_hpreu != Devicesensorparameter.T_hpreu ||
+			Devicesensorparameter_r.T_enprelnote != Devicesensorparameter.T_enprelnote ||
+			Devicesensorparameter_r.T_en != Devicesensorparameter.T_en ||
+			Devicesensorparameter_r.T_free != Devicesensorparameter.T_free {
+
+			// 添加记录
+			id, is := Add_DeviceSensorParameter(Devicesensorparameter)
+			if !is {
+				logs.PrintlnError("Add_DeviceSensorParameter_fun", "插入失败 [DeviceParameter]")
+			}
+			return int(id)
+		}
+	}
+	//else {
+	//	// 添加记录
+	//	id, is := Add_DeviceSensorParameter(Devicesensorparameter)
+	//	if !is {
+	//		logs.PrintlnError("Add_DeviceSensorParameter_fun", "插入失败 [DeviceParameter]")
+	//	}
+	//	return int(id), true
+	//}
+	return Devicesensorparameter_r.Id
+}
+
+// 添加
+func Add_DeviceSensorParameter(m DeviceSensorParameter) (int64, bool) {
+	o := orm.NewOrm()
+	m.Id = 0
+	key := m.T_sn + "|" + strconv.Itoa(m.T_id)
+	m.T_SendState = 0
+	m.T_State = 1
+	id, err := o.Insert(&m)
+	if err != nil {
+		logs.PrintlnError("Add_DeviceSensorParameter", err)
+		return id, false
+	}
+	Redis_DeviceSensorParameter_Set(key, m)
+	return id, true
+}
+
+// 获取设备参数
+func Read_DeviceSensorParameter(T_sn string, T_id int) (t DeviceSensorParameter, bool bool) {
+	key := T_sn + "|" + strconv.Itoa(T_id)
+	if t, is := Redis_DeviceSensorParameter_Get(key); is {
+		return t, true
+	}
+
+	o := orm.NewOrm()
+
+	qs := o.QueryTable(new(DeviceSensorParameter))
+
+	cond := orm.NewCondition()
+	cond = cond.And("T_sn", T_sn).And("T_id", T_id).And("T_State", 1)
+
+	qs.SetCond((*orm2.Condition)(cond)).OrderBy("-UpdateTime").One(&t)
+	// 判断是否有数据
+	if t.Id == 0 {
+		return t, false
+	}
+
+	Redis_DeviceSensorParameter_Set(key, t)
+	return t, true
+}
+
+// 修改
+func Update_DeviceSensorParameter(r DeviceSensorParameter, cols ...string) bool {
+	o := orm.NewOrm()
+	if _, err := o.Update(&r, cols...); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+		return true
+	}
+	return false
+}
+
+// 获取最新数据
+func UPDATE_DeviceSensorParamete_SN_T_SendState_2_sql(T_sn string, T_id string) {
+	o := orm.NewOrm()
+
+	res, err := o.Raw("UPDATE device_sensor_parameter SET `t__send_state` = 3 WHERE `t_sn` LIKE '%" + T_sn + "%' AND `t_id` = " + T_id + " AND `t__send_state` = '0' AND `t__state` = '2'").Exec()
+	if err == nil {
+		num, _ := res.RowsAffected()
+		logs.Println("mysql row affected nums: ", num)
+	}
+
+}
+
+// 获取列表
+func Read_DeviceSensorParameter_SN_T_id_Msid(T_sn string, T_Msid int64) (DeviceSensorParameter, bool) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+	var r []DeviceSensorParameter
+	qs := o.QueryTable(new(DeviceSensorParameter))
+
+	qs.Filter("T_sn", T_sn).Filter("T_Msid", T_Msid).OrderBy("-Id").All(&r)
+
+	if len(r) == 0 {
+		return DeviceSensorParameter{}, false
+	}
+
+	return r[0], true
+}
+
+// 获取最新数据
+func Read_DeviceSensorParameter_SN_T_SendState_0_sql(sn string) (maps []orm2.ParamsList) {
+	o := orm.NewOrm()
+
+	sql := "SELECT DISTINCT t_sn,t_id FROM device_sensor_parameter WHERE  `t_sn` = '" + sn + "' AND  `t__send_state` = '0' AND `t__state` = '2'"
+
+	logs.Println(sql)
+	o.Raw(sql).ValuesList(&maps)
+
+	return maps
+}
+
+// 获取列表
+func Read_DeviceSensorParameter_SN_T_id(T_sn string, T_id int) (r []DeviceSensorParameter) {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(DeviceSensorParameter))
+
+	qs.Limit(3, 0).Filter("T_sn", T_sn).Filter("T_id", T_id).Filter("T_State", 2).OrderBy("-Id").All(&r)
+
+	return r
+}

+ 31 - 0
models/Device/DeviceSnOld.go

@@ -0,0 +1,31 @@
+package Device
+
+import (
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+)
+
+type DeviceSnOld struct {
+	Id   int    `orm:"column(ID);size(11);auto;pk"`
+	T_sn string `orm:"size(256);"` // 分类
+}
+
+func (t *DeviceSnOld) TableName() string {
+	return "device_sn_old" // 数据库名称   // ************** 替换 DesignClass **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceSnOld))
+
+}
+
+// ---------------- 特殊方法 -------------------
+
+// 添加
+func Add_DeviceSnOld(T_sn string) {
+	o := orm.NewOrm()
+	user := DeviceSnOld{T_sn: T_sn}
+	// 三个返回参数依次为:是否新创建的,对象 Id 值,错误
+	o.ReadOrCreate(&user, "T_sn")
+}

+ 80 - 0
models/Device/DeviceTask.go

@@ -0,0 +1,80 @@
+package Device
+
+import (
+	"Cold_mqtt/logs"
+
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 模板
+type DeviceTask struct {
+	Id         int       `orm:"column(ID);size(11);auto;pk"`
+	T_sn       string    `orm:"size(256);null"`        // 设备序列号 KF开头,环境监测主机。 YD开头,温途监测主机
+	T_Ut_start time.Time `orm:"type(timestamp);null;"` // 采集时间
+	T_Ut_end   time.Time `orm:"type(timestamp);null;"` // 采集时间
+
+	T_Ut    time.Time `orm:"type(timestamp);null;"` // 采集时间
+	T_State int       `orm:"size(2);default(1)"`
+	//  1   start:开始监测任务,
+	//  2   stop:结束监测任务,
+	//  3   print:中途打印任务
+	//CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	//UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+
+}
+
+func (t *DeviceTask) TableName() string {
+	return "device_task" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(DeviceTask))
+}
+
+// ---------------- 特殊方法 -------------------
+// 添加
+func Add_DeviceTask(m DeviceTask) (id int64, err error) {
+	//if(conf.Test_server){
+	//	logs.Println("Add_DeviceTask:",m)
+	//	return id, err
+	//}
+	o := orm.NewOrm()
+	id, err = o.Insert(&m)
+	if err != nil {
+		logs.Println("Add_DeviceTask", err)
+	}
+	return id, err
+}
+
+// 修改
+func Update_DeviceTask(m DeviceTask) (err error) {
+	o := orm.NewOrm()
+	o.Update(&m, "T_Ut_start", "T_Ut_end", "T_Ut", "T_State")
+	return err
+}
+
+//// 获取 ById
+//func Read_DeviceTask_ById(id int) (r DeviceTask) {
+//	o := orm.NewOrm()
+//	r = DeviceTask{Id: id}
+//	err := o.Read(&r) // o.Read(&r,"Tokey") 如果不是 主键 就得指定字段名
+//	if err != nil {
+//		logs.Println(err)
+//	}
+//	return r
+//}
+
+// 获取全部
+func Read_DeviceTask_All_Limit_1(T_sn string) (DeviceTask, bool) {
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(DeviceTask))
+	var r_l []DeviceTask
+	qs.Limit(1, 0).Filter("T_sn", T_sn).OrderBy("-ID").All(&r_l)
+	if len(r_l) > 0 {
+		return r_l[0], true
+	}
+	return DeviceTask{}, false
+}

+ 33 - 0
models/GTime.go

@@ -0,0 +1,33 @@
+package models
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"time"
+)
+
+type JsonTime struct {
+	time.Time
+}
+
+func (t JsonTime) MarshalJSON() ([]byte, error) {
+	str := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
+	return []byte(str), nil
+}
+
+func (t JsonTime) Value() (driver.Value, error) {
+	var zeroTime time.Time
+	if t.Time.UnixNano() == zeroTime.UnixNano() {
+		return nil, nil
+	}
+	return t.Time, nil
+}
+
+func (t *JsonTime) Scan(v interface{}) error {
+	value, ok := v.(time.Time)
+	if ok {
+		*t = JsonTime{Time: value}
+		return nil
+	}
+	return fmt.Errorf("error %v", v)
+}

+ 76 - 0
models/Product/ProductType.go

@@ -0,0 +1,76 @@
+package Product
+
+import (
+	"Cold_mqtt/logs"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 产品类型
+type ProductType struct {
+	Id      int    `orm:"column(ID);size(11);auto;pk"`
+	T_name  string `orm:"size(256);"` // 产品统称 + 类型
+	T_model string `orm:"size(256);"` // 产品型号
+}
+
+// 产品统称	产品型号	类型
+// 库房采集器(高配版)	KF100N	RS485(NB-Iot)
+// 库房采集器(标准版)	KF100B	RS485
+// 库房采集器(黑白版)	KF200L	RS485
+// 冰箱采集器(数码管版)	BX200GSE	4G-Cat.1
+// 冰箱采集器(黑白版)	BX200GBL	4G-Cat.1
+// 保温箱采集器V1	BW100	4G-Cat.1
+// 运输采集器V1	YD100	4G-Cat.1/GPS
+// 保温箱采集器V2	BW200	4G-Cat.1
+// 运输采集器V2	YD200	4G-Cat.1/GPS
+// 新管理主机	MD100	TCP/4G/WiFi
+// 小管理主机	MD200G	4G-Cat.1/RS485
+
+var ProductType_list map[string]string // 泛型
+
+func (t *ProductType) TableName() string {
+	return "product_type" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+//var redisCache_WarningType cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(ProductType))
+
+	ProductType_list = make(map[string]string)
+
+	go Read_ProductType_All()
+}
+
+// 获取全部
+func Read_ProductType_All() {
+	time.Sleep(time.Second)
+
+	logs.Println("=========== 初始化产品类型 =========")
+
+	o := orm.NewOrm()
+	for true {
+		var r []ProductType
+		qs := o.QueryTable(new(ProductType))
+		qs.All(&r)
+
+		for _, v := range r {
+			ProductType_list[v.T_model] = v.T_name
+		}
+		time.Sleep(1 * time.Hour)
+	}
+
+}
+func Read_ProductType_Get(T_model string) string {
+	// 有先加入 给全部人发消息
+	v, ok := ProductType_list[T_model] /*如果确定是真实的,则存在,否则不存在 */
+	if ok {
+		return v
+	} else {
+		logs.Println("Read_ProductType_Get:", T_model, "未知类型")
+		return "未知类型"
+	}
+}

+ 36 - 0
models/Product/ProductUpgrade.go

@@ -0,0 +1,36 @@
+package Product
+
+import (
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 产品类型
+type ProductUpgrade struct {
+	Id         int    `orm:"column(ID);size(11);auto;pk"`
+	T_model    string `orm:"size(256);"` // 产品型号
+	T_version  string `orm:"size(256);"` // 版本
+	T_file     string `orm:"size(256);"` // 文件
+	T_remarks  string `orm:"size(256);"` // 备注
+	T_carryout int    `orm:"size(1);"`   // 全量发布
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);;auto_now_add"` //auto_now_add 第一次保存时才设置时间
+}
+
+type ProductUpgrade_T struct {
+	T_sn      string `orm:"size(256);null"` // 设备编号
+	T_version string `orm:"size(256);null"` // 版本
+	T_file    string `orm:"size(256);null"` // 文件
+}
+
+func (t *ProductUpgrade) TableName() string {
+	return "product_upgrade" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(ProductUpgrade))
+
+}

+ 33 - 0
models/System/Logs.go

@@ -0,0 +1,33 @@
+package System
+
+import (
+	"github.com/beego/beego/v2/adapter/orm"
+	"time"
+)
+
+type Logs struct {
+	Id         int    `orm:"column(ID);size(11);auto;pk"`
+	Logs_class string `orm:"size(256);"`  //
+	Logs_Title string `orm:"size(256);"`  // 标题
+	Logs_Txt   string `orm:"type(text);"` // 详情
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);auto_now_add"` //auto_now 每次 model 保存时都会对时间自动更新
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);auto_now"`     //auto_now_add 第一次保存时才设置时间
+}
+
+func (t *Logs) TableName() string {
+	return "Logs" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(Logs))
+
+}
+
+// 添加  logs.Println("MqttServer","参数请求 [Rt_Parameter]","base")
+func Add_Logs(Logs_class string, Logs_Title string, Logs_Txt string) {
+	o := orm.NewOrm()
+	m := Logs{Logs_class: Logs_class, Logs_Title: Logs_Title, Logs_Txt: Logs_Txt}
+	o.Insert(&m)
+}

+ 429 - 0
models/Warning/Warning.go

@@ -0,0 +1,429 @@
+package Warning
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	"fmt"
+	"github.com/astaxie/beego/cache"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type Warning struct {
+	Id        int64     `orm:"pk;column(ID);size(11);auto;"`
+	T_pid     int       `orm:"size(256);null"`              // Account.Company 绑定公司
+	T_tp      int       `orm:"index;size(200);null"`        // 报警类型   ->WarningList
+	T_sn      string    `orm:"index;size(256);null"`        // 设备序列号
+	T_D_name  string    `orm:"size(256);null"`              // 设备名称
+	T_id      int       `orm:"index;size(200);null"`        // 传感器 ID
+	T_DS_name string    `orm:"size(256);null"`              // 传感器名称
+	T_Remark  string    `orm:"type(text);"`                 // 采集内容
+	T_t       float32   `orm:"size(256);"`                  // 温度
+	T_rh      float32   `orm:"size(256);"`                  // 湿度
+	T_site    string    `orm:"size(256);"`                  // GPS
+	T_sp      int       `orm:"size(11);"`                   // 传感器参数id
+	T_Ut      time.Time `orm:"index;type(timestamp);null;"` // 采集时间
+	T_fUt     time.Time `orm:"type(timestamp);null;"`       // 首次采集时间
+	T_Text    string    `orm:"type(text);null"`             // 处理备注
+	T_Log     string    `orm:"type(text);null"`             // 通知日志
+	T_Msid    int64     `orm:"size(256);null"`              // 消息ID
+
+	T_State    int       `orm:"size(2);default(2)"`                                    // 0 删除   1 不处理   2 已处理   3 未处理
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     // auto_now 每次 model 保存时都会对时间自动更新
+}
+
+// 多字段索引
+func (u *Warning) TableIndex() [][]string {
+	return [][]string{
+		[]string{"T_pid", "T_tp", "T_State"},
+	}
+}
+
+func (t *Warning) TableName() string {
+	return "warning" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+var redisCache_Warning cache.Cache
+var redisCache_WarningStart cache.Cache // 缓存报警开始 时间点
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(Warning))
+
+	config := fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_WarningNum", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	var err error
+	redisCache_Warning, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_Warning == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+
+	config = fmt.Sprintf(`{"key":"%s","conn":"%s","dbNum":"%s","password":"%s"}`,
+		"redis_WarningStart", conf.Redis_address, conf.Redis_dbNum, conf.Redis_password)
+	logs.Println(config)
+	redisCache_WarningStart, err = cache.NewCache("redis", config)
+	if err != nil || redisCache_WarningStart == nil {
+		errMsg := "failed to init redis"
+		logs.Println(errMsg, err)
+	}
+}
+
+// ---------------- Redis -------------------
+// Redis_Set(m.T_sn,m) // Redis 更新缓存
+func Redis_Warning_Set(key string) (err error) {
+	err = redisCache_Warning.Put(key, "", 24*time.Hour)
+	if err != nil {
+		logs.Println("set key:", key)
+	}
+	return
+}
+func Redis_Warning_Repeat_T_sn_Msid(key string) (is bool) {
+	if redisCache_Warning.IsExist(key) {
+		redisCache_Warning.Put(key, "", 60*time.Minute)
+		//println("找到key:",key)
+		return true
+	}
+	redisCache_Warning.Put(key, "", 60*time.Minute)
+	return false
+}
+
+// 缓存报警次数     cut 持续秒        (计次,剩余时间s)
+func Redis_Warning_Num(key string) (int64, int) {
+	if redisCache_Warning.IsExist(key) {
+		//logs.Println("Redis_Warning_Num  找到key:", key)
+		r := redisCache_Warning.Get(key)
+		if r == nil {
+			return 0, 0
+		}
+		value_str := string(r.([]byte))
+		value_str_list := strings.Split(value_str, "|")
+		if len(value_str_list) != 2 {
+			return 0, 0
+		}
+		logs.Println("value_str_list:", value_str_list) //value_str_list: [[1 1677724222]]
+		value_int, _ := strconv.ParseInt(value_str_list[0], 10, 64)
+		cut := int(time.Unix(lib.To_int64(value_str_list[1]), 0).Unix() - time.Now().Unix()) // 计算 还剩 s秒时间
+		return value_int, cut
+	}
+	return 0, 0
+}
+
+// 缓存报警次数     cut 持续秒        (计次,剩余时间s)
+func Redis_Warning_Num_W(key, cuts string) (int64, int) {
+	cut, err := strconv.Atoi(cuts)
+	if err != nil {
+		logs.PrintlnError("Redis_Warning_Num err:", err)
+	}
+
+	if redisCache_Warning.IsExist(key) {
+		//logs.Println("Redis_Warning_Num  找到key:", key)
+		r := redisCache_Warning.Get(key)
+		if r == nil {
+			goto breakHere // 跳转到标签
+		}
+		value_str := string(r.([]byte))
+		value_str_list := strings.Split(value_str, "|")
+		logs.Println("value_str_list:", value_str_list) //value_str_list: [[1 1677724222]]
+		if len(value_str_list) != 2 {
+			goto breakHere // 跳转到标签
+		}
+		value_int, _ := strconv.ParseInt(value_str_list[0], 10, 64)
+		value_int += 1 // 数量
+		//logs.Println("有数据 value_int:", value_int)
+		cut = int(time.Unix(lib.To_int64(value_str_list[1]), 0).Unix() - time.Now().Unix()) // 计算 还剩 s秒时间
+		redisCache_Warning.Put(key, fmt.Sprintf("%d|%s", value_int, value_str_list[1]), time.Duration(cut)*time.Second)
+		logs.Println("Redis_Warning_Num KEY:", key, "->", value_int, "  还剩时间 ->", cut)
+
+		return value_int, cut
+	}
+breakHere:
+	//logs.Println("没有数据 value_int:", 1)
+	s, _ := time.ParseDuration(fmt.Sprintf("%ds", cut))
+	redisCache_Warning.Put(key, fmt.Sprintf("1|%d", time.Now().Add(s).Unix()), time.Duration(cut)*time.Second)
+	logs.Println("Redis_Warning_Num NewKEY:", key, " cut:", cut)
+	return 1, cut
+}
+
+// 管理员 缓存报警次数
+func Redis_WarningToAdmin_Num(key string) bool {
+	if redisCache_Warning.IsExist(key) {
+		////logs.Println("Redis_Warning_Num  找到key:", key)
+		//r := redisCache_Warning.Get(key)
+		//value_str := string(r.([]byte))
+		//value_int, _ := strconv.ParseInt(value_str, 10, 64)
+		//value_int += 1
+		////logs.Println("有数据 value_int:", value_int)
+		////redisCache_Warning.Put(key, strconv.FormatInt(value_int, 10), 11*time.Minute)
+		return false
+	}
+	//logs.Println("没有数据 value_int:", 1)
+	redisCache_Warning.Put(key, "1", 30*time.Minute)
+	return true
+}
+
+// ---  缓存 报警时间,减轻数据库压力
+func Redis_WarningTimeListIs(m Warning) bool {
+	key := m.T_sn + "|" + strconv.Itoa(m.T_id) + "|" + strconv.Itoa(m.T_tp) + "|" + m.T_Ut.Format("2006-01-02 15:04:05")
+	if redisCache_WarningStart.IsExist(key) {
+
+		return true
+	}
+	redisCache_WarningStart.Put(key, "", 60*time.Minute)
+
+
+	return false
+}
+// 首次开始时间
+func Redis_WarningStart(m Warning) time.Time {
+	key := m.T_sn + "|" + strconv.Itoa(m.T_id) + "|" + strconv.Itoa(m.T_tp)
+	if redisCache_WarningStart.IsExist(key) {
+		// 上一条报警时间
+		val := redisCache_WarningStart.Get(key)
+		// 将字符串转换为整数
+		unixTime, err := strconv.ParseInt(string(val.([]uint8)), 10, 64)
+		if err == nil {
+			// 转换为 time.Time 类型
+			time_ut_lod := time.Unix(unixTime, 0)
+			if time_ut_lod != m.T_fUt {
+				logs.Println("报警首时间不一样 (提示):",time_ut_lod.Format("2006-01-02 15:04:05"),m.T_fUt.Format("2006-01-02 15:04:05"))
+			}
+			return time_ut_lod
+		}
+		logs.Println("时间转换错误:",string(val.([]uint8)), err.Error())
+	}
+	redisCache_WarningStart.Put(key, fmt.Sprintf("%d", m.T_fUt.Unix()), 60*time.Minute)
+	return m.T_fUt
+}
+//func Redis_WarningStart(m Warning, time_ut time.Time) (bool, time.Time) {
+//	key := m.T_sn + "|" + strconv.Itoa(m.T_id) + "|" + strconv.Itoa(m.T_tp)
+//	logs_s := ""
+//	logs_s += key + "|" + time_ut.Format("2006-01-02 15:04:05") + "=>"
+//	if redisCache_WarningStart.IsExist(key) {
+//		// 上一条报警时间
+//		val := redisCache_WarningStart.Get(key)
+//
+//		// 将字符串转换为整数
+//		//logs.Println("val:[]uint8 ",val.([]uint8), string(val.([]uint8)))
+//		unixTime, err := strconv.ParseInt(string(val.([]uint8)), 10, 64)
+//		if err != nil {
+//			logs.Println("时间转换错误:",string(val.([]uint8)), err.Error())
+//			goto to1
+//		}
+//
+//		// 转换为 time.Time 类型
+//		time_ut_lod := time.Unix(unixTime, 0)
+//
+//		logs_s += "找到上一条报警时间" + time_ut_lod.Format("2006-01-02 15:04:05") + ","
+//		// 计算时间差
+//		duration := time_ut.Sub(time_ut_lod)
+//
+//		// 判断是否在  2~4 分钟范围
+//		if duration < 2*time.Minute || duration >= 4*time.Minute {
+//			logs_s += "距离间隔" + strconv.Itoa(int(duration)) + "秒,时间不在2~4分钟间隔范围,跳过报警!"
+//			logs.Println("报警时间计算处理:", logs_s)
+//			return false, time_ut_lod
+//		}
+//
+//		lib.PeriodWTime(m.T_fUt,m.T_fUt,2)
+//		// 在当前时间上加2分钟
+//		time_ut = time_ut.Add(2 * time.Minute)
+//
+//		redisCache_WarningStart.Put(key, fmt.Sprintf("%d", time_ut.Unix()) , 60*time.Minute)
+//		logs_s += "最终时间" + time_ut_lod.Format("2006-01-02 15:04:05")
+//		logs.Println("报警时间计算处理:", logs_s)
+//		return true, time_ut
+//	}
+//to1:
+//	redisCache_WarningStart.Put(key, fmt.Sprintf("%d", time_ut.Unix()), 60*time.Minute)
+//	logs_s += "最终时间" + time_ut.Format("2006-01-02 15:04:05")
+//	logs.Println("报警时间计算处理:", logs_s)
+//	return true, time_ut
+//}
+
+// 清除报警机制
+func Redis_WarningStart_DelK(m Warning) (time.Time, bool) {
+	key := m.T_sn + "|" + strconv.Itoa(m.T_id) + "|" + strconv.Itoa(m.T_tp)
+	if redisCache_WarningStart.IsExist(key) {
+		val := redisCache_WarningStart.Get(key)
+		redisCache_WarningStart.Delete(key)
+		unixTime, err := strconv.ParseInt(string(val.([]uint8)), 10, 64)
+		if err != nil {
+			logs.Println("时间转换错误:", string(val.([]uint8)), err.Error())
+		}
+		// 转换为 time.Time 类型
+		time_ut_lod := time.Unix(unixTime, 0)
+		return time_ut_lod, true
+	}
+
+	return time.Now(), false
+}
+
+// ---------------- 特殊方法 -------------------
+//
+//	func Is2Subtable(T_tp int) bool {
+//		//109	市电断电报警	市电断电报警,0,1,0,0,99999,0,0,0,0,0,0,0,0,0,0,0,0|	1
+//		//110	市电已恢复	市电已恢复,0,1,0,0,99999,0,0,0,0,0,0,0,0,0,0,0,0|	1
+//		values := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 109, 110}
+//
+//		for _, value := range values {
+//			if T_tp == value {
+//
+//				return true
+//			}
+//		}
+//		return false
+//	}
+//
+// 添加
+func Add_Warning(m Warning) (id int64, err error) {
+
+	// 查看 sn 是否有归属
+	if m.T_pid == 0 { //  寻找报警归属
+		if len(m.T_sn) > 1 {
+			r_Device, err := Device.Read_Device_ByT_sn(m.T_sn)
+			if err != nil {
+				logs.Println("MessageDisconnected 没有该设备:", m.T_sn)
+			}
+			m.T_pid = r_Device.T_pid
+		}
+	}
+	if m.T_id == -1 {
+		m.T_DS_name = m.T_D_name
+	}
+	if len(m.T_DS_name) == 0 {
+		m.T_DS_name = m.T_D_name
+	}
+
+	//if(conf.Test_server){
+	//	logs.Println("Add_Warning:",m)
+	//	return id, err
+	//}
+	o := orm.NewOrm()
+
+	id, err = o.Insert(&m)
+	if err != nil {
+		logs.PrintlnError("Add_Warning err:", err)
+	}
+
+	// 插入分表
+	//warningDB := WarningDB{
+	//	T_pid:      int32(m.T_pid),
+	//	T_tp:       m.T_tp,
+	//	T_sn:       m.T_sn,
+	//	T_D_name:   m.T_D_name,
+	//	T_id:       int32(m.T_id),
+	//	T_DS_name:  m.T_DS_name,
+	//	T_Remark:   m.T_Remark,
+	//	T_Ut:       m.T_Ut,
+	//	T_fUt:      m.T_fUt,
+	//	T_Text:     m.T_Text,
+	//	T_Log:      m.T_Log,
+	//	T_Msid:     m.T_Msid,
+	//	T_State:    m.T_State,
+	//	CreateTime: m.CreateTime,
+	//	UpdateTime: m.UpdateTime,
+	//}
+	//warningDB.Create("warning_" + m.T_Ut.Format("2006_01"))
+
+	INSERT("warning_"+m.T_Ut.Format("2006_01"), m.Id) // 旧 备份
+
+	logs.PrintlnWData("WData", m.T_pid, m.T_tp, m.T_sn, m.T_Remark) // 记录报警日志
+
+	return id, err
+}
+
+// 修改
+func Update_Warning(r Warning, cols ...string) bool {
+	o := orm.NewOrm()
+	if _, err := o.Update(&r, cols...); err == nil {
+		//logs.Println("Number of records updated in database:", num)
+		// 插入备份表
+		UPDATE("warning_"+r.T_Ut.Format("2006_01"), r.Id, r.T_Log)
+		return true
+	}
+	return false
+}
+
+// 追加 日志
+func Add_Warning_Log(r *Warning, T_Log string) {
+	r.T_Log = r.T_Log + T_Log
+	return
+}
+
+func Add_DeviceLogs(T_tp int, r_Device Device.Device, T_Remark string) Warning {
+	var Warning_r Warning
+	Warning_r.T_pid = r_Device.T_pid
+	Warning_r.T_tp = T_tp
+	Warning_r.T_sn = r_Device.T_sn
+	Warning_r.T_D_name = r_Device.T_devName
+	Warning_r.T_DS_name = r_Device.T_devName
+	Warning_r.T_id = -1
+	Warning_r.T_Remark = T_Remark
+	Warning_r.T_Ut = time.Now()
+	Warning_r.T_State = 1
+
+	// 查看 sn 是否有归属
+	if Warning_r.T_pid == 0 { //  寻找报警归属
+		if len(Warning_r.T_sn) > 10 {
+			r_Device, err := Device.Read_Device_ByT_sn(Warning_r.T_sn)
+			if err != nil {
+				logs.Println("MessageDisconnected 没有该设备:", Warning_r.T_sn)
+			}
+			Warning_r.T_pid = r_Device.T_pid
+		}
+	}
+
+	// 添加报警
+	id, _ := Add_Warning(Warning_r)
+	Warning_r.Id = id
+
+	return Warning_r
+}
+
+// 指定设备与类型最新的记录
+func Read_T_snT_tp(T_sn string, T_tp int) Warning {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Warning))
+	var r Warning
+	qs.Filter("T_sn", T_sn).Filter("T_tp", T_tp).OrderBy("-T_Ut").One(&r)
+
+	return r
+}
+
+// 指定设备与类型消息ID最新的记录
+func Read_T_snT_tpT_Msid(T_sn string, T_tp, T_Msid int) Warning {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Warning))
+	var r Warning
+	qs.Filter("T_sn", T_sn).Filter("T_tp", T_tp).Filter("T_Msid", T_Msid).OrderBy("-T_Ut").One(&r)
+
+	return r
+}
+
+// 指定设备与类型消息ID最新的记录
+func Read_T_snT_idT_tp(T_sn string, T_id, T_tp int) Warning {
+
+	o := orm.NewOrm()
+	// 也可以直接使用 Model 结构体作为表名
+
+	qs := o.QueryTable(new(Warning))
+	var r Warning
+	qs.Filter("T_sn", T_sn).Filter("T_id", T_id).Filter("T_tp", T_tp).OrderBy("-T_Ut").One(&r)
+
+	return r
+}

+ 105 - 0
models/Warning/WarningBack.go

@@ -0,0 +1,105 @@
+package Warning
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/logs"
+	"github.com/jinzhu/gorm"
+	_ "github.com/jinzhu/gorm/dialects/mysql"
+	"log"
+	"strings"
+	"sync"
+	"time"
+)
+
+var Db *gorm.DB
+var SysSeekCREATE sync.Map
+
+func init() {
+	// 连接数据库
+	var err error
+	Db, err = gorm.Open("mysql", conf.MysqlServer_Username+":"+conf.MysqlServer_Password+"@tcp("+conf.MysqlServer_UrlPort+")/"+conf.MysqlServer_Database+"?charset=utf8mb4&loc=Local&parseTime=True")
+	if err != nil {
+		log.Fatal(err)
+	}
+	// SetMaxIdleConns 设置空闲连接池中的最大连接数。
+	Db.DB().SetMaxIdleConns(10)
+
+	// SetMaxOpenConns 设置数据库连接最大打开数。
+	Db.DB().SetMaxOpenConns(600)
+
+	// SetConnMaxLifetime 设置可重用连接的最长时间
+	Db.DB().SetConnMaxLifetime(time.Hour)
+	//Db.AutoMigrate(&DeviceWarning{})
+	// 开启 Logger, 以展示详细的日志
+	Db.LogMode(true)
+	Db.SetLogger(log.New(logs.LogxOrm, "\r\nWarningBack:", 0))
+
+}
+
+//func Close() {
+//	Db.Close()
+//}
+
+//type JsonTime struct {
+//	time.Time
+//}
+//
+//func (t JsonTime) MarshalJSON() ([]byte, error) {
+//	str := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
+//	return []byte(str), nil
+//}
+//
+//func (t JsonTime) Value() (driver.Value, error) {
+//	var zeroTime time.Time
+//	if t.Time.UnixNano() == zeroTime.UnixNano() {
+//		return nil, nil
+//	}
+//	return t.Time, nil
+//}
+//
+//func (t *JsonTime) Scan(v interface{}) error {
+//	value, ok := v.(time.Time)
+//	if ok {
+//		*t = JsonTime{Time: value}
+//		return nil
+//	}
+//	return fmt.Errorf("error %v", v)
+//}
+
+// 创建备份报警表
+func SeekCREATE(name string) {
+	_, ok := SysSeekCREATE.Load(name)
+	if ok {
+		return
+	}
+	rows, _ := Db.Raw("select * from information_schema.TABLES where TABLE_NAME = ? ;", name).Rows() // (*sql.Rows, error)
+	defer rows.Close()
+	if !rows.Next() {
+		logs.Println("没有表:", name, " 现在新建!")
+		sql := "CREATE TABLE `warning`  (\n                            `ID` bigint NOT NULL AUTO_INCREMENT,\n                            `t_pid` int NULL DEFAULT NULL,\n                            `t_tp` int NULL DEFAULT NULL,\n                            `t_sn` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n                            `t__d_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n                            `t_id` int NULL DEFAULT NULL,\n                            `t__d_s_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n                            `t__remark` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,\n                            `t__ut` datetime NULL DEFAULT NULL,\n                            `t_f_ut` datetime NULL DEFAULT NULL,\n                            `t__text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,\n                            `t__log` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,\n                            `t__msid` bigint NULL DEFAULT NULL,\n                            `t__state` int NOT NULL DEFAULT 2,\n                            `create_time` datetime NULL DEFAULT NULL,\n                            `update_time` datetime NULL DEFAULT NULL,\n                            PRIMARY KEY (`ID`) USING BTREE,\n                            INDEX `warning_t__ut`(`t__ut`) USING BTREE,\n                            INDEX `warning_t_pid_t_tp_t__state`(`t_pid`, `t_tp`, `t__state`) USING BTREE,\n                            INDEX `warning_t_sn`(`t_sn`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 7522219 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;"
+		sql = strings.Replace(sql, "warning", name, -1)
+		Db.Exec(sql)
+
+		time.Sleep(time.Second)
+	}
+	SysSeekCREATE.Store(name, "")
+	return
+}
+
+// 插入 备份报警表
+func INSERT(name string, ID int64) {
+	SeekCREATE(name) // 创建备份报警表
+	logs.Println("导入数据:", name, " ID:", ID)
+	Db.Exec("INSERT INTO "+name+" SELECT * FROM warning WHERE ID = ? ;", ID)
+}
+
+func UPDATE(name string, ID int64, t__log string) {
+	logs.Println("导入数据:", name, " ID:", ID)
+	Db.Exec("UPDATE "+name+" SET t__log = ?  WHERE ID = ? ;", t__log, ID)
+}
+
+//func DELETE(name string, currentTime string) {
+//	fmt.Println("删除数据:", name, currentTime)
+//	Db.Exec("DELETE FROM "+name+" WHERE t__ut <= ?;", currentTime+" 23:59:59")
+//
+//}

+ 154 - 0
models/Warning/WarningDB.go

@@ -0,0 +1,154 @@
+package Warning
+
+//
+//import (
+//	"database/sql"
+//	"fmt"
+//	"gorm.io/driver/mysql"
+//	"gorm.io/gorm"
+//	"strings"
+//
+//	"time"
+//)
+//
+//var db *gorm.DB
+//var sqlDB *sql.DB
+//
+//type WarningDB struct {
+//	Id         int64     `gorm:"column:ID;type:bigint;primaryKey"`
+//	T_pid      int32     `gorm:"column:t_pid;index:merge"`                        // Account.Company 绑定公司
+//	T_tp       int       `gorm:"column:t_tp;index:merge"`                         // 报警类型   ->WarningList
+//	T_sn       string    `gorm:"column:t_sn;index:tsn;size:256;"`                 // 设备序列号
+//	T_D_name   string    `gorm:"column:t__d_name;size:256;"`                      // 设备名称
+//	T_id       int32     `gorm:"column:t_id;"`                                    // 传感器 ID
+//	T_DS_name  string    `gorm:"column:t__d_s_name;size:256;"`                    // 传感器名称
+//	T_Remark   string    `gorm:"column:t__remark;type:longtext;"`                 // 采集内容
+//	T_Ut       time.Time `gorm:"column:t__ut;type:datetime;"`                     // 采集时间
+//	T_fUt      time.Time `gorm:"column:t_f_ut;type:datetime;"`                    // 首次采集时间
+//	T_Text     string    `gorm:"column:t__text;type:longtext;"`                   // 处理备注
+//	T_Log      string    `gorm:"column:t__log;type:longtext;"`                    // 通知日志
+//	T_Msid     int64     `gorm:"column:t__msid;"`                                 // 消息ID
+//	T_State    int       `gorm:"column:t__state;size:2;default:2;index:merge"`    // 0 删除   1 不处理   2 已处理   3 未处理
+//	CreateTime time.Time `gorm:"column:create_time;type:datetime;autoUpdateTime"` //auto_now_add 第一次保存时才设置时间
+//	UpdateTime time.Time `gorm:"column:update_time;type:datetime;autoCreateTime"` //auto_now 每次 model 保存时都会对时间自动更新
+//}
+//
+//var MysqlServer_UrlPort = "127.0.0.1:40306"
+//var MysqlServer_Database = "cold"
+//var MysqlServer_Username = "cold"
+//var MysqlServer_Password = "yjwyEckZS7rE5H!"
+//
+//func init() {
+//	// 连接数据库
+//	var err error
+//
+//	dsn := MysqlServer_Username + ":" + MysqlServer_Password + "@tcp(" + MysqlServer_UrlPort + ")/" + MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True"
+//	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+//	if err != nil {
+//
+//		fmt.Println(err)
+//
+//	}
+//	sqlDB, err = db.DB()
+//	// SetMaxIdleConns 设置空闲连接池中的最大连接数。
+//	sqlDB.SetMaxIdleConns(1)
+//
+//	// SetMaxOpenConns 设置数据库连接最大打开数。
+//	sqlDB.SetMaxOpenConns(10)
+//
+//	// SetConnMaxLifetime 设置可重用连接的最长时间
+//	sqlDB.SetConnMaxLifetime(time.Hour)
+//	//db.AutoMigrate(&DeviceWarning{})
+//
+//	//db.SetLogger(log.New(logs.LogxOrm, "\r\nWarningBack:", 0))
+//	fmt.Println("db init")
+//}
+//
+///*
+//time_a  : 开始时间  "2024-01-24 08:21:40"
+//time_b  : 结束时间  "2024-03-24 08:21:40"
+//WHERE_t_pid  : 公司ID   > 0
+//WHERE_t_tp  : 报警类型  > 0
+//WHERE_t__state  :   状态   -1 无限制  0 删除   1 不处理   2 已处理   3 未处理
+//WHERE_t_sn  : 设备序列号
+//PageIndex  : 页码值
+//PageSize  : 每页多少
+//*/
+//
+//func Handy(time_a, time_b string, WHERE_t_pid int, WHERE_t_tp int, WHERE_t__state int, WHERE_t_sn string, PageIndex int, PageSize int) ([]WarningDB, int) {
+//	var offset int // 数据起点
+//	if PageIndex <= 1 {
+//		offset = 0
+//	} else {
+//		offset = (PageIndex - 1) * PageSize
+//	}
+//	var TABLE = "warning_"
+//	monthsArray := generateMonthArray(time_a, time_b)
+//
+//	var Warning_List []WarningDB
+//	var Count_List []int
+//	var Count_Num int // 累积数量
+//	for _, v := range monthsArray {
+//		var count int64
+//		var countoffset int // 当前数据起点
+//
+//		db.Table(TABLE+v).Where("t_tp = ?", 110).Count(&count)
+//		Count_List = append(Count_List, int(count))
+//		fmt.Println("TABLE:", TABLE+v, count)
+//		countoffset = offset - Count_Num // 当前数据起点
+//		if countoffset < 0 {
+//			countoffset = 0
+//		}
+//		Count_Num += int(count)
+//		Limit := PageSize - len(Warning_List) // 还缺多少数量
+//		if offset <= Count_Num && Limit > 0 {
+//			var warninglist []WarningDB
+//			dbW := db.Table(TABLE + v)
+//			if WHERE_t_pid > 0 {
+//				dbW.Where("t_tp = ? ", WHERE_t_pid)
+//			}
+//			if WHERE_t_tp > 0 {
+//				dbW.Where("t_pid = ? ", WHERE_t_pid)
+//			}
+//			if WHERE_t__state != -1 {
+//				dbW.Where("t__state = ? ", WHERE_t__state)
+//			}
+//			if len(WHERE_t_sn) > 0 {
+//				dbW.Where("t__state LIKE ? ", WHERE_t_sn)
+//			}
+//			dbW.Order("t__ut").Offset(countoffset).Limit(Limit).Find(&warninglist)
+//			Warning_List = append(Warning_List, warninglist...)
+//		}
+//
+//	}
+//	return Warning_List, Count_Num
+//}
+//
+//func (t *WarningDB) Create(TableName string) {
+//CreateSQL:
+//	result := db.Table(TableName).Create(t) // 插入数据
+//	if result.Error != nil {
+//		if strings.Compare(result.Error.Error(), "doesn't exist") != 0 {
+//			db.Table(TableName).AutoMigrate(&WarningDB{}) // 建表
+//			goto CreateSQL                                // 返回 重新插入
+//		}
+//	}
+//}
+//
+////func UPDATE(name string, ID int64, t__log string) {
+////	logs.Println("修改数据:", name, " ID:", ID)
+////	db.Exec("UPDATE "+name+" SET t__log = ?  WHERE ID = ? ;", t__log, ID)
+////}
+//
+//func generateMonthArray(startDateStr string, endDateStr string) []string {
+//	startDate, _ := time.Parse("2006-01-02 15:04:05", startDateStr)
+//	endDate, _ := time.Parse("2006-01-02 15:04:05", endDateStr)
+//
+//	monthsArray := []string{}
+//	for startDate.Before(endDate) || startDate.Equal(endDate) {
+//		monthsArray = append(monthsArray, fmt.Sprintf("%d_%02d", startDate.Year(), startDate.Month()))
+//		startDate = startDate.AddDate(0, 1, 0) // Add one month
+//	}
+//
+//	return monthsArray
+//}

+ 82 - 0
models/Warning/WarningSand.go

@@ -0,0 +1,82 @@
+package Warning
+
+import (
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 报警发送统计
+type WarningSand struct {
+	Id       int    `orm:"column(ID);size(11);auto;pk"`
+	T_pid    int    `orm:"index;size(100);"` // Account.Company 绑定公司, -1:未知   0:管理员  >0 :绑定公司
+	T_Spid   int    `orm:"index;size(100);"` // 触发公司 ID
+	T_tp     int    `orm:"size(20);"`        // 报警类型   ->WarningList
+	T_Ntype  int    `orm:"size(4);"`         // 通知方式    0:微信    1:短信 ¥0.1        2:电话 ¥0.2
+	T_Notice string `orm:"size(256);"`       // 18888888888
+	T_Remark string `orm:"type(text);"`      // 备注
+	//CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
+}
+
+func (t *WarningSand) TableName() string {
+	return "warning_sand" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(WarningSand))
+
+}
+
+func CREATE(name string) string {
+	sql := "CREATE TABLE `warning_sand`  (\n  `ID` int NOT NULL AUTO_INCREMENT,\n  `t_pid` int NULL DEFAULT NULL,\n  `t_tp` int NULL DEFAULT NULL,\n  `t__notice` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `t_type` int NULL DEFAULT NULL,\n  `t__remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `create_time` datetime NULL DEFAULT NULL,\n  `t__ntype` int NOT NULL DEFAULT 0,\n  `t__spid` int NOT NULL DEFAULT 0,\n  PRIMARY KEY (`ID`) USING BTREE,\n  INDEX `warning_sand_t_pid`(`t_pid`) USING BTREE,\n  INDEX `warning_sand_t_tp`(`t_tp`) USING BTREE,\n  INDEX `warning_sand_t_type`(`t_type`) USING BTREE,\n  INDEX `warning_sand_t__spid`(`t__spid`) USING BTREE\n) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;"
+	sql = strings.Replace(sql, "warning_sand", name, -1)
+	return sql
+}
+
+// 添加 数据   返回   真:替换   假:第一条无替换
+func Add_WarningSand(v WarningSand) bool {
+	o := orm.NewOrm()
+	currentTime := time.Now()
+	TABLE_name := fmt.Sprintf("warning_sand_%d%02d", currentTime.Year(), currentTime.Month())
+breakHere:
+	// 开始插入数据
+	sql := "INSERT INTO " + TABLE_name + "(`t_pid`, `t_tp`, `t__ntype`, `t__notice`, `t__remark`, `create_time`) " +
+		"VALUES (" + strconv.Itoa(v.T_pid) + " , " + strconv.Itoa(v.T_tp) + ", " + strconv.Itoa(v.T_Ntype) + ", '" + v.T_Notice + "', '" + v.T_Remark + "', '" + currentTime.Format("2006-01-02 15:04:05") + "')"
+	//  这里有时间优化  用于一次 prepare 多次 exec,以提高批量执行的速度
+	logs.Println(sql)
+	_, err := o.Raw(sql).Exec()
+	if err != nil {
+		//Error 1146: Table 'cold.z_device_data_202335objgv2pzk' doesn't exist
+		if strings.Contains(err.Error(), "1146") { // 没有创建表
+			_, err = o.Raw(CREATE(TABLE_name)).Exec()
+			if err != nil {
+				logs.PrintlnError("SELECT_Add_WarningSand:" + err.Error())
+				return false
+			}
+			goto breakHere // 跳转到标签
+		}
+
+	}
+	return true
+}
+
+// 获取 发送统计数量
+func Get_WarningSandNum(TABLE_name string, Pid, Ntype int, StartTine, EndTime string) int {
+	o := orm.NewOrm()
+	var maps []orm.Params
+	num, err := o.Raw("SELECT count(ID) AS count FROM " + TABLE_name + " WHERE `t_pid` = " + strconv.Itoa(Pid) + " AND `t__ntype` = " + strconv.Itoa(Ntype) + " AND `create_time` >= '" + StartTine + "' AND `create_time` <= '" + EndTime + "'").Values(&maps)
+	if err == nil && num > 0 {
+		intx, _ := strconv.Atoi(lib.To_string(maps[0]["count"]))
+		logs.Println("Get_WarningSandNum:", TABLE_name, Pid, Ntype, StartTine, EndTime, intx) // slene
+		return intx
+	}
+	logs.PrintlnError("执行错误!!!", "Get_WarningSandNum:", TABLE_name, Pid, Ntype, StartTine, EndTime, maps) // slene
+	return 0
+}

+ 94 - 0
models/Warning/WarningType.go

@@ -0,0 +1,94 @@
+package Warning
+
+import (
+	"Cold_mqtt/logs"
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+// 模板
+type WarningType struct {
+	Id     int    `orm:"column(ID);size(11);auto;pk"`
+	T_name string `orm:"size(256);"`            // 分类
+	T_Tips string `orm:"type(text);size(256);"` // 提示
+}
+
+//1	温度超上限报警
+//2	温度超下限报警
+//3	温度恢复正常
+//4	湿度超上限报警
+//5	湿度超下限报警
+//6	湿度恢复正常
+//7	温度超上限预警
+//8	温度超下限预警
+//9	温度预警恢复正常
+//10	湿度超上限预警
+//11	湿度超下限预警
+//12	湿度预警恢复正常
+//101	主机报警
+//102	操作日志
+//103	普通日志
+//109	市电断电报警
+//110	市电已恢复
+//111	电池电量低报警
+//112	电池电量恢复
+//113	传感器故障报警
+//114	传感器恢复
+//115	开始监测任务
+//116	中途打印任务
+//117	结束监测任务
+//1001	设备状态异常
+//1002	用户余额通知
+//1011	报警策略异常
+//1012	传感器数据异常
+//1013	系统预警通知
+//1014	运维预警通知
+
+var WarningType_list map[int]string // 泛型
+
+func (t *WarningType) TableName() string {
+	return "warning_type" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+//var redisCache_WarningType cache.Cache
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(WarningType))
+
+	WarningType_list = make(map[int]string)
+
+	go Read_WarningType_All()
+
+}
+
+// 获取全部
+func Read_WarningType_All() {
+	time.Sleep(time.Second)
+
+	logs.Println("=========== 初始化报警规则 =========")
+
+	o := orm.NewOrm()
+	for true {
+		var r []WarningType
+		qs := o.QueryTable(new(WarningType))
+		qs.All(&r)
+		for _, v := range r {
+			WarningType_list[v.Id] = v.T_name
+			//logs.Println(v.T_name,v.Id)
+		}
+		time.Sleep(1 * time.Hour)
+	}
+
+}
+func Read_WarningType_Get(Id int) string {
+	// 有先加入 给全部人发消息
+	v, ok := WarningType_list[Id] /*如果确定是真实的,则存在,否则不存在 */
+	if ok {
+		return v
+	} else {
+		return "未知报警"
+	}
+}

+ 10 - 0
routers/router.go

@@ -0,0 +1,10 @@
+package routers
+
+import (
+	"Cold_mqtt/controllers"
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+func init() {
+	beego.Router("/", &controllers.MainController{})
+}

+ 12 - 0
run.sh

@@ -0,0 +1,12 @@
+#!/bin/sh 
+#获取前一天的日期 
+date=`date -d "yesterday" +%Y_%m_%d` 
+#复制原始nohup.out到备份目录,并以前一天的日期进行命名 
+cp /bzd/project/Cold_Api6200/logs/nohup.out /bzd/project/Cold_Api6200/logs/$date.out 
+#清空原始nohup.out中的内容
+cat /dev/null > /bzd/project/Cold_Api6200/logs/nohup.out
+
+
+export GO111MODULE=off
+nohup ./Cold_Api6200 >> ./logs/nohup.out 2>&1 &
+

+ 1 - 0
static/js/reload.min.js

@@ -0,0 +1 @@
+function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)};

+ 147 - 0
tests/Merge.go

@@ -0,0 +1,147 @@
+package test
+
+import (
+	"database/sql"
+	"fmt"
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+	"strings"
+
+	"time"
+)
+
+var db *gorm.DB
+var sqlDB *sql.DB
+
+type WarningDB struct {
+	Id         int64     `gorm:"column:ID;type:bigint;primaryKey"`
+	T_pid      int32     `gorm:"column:t_pid;index:merge"`                        // Account.Company 绑定公司
+	T_tp       int       `gorm:"column:t_tp;index:merge"`                         // 报警类型   ->WarningList
+	T_sn       string    `gorm:"column:t_sn;index:tsn;size:256;"`                 // 设备序列号
+	T_D_name   string    `gorm:"column:t__d_name;size:256;"`                      // 设备名称
+	T_id       int32     `gorm:"column:t_id;"`                                    // 传感器 ID
+	T_DS_name  string    `gorm:"column:t__d_s_name;size:256;"`                    // 传感器名称
+	T_Remark   string    `gorm:"column:t__remark;type:longtext;"`                 // 采集内容
+	T_Ut       time.Time `gorm:"column:t__ut;type:datetime;"`                     // 采集时间
+	T_fUt      time.Time `gorm:"column:t_f_ut;type:datetime;"`                    // 首次采集时间
+	T_Text     string    `gorm:"column:t__text;type:longtext;"`                   // 处理备注
+	T_Log      string    `gorm:"column:t__log;type:longtext;"`                    // 通知日志
+	T_Msid     int64     `gorm:"column:t__msid;"`                                 // 消息ID
+	T_State    int       `gorm:"column:t__state;size:2;default:2;index:merge"`    // 0 删除   1 不处理   2 已处理   3 未处理
+	CreateTime time.Time `gorm:"column:create_time;type:datetime;autoUpdateTime"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `gorm:"column:update_time;type:datetime;autoCreateTime"` //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+var MysqlServer_UrlPort = "127.0.0.1:40306"
+var MysqlServer_Database = "cold"
+var MysqlServer_Username = "cold"
+var MysqlServer_Password = "yjwyEckZS7rE5H!"
+
+func init() {
+	// 连接数据库
+	var err error
+
+	dsn := MysqlServer_Username + ":" + MysqlServer_Password + "@tcp(" + MysqlServer_UrlPort + ")/" + MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True"
+	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+	if err != nil {
+
+		fmt.Println(err)
+
+	}
+	sqlDB, err = db.DB()
+	// SetMaxIdleConns 设置空闲连接池中的最大连接数。
+	sqlDB.SetMaxIdleConns(1)
+
+	// SetMaxOpenConns 设置数据库连接最大打开数。
+	sqlDB.SetMaxOpenConns(10)
+
+	// SetConnMaxLifetime 设置可重用连接的最长时间
+	sqlDB.SetConnMaxLifetime(time.Hour)
+	//db.AutoMigrate(&DeviceWarning{})
+
+	//db.SetLogger(log.New(logs.LogxOrm, "\r\nWarningBack:", 0))
+	fmt.Println("db init")
+}
+
+/*
+time_a  : 开始时间  "2024-01-24 08:21:40"
+time_b  : 结束时间  "2024-03-24 08:21:40"
+WHERE_t_pid  : 公司ID   > 0
+WHERE_t_tp  : 报警类型  > 0
+WHERE_t__state  :   状态   -1 无限制  0 删除   1 不处理   2 已处理   3 未处理
+WHERE_t_sn  : 设备序列号
+PageIndex  : 页码值
+PageSize  : 每页多少
+*/
+
+func Handy(time_a, time_b string, WHERE_t_pid int, WHERE_t_tp int, WHERE_t__state int, WHERE_t_sn string, PageIndex int, PageSize int) ([]WarningDB, int) {
+	var offset int // 数据起点
+	if PageIndex <= 1 {
+		offset = 0
+	} else {
+		offset = (PageIndex - 1) * PageSize
+	}
+	var TABLE = "warning_"
+	monthsArray := generateMonthArray(time_a, time_b)
+
+	var Warning_List []WarningDB
+	var Count_List []int
+	var Count_Num int // 累积数量
+	for _, v := range monthsArray {
+		var count int64
+		var countoffset int // 当前数据起点
+
+		db.Table(TABLE+v).Where("t_tp = ?", 110).Count(&count)
+		Count_List = append(Count_List, int(count))
+		fmt.Println("TABLE:", TABLE+v, count)
+		countoffset = offset - Count_Num // 当前数据起点
+		if countoffset < 0 {
+			countoffset = 0
+		}
+		Count_Num += int(count)
+		Limit := PageSize - len(Warning_List) // 还缺多少数量
+		if offset <= Count_Num && Limit > 0 {
+			var warninglist []WarningDB
+			dbW := db.Table(TABLE + v)
+			if WHERE_t_pid > 0 {
+				dbW.Where("t_tp = ? ", WHERE_t_pid)
+			}
+			if WHERE_t_tp > 0 {
+				dbW.Where("t_pid = ? ", WHERE_t_pid)
+			}
+			if WHERE_t__state != -1 {
+				dbW.Where("t__state = ? ", WHERE_t__state)
+			}
+			if len(WHERE_t_sn) > 0 {
+				dbW.Where("t__state LIKE ? ", WHERE_t_sn)
+			}
+			dbW.Order("t__ut").Offset(countoffset).Limit(Limit).Find(&warninglist)
+			Warning_List = append(Warning_List, warninglist...)
+		}
+
+	}
+	return Warning_List, Count_Num
+}
+
+func (t *WarningDB) Create(TableName string) {
+CreateSQL:
+	result := db.Table(TableName).Create(t) // 插入数据
+	if result.Error != nil {
+		if strings.Compare(result.Error.Error(), "doesn't exist") != 0 {
+			db.Table(TableName).AutoMigrate(&WarningDB{}) // 建表
+			goto CreateSQL                                // 返回 重新插入
+		}
+	}
+}
+func generateMonthArray(startDateStr string, endDateStr string) []string {
+	startDate, _ := time.Parse("2006-01-02 15:04:05", startDateStr)
+	endDate, _ := time.Parse("2006-01-02 15:04:05", endDateStr)
+
+	monthsArray := []string{}
+	for startDate.Before(endDate) || startDate.Equal(endDate) {
+		monthsArray = append(monthsArray, fmt.Sprintf("%d_%02d", startDate.Year(), startDate.Month()))
+		startDate = startDate.AddDate(0, 1, 0) // Add one month
+	}
+
+	return monthsArray
+}

+ 39 - 0
tests/conf/app.conf

@@ -0,0 +1,39 @@
+appname = Cold_mqtt
+httpport = 6202
+runmode = dev
+
+
+# Nats
+NatsServer_Url = "127.0.0.1:43422"
+
+
+# Mysql
+
+MysqlServer_UrlPort = "127.0.0.1:40306"
+MysqlServer_Database = "cold"
+MysqlServer_Username = "cold"
+MysqlServer_Password = "yjwyEckZS7rE5H!"
+MysqlServer_MaxIdleConnections = 100
+MysqlServer_MaxOpenConnections = 200
+
+
+
+# Redis
+Redis_address = "127.0.0.1:43379"
+Redis_password = ""
+Redis_dbNum = "1"
+
+# Mqtt
+# 192.168.0.7
+# Mqtt
+# 192.168.0.7
+ # mqttjxit 140.246.233.197   mqttlodr 203.195.71.200  mqttyuht 203.57.71.139
+MqttServer_id = "text_cold_mqtt"
+MqttServer_Url = "203.57.71.139:1883"
+MqttServer_ClientID = "text_cold_mqtt"
+MqttServer_Username = "admin"
+MqttServer_Password = "8f9qRNixEMhCVrF"
+
+
+
+

+ 35 - 0
tests/default_test.go

@@ -0,0 +1,35 @@
+package test
+
+import (
+	"fmt"
+	"math"
+	"testing"
+	"time"
+)
+
+func TestName(t *testing.T) {
+
+	println(time.Now().Format("15:04:05"))
+
+	/*
+		time_a  : 开始时间  "2024-01-24 08:21:40"
+		time_b  : 结束时间  "2024-03-24 08:21:40"
+		WHERE_t_pid  : 公司ID   > 0
+		WHERE_t_tp  : 报警类型  > 0
+		WHERE_t__state  :   状态   -1 无限制  0 删除   1 不处理   2 已处理   3 未处理
+		WHERE_t_sn  : 设备序列号
+		PageIndex  : 页码值
+		PageSize  : 每页多少
+	*/
+	//Handy("2024-01-24 08:21:40","2024-03-24 08:21:40",0,110,-1,"",1,10)
+
+	startTime := time.Now()
+	time.Sleep(2 * time.Second) // Simulate some time passing
+	endTime := time.Now()
+
+	duration := startTime.Sub(endTime)
+	// 将时间差表示为秒
+	seconds := int(math.Abs(duration.Seconds()))
+
+	fmt.Println("seconds:", seconds)
+}

+ 120 - 0
tests/device/MqttServer.go

@@ -0,0 +1,120 @@
+package MqttServer
+
+import (
+	"fmt"
+	"github.com/yosssi/gmq/mqtt"
+	"github.com/yosssi/gmq/mqtt/client"
+	"time"
+)
+
+var (
+	cli    *client.Client
+	T_sn   string
+	T_pass string
+)
+
+func init() {
+	T_sn = "2024168563127390"
+	T_pass = "qMua4PZyl2vwchNL"
+	Run_MqttServer()
+}
+
+/*
+{
+	"type": 203,
+	"data": {
+		"sn": "2024666666666666",
+		"mqtt1": "mqttyuht.coldbaozhida.com",
+		"mqtt2": "mqttlodr.coldbaozhida.com",
+		"mqtt3": "mqttyuht.coldbaozhida.com",
+		"mqtt4": "",
+		"mqtt5": "",
+		"port": 1883,
+		"user": "2024666666666666",
+		"pass": "qMua4PZyl2vwchNL"
+	}
+}
+*/
+
+func Run_MqttServer() {
+	time.Sleep(3 * time.Second)
+	fmt.Println("============Run_MqttServer=============", "")
+
+	// Create an MQTT Client.
+	cli = client.New(&client.Options{
+		// Define the processing of the error handler.
+		ErrorHandler: func(err error) {
+			fmt.Println("err!!!!!! Run_MqttServer:", err.Error())
+
+			time.Sleep(3 * time.Second)
+			go Run_MqttServer() // MQTT 通讯
+			return
+		},
+	})
+
+	// Terminate the Client.
+	defer cli.Terminate()
+
+	c := client.ConnectOptions{
+		Network:  "tcp",
+		Address:  "mqttyuht.coldbaozhida.com",
+		ClientID: []byte(T_sn + "_test"),
+		UserName: []byte(T_sn),
+		Password: []byte(T_pass),
+	}
+	fmt.Println("Address:", c.Address)
+	fmt.Println("ClientID:", string(c.ClientID))
+
+	// Connect to the MQTT Server.
+	err := cli.Connect(&c)
+
+	if err != nil {
+		fmt.Println("MqttServer", "连接MQTT失败 [cli.Connect]", "")
+		fmt.Println("err!!!!!! Run_MqttServer:", "连接MQTT失败:", err)
+		fmt.Println("err!!!!!! 连接MQTT失败:", err)
+		cli.Terminate()
+		time.Sleep(3 * time.Second)
+		go Run_MqttServer() // MQTT 通讯
+
+		return
+	}
+
+	// Subscribe to topics.
+	err = cli.Subscribe(&client.SubscribeOptions{
+		SubReqs: []*client.SubReq{
+			&client.SubReq{
+				TopicFilter: []byte("/pub/2024168563127390"),
+				QoS:         mqtt.QoS0,
+				// Define the processing of the message handler.
+				Handler: func(topicName, message []byte) {
+					fmt.Println(">-" + string(topicName) + " " + string(message))
+
+				},
+			},
+		},
+	})
+	if err != nil {
+		fmt.Println("MqttServer", "订阅消息 [cli.Subscribe]", "")
+		fmt.Println("err!!!!!! Run_MqttServer:", "连接MQTT失败:", err)
+
+	}
+
+	fmt.Println("MQTT ok!")
+}
+
+// 发送数据
+func Mqtt_publish(topic string, text string) {
+
+	// Publish a message.
+	err := cli.Publish(&client.PublishOptions{
+		QoS:       mqtt.QoS0,
+		TopicName: []byte("/pub/" + topic),
+		Message:   []byte(text),
+	})
+
+	fmt.Println("-> /pub/" + topic + " " + text)
+	if err != nil {
+		fmt.Println("MqttServer", "发送消息失败 [cli.Publish]", text)
+	}
+
+}

+ 1 - 0
tests/fa_test.go

@@ -0,0 +1 @@
+package test

+ 216 - 0
tests/logrizhi_test.go

@@ -0,0 +1,216 @@
+package test
+
+import (
+	"Cold_mqtt/conf"
+	"Cold_mqtt/lib"
+	"Cold_mqtt/logs"
+	"Cold_mqtt/models/Device"
+	_ "Cold_mqtt/routers"
+	"bufio"
+	"encoding/json"
+	"fmt"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"github.com/yosssi/gmq/mqtt"
+	"github.com/yosssi/gmq/mqtt/client"
+	"math"
+	"os"
+	"regexp"
+	"strconv"
+	"strings"
+	"testing"
+	"time"
+)
+
+var (
+	cli *client.Client
+)
+
+func init() {
+
+	orm.RegisterDriver("mysql", orm.DRMySQL)
+	orm.RegisterDataBase("default", "mysql",
+		conf.MysqlServer_Username+":"+conf.MysqlServer_Password+"@tcp("+conf.MysqlServer_UrlPort+")/"+conf.MysqlServer_Database+"?charset=utf8mb4&loc=Local&parseTime=True",
+		conf.MysqlServer_MaxIdleConnections, conf.MysqlServer_MaxOpenConnections)
+	orm.RunSyncdb("default", false, true) // 创建数据库
+
+	//logs.Println("MysqlServer:" + conf.MysqlServer_Username + ":" + conf.MysqlServer_Password + "@tcp(" + conf.MysqlServer_UrlPort + ")/" + conf.MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True")
+
+}
+
+// 通过 MQTT 日志恢复 所有记录数据,直接插入数据库 过滤方式, 运行目录下 mqtt.log
+func Test_MQTTlog_iDB(t *testing.T) {
+	// 打开文件
+	file, err := os.Open("logx.log")
+	if err != nil {
+		fmt.Println("无法打开文件:", err)
+		return
+	}
+	defer file.Close()
+
+	// 创建一个 Scanner 来读取文件内容
+	scanner := bufio.NewScanner(file)
+
+	// 逐行读取文件内容
+	for scanner.Scan() {
+		line := scanner.Text()
+		if strings.Contains(line, "<-/sub/") && strings.Contains(line, "\"type\":1,") {
+			//fmt.Println("包含字符串", "的行:", line)
+			logLine := line
+
+			// 定义正则表达式
+			re := regexp.MustCompile(`(?P<json>{.*})`)
+
+			// 在日志行中查找匹配的内容
+			T_json_match := re.FindStringSubmatch(logLine)
+
+			// 定义正则表达式
+			re1 := regexp.MustCompile(`\/sub\/([A-Za-z0-9]+)`)
+
+			// 在日志行中查找匹配的内容
+			T_sn_match := re1.FindStringSubmatch(logLine)
+			T_sn := ""
+			// 输出匹配结果
+			if len(T_sn_match) > 1 {
+				T_sn = T_sn_match[1]
+				//fmt.Println("T_sn:", T_sn)
+
+			} else {
+				fmt.Println("未找到匹配的字符串", logLine)
+				continue
+			}
+
+			// 输出匹配结果
+			if len(T_json_match) > 0 {
+				result := T_json_match[1] // 使用索引1获取命名捕获组"json"的内容
+				//fmt.Println("提取的字符串:", result)
+				// 实体类-数值
+				type Ms2_Project_list struct {
+					Type int                      `json:"type"`
+					Msid int64                    `json:"mid"`
+					Data []map[string]interface{} `json:"data"`
+				}
+
+				var ms2_Project_list Ms2_Project_list
+
+				err := json.Unmarshal([]byte(result), &ms2_Project_list)
+				if err != nil {
+					logs.Println("JSON反序列化失败[Ms_project_1],err=", err)
+					break
+				}
+
+				for _, v := range ms2_Project_list.Data {
+
+					int64_, _ := strconv.ParseInt(lib.To_string(v["ut"]), 10, 64)
+					UT := time.Unix(int64_, 0)
+
+					// 是否存在传感器  不存在 跳过
+					DeviceSensor_r, is := Device.Read_DeviceSensor_ByT_sn(T_sn, lib.To_int(v["id"]))
+					if !is {
+						logs.Println("MqttServer", "记录数据 传感器不存在 跳过处理", T_sn+lib.To_string(v["id"]))
+						continue
+					}
+					// 获取传感器参数
+					DeviceSensorParameter_r, is := Device.Read_DeviceSensorParameter(T_sn, DeviceSensor_r.T_id)
+					if !is {
+						logs.Println("MqttServer", "记录数据 传感器参数不存在 跳过处理", T_sn+lib.To_string(v["id"]))
+						continue
+					}
+
+					DeviceData_t := Device.DeviceData_R{
+						T_t:    float32(math.Ceil(float64(lib.To_float32(v["t"])*10)) / 10),
+						T_rh:   float32(math.Ceil(float64(lib.To_float32(v["h"])*10)) / 10),
+						T_Site: lib.To_string(v["s"]),
+						T_time: UT,
+						T_sp:   DeviceSensorParameter_r.Id,
+					}
+
+					if r_, _ := Device.Add_DeviceData(T_sn, DeviceSensor_r.T_id, DeviceData_t, true); r_ {
+						fmt.Println("--------- EE >", T_sn, DeviceSensor_r.T_id, DeviceData_t.T_time, DeviceData_t)
+						// 被替换
+						//Device.Add_DeviceDataOld(Device.DeviceDataOld{
+						//	T_sn:        T_sn,
+						//	T_id:        DeviceSensor_r.T_id,
+						//	T_t:         DeviceData_old_r.T_t,
+						//	T_rh:        DeviceData_old_r.T_rh,
+						//	T_Site:      DeviceData_old_r.T_site,
+						//	T_time:      UT,
+						//	T_operation: 2,
+						//	T_uuid:      "test",
+						//})
+					} else {
+						fmt.Println("--------- OK >", T_sn, DeviceSensor_r.T_id, DeviceData_t.T_time, DeviceData_t)
+					}
+
+				}
+			} else {
+				fmt.Println("未找到匹配的字符串")
+			}
+		}
+
+	}
+
+	// 检查是否有错误发生
+	if err := scanner.Err(); err != nil {
+		fmt.Println("读取文件时发生错误:", err)
+	}
+}
+
+func TestLoge(t *testing.T) {
+	time.Sleep(3 * time.Second)
+	logs.Println("============Run_MqttServer=============", "")
+
+	// Create an MQTT Client.
+	cli = client.New(&client.Options{
+		// Define the processing of the error handler.
+		ErrorHandler: func(err error) {
+			logs.PrintlnError("err!!!!!! Run_MqttServer:", err.Error())
+			return
+		},
+	})
+
+	// Terminate the Client.
+	defer cli.Terminate()
+
+	c := client.ConnectOptions{
+		Network:  "tcp",
+		Address:  conf.MqttServer_Url,
+		ClientID: []byte(conf.MqttServer_ClientID + "TestLoge"),
+		UserName: []byte(conf.MqttServer_Username),
+		Password: []byte(conf.MqttServer_Password),
+	}
+	logs.Println("Address:", c.Address)
+	logs.Println("ClientID:", string(c.ClientID))
+
+	// Connect to the MQTT Server.
+	err := cli.Connect(&c)
+
+	if err != nil {
+		logs.Println("MqttServer", "连接MQTT失败 [cli.Connect]", "")
+		logs.Println("err!!!!!! Run_MqttServer:", "连接MQTT失败:", err)
+		fmt.Println("err!!!!!! 连接MQTT失败:", err)
+		cli.Terminate()
+		time.Sleep(3 * time.Second)
+		return
+	}
+
+	time.Sleep(30 * time.Second)
+
+}
+
+// 发送数据
+func Mqtt_publish(topic string, text string) {
+
+	// Publish a message.
+	err := cli.Publish(&client.PublishOptions{
+		QoS:       mqtt.QoS0,
+		TopicName: []byte("/pub/" + topic),
+		Message:   []byte(text),
+	})
+
+	logs.PrintlnMqtt("-> /pub/" + topic + " " + text)
+	if err != nil {
+		logs.PrintlnError("MqttServer", "发送消息失败 [cli.Publish]", text)
+	}
+
+}

+ 60 - 0
tests/period_test.go

@@ -0,0 +1,60 @@
+package test
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+/*周期记录时间测试
+1、2、5、6、10、10、15、20、30、60
+
+*/
+
+func TestPeriodw(t *testing.T) {
+	// 基准时间
+	baseTime, _ := time.Parse("2006-01-02 15:04:05", "2017-03-13 13:20:45")
+
+	// 输入的时间列表
+	inputTimes := []string{
+		"2017-03-13 13:20:45",
+		"2017-03-13 13:24:45",
+		"2024-04-14 13:26:10",
+		"2024-04-15 13:29:59",
+		"2023-04-11 13:30:10",
+		"2023-05-11 13:33:23",
+		"2023-05-11 13:39:38",
+		"2023-05-11 13:41:23",
+		"2023-05-11 13:43:34",
+		"2023-05-11 13:54:30",
+		"2023-05-11 13:50:00",
+		"2023-05-11 13:51:00",
+	}
+
+	// 设置时间间隔(分钟)
+	interval := 2
+
+	// 遍历输入时间并转换
+	for _, inputTimeStr := range inputTimes {
+		inputTime, _ := time.Parse("2006-01-02 15:04:05", inputTimeStr)
+		transformedTime := ConvertTime(baseTime, inputTime, interval)
+		fmt.Printf("%s 转换为 %s\n", inputTimeStr, transformedTime.Format("2006-01-02 15:04:05"))
+	}
+}
+// ConvertTime 根据基准时间和间隔将输入时间转换为指定格式
+func ConvertTime(baseTime time.Time, inputTime time.Time, intervalMinutes int) time.Time {
+	// 计算输入时间距离基准时间的总分钟数
+	minutesDiff := int(inputTime.Sub(baseTime).Minutes())
+
+	// 计算离基准时间最近的间隔
+	if minutesDiff < 0 {
+		// 如果输入时间早于基准时间,返回基准时间
+		return baseTime
+	}
+
+	// 计算最近的间隔
+	closestInterval := (minutesDiff / intervalMinutes) * intervalMinutes
+
+	// 返回基准时间加上最近的间隔
+	return baseTime.Add(time.Duration(closestInterval) * time.Minute)
+}

+ 89 - 0
tests/periodw_test.go

@@ -0,0 +1,89 @@
+package test
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+	"time"
+)
+
+/*周期记录时间测试
+1、2、5、6、10、10、15、20、30、60
+
+*/
+
+func TestPeriod(t *testing.T) {
+	//// 更新记录 - 缓存
+	// 输入时间列表
+	timeStrings := []string{
+		"2017-04-11 13:00:00",
+		"2017-04-11 13:01:01",
+		"2017-04-11 13:02:12",
+		"2017-04-11 13:03:12",
+		"2017-04-11 13:04:12",
+		"2017-04-11 13:05:12",
+		"2017-04-11 13:06:12",
+		"2017-04-11 13:07:12",
+		"2017-04-11 13:08:12",
+		"2017-04-11 13:09:12",
+		"2017-04-11 13:10:12",
+		"2017-04-11 13:20:32",
+		"2017-02-12 13:22:12",
+		"2017-03-13 13:24:45",
+		"2024-04-14 13:26:10",
+		"2024-04-15 13:29:59",
+		"2023-04-11 13:30:10",
+		"2023-05-11 13:33:23",
+		"2023-05-11 13:39:38",
+		"2023-05-11 13:41:23",
+		"2023-05-11 13:43:34",
+		"2023-05-11 13:54:30",
+		"2023-05-11 13:50:00",
+		"2023-05-11 13:51:00",
+		"2023-05-11 13:52:00",
+		"2023-05-11 13:53:00",
+		"2023-05-11 13:54:00",
+		"2023-05-11 13:55:12",
+		"2023-05-11 13:56:19",
+		"2023-05-11 13:56:29",
+		"2023-05-11 13:58:49",
+		"2023-05-11 13:59:59",
+		"2023-05-11 14:00:05",
+		"2023-05-11 14:01:05",
+		"2023-05-11 14:01:15",
+		"2023-05-11 14:02:05",
+		"2023-05-11 14:03:05",
+		"2023-05-11 14:04:05",
+		"2023-05-11 14:05:05",
+		"2023-05-11 14:06:05",
+		"2023-05-11 14:07:05",
+		"2023-05-11 14:08:05",
+		"2023-05-11 14:09:05",
+		"2023-05-11 14:10:05",
+		"2024-09-30 00:30:00",
+	}
+	for _, ts := range timeStrings {
+		time_r, err := PeriodTime(ts, 30)
+		if err != nil {
+			fmt.Println(err)
+		}
+		fmt.Println(time_r.Format("2006-01-02 15:04:05"))
+	}
+}
+func PeriodTime(time_s string, T_saveT int) (time.Time, error) {
+	period := time.Minute * time.Duration(T_saveT)
+	// 解析时间字符串
+	t, err := time.Parse("2006-01-02 15:04:05", time_s)
+	if err != nil {
+		fmt.Println("时间解析错误:", err)
+		return time.Now(), errors.New("时间解析错误!")
+	}
+	// 计算归一化的分钟
+	minutes := t.Minute()
+	normalizedMinutes := (minutes / int(period.Minutes())) * int(period.Minutes())
+	// 创建新的时间对象,使用归一化后的分钟
+	normalizedTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), normalizedMinutes, 0, 0, t.Location())
+	// 输出结果
+	//fmt.Println(normalizedTime.Format("2006-01-02 15:04:05"))
+	return normalizedTime, nil
+}

+ 10 - 0
tests/warming_test.go

@@ -0,0 +1,10 @@
+package test
+
+import (
+	"Cold_mqtt/models/Device"
+	"testing"
+)
+
+func TestWarniData(t *testing.T) {
+	Device.RedisDeviceData_Get("2024373764352840", 2)
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 6 - 0
views/index.tpl


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels