using System; using System.IO; using System.Text; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using System.Text.RegularExpressions; namespace OneSTechLog { /// /// Represents methods for the parsing of the 1C technological log /// public class TechLogParser { private string folder; private Action> eventHandler; private ExecutionDataflowBlockOptions readBlockOptions; private ExecutionDataflowBlockOptions parseBlockOptions; private ExecutionDataflowBlockOptions eventHandlerBlockOptions; /// /// Folder of the technological log data /// public string Folder { get => folder; private set => folder = value; } /// /// Action for the event handling /// public Action> EventHandler { get => eventHandler; set => eventHandler = value; } /// /// Creates a new instance of TechLogParser class /// /// /// public TechLogParser(string folder, Action> eventHandler) { Folder = folder; EventHandler = eventHandler; readBlockOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; parseBlockOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, BoundedCapacity = 10000 }; eventHandlerBlockOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, BoundedCapacity = 10000 }; } /// /// Starts parsing of the technological log data /// /// public async Task Parse() { var eventHandlerBlock = new ActionBlock>(EventHandler, eventHandlerBlockOptions); var parseEventBlock = new TransformBlock>(ParseEventData, parseBlockOptions); var readFileBlock = new ActionBlock((filePath) => ReadFile(filePath, parseEventBlock), readBlockOptions); parseEventBlock.LinkTo(eventHandlerBlock); var files = GetTechLogFiles(); foreach (var filePath in files) { SendDataToNextBlock(filePath, readFileBlock); } var readBlockTask = readFileBlock.Completion.ContinueWith(c => parseEventBlock.Complete()); var parseEventBlockTask = parseEventBlock.Completion.ContinueWith(c => eventHandlerBlock.Complete()); readFileBlock.Complete(); await Task.WhenAll(readBlockTask, parseEventBlockTask, eventHandlerBlock.Completion); } private void ReadFile(string filePath, ITargetBlock nextBlock) { using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) using (var reader = new StreamReader(stream)) { string fileDateTime = GetFileDateTime(filePath); StringBuilder currentEvent = new StringBuilder(); bool firstEvent = true; do { var currentLine = reader.ReadLine(); if (Regex.IsMatch(currentLine, @"^\d\d:\d\d\.", RegexOptions.Compiled)) { if (firstEvent) { firstEvent = false; } else { SendDataToNextBlock(fileDateTime + ":" + currentEvent.ToString(), nextBlock); currentEvent.Clear(); } currentEvent.AppendLine(currentLine); } else { currentEvent.AppendLine(currentLine); } } while (!reader.EndOfStream); SendDataToNextBlock(fileDateTime + ":" + currentEvent.ToString(), nextBlock); } } private Dictionary ParseEventData(string eventData) { var properties = new Dictionary { ["EventName"] = Regex.Match(eventData, @",.*?,", RegexOptions.Compiled).ToString().Trim(','), ["DateTime"] = Regex.Match(eventData, @"^.*?\.\d+", RegexOptions.Compiled).ToString(), ["Duration"] = Regex.Match(eventData, @"-\d+?,", RegexOptions.Compiled).ToString().Trim('-', ',') }; var props = Regex.Matches(eventData, @",[\w:]+=.*?(?=(,[\w:]+=|$))", RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.Compiled); for (int x = 0; x < props.Count; x++) { var propText = props[x].ToString(); var splInd = propText.IndexOf('='); var propName = propText.Substring(0, splInd).Trim(','); var propVal = propText.Substring(splInd + 1).Trim('\'', '"'); properties[propName] = propVal; } return properties; } private void SendDataToNextBlock(T data, ITargetBlock nextBlock) { while (!nextBlock.Post(data)) ; } private string[] GetTechLogFiles() { return Directory.GetFiles(Folder, "*.log"); } private string GetFileDateTime(string filePath) { var info = Path.GetFileNameWithoutExtension(filePath); return "20" + info.Substring(0, 2) + "-" + info.Substring(2, 2) + "-" + info.Substring(4, 2) + " " + info.Substring(6, 2); } } }