| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- package models
- import (
- "fmt"
- "sync"
- "time"
- )
- // DiandiaoModel 表示电调的 JSON 结构
- type DiandiaoModel struct {
- CMDType int `json:"CMDType"` //0:设备状态1设备数据控制
- CtrlType int `json:"CtrlType"`
- EqpmtStatus int `json:"EqpmtStatus"` //设备状态值
- EqpmtStdName string `json:"EqpmtStdName"` //设备标准名称
- EqpmtStrID string `json:"EqpmtStrID"`
- IsReset int `json:"IsReset"`
- LineID int `json:"LineID"` //线路号
- OpeMark int `json:"OpeMark"`
- FValue int `json:"fValue"` //浮点数值
- NValueType int `json:"nValueType"`
- EqpmtTypeID int `json:"EqpmtTypeID"`
- }
- // ... existing code ...
- // generateModelKey 生成电调模型唯一键
- func (em *DiandiaoModel) GenerateModelKey() string {
- //return fmt.Sprintf("%d:%d:%d", em.LineID, em.StationID, em.ViewID)
- return fmt.Sprintf("%d", em.LineID)
- }
- // 单图元电调模型
- type EViewMapModel struct {
- Models []DiandiaoModel // key: modelKey, value: 设备列表
- ModelKey string // 唯一键
- LastBatchUpdate time.Time // 最后总包更新时间
- LastDeviceUpdate time.Time // 单设备更新时间
- }
- // ElectricModelManager 电调模型管理器
- type ElectricModelManager struct {
- Models map[string]EViewMapModel // key: modelKey, value: 单图元电调模型
- Mutex sync.RWMutex // 读写锁保护并发访问
- once sync.Once // 确保初始化只执行一次
- // 用于跟踪客户端连接和上次发送的数据
- ClientData map[string]map[string]string // clientID -> (eqpmtStrID -> value)
- ClientLastFullData time.Time // 上次发送全量数据的时间
- ClientLastAccess map[string]time.Time // clientID -> 最后访问时间
- }
- // 使用 sync.Once 确保线程安全的单例实例创建
- var (
- instance *ElectricModelManager
- once sync.Once
- )
- // GetInstance 获取 ElectricModelManager 的单例实例
- func GetInstance() *ElectricModelManager {
- once.Do(func() {
- instance = &ElectricModelManager{
- Models: make(map[string]EViewMapModel),
- ClientData: make(map[string]map[string]string),
- ClientLastAccess: make(map[string]time.Time),
- }
- })
- // 定期清理长时间未访问的客户端数据
- go instance.cleanupInactiveClients()
- return instance
- }
- // UpdateBatchData 更新总包数据
- func (em *ElectricModelManager) UpdateBatchData(models []DiandiaoModel) {
- em.once.Do(func() {
- em.Models = make(map[string]EViewMapModel)
- })
- // 创建新的映射而不是直接清空,提高并发安全性
- newModels := make(map[string]EViewMapModel)
- for _, model := range models {
- key := model.GenerateModelKey()
- if viewMapModel, exists := newModels[key]; exists {
- viewMapModel.Models = append(viewMapModel.Models, model)
- viewMapModel.LastBatchUpdate = time.Now()
- newModels[key] = viewMapModel
- } else {
- newModels[key] = EViewMapModel{
- Models: []DiandiaoModel{model},
- ModelKey: key,
- LastBatchUpdate: time.Now(),
- LastDeviceUpdate: time.Time{},
- }
- }
- }
- // 原子性地替换整个映射
- em.Mutex.Lock()
- em.Models = newModels
- em.Mutex.Unlock()
- }
- // GetModelByKey 根据Key获取EViewMapModel
- func (em *ElectricModelManager) GetModelByKey(key string) (EViewMapModel, bool) {
- em.Mutex.RLock()
- defer em.Mutex.RUnlock()
- model, exists := em.Models[key]
- if !exists {
- // 返回空值和false表示未找到
- return EViewMapModel{}, false
- }
- // 返回拷贝以避免外部修改影响内部状态
- modelsCopy := make([]DiandiaoModel, len(model.Models))
- copy(modelsCopy, model.Models)
- return EViewMapModel{
- Models: modelsCopy,
- ModelKey: model.ModelKey,
- LastBatchUpdate: model.LastBatchUpdate,
- LastDeviceUpdate: model.LastDeviceUpdate,
- }, true
- }
- // UpdateSingleDevice 更新单设备数据
- func (em *ElectricModelManager) UpdateSingleDevice(model DiandiaoModel) {
- key := model.GenerateModelKey()
- em.Mutex.Lock()
- defer em.Mutex.Unlock()
- if viewMapModel, exists := em.Models[key]; exists {
- // 查找是否已存在该设备
- found := false
- for i, existingModel := range viewMapModel.Models {
- if existingModel.EqpmtStrID == model.EqpmtStrID {
- viewMapModel.Models[i] = model
- found = true
- break
- }
- }
- // 如果未找到该设备,则添加新设备
- if !found {
- viewMapModel.Models = append(viewMapModel.Models, model)
- }
- viewMapModel.LastDeviceUpdate = time.Now()
- em.Models[key] = viewMapModel
- } else {
- // 如果不存在该图元,则创建新的
- em.Models[key] = EViewMapModel{
- Models: []DiandiaoModel{model},
- ModelKey: key,
- LastBatchUpdate: time.Time{},
- LastDeviceUpdate: time.Now(),
- }
- }
- }
- // RegisterClient 注册客户端连接
- func (em *ElectricModelManager) RegisterClient(clientID string) {
- em.Mutex.Lock()
- defer em.Mutex.Unlock()
- em.ClientLastAccess[clientID] = time.Now()
- if _, exists := em.ClientData[clientID]; !exists {
- em.ClientData[clientID] = make(map[string]string)
- }
- }
- // ShouldSendFullData 判断是否应该发送全量数据
- func (em *ElectricModelManager) ShouldSendFullData(intervalSeconds int) bool {
- em.Mutex.RLock()
- defer em.Mutex.RUnlock()
- return time.Since(em.ClientLastFullData) >= time.Duration(intervalSeconds)*time.Second
- }
- // UpdateFullDataTimestamp 更新全量数据发送时间戳
- func (em *ElectricModelManager) UpdateFullDataTimestamp() {
- em.Mutex.Lock()
- defer em.Mutex.Unlock()
- em.ClientLastFullData = time.Now()
- }
- // CompareWithClientData 比较当前数据与客户端数据的差异
- func (em *ElectricModelManager) CompareWithClientData(clientID string, currentData map[string]string) map[string]string {
- em.Mutex.RLock()
- defer em.Mutex.RUnlock()
- clientData, exists := em.ClientData[clientID]
- if !exists {
- // 客户端尚未注册或无历史数据,返回全部当前数据
- return currentData
- }
- changedData := make(map[string]string)
- for id, value := range currentData {
- if clientValue, ok := clientData[id]; !ok || clientValue != value {
- changedData[id] = value
- }
- }
- return changedData
- }
- // UpdateClientData 更新客户端数据记录
- func (em *ElectricModelManager) UpdateClientData(clientID string, data map[string]string) {
- em.Mutex.Lock()
- defer em.Mutex.Unlock()
- em.ClientData[clientID] = data
- em.ClientLastAccess[clientID] = time.Now()
- }
- // cleanupInactiveClients 定期清理长时间未访问的客户端数据
- func (em *ElectricModelManager) cleanupInactiveClients() {
- ticker := time.NewTicker(1 * time.Minute)
- defer ticker.Stop()
- for {
- select {
- case <-ticker.C:
- em.Mutex.Lock()
- now := time.Now()
- for clientID, lastAccess := range em.ClientLastAccess {
- // 如果客户端超过5分钟未访问,则清理其数据
- if now.Sub(lastAccess) > 5*time.Minute {
- delete(em.ClientData, clientID)
- delete(em.ClientLastAccess, clientID)
- }
- }
- em.Mutex.Unlock()
- }
- }
- }
|