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