1
0
mirror of https://github.com/krugersu/YY.TechJournalReaderAssistant.git synced 2025-10-08 22:42:05 +02:00

Добавлен каркас проекта библиотеки чтения файлов технологического журнала

This commit is contained in:
YPermitin
2020-11-08 19:49:01 +05:00
parent 0511bd4e8c
commit 1feacb2fdc
18 changed files with 799 additions and 0 deletions

6
.gitignore vendored
View File

@@ -348,3 +348,9 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# JetBreins Raider
.idea/
# Config files
**/launchSettings.json

BIN
Nuget/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,14 @@
using System;
using Xunit;
namespace YY.TechJournalReaderAssistant.Tests
{
public class TechJournalReaderTests
{
[Fact]
public void TestOfNothing()
{
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\YY.TechJournalReaderAssistant\YY.TechJournalReaderAssistant.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,54 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30523.141
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YY.TechJournalReaderAssistant", "YY.TechJournalReaderAssistant\YY.TechJournalReaderAssistant.csproj", "{DC56F5D4-E238-4B22-922C-310AA613B7ED}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFC71FD2-2817-4913-808A-82F6F0CE18E0}"
ProjectSection(SolutionItems) = preProject
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BACA524F-11E4-4932-A19F-62EB1D15EFBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YY.TechJournalReaderAssistantConsoleApp", "YY.TechJournalReaderAssistantConsoleApp\YY.TechJournalReaderAssistantConsoleApp.csproj", "{79D710C6-CA74-4FAD-803F-A153AD44845C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YY.TechJournalReaderAssistant.Tests", "Tests\YY.TechJournalReaderAssistant.Tests\YY.TechJournalReaderAssistant.Tests.csproj", "{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nuget", "Nuget", "{11E276EC-CC44-4221-84D8-9B90190C0323}"
ProjectSection(SolutionItems) = preProject
Nuget\icon.png = Nuget\icon.png
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DC56F5D4-E238-4B22-922C-310AA613B7ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC56F5D4-E238-4B22-922C-310AA613B7ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC56F5D4-E238-4B22-922C-310AA613B7ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC56F5D4-E238-4B22-922C-310AA613B7ED}.Release|Any CPU.Build.0 = Release|Any CPU
{79D710C6-CA74-4FAD-803F-A153AD44845C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79D710C6-CA74-4FAD-803F-A153AD44845C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79D710C6-CA74-4FAD-803F-A153AD44845C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79D710C6-CA74-4FAD-803F-A153AD44845C}.Release|Any CPU.Build.0 = Release|Any CPU
{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D79399CD-DBA6-4CB1-8BD1-D32F28ADDE9C} = {BACA524F-11E4-4932-A19F-62EB1D15EFBC}
{11E276EC-CC44-4221-84D8-9B90190C0323} = {FFC71FD2-2817-4913-808A-82F6F0CE18E0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {035380E7-626E-43B7-A8A9-EE3F29295F21}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,17 @@
using System;
using YY.TechJournalReaderAssistant.Models;
namespace YY.TechJournalReaderAssistant.EventArguments
{
public sealed class AfterReadEventArgs : EventArgs
{
public AfterReadEventArgs(RowData rowData, long eventNumber)
{
RowData = rowData;
EventNumber = eventNumber;
}
public RowData RowData { get; }
public long EventNumber { get; }
}
}

View File

@@ -0,0 +1,14 @@
using System;
namespace YY.TechJournalReaderAssistant.EventArguments
{
public sealed class AfterReadFileEventArgs : EventArgs
{
public AfterReadFileEventArgs(string fileName)
{
FileName = fileName;
}
public string FileName { get; }
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace YY.TechJournalReaderAssistant.EventArguments
{
public sealed class BeforeReadEventArgs : EventArgs
{
public BeforeReadEventArgs(string sourceData, long eventNumber)
{
SourceData = sourceData;
EventNumber = eventNumber;
}
public string SourceData { get; }
public long EventNumber { get; }
}
}

View File

@@ -0,0 +1,14 @@
namespace YY.TechJournalReaderAssistant.EventArguments
{
public sealed class BeforeReadFileEventArgs : System.EventArgs
{
public BeforeReadFileEventArgs(string fileName)
{
FileName = fileName;
Cancel = false;
}
public string FileName { get; }
public bool Cancel { get; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
namespace YY.TechJournalReaderAssistant.EventArguments
{
public sealed class OnErrorEventArgs : EventArgs
{
public OnErrorEventArgs(Exception exception, string sourceData, bool critical)
{
Exception = exception;
SourceData = sourceData;
Critical = critical;
}
public Exception Exception { get; }
public string SourceData { get; }
public bool Critical { get; }
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.IO;
using System.Reflection;
using System.Text;
namespace YY.TechJournalReaderAssistant.Helpers
{
internal static class StreamReaderExtensions
{
#region Private Member Variables
private static readonly FieldInfo CharPosField = typeof(StreamReader).GetField("_charPos", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
static readonly FieldInfo ByteLenField = typeof(StreamReader).GetField("_byteLen", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
static readonly FieldInfo CharBufferField = typeof(StreamReader).GetField("_charBuffer", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#endregion
#region Public Methods
public static string ReadLineWithoutNull(this StreamReader reader)
{
StringBuilder sb = new StringBuilder();
while (!reader.EndOfStream)
{
Char c = (char)reader.Read();
if (c == '\0')
continue;
sb.Append(c);
if (c == '\n')
break;
}
var lineString = sb.Length > 0 ? sb.ToString().Trim() : null;
return lineString;
}
public static long GetPosition(this StreamReader reader)
{
int byteLen = (int)ByteLenField.GetValue(reader);
var position = reader.BaseStream.Position - byteLen;
int charPos = (int)CharPosField.GetValue(reader);
if (charPos > 0)
{
var charBuffer = (char[])CharBufferField.GetValue(reader);
var encoding = reader.CurrentEncoding;
var bytesConsumed = encoding.GetBytes(charBuffer, 0, charPos).Length;
position += bytesConsumed;
}
return position;
}
public static void SetPosition(this StreamReader reader, long position)
{
reader.DiscardBufferedData();
reader.BaseStream.Seek(position, SeekOrigin.Begin);
}
public static void SkipLine(this StreamReader stream, long numberToSkip)
{
for (int i = 0; i < numberToSkip; i++)
{
stream.ReadLine();
}
}
#endregion
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace YY.TechJournalReaderAssistant
{
public interface ITechJournalReader
{
bool Read();
bool GoToEvent(long eventNumber);
TechJournalPosition GetCurrentPosition();
void SetCurrentPosition(TechJournalPosition newPosition);
long Count();
void Reset();
void NextFile();
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace YY.TechJournalReaderAssistant.Models
{
public class RowData
{
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace YY.TechJournalReaderAssistant
{
public sealed class TechJournalPosition
{
#region Constructor
public TechJournalPosition(long eventNumber, string currentFileData, long? streamPosition)
{
EventNumber = eventNumber;
CurrentFileData = currentFileData;
StreamPosition = streamPosition;
}
#endregion
#region Public Properties
public long EventNumber { get; }
public string CurrentFileData { get; }
public long? StreamPosition { get; }
#endregion
}
}

View File

@@ -0,0 +1,442 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using YY.TechJournalReaderAssistant.EventArguments;
using YY.TechJournalReaderAssistant.Helpers;
using YY.TechJournalReaderAssistant.Models;
namespace YY.TechJournalReaderAssistant
{
public sealed class TechJournalReader : ITechJournalReader, IDisposable
{
#region Public Static Methods
public static TechJournalReader CreateReader(string pathLogFile)
{
return new TechJournalReader(pathLogFile);
}
#endregion
#region Private Member Variables
private readonly string _logFilePath;
private readonly string _logFileDirectoryPath;
private string[] _logFilesWithData;
private int _indexCurrentFile;
private long _currentFileEventNumber;
private StreamReader _stream;
private readonly StringBuilder _eventSource;
private bool _logFileSourcePathIsDirectory;
private long _eventCount = -1;
private RowData _currentRow;
#endregion
#region Public Properties
public string CurrentFile
{
get
{
if (_logFilesWithData.Length <= _indexCurrentFile)
return null;
else
return _logFilesWithData[_indexCurrentFile];
}
}
#endregion
#region Constructor
internal TechJournalReader()
{ }
internal TechJournalReader(string logFilePath)
{
_eventSource = new StringBuilder();
if (File.GetAttributes(logFilePath).HasFlag(FileAttributes.Directory))
{
_logFileDirectoryPath = logFilePath;
_logFileSourcePathIsDirectory = true;
UpdateEventLogFilesFromDirectory();
if (_logFilesWithData.Length > 0)
_logFilePath = _logFilesWithData[0];
}
else
{
_logFileSourcePathIsDirectory = false;
_logFilesWithData = new[] { _logFilePath };
_logFilePath = _logFilesWithData[0];
_logFileDirectoryPath = new FileInfo(_logFilePath).Directory?.FullName;
}
}
#endregion
#region Public Methods
public bool Read()
{
bool output = false;
try
{
if (!InitializeReadFileStream())
return false;
RaiseBeforeReadFileEvent(out bool cancelBeforeReadFile);
if (cancelBeforeReadFile)
{
NextFile();
return Read();
}
// TODD:
// bool newLine = true, textBlockOpen = false;
// int countBracket = 0;
// while (true)
// {
// string sourceData = ReadSourceDataFromStream();
// if (sourceData == null)
// {
// NextFile();
// output = Read();
// break;
// }
// AddNewLineToSource(sourceData, newLine);
// if (LogParserLGF.ItsEndOfEvent(sourceData, ref countBracket, ref textBlockOpen))
// {
// _currentFileEventNumber += 1;
// string preparedSourceData = _eventSource.ToString();
// RaiseBeforeRead(new BeforeReadEventArgs(preparedSourceData, _currentFileEventNumber));
// try
// {
// RowData eventData = ReadRowData(preparedSourceData);
// _currentRow = eventData;
// RaiseAfterRead(new AfterReadEventArgs(_currentRow, _currentFileEventNumber));
// output = true;
// break;
// }
// catch (Exception ex)
// {
// RaiseOnError(new OnErrorEventArgs(ex, preparedSourceData, false));
// _currentRow = null;
// output = true;
// break;
// }
// }
// newLine = false;
// }
}
catch (Exception ex)
{
RaiseOnError(new OnErrorEventArgs(ex, null, true));
_currentRow = null;
output = false;
}
return output;
}
public bool GoToEvent(long eventNumber)
{
Reset();
int fileIndex = -1;
long currentLineNumber = -1;
long currentEventNumber = 0;
bool moved = false;
foreach (string logFile in _logFilesWithData)
{
fileIndex += 1;
currentLineNumber = -1;
IEnumerable<string> allLines = File.ReadLines(logFile);
foreach (string line in allLines)
{
currentLineNumber += 1;
// TPDL: if (LogParserLGF.ItsBeginOfEvent(line))
if(false)
{
currentEventNumber += 1;
}
if (currentEventNumber == eventNumber)
{
moved = true;
break;
}
}
if (currentEventNumber == eventNumber)
{
moved = true;
break;
}
}
if (moved && fileIndex >= 0 && currentLineNumber >= 0)
{
InitializeStream(currentLineNumber, fileIndex);
_eventCount = eventNumber - 1;
_currentFileEventNumber = eventNumber;
return true;
}
else
{
return false;
}
}
public TechJournalPosition GetCurrentPosition()
{
return new TechJournalPosition(
_currentFileEventNumber,
CurrentFile,
GetCurrentFileStreamPosition());
}
public void SetCurrentPosition(TechJournalPosition newPosition)
{
if (ApplyEventLogPosition(newPosition) == false)
return;
InitializeStream(0, _indexCurrentFile);
long beginReadPosition = _stream.GetPosition();
long newStreamPosition = Math.Max(beginReadPosition, newPosition.StreamPosition ?? 0);
long sourceStreamPosition = newStreamPosition;
string currentFilePath = _logFilesWithData[_indexCurrentFile];
FixEventPosition(currentFilePath, ref newStreamPosition, sourceStreamPosition);
if (newPosition.StreamPosition != null)
SetCurrentFileStreamPosition(newStreamPosition);
}
public long Count()
{
if (_eventCount < 0)
_eventCount = GetEventCount();
return _eventCount;
}
public void Reset()
{
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
_indexCurrentFile = 0;
UpdateEventLogFilesFromDirectory();
_currentFileEventNumber = 0;
_currentRow = null;
}
public void NextFile()
{
RaiseAfterReadFile(new AfterReadFileEventArgs(CurrentFile));
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
_indexCurrentFile += 1;
}
public void Dispose()
{
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
}
#endregion
#region Private Memthods
private void FindNearestBeginEventPosition(ref bool isCorrectBeginEvent, string currentFilePath, ref long newStreamPosition, int stepSize = 1)
{
int attemptToFoundBeginEventLine = 0;
while (!isCorrectBeginEvent && attemptToFoundBeginEventLine < 10)
{
string beginEventLine;
using (FileStream fileStreamCheckPosition =
new FileStream(currentFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fileStreamCheckPosition.Seek(newStreamPosition, SeekOrigin.Begin);
using (StreamReader fileStreamCheckReader = new StreamReader(fileStreamCheckPosition))
beginEventLine = fileStreamCheckReader.ReadLineWithoutNull();
}
if (beginEventLine == null)
{
isCorrectBeginEvent = false;
break;
}
isCorrectBeginEvent = false; // TODO: LogParserLGF.ItsBeginOfEvent(beginEventLine);
if (!isCorrectBeginEvent)
{
newStreamPosition -= stepSize;
attemptToFoundBeginEventLine += 1;
}
}
}
private void FixEventPosition(string currentFilePath, ref long newStreamPosition, long sourceStreamPosition)
{
bool isCorrectBeginEvent = false;
FindNearestBeginEventPosition(
ref isCorrectBeginEvent,
currentFilePath,
ref newStreamPosition);
if (!isCorrectBeginEvent)
{
newStreamPosition = sourceStreamPosition;
FindNearestBeginEventPosition(
ref isCorrectBeginEvent,
currentFilePath,
ref newStreamPosition,
-1);
}
}
private bool ApplyEventLogPosition(TechJournalPosition position)
{
Reset();
if (position == null)
return false;
int indexOfFileData = Array.IndexOf(_logFilesWithData, position.CurrentFileData);
if (indexOfFileData < 0)
throw new Exception("Invalid data file");
_indexCurrentFile = indexOfFileData;
_currentFileEventNumber = position.EventNumber;
return true;
}
private void SetCurrentFileStreamPosition(long position)
{
_stream?.SetPosition(position);
}
private long GetCurrentFileStreamPosition()
{
return _stream?.GetPosition() ?? 0;
}
private long GetEventCount()
{
long eventCount = 0;
foreach (var logFile in _logFilesWithData)
{
using (StreamReader logFileStream = new StreamReader(File.Open(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
do
{
string logFileCurrentString = logFileStream.ReadLineWithoutNull();
// TODO: if (LogParserLGF.ItsBeginOfEvent(logFileCurrentString))
// eventCount++;
} while (!logFileStream.EndOfStream);
}
}
return eventCount;
}
private void RaiseBeforeReadFileEvent(out bool cancel)
{
BeforeReadFileEventArgs beforeReadFileArgs = new BeforeReadFileEventArgs(CurrentFile);
if (_currentFileEventNumber == 0)
RaiseBeforeReadFile(beforeReadFileArgs);
cancel = beforeReadFileArgs.Cancel;
}
private void UpdateEventLogFilesFromDirectory()
{
if(_logFileSourcePathIsDirectory)
{
_logFilesWithData = Directory
.GetFiles(_logFileDirectoryPath, "*.log")
.OrderBy(i => i)
.ToArray();
}
}
private bool InitializeReadFileStream()
{
if (_stream == null)
{
if (_logFilesWithData.Length <= _indexCurrentFile)
{
_currentRow = null;
return false;
}
InitializeStream(0, _indexCurrentFile);
_currentFileEventNumber = 0;
}
_eventSource?.Clear();
return true;
}
private void InitializeStream(long linesToSkip, int fileIndex = 0)
{
FileStream fs = new FileStream(_logFilesWithData[fileIndex], FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_stream = new StreamReader(fs);
_stream.SkipLine(linesToSkip);
}
#endregion
#region Events
public delegate void BeforeReadFileHandler(TechJournalReader sender, BeforeReadFileEventArgs args);
public delegate void AfterReadFileHandler(TechJournalReader sender, AfterReadFileEventArgs args);
public delegate void BeforeReadEventHandler(TechJournalReader sender, BeforeReadEventArgs args);
public delegate void AfterReadEventHandler(TechJournalReader sender, AfterReadEventArgs args);
public delegate void OnErrorEventHandler(TechJournalReader sender, OnErrorEventArgs args);
public event BeforeReadFileHandler BeforeReadFile;
public event AfterReadFileHandler AfterReadFile;
public event BeforeReadEventHandler BeforeReadEvent;
public event AfterReadEventHandler AfterReadEvent;
public event OnErrorEventHandler OnErrorEvent;
protected void RaiseBeforeReadFile(BeforeReadFileEventArgs args)
{
BeforeReadFile?.Invoke(this, args);
}
protected void RaiseAfterReadFile(AfterReadFileEventArgs args)
{
AfterReadFile?.Invoke(this, args);
}
protected void RaiseBeforeRead(BeforeReadEventArgs args)
{
BeforeReadEvent?.Invoke(this, args);
}
protected void RaiseAfterRead(AfterReadEventArgs args)
{
AfterReadEvent?.Invoke(this, args);
}
protected void RaiseOnError(OnErrorEventArgs args)
{
OnErrorEvent?.Invoke(this, args);
}
#endregion
}
}

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Authors>Permitin Yuriy</Authors>
<Product>Technological journal's reader assistant</Product>
<Description>Library for reading 1C:Enterprise 8.x platform's technological journal files</Description>
<Version>1.0.0.1</Version>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/YPermitin/YY.TechJournalReaderAssistant</PackageProjectUrl>
<RepositoryUrl>https://github.com/YPermitin/YY.TechJournalReaderAssistant</RepositoryUrl>
<RepositoryType>GIT</RepositoryType>
<PackageTags>technological, journal, log, 1C, enterprise</PackageTags>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="..\Nuget\icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using System;
using YY.TechJournalReaderAssistant;
namespace YY.TechJournalReaderAssistantConsoleApp
{
class Program
{
private static int _eventNumber;
static void Main(string[] args)
{
TechJournalReader reader = TechJournalReader.CreateReader(args[0]);
_eventNumber = 0;
while (reader.Read())
{
_eventNumber += 1;
Console.WriteLine($"Прочитано событий: {_eventNumber}");
}
Console.WriteLine($"{DateTime.Now}: Для выхода нажмите любую клавишу...");
Console.ReadKey();
}
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\YY.TechJournalReaderAssistant\YY.TechJournalReaderAssistant.csproj" />
</ItemGroup>
</Project>