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
358 lines
10 KiB
using Mirle.Component.MPLC.DataBlocks;
|
|
using Mirle.Component.MPLC.Interfaces;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Mirle.Component.MPLC.FileData
|
|
{
|
|
/// <summary>
|
|
/// 檔案讀取器
|
|
/// </summary>
|
|
public class FileReader : IMPLCProvider, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// 建構式
|
|
/// </summary>
|
|
public FileReader() { }
|
|
/// <summary>
|
|
/// 檔案集
|
|
/// </summary>
|
|
private readonly List<string> files = new List<string>();
|
|
/// <summary>
|
|
/// 建立快取任務
|
|
/// </summary>
|
|
private Task _createCacheTask;
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
private CancellationTokenSource _cancellationTokenSource;
|
|
/// <summary>
|
|
/// 資料區塊
|
|
/// </summary>
|
|
private readonly List<FileDataBlock> _dataBlocks = new List<FileDataBlock>();
|
|
/// <summary>
|
|
/// 原始資料
|
|
/// </summary>
|
|
protected ConcurrentDictionary<DateTime, RawRecord> _rawData = new ConcurrentDictionary<DateTime, RawRecord>();
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public double CachingPercentage => (double)_rawData.Values.Count(r => r.IsCached) / _rawData.Count * 100;
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public DateTime CurrentRowTime { get; private set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public bool IsConnected => true;
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="newDataBlock"></param>
|
|
public void AddDataBlock(FileDataBlock newDataBlock)
|
|
{
|
|
_dataBlocks.Add(newDataBlock);
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerable<FileDataBlock> GetDataBlocks()
|
|
{
|
|
return _dataBlocks;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="FileName"></param>
|
|
public void AddFile(string FileName)
|
|
{
|
|
WaitForCreateCacheTask();
|
|
files.Add(FileName);
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public void ClearFile()
|
|
{
|
|
WaitForCreateCacheTask();
|
|
_rawData.Clear();
|
|
files.Clear();
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public void OpenFile()
|
|
{
|
|
WaitForCreateCacheTask();
|
|
foreach (string f in files)
|
|
{
|
|
try
|
|
{
|
|
string[] rows = System.IO.File.ReadAllLines(f);
|
|
foreach (string row in rows)
|
|
{
|
|
var time = DateTime.ParseExact(row.Substring(row.IndexOf('[') + 1, 14), "HH:mm:ss.fffff", null);
|
|
var rawRecord = CreateRawRecord(row.Substring(row.IndexOf(']') + 2));
|
|
_rawData.TryAdd(time, rawRecord);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
_createCacheTask = new Task(() => CreateCache(_cancellationTokenSource.Token));
|
|
_createCacheTask.Start();
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
private void WaitForCreateCacheTask()
|
|
{
|
|
if (_createCacheTask != null && _createCacheTask.Status == TaskStatus.Running)
|
|
{
|
|
_cancellationTokenSource.Cancel();
|
|
_createCacheTask.Wait(1000);
|
|
}
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="token"></param>
|
|
private void CreateCache(CancellationToken token)
|
|
{
|
|
try
|
|
{
|
|
ParallelOptions po = new ParallelOptions
|
|
{
|
|
CancellationToken = token,
|
|
MaxDegreeOfParallelism = Environment.ProcessorCount
|
|
};
|
|
Parallel.ForEach(_rawData.Values, po, (r) => { r.CreateCache(); });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="rawString"></param>
|
|
/// <returns></returns>
|
|
protected virtual RawRecord CreateRawRecord(string rawString)
|
|
{
|
|
return new RawRecord(rawString);
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerable<DateTime> GetDateTimeIndexes()
|
|
{
|
|
return _rawData.Keys.OrderBy(k => k);
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
public void Refresh(int index)
|
|
{
|
|
try
|
|
{
|
|
foreach (FileDataBlock block in _dataBlocks)
|
|
{
|
|
try
|
|
{
|
|
var rawRecords = _rawData.OrderBy(r => r.Key).ToList();
|
|
var rawRecord = rawRecords[index];
|
|
CurrentRowTime = rawRecord.Key;
|
|
byte[] newByteArray = rawRecord.Value.GetBlockByIndex(block.ColumnIndex);
|
|
block.SetRawData(newByteArray);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
public void Refresh(DateTime index)
|
|
{
|
|
if (_rawData.TryGetValue(index, out var value))
|
|
{
|
|
try
|
|
{
|
|
foreach (var block in _dataBlocks)
|
|
{
|
|
try
|
|
{
|
|
byte[] newByteArray = value.GetBlockByIndex(block.ColumnIndex);
|
|
block.SetRawData(newByteArray);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
CurrentRowTime = index;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"{ex.Message}-{ex.StackTrace}");
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
/// <returns></returns>
|
|
|
|
public bool GetBit(string address)
|
|
{
|
|
foreach (var block in _dataBlocks)
|
|
{
|
|
if (block.TryGetBit(address, out bool value))
|
|
return value;
|
|
}
|
|
return false;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
public void SetBitOn(string address)
|
|
{
|
|
return;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
public void SetBitOff(string address)
|
|
{
|
|
return;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
/// <returns></returns>
|
|
public int ReadWord(string address)
|
|
{
|
|
foreach (var block in _dataBlocks)
|
|
{
|
|
if (block.TryGetWord(address, out int value))
|
|
return value;
|
|
}
|
|
return 0;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
/// <param name="data"></param>
|
|
public void WriteWord(string address, int data)
|
|
{
|
|
return;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="startAddress"></param>
|
|
/// <param name="length"></param>
|
|
/// <returns></returns>
|
|
public int[] ReadWords(string startAddress, int length)
|
|
{
|
|
foreach (var block in _dataBlocks)
|
|
{
|
|
if (block.TryGetWords(startAddress, out int[] data, length))
|
|
return data;
|
|
}
|
|
return new int[length];
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="startAddress"></param>
|
|
/// <param name="data"></param>
|
|
public void WriteWords(string startAddress, int[] data)
|
|
{
|
|
return;
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public FileDataViewer GetDataView()
|
|
{
|
|
return new FileDataViewer(this);
|
|
}
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="dateTimeIndex"></param>
|
|
/// <param name="blockColumnIndex"></param>
|
|
/// <returns></returns>
|
|
public byte[] GetRawDataByDateTimeIndex(DateTime dateTimeIndex, int blockColumnIndex)
|
|
{
|
|
if (_rawData.TryGetValue(dateTimeIndex, out var value))
|
|
return value.GetBlockByIndex(blockColumnIndex);
|
|
return null;
|
|
}
|
|
|
|
#region IDisposable Support
|
|
|
|
private bool disposedValue = false; // 偵測多餘的呼叫
|
|
/// <summary>
|
|
/// 釋放資源
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!disposedValue)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_createCacheTask?.Dispose();
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
disposedValue = true;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 建構式
|
|
/// </summary>
|
|
~FileReader()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
/// <summary>
|
|
/// 釋放資源
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|