EquipmentDataModel.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. using SWRIS.Controls;
  2. using SWRIS.Dtos;
  3. using SWRIS.Enums;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Collections.ObjectModel;
  7. using System.ComponentModel;
  8. using System.Linq;
  9. using System.Net.Sockets;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. namespace SWRIS.Models
  13. {
  14. public class EquipmentDataModel : INotifyPropertyChanged
  15. {
  16. private int ropeNumber;
  17. private string ropeName;
  18. private int trolleyId;
  19. private string ipAddress;
  20. private string serialNo;
  21. private bool isConnect = false;
  22. private int sensorCount = 4;
  23. private double liftHightRatio = 1;
  24. private double ropeLength = 100f; // 默认长度为100米
  25. private RunningStatus runningStatus = RunningStatus.Stopped;
  26. private DirectionState direction;
  27. private FaultType? faultType;
  28. private double speed;
  29. private double position;
  30. private double absolutePosition;
  31. private double liftHeight;
  32. private double maxDamageValue;
  33. private double avgDamageValue;
  34. private int damageCount;
  35. private int mildCount;
  36. private int lightCount;
  37. private int moderateCount;
  38. private int severeCount;
  39. private int criticalCount;
  40. private int extremeCount;
  41. private DetectionDataModel detectionData;
  42. private RiskLevel riskLevel = RiskLevel.Normal;
  43. private Socket clientSocket;
  44. private double samplingStep;
  45. private DebugMessageModel debugMessage = new DebugMessageModel(string.Empty);
  46. private bool isAlerting = false;
  47. private bool isSpeedStable = true;
  48. private double variationThreshold = 0.25;
  49. private bool isHeartbeatReceived;
  50. private uint detectionNo;
  51. private bool disableAlarm;
  52. private int[] inUseSensor = new int[] { 1, 2, 3, 4 };
  53. private RealTimeLineChart chart;
  54. /// <summary>
  55. /// 钢丝绳绳号
  56. /// </summary>
  57. public int RopeNumber
  58. {
  59. get => ropeNumber;
  60. set { ropeNumber = value; OnPropertyChanged(nameof(RopeNumber)); }
  61. }
  62. /// <summary>
  63. /// 钢丝绳名称
  64. /// </summary>
  65. public string RopeName
  66. {
  67. get => ropeName;
  68. set { ropeName = value; OnPropertyChanged(nameof(RopeName)); }
  69. }
  70. /// <summary>
  71. /// 小车Id
  72. /// </summary>
  73. public int TrolleyId
  74. {
  75. get => trolleyId;
  76. set { trolleyId = value; OnPropertyChanged(nameof(TrolleyId)); }
  77. }
  78. /// <summary>
  79. /// 设备Ip地址
  80. /// </summary>
  81. public string IpAddress
  82. {
  83. get => ipAddress;
  84. set { ipAddress = value; OnPropertyChanged(nameof(IpAddress)); }
  85. }
  86. /// <summary>
  87. /// 设备序列号
  88. /// </summary>
  89. public string SerialNo
  90. {
  91. get => serialNo;
  92. set { serialNo = value; OnPropertyChanged(nameof(SerialNo)); }
  93. }
  94. /// <summary>
  95. /// 连接状态
  96. /// </summary>
  97. public bool IsConnect
  98. {
  99. get => isConnect;
  100. set { isConnect = value; OnPropertyChanged(nameof(IsConnect)); }
  101. }
  102. /// <summary>
  103. /// 传感器数量
  104. /// </summary>
  105. public int SensorCount
  106. {
  107. get => sensorCount;
  108. set { sensorCount = value; OnPropertyChanged(nameof(SensorCount)); }
  109. }
  110. /// <summary>
  111. /// 钢丝绳绳长
  112. /// </summary>
  113. public double RopeLength
  114. {
  115. get => ropeLength;
  116. set { ropeLength = value; OnPropertyChanged(nameof(RopeLength)); }
  117. }
  118. public double LiftHightRatio
  119. {
  120. get => liftHightRatio;
  121. set { liftHightRatio = value; OnPropertyChanged(nameof(LiftHightRatio)); }
  122. }
  123. /// <summary>
  124. /// 采样步长
  125. /// </summary>
  126. public double SamplingStep
  127. {
  128. get => samplingStep;
  129. set { samplingStep = value; OnPropertyChanged(nameof(SamplingStep)); }
  130. }
  131. /// <summary>
  132. /// 运行状态
  133. /// </summary>
  134. public RunningStatus RunningStatus
  135. {
  136. get => runningStatus;
  137. set { runningStatus = value; OnPropertyChanged(nameof(RunningStatus)); }
  138. }
  139. /// <summary>
  140. /// 预状态挂起时间戳(UtcNow.Ticks),0 表示无挂起的预状态
  141. /// </summary>
  142. public long PreStatusTimestamp { get; set; }
  143. /// <summary>
  144. /// 预运行请求的检测模式(TemporalNormal=时域,SpatialNormal=空域),用于 0x06 应答确认时恢复正确模式
  145. /// </summary>
  146. public RunningStatus PreStatusRunningMode { get; set; }
  147. /// <summary>
  148. /// 标记预状态位并记录挂起时间戳(本地先行、等待设备确认)
  149. /// </summary>
  150. public void MarkPreStatus(RunningStatus flag)
  151. {
  152. RunningStatus |= flag;
  153. PreStatusTimestamp = DateTime.UtcNow.Ticks;
  154. }
  155. /// <summary>
  156. /// 标记预运行状态并记录本次请求的检测模式(时域/空域),清除旧模式位避免切换时残留
  157. /// </summary>
  158. public void MarkPreRunning(RunningStatus mode)
  159. {
  160. RunningStatus = (RunningStatus & ~(RunningStatus.SpatialNormal | RunningStatus.TemporalNormal)) | mode | RunningStatus.PreRunning;
  161. PreStatusRunningMode = mode;
  162. PreStatusTimestamp = DateTime.UtcNow.Ticks;
  163. }
  164. /// <summary>
  165. /// 设备已确认命令:按位清除对应预状态位并合并实际状态,保留报警/故障等其他状态位
  166. /// </summary>
  167. public void ConfirmPreStatus(RunningStatus preFlag, RunningStatus actualStatus)
  168. {
  169. RunningStatus = (RunningStatus & ~preFlag) | actualStatus;
  170. if (preFlag == RunningStatus.PreRunning)
  171. {
  172. PreStatusRunningMode = 0;
  173. }
  174. // 仅当没有其他挂起的预状态位时才清零挂起时间戳,避免影响其他预状态的超时判断
  175. if ((RunningStatus & (RunningStatus.PreStop | RunningStatus.PreRunning | RunningStatus.PreCalibration)) == 0)
  176. {
  177. PreStatusTimestamp = 0;
  178. }
  179. }
  180. /// <summary>
  181. /// 清除所有挂起的预状态位(用于断线、超时等场景)
  182. /// </summary>
  183. public void ClearAllPreStatus()
  184. {
  185. RunningStatus &= ~(RunningStatus.PreStop | RunningStatus.PreRunning | RunningStatus.PreCalibration);
  186. PreStatusRunningMode = 0;
  187. PreStatusTimestamp = 0;
  188. }
  189. /// <summary>
  190. /// 运行方向
  191. /// </summary>
  192. public DirectionState Direction
  193. {
  194. get => direction;
  195. set { direction = value; OnPropertyChanged(nameof(Direction)); }
  196. }
  197. /// <summary>
  198. /// 故障类型
  199. /// </summary>
  200. public FaultType? FaultType
  201. {
  202. get => faultType;
  203. set { faultType = value; OnPropertyChanged(nameof(FaultType)); }
  204. }
  205. /// <summary>
  206. /// 速度
  207. /// </summary>
  208. public double Speed
  209. {
  210. get => speed;
  211. set { speed = value; OnPropertyChanged(nameof(Speed)); }
  212. }
  213. /// <summary>
  214. /// 速度变化率阈值
  215. /// </summary>
  216. public double VariationThreshold
  217. {
  218. get => variationThreshold;
  219. set { variationThreshold = value; OnPropertyChanged(nameof(VariationThreshold)); }
  220. }
  221. /// <summary>
  222. /// 速度是否稳定
  223. /// </summary>
  224. public bool IsSpeedStable
  225. {
  226. get => isSpeedStable;
  227. set { isSpeedStable = value; OnPropertyChanged(nameof(IsSpeedStable)); }
  228. }
  229. /// <summary>
  230. /// 位置
  231. /// </summary>
  232. public double Position
  233. {
  234. get => position;
  235. set { position = value; OnPropertyChanged(nameof(Position)); }
  236. }
  237. /// <summary>
  238. /// 绝对位置
  239. /// </summary>
  240. public double AbsolutePosition
  241. {
  242. get => absolutePosition;
  243. set { absolutePosition = value; OnPropertyChanged(nameof(AbsolutePosition)); }
  244. }
  245. /// <summary>
  246. /// 起升高度
  247. /// </summary>
  248. public double LiftHeight
  249. {
  250. get => liftHeight;
  251. set { liftHeight = value; OnPropertyChanged(nameof(LiftHeight)); }
  252. }
  253. /// <summary>
  254. /// 健康程度
  255. /// </summary>
  256. public RiskLevel RiskLevel
  257. {
  258. get => riskLevel;
  259. set { riskLevel = value; OnPropertyChanged(nameof(RiskLevel)); }
  260. }
  261. /// <summary>
  262. /// 平均损伤值
  263. /// </summary>
  264. public double AvgDamageValue
  265. {
  266. get => avgDamageValue;
  267. set { avgDamageValue = value; OnPropertyChanged(nameof(AvgDamageValue)); }
  268. }
  269. /// <summary>
  270. /// 最大损伤值
  271. /// </summary>
  272. public double MaxDamageValue
  273. {
  274. get => maxDamageValue;
  275. set { maxDamageValue = value; OnPropertyChanged(nameof(MaxDamageValue)); }
  276. }
  277. /// <summary>
  278. /// 损伤报警数量
  279. /// </summary>
  280. public int DamageCount
  281. {
  282. get => damageCount;
  283. set { damageCount = value; OnPropertyChanged(nameof(DamageCount)); }
  284. }
  285. /// <summary>
  286. /// 轻微损伤数量
  287. /// </summary>
  288. public int MildCount
  289. {
  290. get => mildCount;
  291. set { mildCount = value; OnPropertyChanged(nameof(MildCount)); }
  292. }
  293. /// <summary>
  294. /// 轻度损伤数量
  295. /// </summary>
  296. public int LightCount
  297. {
  298. get => lightCount;
  299. set { lightCount = value; OnPropertyChanged(nameof(LightCount)); }
  300. }
  301. /// <summary>
  302. /// 中度损伤数量
  303. /// </summary>
  304. public int ModerateCount
  305. {
  306. get => moderateCount;
  307. set { moderateCount = value; OnPropertyChanged(nameof(ModerateCount)); }
  308. }
  309. /// <summary>
  310. /// 较重损伤数量
  311. /// </summary>
  312. public int SevereCount
  313. {
  314. get => severeCount;
  315. set { severeCount = value; OnPropertyChanged(nameof(SevereCount)); }
  316. }
  317. /// <summary>
  318. /// 严重损伤数量
  319. /// </summary>
  320. public int CriticalCount
  321. {
  322. get => criticalCount;
  323. set { criticalCount = value; OnPropertyChanged(nameof(CriticalCount)); }
  324. }
  325. /// <summary>
  326. /// 超限损伤数量
  327. /// </summary>
  328. public int ExtremeCount
  329. {
  330. get => extremeCount;
  331. set { extremeCount = value; OnPropertyChanged(nameof(ExtremeCount)); }
  332. }
  333. /// <summary>
  334. /// 最后一次的检测数据
  335. /// </summary>
  336. public DetectionDataModel DetectionData
  337. {
  338. get => detectionData;
  339. set { detectionData = value; OnPropertyChanged(nameof(DetectionData)); }
  340. }
  341. /// <summary>
  342. /// 调试信息
  343. /// </summary>
  344. public DebugMessageModel DebugMessage
  345. {
  346. get => debugMessage;
  347. set { debugMessage = value; OnPropertyChanged(nameof(DebugMessage)); }
  348. }
  349. /// <summary>
  350. /// TCP 客户端
  351. /// </summary>
  352. public Socket ClientSocket
  353. {
  354. get => clientSocket;
  355. set { clientSocket = value; OnPropertyChanged(nameof(ClientSocket)); }
  356. }
  357. /// <summary>
  358. /// 是否报警中
  359. /// </summary>
  360. public bool IsAlerting
  361. {
  362. get => isAlerting;
  363. set { isAlerting = value; OnPropertyChanged(nameof(IsAlerting)); }
  364. }
  365. public bool IsHeartbeatReceived
  366. {
  367. get => isHeartbeatReceived;
  368. set
  369. {
  370. isHeartbeatReceived = value;
  371. OnPropertyChanged(nameof(IsHeartbeatReceived));
  372. if (value)
  373. {
  374. // 延迟一段时间后重置,以便动画可以重复触发
  375. Task.Delay(600).ContinueWith(_ =>
  376. {
  377. Application.Current.Dispatcher.Invoke(() =>
  378. {
  379. IsHeartbeatReceived = false;
  380. });
  381. });
  382. }
  383. }
  384. }
  385. /// <summary>
  386. /// 检测编号
  387. /// </summary>
  388. public uint DetectionNo
  389. {
  390. get => detectionNo;
  391. set { detectionNo = value; OnPropertyChanged(nameof(DetectionNo)); }
  392. }
  393. /// <summary>
  394. /// 禁用报警
  395. /// </summary>
  396. public bool DisableAlarm
  397. {
  398. get => disableAlarm;
  399. set { disableAlarm = value; OnPropertyChanged(nameof(DisableAlarm)); }
  400. }
  401. /// <summary>
  402. /// 在用传感器
  403. /// </summary>
  404. public int[] InUseSensor
  405. {
  406. get => inUseSensor;
  407. set { inUseSensor = value; OnPropertyChanged(nameof(InUseSensor)); }
  408. }
  409. public RealTimeLineChart Chart
  410. {
  411. get => chart;
  412. set
  413. {
  414. chart = value;
  415. }
  416. }
  417. public ObservableCollection<FaultDataModel> Faults { get; set; } = new ObservableCollection<FaultDataModel>();
  418. public ObservableCollection<AlarmDataModel> Alarms { get; set; } = new ObservableCollection<AlarmDataModel>();
  419. public ObservableCollection<LimitModel> Limits { get; set; } = new ObservableCollection<LimitModel>();
  420. public Queue<double> RecentSpeeds { get; internal set; } = new Queue<double>();
  421. private readonly object _liveStreamLock = new object();
  422. public Queue<int[]> LiveStreams { get; internal set; } = new Queue<int[]>();
  423. public ObservableCollection<int> LiveStreamValues { get; set; } = new ObservableCollection<int>();
  424. public void AddLiveStream(LiveStreamDataModel liveStream)
  425. {
  426. if (liveStream?.Data == null || liveStream.Data.Length == 0)
  427. return;
  428. lock (_liveStreamLock)
  429. {
  430. // 新数据到来时清空之前的实时流数据
  431. LiveStreams.Clear();
  432. int sensorCount = liveStream.SensorCount;
  433. int sampleCount = liveStream.Data.Length;
  434. // 本次与上次接收的时间间隔对应的采样点数量(每50ms 1个点)
  435. int targetCount = Math.Max(1, liveStream.SampleInterval / 50);
  436. int actualCount = Math.Min(targetCount, sampleCount);
  437. // 从 sampleCount 个采样点中平均抽取 actualCount 个点
  438. for (int i = 0; i < actualCount; i++)
  439. {
  440. int sourceIndex = actualCount == 1
  441. ? 0
  442. : (int)Math.Round((double)i * (sampleCount - 1) / (actualCount - 1));
  443. var point = new int[sensorCount];
  444. for (int s = 0; s < sensorCount; s++)
  445. {
  446. point[s] = liveStream.Data[sourceIndex][s];
  447. }
  448. LiveStreams.Enqueue(point);
  449. }
  450. }
  451. }
  452. /// <summary>
  453. /// 从实时流队列取出并移除一项(取出即移出)
  454. /// </summary>
  455. public bool TryDequeueLiveStream(out int[] sample)
  456. {
  457. lock (_liveStreamLock)
  458. {
  459. if (LiveStreams.Count > 0)
  460. {
  461. sample = LiveStreams.Dequeue();
  462. return true;
  463. }
  464. }
  465. sample = null;
  466. return false;
  467. }
  468. public void AddFault(FaultDataModel fault)
  469. {
  470. if (fault == null)
  471. return;
  472. // 无故障(HasFault 为 false):取消对应的故障(含编码器故障)
  473. if (!fault.HasFault)
  474. {
  475. RemoveFault(fault.FaultType);
  476. return;
  477. }
  478. // 有故障:查找同类型的已有故障
  479. var existingFault = Faults.FirstOrDefault(c => c.FaultType == fault.FaultType);
  480. if (existingFault == null)
  481. {
  482. // 无同类型故障,直接添加
  483. Faults.Add(fault);
  484. AddFaultMessage(fault);
  485. return;
  486. }
  487. // 已有同类型故障:判断故障传感器是否一致
  488. if (!existingFault.FaultySensors.SequenceEqual(fault.FaultySensors))
  489. {
  490. // 故障传感器不一致,更新为最新的故障信息
  491. existingFault.FaultCode = fault.FaultCode;
  492. UpdateFaultMessage(fault.FaultType, fault.FaultCode);
  493. }
  494. // 传感器一致则忽略,不重复添加
  495. }
  496. /// <summary>
  497. /// 取消指定类型的故障(含编码器故障)
  498. /// </summary>
  499. private void RemoveFault(FaultType faultType)
  500. {
  501. var existing = Faults.FirstOrDefault(c => c.FaultType == faultType);
  502. if (existing != null)
  503. {
  504. Faults.Remove(existing);
  505. }
  506. var message = App.DataCenter.Messages.FirstOrDefault(m => m.RopeName == RopeName);
  507. if (message != null)
  508. {
  509. Application.Current.Dispatcher.Invoke(() =>
  510. {
  511. message.Messages.RemoveAll(c => c.FaultType == faultType);
  512. });
  513. }
  514. }
  515. /// <summary>
  516. /// 添加故障到消息中心
  517. /// </summary>
  518. private void AddFaultMessage(FaultDataModel fault)
  519. {
  520. var message = App.DataCenter.Messages.FirstOrDefault(m => m.RopeName == RopeName);
  521. if (message == null)
  522. {
  523. Application.Current.Dispatcher.Invoke(() =>
  524. {
  525. App.DataCenter.Messages.Add(new FaultsMessageDto
  526. {
  527. RopeName = RopeName,
  528. Messages = new List<FaultDataModel> { fault }
  529. });
  530. });
  531. }
  532. else
  533. {
  534. Application.Current.Dispatcher.Invoke(() =>
  535. {
  536. message.Messages.Add(fault);
  537. });
  538. }
  539. }
  540. /// <summary>
  541. /// 更新消息中心中指定类型的故障码
  542. /// </summary>
  543. private void UpdateFaultMessage(FaultType faultType, int faultCode)
  544. {
  545. var message = App.DataCenter.Messages.FirstOrDefault(m => m.RopeName == RopeName);
  546. if (message != null)
  547. {
  548. Application.Current.Dispatcher.Invoke(() =>
  549. {
  550. var msgFault = message.Messages.FirstOrDefault(c => c.FaultType == faultType);
  551. if (msgFault != null)
  552. {
  553. msgFault.FaultCode = faultCode;
  554. }
  555. });
  556. }
  557. }
  558. public void AddSpeedData(double speed)
  559. {
  560. // 更新数据窗口
  561. RecentSpeeds.Enqueue(speed);
  562. if (RecentSpeeds.Count > 3)
  563. RecentSpeeds.Dequeue();
  564. if (RecentSpeeds.Count < 2)
  565. return;
  566. double avgSpeed = RecentSpeeds.Average();
  567. if (avgSpeed == 0)
  568. {
  569. IsSpeedStable = true;
  570. return;
  571. }
  572. // 计算当前速度相对于平均值的偏差
  573. bool _isStable = (Math.Abs(speed - avgSpeed) / avgSpeed) <= VariationThreshold;
  574. if (IsSpeedStable != _isStable)
  575. {
  576. IsSpeedStable = _isStable;
  577. }
  578. }
  579. public void AddAlarm(AlarmDataModel alarm)
  580. {
  581. DamageCount++;
  582. MaxDamageValue = alarm.DamageValue > MaxDamageValue ? alarm.DamageValue : MaxDamageValue;
  583. AvgDamageValue = (AvgDamageValue * (DamageCount - 1) + alarm.DamageValue) / DamageCount;
  584. switch (alarm.DamageLevel)
  585. {
  586. case DamageLevel.Mild:
  587. MildCount++;
  588. break;
  589. case DamageLevel.Light:
  590. LightCount++;
  591. break;
  592. case DamageLevel.Moderate:
  593. ModerateCount++;
  594. break;
  595. case DamageLevel.Severe:
  596. SevereCount++;
  597. break;
  598. case DamageLevel.Critical:
  599. CriticalCount++;
  600. break;
  601. case DamageLevel.ExceededLimit:
  602. ExtremeCount++;
  603. break;
  604. }
  605. Alarms.Add(alarm);
  606. RiskLevel = Alarms.Any() ? (RiskLevel)Alarms.Max(c => (int)c.DamageLevel) : RiskLevel.Normal;
  607. IsAlerting = true;
  608. Task.Run(() =>
  609. {
  610. Task.Delay(3000).Wait();
  611. IsAlerting = false;
  612. });
  613. }
  614. public void ClearData()
  615. {
  616. Speed = 0;
  617. MaxDamageValue = 0;
  618. AvgDamageValue = 0;
  619. DamageCount = 0;
  620. MildCount = 0;
  621. LightCount = 0;
  622. ModerateCount = 0;
  623. SevereCount = 0;
  624. CriticalCount = 0;
  625. ExtremeCount = 0;
  626. RiskLevel = RiskLevel.Normal;
  627. DetectionNo += 1;
  628. Application.Current.Dispatcher.Invoke(() => Alarms.Clear());
  629. Task.Run(async () =>
  630. {
  631. // 持续监控直到条件满足
  632. while (DetectionData?.RawDataReceiving == true)
  633. {
  634. await Task.Delay(100); // 避免CPU占用过高
  635. }
  636. // 条件满足时执行
  637. DetectionData = null;
  638. });
  639. }
  640. public event PropertyChangedEventHandler PropertyChanged;
  641. protected internal virtual void OnPropertyChanged(string propertyName)
  642. {
  643. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  644. }
  645. }
  646. }