Skip to content

Commit 417378f

Browse files
committed
23 - Added TelegramImporter
- Added TelegramImporter class and unit tests - Added optional leaveOpen argument to constructors
1 parent 356cb5d commit 417378f

File tree

4 files changed

+185
-16
lines changed

4 files changed

+185
-16
lines changed

RS485 Monitor/src/Utils/Storage/TelegramWriter.cs renamed to RS485 Monitor/src/Utils/Storage/TelegramExporter.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ namespace RS485_Monitor.Utils.Storage
55
/// <summary>
66
/// This class will export the telegrams into a binary format to a stream.
77
/// </summary>
8-
public class TelegramWriter :IDisposable
8+
public class TelegramExporter :IDisposable
99
{
1010
public const byte VERSION = 1;
1111
public const string IDENTIFIER = "RS485MONITOR";
12-
public readonly byte[] MAGIC_NUMBER = Encoding.ASCII.GetBytes(IDENTIFIER);
12+
public static readonly byte[] MAGIC_NUMBER = Encoding.ASCII.GetBytes(IDENTIFIER);
1313

1414
private readonly BinaryWriter _writer;
1515

16-
public TelegramWriter(Stream stream)
16+
/// <summary>
17+
/// Create a new TelegramExporter from a stream.
18+
/// </summary>
19+
/// <param name="stream">stream to read from</param>
20+
/// <param name="leaveOpen">leave the stream open after reading</param>
21+
public TelegramExporter(Stream stream, bool leaveOpen = false)
1722
{
18-
_writer = new BinaryWriter(stream);
23+
_writer = new BinaryWriter(stream, new UTF8Encoding(), leaveOpen);
1924
WriteHeader();
2025
}
2126

22-
public TelegramWriter(string path)
27+
public TelegramExporter(string path, bool leaveOpen = false)
2328
{
2429
FileStream stream = new(path, FileMode.Create);
25-
_writer = new BinaryWriter(stream);
30+
_writer = new BinaryWriter(stream, new UTF8Encoding(), leaveOpen);
2631
WriteHeader();
2732
}
2833

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Text;
2+
using RS485Monitor.Telegrams;
3+
4+
namespace RS485_Monitor.Utils.Storage
5+
{
6+
/// <summary>
7+
/// This class will import telegrams from a stream created by the TelegramExporter.
8+
/// </summary>
9+
///
10+
public class TelegramImporter : IDisposable
11+
{
12+
private readonly BinaryReader _reader;
13+
/// <summary>
14+
/// Version read from the stream
15+
/// </summary>
16+
public byte FormatVersion { get; }
17+
18+
/// <summary>
19+
/// Create a new TelegramImporter from a stream.
20+
/// </summary>
21+
/// <param name="stream">Stream to read from</param>
22+
/// <exception cref="InvalidDataException">Invalid file format</exception>
23+
public TelegramImporter(Stream stream, bool leaveOpen = false)
24+
{
25+
_reader = new BinaryReader(stream, new UTF8Encoding(), leaveOpen);
26+
FormatVersion = ReadHeader();
27+
}
28+
29+
/// <summary>
30+
/// Create a new TelegramImporter from a file.
31+
/// </summary>
32+
/// <param name="path">Path to the file</param>
33+
/// <exception cref="InvalidDataException">Invalid file format</exception>
34+
public TelegramImporter(string path, bool leaveOpen = false)
35+
{
36+
FileStream stream = new(path, FileMode.Open);
37+
_reader = new BinaryReader(stream, new UTF8Encoding(), leaveOpen);
38+
FormatVersion = ReadHeader();
39+
}
40+
41+
/// <summary>
42+
/// Iterator function to iterate over all telegrams in the file.
43+
/// </summary>
44+
/// <returns>An enumerable of BaseTelegram objects.</returns>
45+
public IEnumerable<BaseTelegram> GetTelegram()
46+
{
47+
while (_reader.BaseStream.Position < _reader.BaseStream.Length)
48+
{
49+
yield return PopTelegram();
50+
}
51+
}
52+
53+
public void Dispose()
54+
{
55+
_reader?.Dispose();
56+
}
57+
58+
/// <summary>
59+
/// Read the header of the file and check if it is a valid file.
60+
/// </summary>
61+
protected byte ReadHeader()
62+
{
63+
byte[] magic_number = _reader.ReadBytes(TelegramExporter.IDENTIFIER.Length);
64+
byte version = _reader.ReadByte();
65+
66+
if (!magic_number.SequenceEqual(TelegramExporter.MAGIC_NUMBER) || version != TelegramExporter.VERSION)
67+
{
68+
throw new InvalidDataException("Invalid file format");
69+
}
70+
71+
return version;
72+
}
73+
74+
/// <summary>
75+
/// Pop the next telegram from the stream
76+
/// </summary>
77+
/// <returns></returns>
78+
/// <exception cref="InvalidDataException">Could not find an endTag</exception>
79+
/// <exception cref="EndOfStreamException"></exception>
80+
protected BaseTelegram PopTelegram()
81+
{
82+
long timestamp = _reader.ReadInt64();
83+
List<byte> raw = [];
84+
byte b;
85+
86+
// Read until end telegram is found or max length is reached
87+
do
88+
{
89+
b = _reader.ReadByte();
90+
raw.Add(b);
91+
} while (b != BaseTelegram.END_TELEGRAM && raw.Count < BaseTelegram.MAX_RAW_DATA_LEN);
92+
93+
if (raw.Count >= BaseTelegram.MAX_RAW_DATA_LEN)
94+
{
95+
throw new InvalidDataException("Endtag not found. File is corrupted.");
96+
}
97+
98+
// Create and specialize the telegram
99+
BaseTelegram baseTelegram = new(raw.ToArray<byte>(), DateTime.FromBinary(timestamp));
100+
return TelegramSpecializer.Specialize(baseTelegram);
101+
}
102+
}
103+
}
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ namespace RS485_Monitor.tests;
44
using System.Text;
55

66
[TestFixture]
7-
public class TelegramWriterTest
7+
public class TelegramExporterTest
88
{
99

1010
[Test]
1111
public void WriteHeaderTest()
1212
{
1313
MemoryStream stream = new();
14-
using TelegramWriter writer = new(stream);
14+
using TelegramExporter writer = new(stream);
1515

1616
// Extract identifier
17-
byte[] expected = Encoding.ASCII.GetBytes(TelegramWriter.IDENTIFIER);
17+
byte[] expected = Encoding.ASCII.GetBytes(TelegramExporter.IDENTIFIER);
1818
byte[] actual = new byte[expected.Length];
1919

2020
stream.Seek(0, SeekOrigin.Begin);
@@ -33,20 +33,20 @@ public void WriteHeaderTest()
3333
public void CreateFileSuccess() {
3434
string path = Path.GetTempFileName();
3535

36-
using (TelegramWriter writer = new(path)) {
36+
using (TelegramExporter writer = new(path)) {
3737
Assert.That(File.Exists(path), Is.True);
3838
}
3939

4040
using (Stream stream = new FileStream(path, FileMode.Open)) {
4141

4242
// Read the content of the file and check the header
4343
using (var reader = new BinaryReader(stream)) {
44-
byte[] actual = new byte[TelegramWriter.IDENTIFIER.Length];
45-
reader.Read(actual, 0, TelegramWriter.IDENTIFIER.Length);
44+
byte[] actual = new byte[TelegramExporter.IDENTIFIER.Length];
45+
reader.Read(actual, 0, TelegramExporter.IDENTIFIER.Length);
4646
byte actual_version = reader.ReadByte();
4747

48-
byte[] expected = Encoding.ASCII.GetBytes(TelegramWriter.IDENTIFIER);
49-
byte expected_version = TelegramWriter.VERSION;
48+
byte[] expected = Encoding.ASCII.GetBytes(TelegramExporter.IDENTIFIER);
49+
byte expected_version = TelegramExporter.VERSION;
5050

5151
Assert.That(actual, Is.EqualTo(expected));
5252
Assert.That(actual_version, Is.EqualTo(expected_version));
@@ -61,7 +61,7 @@ public void CreateFileSuccess() {
6161
[Test]
6262
public void PushTelegrams() {
6363
MemoryStream stream = new();
64-
using TelegramWriter writer = new(stream);
64+
using TelegramExporter writer = new(stream);
6565

6666

6767
byte[] raw1 = [0xB6, 0x6B, 0xAA, 0x5A, 0x0A, 0x4D, 0x48, 0x17, 0x00, 0x00, 0x23, 0x00, 0x0B, 0x00, 0x00, 0x30, 0x0D];
@@ -74,7 +74,7 @@ public void PushTelegrams() {
7474
}
7575

7676
// Jump over the header and start reading the telegrams
77-
var headerlen = TelegramWriter.IDENTIFIER.Length + 1;
77+
var headerlen = TelegramExporter.IDENTIFIER.Length + 1;
7878
stream.Seek(headerlen, SeekOrigin.Begin);
7979

8080
using (var reader = new BinaryReader(stream)) {

tests/TelegramImporterTest.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
namespace RS485_Monitor.tests;
2+
using RS485_Monitor.Utils.Storage;
3+
using NUnit.Framework;
4+
5+
[TestFixture]
6+
public class TelegramImportTest
7+
{
8+
public required Stream stream;
9+
public required List<BaseTelegram> telegrams;
10+
11+
[OneTimeSetUp]
12+
public void SetUp()
13+
{
14+
stream = new MemoryStream();
15+
telegrams = [];
16+
17+
telegrams.Add(new BaseTelegram([0xB6, 0x6B, 0xAA, 0x5A, 0x0A, 0x4D, 0x48, 0x17, 0x00, 0x00, 0x23, 0x00, 0x0B, 0x00, 0x00, 0x30, 0x0D]));
18+
telegrams.Add(new BaseTelegram([0xC5, 0x5C, 0x5A, 0xAA, 0x01, 0x00, 0x30, 0x0D]));
19+
20+
using TelegramExporter writer = new(stream, leaveOpen: true);
21+
foreach (BaseTelegram telegram in telegrams)
22+
{
23+
writer.PushTelegram(telegram);
24+
}
25+
}
26+
27+
[OneTimeTearDown]
28+
public void TearDown()
29+
{
30+
stream.Dispose();
31+
}
32+
33+
[SetUp]
34+
public void SetUpTest()
35+
{
36+
stream.Seek(0, SeekOrigin.Begin);
37+
}
38+
39+
[Test]
40+
public void ReadHeaderTest()
41+
{
42+
using TelegramImporter reader = new(stream, leaveOpen: true);
43+
Assert.That(reader.FormatVersion, Is.EqualTo(1));
44+
}
45+
46+
[Test]
47+
public void ReadTelegrams()
48+
{
49+
using TelegramImporter reader = new(stream, leaveOpen: true);
50+
List<BaseTelegram> actual = [];
51+
52+
// read the telegrams
53+
foreach (BaseTelegram telegram in reader.GetTelegram())
54+
{
55+
actual.Add(telegram);
56+
}
57+
58+
// Check that the read telegrams are the same as the ones we wrote
59+
Assert.That(actual, Is.EqualTo(telegrams));
60+
}
61+
}

0 commit comments

Comments
 (0)