I've been sending a bunch of logs with self-writing classes of the estate machine.DisclaimerI didn't take a look at it anywhere, I wrote how I thought it was, in principle.
It was the first time I ever tried to use the car.
Contents in the form of separate implementation and classes for each condition. Don't waste your time, so I'm putting it out.I understand that it's not the best idea to write in classrooms, but it's time to stop, it's too much time for this sophthine:What good do I see in the use of a car of stock is that in every condition only a part of my checks, not checks on on each of the terations everything is possible, the lack of injections in the form of another or the use of the case.In the repository, the code can change, improve, etc. using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using FsmLogParser.Core;
using FsmLogParser.Logs;
using FsmLogParser.Model;
using FsmLogParser.States;
using FsmLogParser.States.Enums;
namespace FsmLogParser
{
public class Program
{
#region Entry point
private static Program _program;
private static Task<int> Main(string[] args)
{
_program = new Program();
return _program.Run(args);
}
#endregion
private const int NOT_ENOUGH_POSITIONAL_CMD_ARGS_SPECIFIED_ERROR = 1;
private const int FILE_NOT_FOUND_ERROR = 2;
private const int EXIT_SUCCESS = 0;
private LogReader _logReader;
private Dictionary<int, SessionInfo> _infos;
public int IterationCounter { get; set; }
private async Task<int> Run(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Not enough positional command-line arguments specified!");
return NOT_ENOUGH_POSITIONAL_CMD_ARGS_SPECIFIED_ERROR;
}
string pathToFile = args[0];
if (!File.Exists(pathToFile))
{
Console.WriteLine($"File \"{pathToFile}\" not found.");
return FILE_NOT_FOUND_ERROR;
}
try
{
var fsm = BuildStateMachine();
_infos = new Dictionary<int, SessionInfo>();
using (_logReader = new LogReader(pathToFile))
{
_logReader.OpenFile();
await fsm.Start(this);
}
Console.WriteLine("Results:");
foreach (var (iteration, sessionInfo) in _infos)
{
Console.WriteLine($"{iteration.ToString()}: {sessionInfo}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return EXIT_SUCCESS;
}
private static StateMachine<ParserState, ParserEvent, Program> BuildStateMachine()
{
var builder = new StateMachineBuilder<ParserState, ParserEvent, Program>();
// Схема машины состояний: https://i.imgur.com/KSy6DLB.jpg
builder.SetInitialState<StartSessionSearchState>(ParserState.StartSessionSearch)
// Перейти в состояние SenderSearchState, если сейчас состояние
// ParserState.StartSessionSearch и пришло событие ParserEvent.StartSessionFound
.AddTransition<SenderSearchState>(ParserState.StartSessionSearch, ParserEvent.StartSessionFound)
.AddTransition<RecipientSearchState>(ParserState.SenderSearch, ParserEvent.SenderFound)
.AddTransition<RecipientSearchState>(ParserState.RecipientSearch, ParserEvent.RecipientFound)
.AddTransition<SessionTokenSearchState>(ParserState.RecipientSearch, ParserEvent.EndOfBlockFound)
.AddTransition<StartSessionSearchState>(ParserState.SessionTokenSearch, ParserEvent.SuccessfulSessionTokenFound)
.AddTransition<StartSessionSearchState>(ParserState.SessionTokenSearch, ParserEvent.EndOfSessionFound)
.AddTransition<DoneState>(ParserState.StartSessionSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.SenderSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.RecipientSearch, ParserEvent.EndOfSearchRangeReached)
.AddTransition<DoneState>(ParserState.SessionTokenSearch, ParserEvent.EndOfSearchRangeReached);
return builder.Build();
}
public Task<string> GetNextLine()
{
return _logReader.ReadLine();
}
public SessionInfo GetSessionInfo(int iteration)
{
if (!_infos.ContainsKey(iteration))
{
_infos.Add(iteration, new SessionInfo());
}
return _infos[iteration];
}
}
}
Example of one condition and transition:using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FsmLogParser.Core.Interfaces;
using FsmLogParser.States.Enums;
namespace FsmLogParser.States
{
/// <summary>
/// <see cref="ParserState.RecipientSearch"/>.
/// </summary>
public class RecipientSearchState : IState<Program, ParserEvent, ParserState>
{
private readonly Regex _regex;
public ParserState State { get; } = ParserState.RecipientSearch;
public RecipientSearchState()
{
var options = RegexOptions.Compiled | RegexOptions.IgnoreCase;
_regex = new Regex(@"RCPT TO:\s+<{0,1}([a-zA-Z0-9-]+@[a-zA-Z0-9-]+\.[a-zA-Z]+)>{0,1}", options);
}
public async Task DoWork(IStateMachine<ParserState, Program, ParserEvent> fsm, Program context)
{
while (true)
{
string line = await context.GetNextLine();
if (line == null)
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.EndOfSearchRangeReached)})");
fsm.SendEvent(ParserEvent.EndOfSearchRangeReached);
break;
}
Match match = await Task.Run(() => _regex.Match(line));
if (match.Success)
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.RecipientFound)})");
if (match.Groups.Count > 1)
{
var info = context.GetSessionInfo(context.IterationCounter);
info.Recipients.Add(match.Groups[1].Value);
}
fsm.SendEvent(ParserEvent.RecipientFound);
break;
}
if (await Task.Run(() => Regex.IsMatch(line, @"\s+QUIT$", RegexOptions.Compiled)))
{
Console.WriteLine($"Current state: {fsm.CurrentState}. SendEvent({nameof(ParserEvent.EndOfBlockFound)})");
fsm.SendEvent(ParserEvent.EndOfBlockFound);
break;
}
}
}
}
}
Reading of file logsusing System;
using System.IO;
using System.Threading.Tasks;
namespace FsmLogParser.Logs
{
public class LogReader : IDisposable
{
private FileStream _fileStream;
private BufferedStream _bufferedStream;
private StreamReader _reader;
public string PathToFile { get; }
public LogReader(string pathToFile)
{
PathToFile = pathToFile;
}
public void OpenFile()
{
_fileStream = File.Open(PathToFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_bufferedStream = new BufferedStream(_fileStream);
_reader = new StreamReader(_bufferedStream);
}
public Task<string> ReadLine()
{
return _reader.ReadLineAsync();
}
public void Dispose()
{
_reader?.Dispose();
_bufferedStream?.Dispose();
_fileStream?.Dispose();
}
}
}
The rest on the reference, too much code needs to be copied here.