wanshenmean
6 小时以前 f288ccc545f8cc32bc922c96dfb3cab9a1f92ec6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_Model.Models;
 
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 机械手状态管理器 - 负责 RobotSocketState 的线程安全更新和克隆
    /// </summary>
    /// <remarks>
    /// 核心功能是通过 IRobotStateRepository 管理数据库中的机械手状态。
    /// 提供乐观并发控制,通过 Version 字段防止并发更新时的数据覆盖问题。
    /// </remarks>
    public class RobotStateManager
    {
        /// <summary>
        /// 仓储服务实例,用于读写数据库中的状态数据
        /// </summary>
        private readonly IRobotStateRepository _repository;
 
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger _logger;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="repository">仓储服务实例</param>
        /// <param name="logger">日志记录器</param>
        public RobotStateManager(IRobotStateRepository repository, ILogger logger)
        {
            _repository = repository;
            _logger = logger;
        }
 
        /// <summary>
        /// 安全更新 RobotSocketState,防止并发覆盖
        /// </summary>
        /// <remarks>
        /// 使用乐观并发模式:先读取当前 Version,执行更新时检查版本是否一致。
        /// 如果 Version 不匹配(说明有其他线程已更新),则更新失败返回 false。
        /// </remarks>
        /// <param name="ipAddress">设备 IP 地址</param>
        /// <param name="updateAction">更新状态的委托函数,传入当前状态副本,返回修改后的新状态</param>
        /// <returns>是否更新成功;false 表示版本冲突或状态不存在</returns>
        public bool TryUpdateStateSafely(string ipAddress, Func<RobotSocketState, RobotSocketState> updateAction)
        {
            // 从数据库获取当前存储的状态
            var currentEntity = _repository.GetByIp(ipAddress);
 
            if (currentEntity == null)
            {
                return false;
            }
 
            // 记录当前存储的 Version,作为更新时的期望版本
            var expectedVersion = currentEntity.Version;
 
            // 创建状态的深拷贝副本(使用 JSON 序列化实现)
            var stateCopy = CloneState(_repository.ToSocketState(currentEntity));
 
            // 执行调用者提供的更新逻辑,传入副本状态,获取新的状态对象
            var newState = updateAction(stateCopy);
 
            // 将新状态转换为数据库实体
            var newEntity = _repository.ToEntity(newState);
            newEntity.Id = currentEntity.Id;
            newEntity.Version = expectedVersion + 1; // 版本自增
 
            // 调用仓储的安全更新方法,传入期望 Version
            // 如果 Version 不一致(已被其他线程更新),则更新失败
            return _repository.TryUpdate(ipAddress, newEntity, expectedVersion);
        }
 
        /// <summary>
        /// 安全更新 RobotSocketState 的重载版本(直接传入新状态)
        /// </summary>
        /// <remarks>
        /// 与上一个重载的区别:此方法直接接收完整的新状态对象,而不是更新委托。
        /// 如果数据库中不存在该设备的状态,则创建新记录。
        /// </remarks>
        /// <param name="ipAddress">设备 IP 地址</param>
        /// <param name="newState">新状态对象</param>
        /// <returns>是否更新成功;新建设置为 true</returns>
        public bool TryUpdateStateSafely(string ipAddress, RobotSocketState newState)
        {
            // 从数据库获取当前存储的状态
            var currentEntity = _repository.GetByIp(ipAddress);
 
            // 如果当前不存在该设备的状态,创建新记录
            if (currentEntity == null)
            {
                _repository.GetOrCreate(newState.IPAddress, newState.RobotCrane ?? new RobotCraneDevice());
                _logger.LogDebug("TryUpdateStateSafely:创建新状态,IP: {IpAddress}", ipAddress);
                QuartzLogger.Debug($"创建新状态,IP: {ipAddress}", ipAddress);
                return true;
            }
 
            // 当前存在状态,记录期望 Version 用于乐观锁检查
            var expectedVersion = currentEntity.Version;
 
            // 将新状态转换为数据库实体
            var newEntity = _repository.ToEntity(newState);
            newEntity.Id = currentEntity.Id;
            newEntity.Version = expectedVersion + 1; // 版本自增
 
            // 尝试安全更新,如果版本冲突则返回 false
            bool success = _repository.TryUpdate(ipAddress, newEntity, expectedVersion);
 
            if (!success)
            {
                _logger.LogWarning("TryUpdateStateSafely:版本冲突,更新失败,IP: {IpAddress},期望版本: {ExpectedVersion}", ipAddress, expectedVersion);
                QuartzLogger.Warn($"版本冲突,更新失败,IP: {ipAddress}", ipAddress);
            }
 
            return success;
        }
 
        /// <summary>
        /// 克隆 RobotSocketState 对象(深拷贝)
        /// </summary>
        /// <remarks>
        /// 使用 JSON 序列化/反序列化实现深拷贝。
        /// 这样可以确保新对象与原对象完全独立,修改新对象不会影响原对象。
        /// </remarks>
        /// <param name="source">源状态对象</param>
        /// <returns>新的状态对象,是源对象的深拷贝</returns>
        public RobotSocketState CloneState(RobotSocketState source)
        {
            var json = JsonConvert.SerializeObject(source);
            return JsonConvert.DeserializeObject<RobotSocketState>(json) ?? new RobotSocketState { IPAddress = source.IPAddress };
        }
 
        /// <summary>
        /// 从数据库获取机械手状态
        /// </summary>
        /// <param name="ipAddress">设备 IP 地址</param>
        /// <returns>如果存在则返回状态对象,否则返回 null</returns>
        public RobotSocketState? GetState(string ipAddress)
        {
            var entity = _repository.GetByIp(ipAddress);
            return entity != null ? _repository.ToSocketState(entity) : null;
        }
 
        /// <summary>
        /// 获取或创建机械手状态
        /// </summary>
        /// <remarks>
        /// 如果数据库中已存在该设备的状态,直接返回。
        /// 如果不存在,则创建新的状态记录并返回。
        /// </remarks>
        /// <param name="ipAddress">设备 IP 地址</param>
        /// <param name="robotCrane">机器人设备信息,用于初始化新状态</param>
        /// <returns>该设备的状态对象</returns>
        public RobotSocketState GetOrCreateState(string ipAddress, RobotCraneDevice robotCrane)
        {
            var entity = _repository.GetOrCreate(ipAddress, robotCrane);
            return _repository.ToSocketState(entity);
        }
    }
}