diff --git a/.editorconfig b/.editorconfig index e394f93..650e00d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -108,6 +108,7 @@ dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031) dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049) dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290) dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified diff --git a/Day/Helper-2023-11-22.cs b/Day/Helper-2023-11-22.cs new file mode 100644 index 0000000..a62aa31 --- /dev/null +++ b/Day/Helper-2023-11-22.cs @@ -0,0 +1,193 @@ +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; +using System.Globalization; +using System.IO.Compression; + +namespace File_Folder_Helper.Day; + +internal static class Helper20231122 +{ + + private record Record(string File, string FileName, string Equipment, string TimeStamp); + + private static ReadOnlyCollection GetRecords(string sourceDirectory, string timestampFormat) + { + List results = []; + string fileName; + string equipment; + string timestamp; + string[] segments; + string[] files = Directory.GetFiles(sourceDirectory, "*.pdsf", SearchOption.TopDirectoryOnly).ToArray(); + foreach (string file in files) + { + fileName = Path.GetFileName(file); + segments = fileName.Split('_'); + if (segments.Length != 2) + continue; + equipment = segments[0]; + timestamp = segments[1].Split('.')[0]; + if (timestamp.Length != timestampFormat.Length) + continue; + results.Add(new(file, fileName, equipment, timestamp)); + } + return new(results.OrderBy(l => l.TimeStamp).ToArray()); + } + + private static void WriteFile(string sourceDirectory, string[] columns, string equipment, List> data, List timestamps, string timestamp, DateTime dateTime) + { + List lines = []; + string checkFile = Path.Combine(sourceDirectory, $"{equipment}-{timestamp}.tvs"); + if (File.Exists(checkFile)) + throw new NotSupportedException(); + lines.Add($"timestamp\t{string.Join('\t', timestamps)}"); + for (int i = 0; i < columns.Length; i++) + lines.Add($"{columns[i]}\t{string.Join('\t', data[i])}"); + File.WriteAllLines(checkFile, lines); + File.SetLastWriteTime(checkFile, dateTime); + } + + private static void ZipAndDeleteFiles(string sourceDirectory, string equipment, string timestamp, List files, DateTime dateTime) + { + string checkFile = Path.Combine(sourceDirectory, $"{equipment}-{timestamp}.zip"); + if (File.Exists(checkFile)) + throw new NotSupportedException(); + using ZipArchive zip = ZipFile.Open(checkFile, ZipArchiveMode.Create); + foreach (string file in files) + { + _ = zip.CreateEntryFromFile(file, Path.GetFileName(file)); + File.Delete(file); + } + File.SetLastWriteTime(checkFile, dateTime); + } + + private static void MoveFilesBack(string sourceDirectory, string parsedDirectory, List parsedFiles) + { + foreach (string parsedFile in parsedFiles) + File.Move(parsedFile, Path.Combine(sourceDirectory, Path.GetFileName(parsedFile))); + if (parsedFiles.Count > 0) + Directory.Delete(parsedDirectory); + } + + private static ReadOnlyDictionary> GetEquipmentToRecords(string sourceDirectory, string timestampFormat) + { + Dictionary> results = []; + List? collection; + Dictionary> keyValuePairs = []; + ReadOnlyCollection records = GetRecords(sourceDirectory, timestampFormat); + foreach (Record record in records) + { + if (!keyValuePairs.TryGetValue(record.Equipment, out collection)) + { + keyValuePairs.Add(record.Equipment, []); + if (!keyValuePairs.TryGetValue(record.Equipment, out collection)) + throw new NotSupportedException(); + } + collection.Add(record); + } + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, new(keyValuePair.Value)); + return new(results); + } + + private static void ParseProcessDataStandardFormatRecords(ILogger logger, string sourceDirectory, string timestampFormat, string keyColumn, string missingKeyDirectory, string parsedDirectory, ReadOnlyCollection records) + { + string[] lines; + string[] values; + string[] columns; + DateTime dateTime; + string parsedFile; + int? keyColumnIndex; + string keyColumnValue; + string? lastColumn = null; + List> data = []; + List timestamps = []; + List parsedFiles = []; + int? lastKeyColumnIndex = null; + string? lastKeyColumnValue = null; + foreach (Record record in records) + { + lines = File.ReadAllLines(record.File); + if (lines.Length != 15) + continue; + if (lines[6].Length < 1 || lines[6][0] != '"' || !lines[6].StartsWith("\"Time\"")) + continue; + if (lines[8].Length < 1 || lines[8][0] != 'N' || lines[8] != "NUM_DATA_ROWS\t000000001") + continue; + keyColumnIndex = null; + columns = lines[6].Split('\t'); + if (columns.Length < 3) + continue; + values = lines[7].Split('\t'); + if (values.Length != columns.Length) + continue; + for (int i = 0; i < columns.Length; i++) + { + if (columns[i] != keyColumn) + continue; + keyColumnIndex = i; + break; + } + if (keyColumnIndex is null) + { + File.Move(record.File, Path.Combine(sourceDirectory, missingKeyDirectory, record.FileName)); + continue; + } + keyColumnValue = values[keyColumnIndex.Value]; + parsedFile = Path.Combine(parsedDirectory, record.FileName); + if ((lastColumn is not null && lines[6] != lastColumn) || (lastKeyColumnIndex is not null && keyColumnIndex.Value != lastKeyColumnIndex.Value) || (lastKeyColumnValue is not null && lastKeyColumnValue != keyColumnValue) || timestamps.Count > 12345) + { + if (!DateTime.TryParseExact(record.TimeStamp, timestampFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + throw new NotSupportedException(); + WriteFile(sourceDirectory, columns, record.Equipment, data, timestamps, record.TimeStamp, dateTime); + ZipAndDeleteFiles(sourceDirectory, record.Equipment, record.TimeStamp, parsedFiles, dateTime); + Directory.Delete(parsedDirectory); + logger.LogInformation("{timestamp} triggered", record.TimeStamp); + parsedFiles.Clear(); + break; + } + parsedFiles.Add(parsedFile); + File.Move(record.File, parsedFile); + timestamps.Add($"'{record.TimeStamp}"); + for (int i = 0; i < columns.Length; i++) + data.Add([]); + for (int i = 0; i < columns.Length; i++) + data[i].Add(values[i]); + lastColumn = lines[6]; + lastKeyColumnIndex = keyColumnIndex; + lastKeyColumnValue = keyColumnValue; + } + MoveFilesBack(sourceDirectory, parsedDirectory, parsedFiles); + } + + private static void ParseProcessDataStandardFormatFiles(ILogger logger, string sourceDirectory, string timestampFormat, string keyColumn, string missingKeyDirectory) + { + string parsedDirectory; + ReadOnlyDictionary> equipmentToRecords = GetEquipmentToRecords(sourceDirectory, timestampFormat); + foreach (KeyValuePair> keyValuePair in equipmentToRecords) + { + parsedDirectory = Path.Combine(sourceDirectory, DateTime.Now.Ticks.ToString()); + if (!Directory.Exists(parsedDirectory)) + _ = Directory.CreateDirectory(parsedDirectory); + ParseProcessDataStandardFormatRecords(logger, sourceDirectory, timestampFormat, keyColumn, missingKeyDirectory, parsedDirectory, keyValuePair.Value); + Thread.Sleep(100); + } + } + + internal static void ProcessDataStandardFormat(ILogger logger, List args) + { + string keyColumn = args[3]; + string sourceDirectory = args[0]; + string timestampFormat = args[2]; + if (!Directory.Exists(sourceDirectory)) + throw new Exception(sourceDirectory); + string missingKeyDirectory = Path.Combine(sourceDirectory, "Missing-Key"); + if (!Directory.Exists(missingKeyDirectory)) + _ = Directory.CreateDirectory(missingKeyDirectory); + while (true) + { + ParseProcessDataStandardFormatFiles(logger, sourceDirectory, timestampFormat, keyColumn, missingKeyDirectory); + Thread.Sleep(5000); + } + } + +} \ No newline at end of file diff --git a/Day/Helper-2023-11-30.cs b/Day/Helper-2023-11-30.cs new file mode 100644 index 0000000..27515aa --- /dev/null +++ b/Day/Helper-2023-11-30.cs @@ -0,0 +1,126 @@ +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; +using System.Globalization; + +namespace File_Folder_Helper.Day; + +internal static class Helper20231130 +{ + + private record Record(string File, string FileName, string Equipment, string TimeStamp); + + private static ReadOnlyCollection GetRecords(string sourceDirectory, string timestampFormat) + { + List results = []; + string fileName; + string equipment; + string timestamp; + string[] segments; + string[] files = Directory.GetFiles(sourceDirectory, "*.pdsf", SearchOption.TopDirectoryOnly).ToArray(); + foreach (string file in files) + { + fileName = Path.GetFileName(file); + segments = fileName.Split('_'); + if (segments.Length != 2) + continue; + equipment = segments[0]; + timestamp = segments[1].Split('.')[0]; + if (timestamp.Length != timestampFormat.Length) + continue; + results.Add(new(file, fileName, equipment, timestamp)); + } + return new(results.OrderBy(l => l.TimeStamp).ToArray()); + } + + private static ReadOnlyDictionary GetSystemStates() + { + Dictionary results = []; + results.Add("1", "cold-idle"); + results.Add("2", "running"); + results.Add("3", "run-wafer"); + results.Add("4", "warm-idle"); + results.Add("5", "pause"); + results.Add("6", "suspend"); + results.Add("7", "startup"); + results.Add("8", "shutdown"); + results.Add("9", "abort"); + results.Add("10", "safety-1"); + results.Add("11", "safety-2"); + results.Add("12", "safety-3"); + return new(results); + } + + internal static void RenameReactorProcessDataStandardFormatFiles(ILogger logger, List args) + { + string line; + string[] lines; + string[] values; + string[] columns; + DateTime dateTime; + int? keyColumnIndex; + string? systemState; + string checkFileName; + string keyColumnValue; + string? lastColumn = null; + List allLines = []; + string keyColumn = args[3]; + string sourceDirectory = args[0]; + string timestampFormat = args[2]; + if (!Directory.Exists(sourceDirectory)) + throw new Exception(sourceDirectory); + string missingKeyDirectory = Path.Combine(sourceDirectory, "Missing-Key"); + if (!Directory.Exists(missingKeyDirectory)) + _ = Directory.CreateDirectory(missingKeyDirectory); + ReadOnlyDictionary systemStates = GetSystemStates(); + ReadOnlyCollection records = GetRecords(sourceDirectory, timestampFormat); + foreach (Record record in records) + { + lines = File.ReadAllLines(record.File); + if (lines.Length < 8) + continue; + if (lines[6].Length < 1 || lines[6][0] != '"' || !lines[6].StartsWith("\"Time\"")) + continue; + if (lastColumn is not null && lines[6] != lastColumn) + break; + keyColumnIndex = null; + lastColumn = lines[6]; + if (allLines.Count == 0) + allLines.Add($"\"Timestamp\"\t{lastColumn}"); + columns = lines[6].Split('\t'); + if (columns.Length < 3) + continue; + values = lines[7].Split('\t'); + if (values.Length != columns.Length) + continue; + for (int i = 0; i < columns.Length; i++) + { + if (columns[i] != keyColumn) + continue; + keyColumnIndex = i; + break; + } + if (keyColumnIndex is null) + { + File.Move(record.File, Path.Combine(sourceDirectory, missingKeyDirectory, record.FileName)); + continue; + } + for (int i = 7; i < lines.Length; i++) + { + line = lines[i]; + if (line.Length < 1 || line[0] == 'N' && line.StartsWith("NUM_DATA_ROWS\t")) + break; + allLines.Add($"'{record.TimeStamp}\t{line}"); + } + keyColumnValue = values[keyColumnIndex.Value]; + logger.LogInformation("{timestamp} triggered", record.TimeStamp); + if (!systemStates.TryGetValue(keyColumnValue, out systemState)) + continue; + checkFileName = Path.Combine(Path.GetDirectoryName(record.File) ?? throw new Exception(), $"{record.Equipment}-{record.TimeStamp}-{systemState}.pdsf"); + File.Move(record.File, checkFileName); + if (DateTime.TryParseExact(record.TimeStamp, timestampFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + File.SetLastWriteTime(checkFileName, dateTime); + } + File.WriteAllLines(Path.Combine(sourceDirectory, $"{DateTime.Now.Ticks}.tsv"), allLines); + } + +} \ No newline at end of file diff --git a/Day/Helper-2023-12-05.cs b/Day/Helper-2023-12-05.cs new file mode 100644 index 0000000..1ea9b6a --- /dev/null +++ b/Day/Helper-2023-12-05.cs @@ -0,0 +1,99 @@ +using Microsoft.Extensions.Logging; +using System.Text.RegularExpressions; + +namespace File_Folder_Helper.Day; + +internal static partial class Helper20231205 +{ + + [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] + private static partial Regex WindowsSafe(); + + private static string? GetStrippedIPV4(string[] segments) + { + string? result = null; + string[] subSegments; + foreach (string segment in segments) + { + subSegments = segment.Split('.'); + if (subSegments.Length != 4) + continue; + if (!subSegments.All(l => int.TryParse(l, out _))) + continue; + result = segment.Replace(".", string.Empty); + } + return result; + } + + private static string? GetStrippedMacAddress(string[] segments) + { + string? result = null; + foreach (string segment in segments) + { + if (segment.Length != 17) + continue; + if (segment[2] is not ':' or '-' || segment[5] is not ':' or '-' || segment[8] is not ':' or '-' || segment[11] is not ':' or '-' || segment[14] is not ':' or '-') + continue; + result = $"{segment[0]}{segment[1]}{segment[3]}{segment[4]}{segment[6]}{segment[7]}{segment[9]}{segment[10]}{segment[12]}{segment[13]}{segment[15]}{segment[16]}".ToLower(); + } + return result; + } + + internal static void SplitMarkdownFile(ILogger logger, List args) + { + string[] lines; + string? fileName; + string[] segments; + string checkFileName; + string? strippedIpV4; + string? strippedMacAddress; + List collection = []; + string sourceDirectory = args[0]; + if (!Directory.Exists(sourceDirectory)) + throw new Exception(sourceDirectory); + string outputDirectory = Path.Combine(sourceDirectory, Path.GetFileNameWithoutExtension(args[2])); + if (!Directory.Exists(outputDirectory)) + _ = Directory.CreateDirectory(outputDirectory); + string[] files = Directory.GetFiles(args[0], args[2], SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + fileName = null; + collection.Clear(); + lines = File.ReadAllLines(file); + foreach (string line in lines) + { + collection.Add(line); + if (line.Length > 0 && line[0] == '#' && line.StartsWith("## ")) + { + segments = line.Split(' '); + strippedIpV4 = GetStrippedIPV4(segments); + strippedMacAddress = GetStrippedMacAddress(segments); + if (strippedMacAddress is null && strippedIpV4 is null) + fileName = $"{WindowsSafe().Replace(line[3..], "-").Trim().ToLower()}.md"; + else if (strippedMacAddress is null) + { + fileName = $"ipv4-{strippedIpV4}.md"; + collection.Insert(0, string.Empty); + collection.Insert(0, $"# {fileName}"); + } + else + { + fileName = $"mac-{strippedMacAddress}.md"; + collection.Insert(0, string.Empty); + collection.Insert(0, $"# {fileName}"); + } + } + if (fileName is null || line != "----") + continue; + collection.RemoveAt(collection.Count - 1); + logger.LogInformation("{fileName} created", fileName); + checkFileName = Path.Combine(outputDirectory, fileName); + if (File.Exists(checkFileName)) + File.Delete(checkFileName); + File.WriteAllLines(checkFileName, collection); + collection.Clear(); + } + } + } + +} \ No newline at end of file diff --git a/Day/Helper-2023-12-12.cs b/Day/Helper-2023-12-12.cs new file mode 100644 index 0000000..f1f607c --- /dev/null +++ b/Day/Helper-2023-12-12.cs @@ -0,0 +1,435 @@ +using Microsoft.Extensions.Logging; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; + +namespace File_Folder_Helper.Day; + +internal static partial class Helper20231212 +{ + + [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] + private static partial Regex WindowsSafe(); + + private record Debugging( + [property: JsonPropertyName("Level")] int Level + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Debugging))] + private partial class DebuggingSourceGenerationContext : JsonSerializerContext + { + } + + private record Distance( + [property: JsonPropertyName("Value")] int Value + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Distance))] + private partial class DistanceSourceGenerationContext : JsonSerializerContext + { + } + + private record Finished( + [property: JsonPropertyName("Elapsed")] double Elapsed, + [property: JsonPropertyName("Exit")] string Exit, + [property: JsonPropertyName("Summary")] string Summary, + [property: JsonPropertyName("Time")] int Time, + [property: JsonPropertyName("TimeStr")] string TimeStr + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Finished))] + private partial class FinishedSourceGenerationContext : JsonSerializerContext + { + } + + private record Hop( + [property: JsonPropertyName("Host")] string Host, + [property: JsonPropertyName("IPAddr")] string IPAddr, + [property: JsonPropertyName("RTT")] double RTT, + [property: JsonPropertyName("TTL")] int TTL + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Hop))] + private partial class HopSourceGenerationContext : JsonSerializerContext + { + } + + private record Host( + [property: JsonPropertyName("Distance")] Distance Distance, + [property: JsonPropertyName("EndTime")] int EndTime, + [property: JsonPropertyName("HostAddress")] IReadOnlyList HostAddresses, + [property: JsonPropertyName("HostNames")] HostNames HostNames, + [property: JsonPropertyName("IPIDSequence")] IPIDSequence IPIDSequence, + [property: JsonPropertyName("OS")] OS OS, + [property: JsonPropertyName("Port")] IReadOnlyList Ports, + [property: JsonPropertyName("StartTime")] int StartTime, + [property: JsonPropertyName("Status")] Status Status, + [property: JsonPropertyName("TCPSequence")] TCPSequence TCPSequence, + [property: JsonPropertyName("TCPTSSequence")] TCPTSSequence TCPTSSequence, + [property: JsonPropertyName("Trace")] Trace Trace, + [property: JsonPropertyName("Uptime")] Uptime Uptime + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Host))] + private partial class HostSourceGenerationContext : JsonSerializerContext + { + } + + private record HostAddress( + [property: JsonPropertyName("Address")] string Address, + [property: JsonPropertyName("AddressType")] string AddressType, + [property: JsonPropertyName("Vendor")] string Vendor + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(HostAddress))] + private partial class HostAddressSourceGenerationContext : JsonSerializerContext + { + } + + private record HostNames( + [property: JsonPropertyName("HostName")] object HostName + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(HostNames))] + private partial class HostNamesSourceGenerationContext : JsonSerializerContext + { + } + + private record Hosts( + [property: JsonPropertyName("Down")] int Down, + [property: JsonPropertyName("Total")] int Total, + [property: JsonPropertyName("Up")] int Up + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Hosts))] + private partial class HostsSourceGenerationContext : JsonSerializerContext + { + } + + private record IPIDSequence( + [property: JsonPropertyName("Class")] string Class, + [property: JsonPropertyName("Values")] string Values + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(IPIDSequence))] + private partial class IPIDSequenceSourceGenerationContext : JsonSerializerContext + { + } + + private record OS( + [property: JsonPropertyName("OSClass")] object OSClass, + [property: JsonPropertyName("OSMatch")] IReadOnlyList OSMatches, + [property: JsonPropertyName("OSPortUsed")] IReadOnlyList OSPortsUsed + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(OS))] + private partial class OSSourceGenerationContext : JsonSerializerContext + { + } + + private record OSMatch( + [property: JsonPropertyName("Accuracy")] string Accuracy, + [property: JsonPropertyName("Line")] string Line, + [property: JsonPropertyName("Name")] string Name + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(OSMatch))] + private partial class OSMatchSourceGenerationContext : JsonSerializerContext + { + } + + private record OSPortUsed( + [property: JsonPropertyName("PortID")] int PortID, + [property: JsonPropertyName("Protocol")] string Protocol, + [property: JsonPropertyName("State")] string State + ); + + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(OSPortUsed))] + private partial class OSPortUsedSourceGenerationContext : JsonSerializerContext + { + } + + private record Port( + [property: JsonPropertyName("PortID")] int PortID, + [property: JsonPropertyName("Protocol")] string Protocol, + [property: JsonPropertyName("Script")] IReadOnlyList