You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
10 KiB

11 months ago
  1. using Mirle.Component.MPLC.DataBlocks;
  2. using Mirle.Component.MPLC.Interfaces;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Linq;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace Mirle.Component.MPLC.FileData
  11. {
  12. /// <summary>
  13. /// 檔案讀取器
  14. /// </summary>
  15. public class FileReader : IMPLCProvider, IDisposable
  16. {
  17. /// <summary>
  18. /// 建構式
  19. /// </summary>
  20. public FileReader() { }
  21. /// <summary>
  22. /// 檔案集
  23. /// </summary>
  24. private readonly List<string> files = new List<string>();
  25. /// <summary>
  26. /// 建立快取任務
  27. /// </summary>
  28. private Task _createCacheTask;
  29. /// <summary>
  30. ///
  31. /// </summary>
  32. private CancellationTokenSource _cancellationTokenSource;
  33. /// <summary>
  34. /// 資料區塊
  35. /// </summary>
  36. private readonly List<FileDataBlock> _dataBlocks = new List<FileDataBlock>();
  37. /// <summary>
  38. /// 原始資料
  39. /// </summary>
  40. protected ConcurrentDictionary<DateTime, RawRecord> _rawData = new ConcurrentDictionary<DateTime, RawRecord>();
  41. /// <summary>
  42. ///
  43. /// </summary>
  44. public double CachingPercentage => (double)_rawData.Values.Count(r => r.IsCached) / _rawData.Count * 100;
  45. /// <summary>
  46. ///
  47. /// </summary>
  48. public DateTime CurrentRowTime { get; private set; }
  49. /// <summary>
  50. ///
  51. /// </summary>
  52. public bool IsConnected => true;
  53. /// <summary>
  54. ///
  55. /// </summary>
  56. /// <param name="newDataBlock"></param>
  57. public void AddDataBlock(FileDataBlock newDataBlock)
  58. {
  59. _dataBlocks.Add(newDataBlock);
  60. }
  61. /// <summary>
  62. ///
  63. /// </summary>
  64. /// <returns></returns>
  65. public IEnumerable<FileDataBlock> GetDataBlocks()
  66. {
  67. return _dataBlocks;
  68. }
  69. /// <summary>
  70. ///
  71. /// </summary>
  72. /// <param name="FileName"></param>
  73. public void AddFile(string FileName)
  74. {
  75. WaitForCreateCacheTask();
  76. files.Add(FileName);
  77. }
  78. /// <summary>
  79. ///
  80. /// </summary>
  81. public void ClearFile()
  82. {
  83. WaitForCreateCacheTask();
  84. _rawData.Clear();
  85. files.Clear();
  86. }
  87. /// <summary>
  88. ///
  89. /// </summary>
  90. public void OpenFile()
  91. {
  92. WaitForCreateCacheTask();
  93. foreach (string f in files)
  94. {
  95. try
  96. {
  97. string[] rows = System.IO.File.ReadAllLines(f);
  98. foreach (string row in rows)
  99. {
  100. var time = DateTime.ParseExact(row.Substring(row.IndexOf('[') + 1, 14), "HH:mm:ss.fffff", null);
  101. var rawRecord = CreateRawRecord(row.Substring(row.IndexOf(']') + 2));
  102. _rawData.TryAdd(time, rawRecord);
  103. }
  104. }
  105. catch (Exception ex)
  106. {
  107. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  108. }
  109. }
  110. _cancellationTokenSource = new CancellationTokenSource();
  111. _createCacheTask = new Task(() => CreateCache(_cancellationTokenSource.Token));
  112. _createCacheTask.Start();
  113. }
  114. /// <summary>
  115. ///
  116. /// </summary>
  117. private void WaitForCreateCacheTask()
  118. {
  119. if (_createCacheTask != null && _createCacheTask.Status == TaskStatus.Running)
  120. {
  121. _cancellationTokenSource.Cancel();
  122. _createCacheTask.Wait(1000);
  123. }
  124. }
  125. /// <summary>
  126. ///
  127. /// </summary>
  128. /// <param name="token"></param>
  129. private void CreateCache(CancellationToken token)
  130. {
  131. try
  132. {
  133. ParallelOptions po = new ParallelOptions
  134. {
  135. CancellationToken = token,
  136. MaxDegreeOfParallelism = Environment.ProcessorCount
  137. };
  138. Parallel.ForEach(_rawData.Values, po, (r) => { r.CreateCache(); });
  139. }
  140. catch (Exception ex)
  141. {
  142. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  143. }
  144. }
  145. /// <summary>
  146. ///
  147. /// </summary>
  148. /// <param name="rawString"></param>
  149. /// <returns></returns>
  150. protected virtual RawRecord CreateRawRecord(string rawString)
  151. {
  152. return new RawRecord(rawString);
  153. }
  154. /// <summary>
  155. ///
  156. /// </summary>
  157. /// <returns></returns>
  158. public IEnumerable<DateTime> GetDateTimeIndexes()
  159. {
  160. return _rawData.Keys.OrderBy(k => k);
  161. }
  162. /// <summary>
  163. ///
  164. /// </summary>
  165. /// <param name="index"></param>
  166. public void Refresh(int index)
  167. {
  168. try
  169. {
  170. foreach (FileDataBlock block in _dataBlocks)
  171. {
  172. try
  173. {
  174. var rawRecords = _rawData.OrderBy(r => r.Key).ToList();
  175. var rawRecord = rawRecords[index];
  176. CurrentRowTime = rawRecord.Key;
  177. byte[] newByteArray = rawRecord.Value.GetBlockByIndex(block.ColumnIndex);
  178. block.SetRawData(newByteArray);
  179. }
  180. catch (Exception ex)
  181. {
  182. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  183. }
  184. }
  185. }
  186. catch (Exception ex)
  187. {
  188. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  189. }
  190. }
  191. /// <summary>
  192. ///
  193. /// </summary>
  194. /// <param name="index"></param>
  195. public void Refresh(DateTime index)
  196. {
  197. if (_rawData.TryGetValue(index, out var value))
  198. {
  199. try
  200. {
  201. foreach (var block in _dataBlocks)
  202. {
  203. try
  204. {
  205. byte[] newByteArray = value.GetBlockByIndex(block.ColumnIndex);
  206. block.SetRawData(newByteArray);
  207. }
  208. catch (Exception ex)
  209. {
  210. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  211. }
  212. }
  213. CurrentRowTime = index;
  214. }
  215. catch (Exception ex)
  216. {
  217. Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
  218. }
  219. }
  220. }
  221. /// <summary>
  222. ///
  223. /// </summary>
  224. /// <param name="address"></param>
  225. /// <returns></returns>
  226. public bool GetBit(string address)
  227. {
  228. foreach (var block in _dataBlocks)
  229. {
  230. if (block.TryGetBit(address, out bool value))
  231. return value;
  232. }
  233. return false;
  234. }
  235. /// <summary>
  236. ///
  237. /// </summary>
  238. /// <param name="address"></param>
  239. public void SetBitOn(string address)
  240. {
  241. return;
  242. }
  243. /// <summary>
  244. ///
  245. /// </summary>
  246. /// <param name="address"></param>
  247. public void SetBitOff(string address)
  248. {
  249. return;
  250. }
  251. /// <summary>
  252. ///
  253. /// </summary>
  254. /// <param name="address"></param>
  255. /// <returns></returns>
  256. public int ReadWord(string address)
  257. {
  258. foreach (var block in _dataBlocks)
  259. {
  260. if (block.TryGetWord(address, out int value))
  261. return value;
  262. }
  263. return 0;
  264. }
  265. /// <summary>
  266. ///
  267. /// </summary>
  268. /// <param name="address"></param>
  269. /// <param name="data"></param>
  270. public void WriteWord(string address, int data)
  271. {
  272. return;
  273. }
  274. /// <summary>
  275. ///
  276. /// </summary>
  277. /// <param name="startAddress"></param>
  278. /// <param name="length"></param>
  279. /// <returns></returns>
  280. public int[] ReadWords(string startAddress, int length)
  281. {
  282. foreach (var block in _dataBlocks)
  283. {
  284. if (block.TryGetWords(startAddress, out int[] data, length))
  285. return data;
  286. }
  287. return new int[length];
  288. }
  289. /// <summary>
  290. ///
  291. /// </summary>
  292. /// <param name="startAddress"></param>
  293. /// <param name="data"></param>
  294. public void WriteWords(string startAddress, int[] data)
  295. {
  296. return;
  297. }
  298. /// <summary>
  299. ///
  300. /// </summary>
  301. /// <returns></returns>
  302. public FileDataViewer GetDataView()
  303. {
  304. return new FileDataViewer(this);
  305. }
  306. /// <summary>
  307. ///
  308. /// </summary>
  309. /// <param name="dateTimeIndex"></param>
  310. /// <param name="blockColumnIndex"></param>
  311. /// <returns></returns>
  312. public byte[] GetRawDataByDateTimeIndex(DateTime dateTimeIndex, int blockColumnIndex)
  313. {
  314. if (_rawData.TryGetValue(dateTimeIndex, out var value))
  315. return value.GetBlockByIndex(blockColumnIndex);
  316. return null;
  317. }
  318. #region IDisposable Support
  319. private bool disposedValue = false; // 偵測多餘的呼叫
  320. /// <summary>
  321. /// 釋放資源
  322. /// </summary>
  323. /// <param name="disposing"></param>
  324. protected virtual void Dispose(bool disposing)
  325. {
  326. if (!disposedValue)
  327. {
  328. if (disposing)
  329. {
  330. _createCacheTask?.Dispose();
  331. _cancellationTokenSource?.Dispose();
  332. }
  333. disposedValue = true;
  334. }
  335. }
  336. /// <summary>
  337. /// 建構式
  338. /// </summary>
  339. ~FileReader()
  340. {
  341. Dispose(false);
  342. }
  343. /// <summary>
  344. /// 釋放資源
  345. /// </summary>
  346. public void Dispose()
  347. {
  348. Dispose(true);
  349. GC.SuppressFinalize(this);
  350. }
  351. #endregion
  352. }
  353. }