Add Transmission Control Protocol file handling and update PCL serialization
- Introduced FileRead and Record classes for handling file reading in the Transmission Control Protocol. - Enhanced Description, Detail, and other related classes with JSON serialization attributes for improved data handling. - Implemented methods for reading and processing files, including network stream management. - Updated unit tests to cover new functionality and ensure robust testing. - Added new PDSF file handling classes and integrated them into the project structure. - Refactored existing code to utilize source generation for JSON serialization, improving performance and maintainability.
This commit is contained in:
250
Adaptation/FileHandlers/TransmissionControlProtocol/FileRead.cs
Normal file
250
Adaptation/FileHandlers/TransmissionControlProtocol/FileRead.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using Adaptation.Eaf.Management.ConfigurationData.CellAutomation;
|
||||
using Adaptation.Ifx.Eaf.EquipmentConnector.File.Configuration;
|
||||
using Adaptation.Shared;
|
||||
using Adaptation.Shared.Duplicator;
|
||||
using Adaptation.Shared.Methods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
|
||||
namespace Adaptation.FileHandlers.TransmissionControlProtocol;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class FileRead : Shared.FileRead, IFileRead
|
||||
{
|
||||
|
||||
private readonly int _Port;
|
||||
private readonly Timer _Timer;
|
||||
private static Record? _Record;
|
||||
private static long _LastWrite;
|
||||
private readonly string _IPAddress;
|
||||
private readonly string _RawDirectory;
|
||||
private readonly int _DelimiterSeconds;
|
||||
private readonly string[] _DelimiterPatterns;
|
||||
private static readonly object _Lock = new();
|
||||
|
||||
public FileRead(ISMTP smtp, Dictionary<string, string> fileParameter, string cellInstanceName, int? connectionCount, string cellInstanceConnectionName, FileConnectorConfiguration fileConnectorConfiguration, string equipmentTypeName, string parameterizedModelObjectDefinitionType, IList<ModelObjectParameterDefinition> modelObjectParameters, string equipmentDictionaryName, Dictionary<string, List<long>> dummyRuns, Dictionary<long, List<Shared.Metrology.WS.Results>> staticRuns, bool useCyclicalForDescription, bool isEAFHosted) :
|
||||
base(new Description(), false, smtp, fileParameter, cellInstanceName, connectionCount, cellInstanceConnectionName, fileConnectorConfiguration, equipmentTypeName, parameterizedModelObjectDefinitionType, modelObjectParameters, equipmentDictionaryName, dummyRuns, staticRuns, useCyclicalForDescription, isEAFHosted: connectionCount is null)
|
||||
{
|
||||
_MinFileLength = 10;
|
||||
_NullData = string.Empty;
|
||||
_Logistics = new(this);
|
||||
if (_FileParameter is null)
|
||||
throw new Exception(cellInstanceConnectionName);
|
||||
if (_ModelObjectParameterDefinitions is null)
|
||||
throw new Exception(cellInstanceConnectionName);
|
||||
if (_IsDuplicator)
|
||||
throw new Exception(cellInstanceConnectionName);
|
||||
string sourceFileLocation = fileConnectorConfiguration.SourceFileLocation.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
string? ipAddress = Path.GetDirectoryName(sourceFileLocation);
|
||||
string port = Path.GetFileName(sourceFileLocation);
|
||||
_Timer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);
|
||||
_Port = int.Parse(port, System.Globalization.CultureInfo.InvariantCulture);
|
||||
_DelimiterPatterns = fileConnectorConfiguration.SourceFileFilter.Split('*');
|
||||
_IPAddress = Path.GetFileName(ipAddress) ?? throw new Exception(sourceFileLocation);
|
||||
_RawDirectory = Path.GetDirectoryName(ipAddress) ?? throw new Exception(sourceFileLocation);
|
||||
DateTime fileAgeThresholdTimeOnly = GetFileAgeThresholdTimeOnly(_FileConnectorConfiguration.FileAgeThreshold);
|
||||
_DelimiterSeconds = fileAgeThresholdTimeOnly.Second;
|
||||
if (Debugger.IsAttached || fileConnectorConfiguration.PreProcessingMode == FileConnectorConfiguration.PreProcessingModeEnum.Process || _FileConnectorConfiguration.FileScanningIntervalInSeconds is null)
|
||||
Callback(null);
|
||||
else
|
||||
{
|
||||
TimeSpan timeSpan = new(DateTime.Now.AddSeconds(_FileConnectorConfiguration.FileScanningIntervalInSeconds.Value).Ticks - DateTime.Now.Ticks);
|
||||
_ = _Timer.Change((long)timeSpan.TotalMilliseconds, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
void IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, Exception exception) => Move(extractResults);
|
||||
|
||||
void IFileRead.WaitForThread() => WaitForThread(thread: null, threadExceptions: null);
|
||||
|
||||
string IFileRead.GetEventDescription()
|
||||
{
|
||||
string result = _Description.GetEventDescription();
|
||||
return result;
|
||||
}
|
||||
|
||||
List<string> IFileRead.GetHeaderNames()
|
||||
{
|
||||
List<string> results = _Description.GetHeaderNames();
|
||||
return results;
|
||||
}
|
||||
|
||||
string[] IFileRead.Move(Tuple<string, Test[], JsonElement[], List<FileInfo>> extractResults, string to, string from, string resolvedFileLocation, Exception exception)
|
||||
{
|
||||
string[] results = Move(extractResults, to, from, resolvedFileLocation, exception);
|
||||
return results;
|
||||
}
|
||||
|
||||
JsonProperty[] IFileRead.GetDefault()
|
||||
{
|
||||
JsonProperty[] results = _Description.GetDefault(this, _Logistics);
|
||||
return results;
|
||||
}
|
||||
|
||||
Dictionary<string, string> IFileRead.GetDisplayNamesJsonElement()
|
||||
{
|
||||
Dictionary<string, string> results = _Description.GetDisplayNamesJsonElement(this);
|
||||
return results;
|
||||
}
|
||||
|
||||
List<IDescription> IFileRead.GetDescriptions(IFileRead fileRead, List<Test> tests, IProcessData processData)
|
||||
{
|
||||
List<IDescription> results = _Description.GetDescriptions(fileRead, _Logistics, tests, processData);
|
||||
return results;
|
||||
}
|
||||
|
||||
Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.GetExtractResult(string reportFullPath, string eventName) => throw new Exception(string.Concat("See ", nameof(Callback)));
|
||||
|
||||
Tuple<string, Test[], JsonElement[], List<FileInfo>> IFileRead.ReExtract() => throw new Exception(string.Concat("See ", nameof(Callback)));
|
||||
|
||||
private static DateTime GetFileAgeThresholdTimeOnly(string fileAgeThreshold)
|
||||
{
|
||||
DateTime result = DateTime.MinValue;
|
||||
string[] segments = fileAgeThreshold.Split(':');
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
result = i switch
|
||||
{
|
||||
0 => result.AddDays(double.Parse(segments[i])),
|
||||
1 => result.AddHours(double.Parse(segments[i])),
|
||||
2 => result.AddMinutes(double.Parse(segments[i])),
|
||||
3 => result.AddSeconds(double.Parse(segments[i])),
|
||||
_ => throw new Exception(),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ReadFiles(log4net.ILog log, FileConnectorConfiguration fileConnectorConfiguration, string ipAddress, string rawDirectory)
|
||||
{
|
||||
List<byte> bytes = new();
|
||||
string[] files = Directory.GetFiles(rawDirectory, $"{ipAddress}-*.raw", SearchOption.TopDirectoryOnly);
|
||||
log.Info($"Read {files.Length} files");
|
||||
foreach (string file in files)
|
||||
{
|
||||
foreach (byte @byte in File.ReadAllBytes(file))
|
||||
bytes.Add(@byte);
|
||||
}
|
||||
if (bytes.Count > 0)
|
||||
{
|
||||
string bytesFile = Path.Combine(fileConnectorConfiguration.TargetFileLocation, $"{ipAddress}-{DateTime.Now.Ticks}{fileConnectorConfiguration.TargetFileName}");
|
||||
File.WriteAllBytes(bytesFile, bytes.ToArray());
|
||||
foreach (string file in files)
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateClient(log4net.ILog log, string ipAddress, int port)
|
||||
{
|
||||
log.Debug(ipAddress);
|
||||
TcpClient tcpClient = new(ipAddress, port);
|
||||
NetworkStream networkStream = tcpClient.GetStream();
|
||||
Type baseType = typeof(NetworkStream);
|
||||
PropertyInfo? propertyInfo = baseType.GetProperty("Socket", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
_Record = new(binaryReader: new(networkStream), binaryWriter: new(networkStream), networkStream: networkStream, propertyInfo: propertyInfo, readTimes: new());
|
||||
}
|
||||
|
||||
private static byte[] GetBytes(NetworkStream networkStream)
|
||||
{
|
||||
List<byte> results = new();
|
||||
byte[] bytes = new byte[1024];
|
||||
do
|
||||
{
|
||||
int count = networkStream.Read(bytes, 0, bytes.Length);
|
||||
if (count > 0)
|
||||
results.AddRange(bytes.Take(count));
|
||||
}
|
||||
while (networkStream.DataAvailable);
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private void Callback()
|
||||
{
|
||||
if (_Record?.NetworkStream is null || _Record.PropertyInfo is null || _Record.PropertyInfo.GetValue(_Record.NetworkStream) is not Socket socket || !socket.Connected)
|
||||
CreateClient(_Log, _IPAddress, _Port);
|
||||
if (_Record is not null)
|
||||
{
|
||||
TimeSpan timeSpan = new(DateTime.Now.Ticks - _LastWrite);
|
||||
if (_LastWrite == 0 || timeSpan.TotalMinutes > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
_Record.NetworkStream.WriteByte(Convert.ToByte('\0'));
|
||||
_LastWrite = DateTime.Now.Ticks;
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
if (_Record?.NetworkStream is not null && _Record.NetworkStream.CanRead && _Record.NetworkStream.DataAvailable)
|
||||
{
|
||||
byte[] bytes = GetBytes(_Record.NetworkStream);
|
||||
_Log.Info($"Read {bytes.Length} bytes");
|
||||
if (bytes.Length > 0)
|
||||
{
|
||||
string path = Path.Combine(_RawDirectory, $"{_IPAddress}-{DateTime.Now.Ticks}.raw");
|
||||
File.WriteAllBytes(path, bytes);
|
||||
string content = Encoding.ASCII.GetString(bytes);
|
||||
_Log.Debug($"Content {content}");
|
||||
foreach (string delimiterPattern in _DelimiterPatterns)
|
||||
{
|
||||
if (content.Contains(delimiterPattern))
|
||||
_Record.ReadTimes.Add(DateTime.Now.Ticks);
|
||||
}
|
||||
if (_Record.ReadTimes.Count > 0)
|
||||
_Record.ReadTimes.Add(DateTime.Now.Ticks);
|
||||
}
|
||||
}
|
||||
if (_Record is not null && _Record.ReadTimes.Count > 0 && _DelimiterSeconds > 0)
|
||||
{
|
||||
TimeSpan? timeSpan = new(DateTime.Now.Ticks - _Record.ReadTimes.Last());
|
||||
if (timeSpan.Value.TotalSeconds > _DelimiterSeconds)
|
||||
{
|
||||
ReadFiles(_Log, _FileConnectorConfiguration, _IPAddress, _RawDirectory);
|
||||
_Record.ReadTimes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Callback(object? state)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_Lock)
|
||||
Callback();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
string subject = string.Concat("Exception:", _CellInstanceConnectionName);
|
||||
string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace);
|
||||
try
|
||||
{ _SMTP.SendHighPriorityEmailMessage(subject, body); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
try
|
||||
{
|
||||
if (_FileConnectorConfiguration.FileScanningIntervalInSeconds is null)
|
||||
throw new Exception(_CellInstanceConnectionName);
|
||||
TimeSpan timeSpan = new(DateTime.Now.AddSeconds(_FileConnectorConfiguration.FileScanningIntervalInSeconds.Value).Ticks - DateTime.Now.Ticks);
|
||||
_ = _Timer.Change((long)timeSpan.TotalMilliseconds, Timeout.Infinite);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
string subject = string.Concat("Exception:", _CellInstanceConnectionName);
|
||||
string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace);
|
||||
try
|
||||
{ _SMTP.SendHighPriorityEmailMessage(subject, body); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user