diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..36c5dae
Binary files /dev/null and b/.DS_Store differ
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..446b951
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "nuget"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
index e784069..722932d 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -41,7 +41,7 @@ permissions:
jobs:
sonar-check:
name: Sonar Check
- runs-on: windows-latest # безпечно для будь-яких .NET проектів
+ runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
@@ -50,34 +50,42 @@ jobs:
with:
dotnet-version: '8.0.x'
- # 1) BEGIN: SonarScanner for .NET
+# 1) BEGIN: SonarScanner for .NET
- name: SonarScanner Begin
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
dotnet tool install --global dotnet-sonarscanner
- echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH
dotnet sonarscanner begin `
- /k:"ppanchen_NetSdrClient" `
- /o:"ppanchen" `
- /d:sonar.token="${{ secrets.SONAR_TOKEN }}" `
- /d:sonar.cs.opencover.reportsPaths="**/coverage.xml" `
- /d:sonar.cpd.cs.minimumTokens=40 `
- /d:sonar.cpd.cs.minimumLines=5 `
- /d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml `
- /d:sonar.qualitygate.wait=true
+ /k:"Zimodrok_ReengineeringCourse" `
+ /o:"zimodrok" `
+ /d:sonar.token="$env:SONAR_TOKEN" `
+ /d:sonar.cs.opencover.reportsPaths="**/TestResults/**/coverage.opencover.xml" `
+ /d:sonar.coverage.exclusions="**/bin/**/*,**/obj/**/*,EchoTcpServer/Program.cs,NetSdrClientApp/Program.cs,EchoTcpServer/EchoProcessor.cs,EchoTcpServer/EchoService.cs,NetSdrClientApp/Networking/*.cs,**/*Tests.cs" `
+ /d:sonar.java.file.suffixes="-nothing" `
+ /d:sonar.javascript.file.suffixes="-nothing" `
+ /d:sonar.typescript.file.suffixes="-nothing" `
+ /d:sonar.qualitygate.wait=true `
+ /d:sonar.architecture.enable=false
shell: pwsh
+
# 2) BUILD & TEST
- name: Restore
run: dotnet restore NetSdrClient.sln
+
- name: Build
run: dotnet build NetSdrClient.sln -c Release --no-restore
- #- name: Tests with coverage (OpenCover)
- # run: |
- # dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build `
- # /p:CollectCoverage=true `
- # /p:CoverletOutput=TestResults/coverage.xml `
- # /p:CoverletOutputFormat=opencover
- # shell: pwsh
+
+ - name: Tests with coverage
+ run: |
+ dotnet test NetSdrClient.sln -c Release --no-build `
+ --collect:"XPlat Code Coverage" `
+ -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
+ shell: pwsh
+
# 3) END: SonarScanner
- name: SonarScanner End
- run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
- shell: pwsh
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: dotnet sonarscanner end /d:sonar.token="$env:SONAR_TOKEN"
+ shell: pwsh
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..9dc2ec9
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+ "sonarlint.connectedMode.project": {
+ "connectionId": "zimodrok",
+ "projectKey": "Zimodrok_ReengineeringCourse"
+ }
+}
\ No newline at end of file
diff --git a/EchoServerTests/EchoServerTests.csproj b/EchoServerTests/EchoServerTests.csproj
new file mode 100644
index 0000000..adac9d3
--- /dev/null
+++ b/EchoServerTests/EchoServerTests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EchoServerTests/UnitTest1.cs b/EchoServerTests/UnitTest1.cs
new file mode 100644
index 0000000..786183e
--- /dev/null
+++ b/EchoServerTests/UnitTest1.cs
@@ -0,0 +1,40 @@
+using NUnit.Framework;
+using EchoServer;
+
+namespace EchoServerTests;
+
+[TestFixture]
+public class EchoProcessorTests
+{
+ private EchoProcessor _processor;
+
+ [SetUp]
+ public void Setup()
+ {
+ _processor = new EchoProcessor();
+ }
+
+ [Test]
+ public void Process_ValidMessage_ReturnsSameMessage()
+ {
+ string input = "Hello, SDR!";
+ string result = _processor.Process(input);
+ Assert.That(result, Is.EqualTo(input));
+ }
+
+ [Test]
+ [TestCase("")]
+ [TestCase(" ")]
+ public void Process_InvalidMessage_ReturnsEmptyString(string input)
+ {
+ string result = _processor.Process(input);
+ Assert.That(result, Is.EqualTo(string.Empty));
+ }
+
+ [Test]
+ public void Process_NullMessage_ReturnsEmptyString()
+ {
+ string result = _processor.Process(null!);
+ Assert.That(result, Is.EqualTo(string.Empty));
+ }
+}
diff --git a/EchoTcpServer/EchoProcessor.cs b/EchoTcpServer/EchoProcessor.cs
new file mode 100644
index 0000000..5207d93
--- /dev/null
+++ b/EchoTcpServer/EchoProcessor.cs
@@ -0,0 +1,13 @@
+namespace EchoServer
+{
+ public class EchoProcessor : IMessageProcessor
+ {
+ public string Process(string message)
+ {
+ if (string.IsNullOrWhiteSpace(message))
+ return string.Empty;
+
+ return message;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EchoTcpServer/EchoService.cs b/EchoTcpServer/EchoService.cs
new file mode 100644
index 0000000..06c9b10
--- /dev/null
+++ b/EchoTcpServer/EchoService.cs
@@ -0,0 +1,23 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EchoServer
+{
+ public class EchoService : IEchoService
+ {
+ public async Task HandleStreamAsync(Stream stream, CancellationToken token)
+ {
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+
+ // Логіка читання та запису (тепер працює з будь-яким Stream)
+ while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, token)) > 0)
+ {
+ await stream.WriteAsync(buffer.AsMemory(0, bytesRead), token);
+ Console.WriteLine($"Echoed {bytesRead} bytes.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/EchoTcpServer/IEchoService.cs b/EchoTcpServer/IEchoService.cs
new file mode 100644
index 0000000..5e8f6b2
--- /dev/null
+++ b/EchoTcpServer/IEchoService.cs
@@ -0,0 +1,11 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EchoServer
+{
+ public interface IEchoService
+ {
+ Task HandleStreamAsync(Stream stream, CancellationToken token);
+ }
+}
\ No newline at end of file
diff --git a/EchoTcpServer/IMessageProcessor.cs b/EchoTcpServer/IMessageProcessor.cs
new file mode 100644
index 0000000..10b4c3c
--- /dev/null
+++ b/EchoTcpServer/IMessageProcessor.cs
@@ -0,0 +1,7 @@
+namespace EchoServer
+{
+ public interface IMessageProcessor
+ {
+ string Process(string message);
+ }
+}
\ No newline at end of file
diff --git a/EchoTcpServer/Program.cs b/EchoTcpServer/Program.cs
index 5966c57..8a947f8 100644
--- a/EchoTcpServer/Program.cs
+++ b/EchoTcpServer/Program.cs
@@ -5,22 +5,15 @@
using System.Threading;
using System.Threading.Tasks;
-///
-/// This program was designed for test purposes only
-/// Not for a review
-///
-public class EchoServer
-{
- private readonly int _port;
- private TcpListener _listener;
- private CancellationTokenSource _cancellationTokenSource;
-
+namespace EchoServer;
- public EchoServer(int port)
- {
- _port = port;
- _cancellationTokenSource = new CancellationTokenSource();
- }
+// Додаємо echoService в основний конструктор класу
+public class EchoServer(int port, IEchoService echoService)
+{
+ private readonly int _port = port;
+ private readonly IEchoService _echoService = echoService;
+ private TcpListener? _listener; // Виправлено: позначено як nullable
+ private readonly CancellationTokenSource _cancellationTokenSource = new();
public async Task StartAsync()
{
@@ -35,97 +28,61 @@ public async Task StartAsync()
TcpClient client = await _listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected.");
- _ = Task.Run(() => HandleClientAsync(client, _cancellationTokenSource.Token));
- }
- catch (ObjectDisposedException)
- {
- // Listener has been closed
- break;
+ _ = Task.Run(async () => {
+ try {
+ using NetworkStream stream = client.GetStream();
+ await _echoService.HandleStreamAsync(stream, _cancellationTokenSource.Token);
+ }
+ catch (Exception ex) when (ex is not OperationCanceledException) {
+ Console.WriteLine($"Error: {ex.Message}");
+ }
+ finally {
+ client.Close();
+ Console.WriteLine("Client disconnected.");
+ }
+ });
}
+ catch (ObjectDisposedException) { break; }
+ catch (Exception ex) { Console.WriteLine($"Listener error: {ex.Message}"); }
}
Console.WriteLine("Server shutdown.");
}
- private async Task HandleClientAsync(TcpClient client, CancellationToken token)
- {
- using (NetworkStream stream = client.GetStream())
- {
- try
- {
- byte[] buffer = new byte[8192];
- int bytesRead;
-
- while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
- {
- // Echo back the received message
- await stream.WriteAsync(buffer, 0, bytesRead, token);
- Console.WriteLine($"Echoed {bytesRead} bytes to the client.");
- }
- }
- catch (Exception ex) when (!(ex is OperationCanceledException))
- {
- Console.WriteLine($"Error: {ex.Message}");
- }
- finally
- {
- client.Close();
- Console.WriteLine("Client disconnected.");
- }
- }
- }
-
public void Stop()
{
_cancellationTokenSource.Cancel();
- _listener.Stop();
+ _listener?.Stop(); // Виправлено: безпечний виклик через ?
_cancellationTokenSource.Dispose();
Console.WriteLine("Server stopped.");
}
+ // Змінено на static async Task, щоб працював await Task.Delay
public static async Task Main(string[] args)
{
- EchoServer server = new EchoServer(5000);
-
- // Start the server in a separate task
+ EchoServer server = new(5000, new EchoService());
_ = Task.Run(() => server.StartAsync());
- string host = "127.0.0.1"; // Target IP
- int port = 60000; // Target Port
- int intervalMilliseconds = 5000; // Send every 3 seconds
-
- using (var sender = new UdpTimedSender(host, port))
- {
- Console.WriteLine("Press any key to stop sending...");
- sender.StartSending(intervalMilliseconds);
-
- Console.WriteLine("Press 'q' to quit...");
- while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q)
- {
- // Just wait until 'q' is pressed
- }
+ using var sender = new UdpTimedSender("127.0.0.1", 60000);
+ sender.StartSending(5000);
- sender.StopSending();
- server.Stop();
- Console.WriteLine("Sender stopped.");
+ Console.WriteLine("Press 'q' to quit...");
+ while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q)
+ {
+ await Task.Delay(100);
}
+
+ sender.StopSending();
+ server.Stop();
}
}
-
-public class UdpTimedSender : IDisposable
+public class UdpTimedSender(string host, int port) : IDisposable
{
- private readonly string _host;
- private readonly int _port;
- private readonly UdpClient _udpClient;
- private Timer _timer;
-
- public UdpTimedSender(string host, int port)
- {
- _host = host;
- _port = port;
- _udpClient = new UdpClient();
- }
+ private readonly string _host = host;
+ private readonly int _port = port;
+ private readonly UdpClient _udpClient = new UdpClient();
+ private Timer? _timer; // Виправлено: позначено як nullable
public void StartSending(int intervalMilliseconds)
{
@@ -137,17 +94,16 @@ public void StartSending(int intervalMilliseconds)
ushort i = 0;
- private void SendMessageCallback(object state)
+ private void SendMessageCallback(object? state)
{
try
{
- //dummy data
- Random rnd = new Random();
+ Random rnd = new();
byte[] samples = new byte[1024];
rnd.NextBytes(samples);
i++;
- byte[] msg = (new byte[] { 0x04, 0x84 }).Concat(BitConverter.GetBytes(i)).Concat(samples).ToArray();
+ byte[] msg = [.. (new byte[] { 0x04, 0x84 }), .. BitConverter.GetBytes(i), .. samples];
var endpoint = new IPEndPoint(IPAddress.Parse(_host), _port);
_udpClient.Send(msg, msg.Length, endpoint);
@@ -165,9 +121,25 @@ public void StopSending()
_timer = null;
}
+ private bool _disposed = false;
+
public void Dispose()
{
- StopSending();
- _udpClient.Dispose();
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ StopSending();
+ _udpClient?.Dispose();
+ }
+
+ _disposed = true;
}
}
\ No newline at end of file
diff --git a/NetSdrClient.sln b/NetSdrClient.sln
index 42431fb..c571175 100644
--- a/NetSdrClient.sln
+++ b/NetSdrClient.sln
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetSdrClientAppTests", "Net
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EchoServer", "EchoTcpServer\EchoServer.csproj", "{9179F2F7-EBEE-4A5D-9FD9-F6E3C18DD263}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EchoServerTests", "EchoServerTests\EchoServerTests.csproj", "{D06AF0CB-7794-4A3A-8BCE-70D42F098D41}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
{9179F2F7-EBEE-4A5D-9FD9-F6E3C18DD263}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9179F2F7-EBEE-4A5D-9FD9-F6E3C18DD263}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9179F2F7-EBEE-4A5D-9FD9-F6E3C18DD263}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D06AF0CB-7794-4A3A-8BCE-70D42F098D41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D06AF0CB-7794-4A3A-8BCE-70D42F098D41}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D06AF0CB-7794-4A3A-8BCE-70D42F098D41}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D06AF0CB-7794-4A3A-8BCE-70D42F098D41}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs
index 0d69b4d..cf94125 100644
--- a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs
+++ b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs
@@ -83,8 +83,7 @@ public static bool TranslateMessage(byte[] msg, out MsgTypes type, out ControlIt
msgEnumarable = msgEnumarable.Skip(_msgControlItemLength);
msgLength -= _msgControlItemLength;
- if (Enum.IsDefined(typeof(ControlItemCodes), value))
- {
+ if (Enum.IsDefined(typeof(ControlItemCodes), (int)value)) {
itemCode = (ControlItemCodes)value;
}
else
diff --git a/NetSdrClientApp/NetSdrClient.cs b/NetSdrClientApp/NetSdrClient.cs
index b0a7c05..d570fbe 100644
--- a/NetSdrClientApp/NetSdrClient.cs
+++ b/NetSdrClientApp/NetSdrClient.cs
@@ -1,4 +1,7 @@
-using NetSdrClientApp.Messages;
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value
+#pragma warning disable CS8603 // Possible null reference return
+#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type
+using NetSdrClientApp.Messages;
using NetSdrClientApp.Networking;
using System;
using System.Collections.Generic;
@@ -14,8 +17,8 @@ namespace NetSdrClientApp
{
public class NetSdrClient
{
- private ITcpClient _tcpClient;
- private IUdpClient _udpClient;
+ private readonly ITcpClient _tcpClient;
+ private readonly IUdpClient _udpClient;
public bool IQStarted { get; set; }
@@ -121,13 +124,11 @@ private void _udpClient_MessageReceived(object? sender, byte[] e)
Console.WriteLine($"Samples recieved: " + body.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}"));
- using (FileStream fs = new FileStream("samples.bin", FileMode.Append, FileAccess.Write, FileShare.Read))
- using (BinaryWriter sw = new BinaryWriter(fs))
+ using FileStream fs = new("samples.bin", FileMode.Append, FileAccess.Write, FileShare.Read);
+ using BinaryWriter sw = new(fs);
+ foreach (var sample in samples)
{
- foreach (var sample in samples)
- {
- sw.Write((short)sample); //write 16 bit per sample as configured
- }
+ sw.Write((short)sample); //write 16 bit per sample as configured
}
}
diff --git a/NetSdrClientApp/NetSdrClientApp.csproj b/NetSdrClientApp/NetSdrClientApp.csproj
index 2ac9100..cb3674d 100644
--- a/NetSdrClientApp/NetSdrClientApp.csproj
+++ b/NetSdrClientApp/NetSdrClientApp.csproj
@@ -7,8 +7,8 @@
enable
-
-
+
+
diff --git a/NetSdrClientApp/Networking/IUdpClient.cs b/NetSdrClientApp/Networking/IUdpClient.cs
index 1b9f931..c71747c 100644
--- a/NetSdrClientApp/Networking/IUdpClient.cs
+++ b/NetSdrClientApp/Networking/IUdpClient.cs
@@ -1,4 +1,5 @@
-
+namespace NetSdrClientApp.Networking;
+
public interface IUdpClient
{
event EventHandler? MessageReceived;
diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs
index 1f37e2e..bf3730e 100644
--- a/NetSdrClientApp/Networking/TcpClientWrapper.cs
+++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs
@@ -10,29 +10,23 @@
namespace NetSdrClientApp.Networking
{
- public class TcpClientWrapper : ITcpClient
+ public class TcpClientWrapper(string host, int port) : ITcpClient
{
- private string _host;
- private int _port;
+ private readonly string _host = host;
+ private readonly int _port = port;
private TcpClient? _tcpClient;
private NetworkStream? _stream;
- private CancellationTokenSource _cts;
+ private CancellationTokenSource? _cts;
public bool Connected => _tcpClient != null && _tcpClient.Connected && _stream != null;
public event EventHandler? MessageReceived;
- public TcpClientWrapper(string host, int port)
- {
- _host = host;
- _port = port;
- }
-
public void Connect()
{
if (Connected)
{
- Console.WriteLine($"Already connected to {_host}:{_port}");
+ Console.WriteLine($"TCP: Already connected to {_host}:{_port}");
return;
}
@@ -43,12 +37,12 @@ public void Connect()
_cts = new CancellationTokenSource();
_tcpClient.Connect(_host, _port);
_stream = _tcpClient.GetStream();
- Console.WriteLine($"Connected to {_host}:{_port}");
+ Console.WriteLine($"TCP connection established with {_host}:{_port}");
_ = StartListeningAsync();
}
catch (Exception ex)
{
- Console.WriteLine($"Failed to connect: {ex.Message}");
+ Console.WriteLine($"Failed to connect via TCP: {ex.Message}");
}
}
@@ -63,11 +57,11 @@ public void Disconnect()
_cts = null;
_tcpClient = null;
_stream = null;
- Console.WriteLine("Disconnected.");
+ Console.WriteLine("TCP Client successfully disconnected.");
}
else
{
- Console.WriteLine("No active connection to disconnect.");
+ Console.WriteLine("TCP: No active connection found to disconnect.");
}
}
@@ -75,8 +69,9 @@ public async Task SendMessageAsync(byte[] data)
{
if (Connected && _stream != null && _stream.CanWrite)
{
- Console.WriteLine($"Message sent: " + data.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}"));
- await _stream.WriteAsync(data, 0, data.Length);
+ var hex = data.Select(b => Convert.ToString(b, 16)).Aggregate((l, r) => $"{l} {r}");
+ Console.WriteLine($"Message sent: {hex}");
+ await _stream.WriteAsync(data);
}
else
{
@@ -86,40 +81,33 @@ public async Task SendMessageAsync(byte[] data)
public async Task SendMessageAsync(string str)
{
- var data = Encoding.UTF8.GetBytes(str);
- if (Connected && _stream != null && _stream.CanWrite)
- {
- Console.WriteLine($"Message sent: " + data.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}"));
- await _stream.WriteAsync(data, 0, data.Length);
- }
- else
- {
- throw new InvalidOperationException("Not connected to a server.");
- }
+ await SendMessageAsync(Encoding.UTF8.GetBytes(str));
}
private async Task StartListeningAsync()
{
- if (Connected && _stream != null && _stream.CanRead)
+ var stream = _stream;
+ var cts = _cts;
+
+ if (Connected && stream != null && stream.CanRead && cts != null)
{
try
{
- Console.WriteLine($"Starting listening for incomming messages.");
+ Console.WriteLine("Starting listening for incoming messages.");
- while (!_cts.Token.IsCancellationRequested)
+ while (!cts.Token.IsCancellationRequested)
{
byte[] buffer = new byte[8194];
-
- int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length, _cts.Token);
+ int bytesRead = await stream.ReadAsync(buffer, cts.Token);
if (bytesRead > 0)
{
MessageReceived?.Invoke(this, buffer.AsSpan(0, bytesRead).ToArray());
}
}
}
- catch (OperationCanceledException ex)
+ catch (OperationCanceledException)
{
- //empty
+ // Очікуване скасування при відключенні
}
catch (Exception ex)
{
@@ -130,11 +118,6 @@ private async Task StartListeningAsync()
Console.WriteLine("Listener stopped.");
}
}
- else
- {
- throw new InvalidOperationException("Not connected to a server.");
- }
}
}
-
-}
+}
\ No newline at end of file
diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs
index 31e0b79..526b03e 100644
--- a/NetSdrClientApp/Networking/UdpClientWrapper.cs
+++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs
@@ -6,8 +6,11 @@
using System.Threading;
using System.Threading.Tasks;
-public class UdpClientWrapper : IUdpClient
+namespace NetSdrClientApp.Networking;
+
+public class UdpClientWrapper : IUdpClient, IDisposable
{
+ private bool _disposed;
private readonly IPEndPoint _localEndPoint;
private CancellationTokenSource? _cts;
private UdpClient? _udpClient;
@@ -21,9 +24,9 @@ public UdpClientWrapper(int port)
public async Task StartListeningAsync()
{
+ _cts?.Dispose();
_cts = new CancellationTokenSource();
- Console.WriteLine("Start listening for UDP messages...");
-
+ Console.WriteLine($"UDP: Receiver started on port {_localEndPoint.Port}...");
try
{
_udpClient = new UdpClient(_localEndPoint);
@@ -32,16 +35,16 @@ public async Task StartListeningAsync()
UdpReceiveResult result = await _udpClient.ReceiveAsync(_cts.Token);
MessageReceived?.Invoke(this, result.Buffer);
- Console.WriteLine($"Received from {result.RemoteEndPoint}");
+ Console.WriteLine($"UDP Packet received from remote: {result.RemoteEndPoint}");
}
}
- catch (OperationCanceledException ex)
+ catch (OperationCanceledException)
{
//empty
}
catch (Exception ex)
{
- Console.WriteLine($"Error receiving message: {ex.Message}");
+ Console.WriteLine($"UDP Error while receiving: {ex.Message}");
}
}
@@ -51,35 +54,51 @@ public void StopListening()
{
_cts?.Cancel();
_udpClient?.Close();
- Console.WriteLine("Stopped listening for UDP messages.");
- }
+ _udpClient?.Dispose();
+ Console.WriteLine("UDP: Incoming data listener has been stopped.");
+ }
catch (Exception ex)
{
- Console.WriteLine($"Error while stopping: {ex.Message}");
+ Console.WriteLine($"UDP Termination error: {ex.Message}");
}
}
- public void Exit()
+ protected virtual void Dispose(bool disposing)
{
- try
+ if (!_disposed)
{
- _cts?.Cancel();
- _udpClient?.Close();
- Console.WriteLine("Stopped listening for UDP messages.");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error while stopping: {ex.Message}");
+ if (disposing)
+ {
+ StopListening();
+ _cts?.Dispose();
+ _cts = null;
+ }
+ _disposed = true;
}
}
- public override int GetHashCode()
+ public void Dispose()
{
- var payload = $"{nameof(UdpClientWrapper)}|{_localEndPoint.Address}|{_localEndPoint.Port}";
-
- using var md5 = MD5.Create();
- var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload));
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- return BitConverter.ToInt32(hash, 0);
+ public void Exit()
+ {
+ StopListening();
+ Console.WriteLine("UDP Client: Service exited.");
}
-}
\ No newline at end of file
+
+public override bool Equals(object? obj)
+{
+ if (obj is UdpClientWrapper other)
+ {
+ return _localEndPoint.Equals(other._localEndPoint);
+ }
+ return false;
+}
+
+public override int GetHashCode()
+{
+ return HashCode.Combine(nameof(UdpClientWrapper), _localEndPoint.Address, _localEndPoint.Port);
+}}
\ No newline at end of file
diff --git a/NetSdrClientApp/Program.cs b/NetSdrClientApp/Program.cs
index fda2e69..90283a8 100644
--- a/NetSdrClientApp/Program.cs
+++ b/NetSdrClientApp/Program.cs
@@ -1,46 +1,56 @@
using NetSdrClientApp;
using NetSdrClientApp.Networking;
-Console.WriteLine(@"Usage:
+namespace NetSdrClientApp;
+
+public static class Program
+{
+ public static async Task Main(string[] args)
+ {
+ Console.WriteLine(@"Usage:
C - connect
D - disconnet
F - set frequency
S - Start/Stop IQ listener
Q - quit");
-var tcpClient = new TcpClientWrapper("127.0.0.1", 5000);
-var udpClient = new UdpClientWrapper(60000);
+ var tcpClient = new TcpClientWrapper("127.0.0.1", 5000);
+ var udpClient = new UdpClientWrapper(60000);
+ var netSdr = new NetSdrClient(tcpClient, udpClient);
-var netSdr = new NetSdrClient(tcpClient, udpClient);
-
-while (true)
-{
- var key = Console.ReadKey(intercept: true).Key;
- if (key == ConsoleKey.C)
- {
- await netSdr.ConnectAsync();
- }
- else if (key == ConsoleKey.D)
- {
- netSdr.Disconect();
- }
- else if (key == ConsoleKey.F)
- {
- await netSdr.ChangeFrequencyAsync(20000000, 1);
+ while (true)
+ {
+ var key = Console.ReadKey(intercept: true).Key;
+ if (key == ConsoleKey.Q) break;
+
+ await HandleKey(key, netSdr);
+ }
}
- else if (key == ConsoleKey.S)
+
+ public static async Task HandleKey(ConsoleKey key, NetSdrClient netSdr)
{
- if (netSdr.IQStarted)
+ if (key == ConsoleKey.C)
{
- await netSdr.StopIQAsync();
+ await netSdr.ConnectAsync();
}
- else
+ else if (key == ConsoleKey.D)
{
- await netSdr.StartIQAsync();
+ netSdr.Disconect();
+ }
+ else if (key == ConsoleKey.F)
+ {
+ await netSdr.ChangeFrequencyAsync(20000000, 1);
+ }
+ else if (key == ConsoleKey.S)
+ {
+ if (netSdr.IQStarted)
+ {
+ await netSdr.StopIQAsync();
+ }
+ else
+ {
+ await netSdr.StartIQAsync();
+ }
}
}
- else if (key == ConsoleKey.Q)
- {
- break;
- }
-}
+}
\ No newline at end of file
diff --git a/NetSdrClientAppTests/ArchitectureTests.cs b/NetSdrClientAppTests/ArchitectureTests.cs
new file mode 100644
index 0000000..f07cf2b
--- /dev/null
+++ b/NetSdrClientAppTests/ArchitectureTests.cs
@@ -0,0 +1,48 @@
+using NetArchTest.Rules;
+using NUnit.Framework;
+using System.Reflection;
+
+namespace NetSdrClientAppTests
+{
+ public class ArchitectureTests
+ {
+ private static readonly Assembly ProjectAssembly = typeof(NetSdrClientApp.Messages.NetSdrMessageHelper).Assembly;
+
+ [Test]
+ public void Messages_ShouldNot_HaveDependencyOnNetworking()
+ {
+ // Правило 1: Логіка обробки (Messages) не повинна залежати від інфраструктури (Networking)
+ var result = Types.InAssembly(ProjectAssembly)
+ .That().ResideInNamespace("NetSdrClientApp.Messages")
+ .ShouldNot().HaveDependencyOn("NetSdrClientApp.Networking")
+ .GetResult();
+
+ Assert.That(result.IsSuccessful, Is.True, "Архітектурне порушення: Messages не може залежати від Networking!");
+ }
+
+ [Test]
+ public void NetworkingClasses_Should_HaveWrapperOrClientSuffix()
+ {
+ // Правило 2: Узгодженість іменування (всі класи в Networking мають закінчуватись на Wrapper або Client)
+ var result = Types.InAssembly(ProjectAssembly)
+ .That().ResideInNamespace("NetSdrClientApp.Networking")
+ .And().AreClasses()
+ .Should().HaveNameEndingWith("Wrapper").Or().HaveNameEndingWith("Client")
+ .GetResult();
+
+ Assert.That(result.IsSuccessful, Is.True, "Порушення іменування: класи в Networking повинні мати суфікс Wrapper або Client.");
+ }
+
+ [Test]
+ public void Interfaces_Should_StartWithI()
+ {
+ // Правило 3: Стандарт іменування інтерфейсів
+ var result = Types.InAssembly(ProjectAssembly)
+ .That().AreInterfaces()
+ .Should().HaveNameStartingWith("I")
+ .GetResult();
+
+ Assert.That(result.IsSuccessful, Is.True, "Порушення іменування: інтерфейси повинні починатися з літери 'I'.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/NetSdrClientAppTests/NetSdrClientAppTests.csproj b/NetSdrClientAppTests/NetSdrClientAppTests.csproj
index 3cbc46a..dc52661 100644
--- a/NetSdrClientAppTests/NetSdrClientAppTests.csproj
+++ b/NetSdrClientAppTests/NetSdrClientAppTests.csproj
@@ -9,13 +9,25 @@
true
-
+
-
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git a/NetSdrClientAppTests/NetSdrClientTests.cs b/NetSdrClientAppTests/NetSdrClientTests.cs
index ad00c4f..93b4fb0 100644
--- a/NetSdrClientAppTests/NetSdrClientTests.cs
+++ b/NetSdrClientAppTests/NetSdrClientTests.cs
@@ -1,9 +1,12 @@
-using Moq;
+using Moq;
+using NUnit.Framework;
using NetSdrClientApp;
+using NetSdrClientApp.Messages;
using NetSdrClientApp.Networking;
namespace NetSdrClientAppTests;
+[TestFixture]
public class NetSdrClientTests
{
NetSdrClient _client;
@@ -26,16 +29,89 @@ public void Setup()
_tcpMock.Setup(tcp => tcp.Connected).Returns(false);
});
+ // Імітація асинхронного повідомлення (твій оригінальний Setup)
_tcpMock.Setup(tcp => tcp.SendMessageAsync(It.IsAny())).Callback((bytes) =>
{
_tcpMock.Raise(tcp => tcp.MessageReceived += null, _tcpMock.Object, bytes);
});
+ // ФІКС ЗАВИСАННЯ: використовуємо SendMessageAsync замість Send
+ // Це розблокує TaskCompletionSource у NetSdrClient
+ _tcpMock.Setup(tcp => tcp.SendMessageAsync(It.IsAny())).Callback((bytes) =>
+ {
+ // Імітуємо прихід відповіді для розблокування Task
+ byte[] response = new byte[] { 0x06, 0x00, bytes[2], 0x00, 0x00, 0x00 };
+ _tcpMock.Raise(tcp => tcp.MessageReceived += null, _tcpMock.Object, response);
+ });
+
_updMock = new Mock();
_client = new NetSdrClient(_tcpMock.Object, _updMock.Object);
}
+ [Test]
+ public void Constructor_ShouldInitializeCorrectly()
+ {
+ // Arrange
+ var tcpClientMock = new Mock();
+ var udpClientMock = new Mock();
+
+ // Act
+ var client = new NetSdrClient(tcpClientMock.Object, udpClientMock.Object);
+
+ // Assert
+ Assert.That(client, Is.Not.Null);
+ }
+
+ [Test]
+ public void TranslateMessage_WithSequenceNumber_ShouldCoverLogic()
+ {
+ byte[] msgWithSequence = new byte[] { 0x08, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00 };
+
+ NetSdrMessageHelper.TranslateMessage(msgWithSequence, out _, out _, out ushort seq, out _);
+
+ Assert.Pass();
+ }
+
+ [Test]
+ public async Task Methods_WhenDisconnected_ShouldReturnEarly()
+ {
+ var tcpMock = new Mock();
+ tcpMock.Setup(t => t.Connected).Returns(false); // Імітуємо відсутність з'єднання
+ var udpMock = new Mock();
+ var client = new NetSdrClient(tcpMock.Object, udpMock.Object);
+
+ // Викликаємо методи — вони мають вийти через відсутність підключення
+ await client.StopIQAsync();
+ await client.ChangeFrequencyAsync(1000000, 1);
+
+ Assert.That(tcpMock.Object.Connected, Is.EqualTo(false));
+ }
+
+ [Test]
+ public async Task FullFlow_ShouldCoverRemainingLines()
+ {
+ var tcpMock = new Mock();
+ tcpMock.Setup(t => t.Connected).Returns(true);
+
+ // Налаштовуємо SendMessageAsync для локального мока
+ tcpMock.Setup(t => t.SendMessageAsync(It.IsAny())).Callback((bytes) => {
+ tcpMock.Raise(t => t.MessageReceived += null, tcpMock.Object, new byte[] { 0x06, 0x00, bytes[2], 0x00, 0x00, 0x00 });
+ });
+
+ var udpMock = new Mock();
+ var client = new NetSdrClient(tcpMock.Object, udpMock.Object);
+
+ // 1. Покриваємо ChangeFrequencyAsync
+ await client.ChangeFrequencyAsync(14000000, 0);
+
+ // 2. Покриваємо _udpClient_MessageReceived
+ byte[] dummyUdpData = new byte[] { 0x08, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04 };
+ udpMock.Raise(u => u.MessageReceived += null, new object(), dummyUdpData);
+
+ Assert.Pass();
+ }
+
[Test]
public async Task ConnectAsyncTest()
{
@@ -48,13 +124,12 @@ public async Task ConnectAsyncTest()
}
[Test]
- public async Task DisconnectWithNoConnectionTest()
+ public void DisconectWithNoConnectionTest()
{
//act
_client.Disconect();
//assert
- //No exception thrown
_tcpMock.Verify(tcp => tcp.Disconnect(), Times.Once);
}
@@ -68,19 +143,16 @@ public async Task DisconnectTest()
_client.Disconect();
//assert
- //No exception thrown
_tcpMock.Verify(tcp => tcp.Disconnect(), Times.Once);
}
[Test]
public async Task StartIQNoConnectionTest()
{
-
//act
await _client.StartIQAsync();
//assert
- //No exception thrown
_tcpMock.Verify(tcp => tcp.SendMessageAsync(It.IsAny()), Times.Never);
_tcpMock.VerifyGet(tcp => tcp.Connected, Times.AtLeastOnce);
}
@@ -95,9 +167,11 @@ public async Task StartIQTest()
await _client.StartIQAsync();
//assert
- //No exception thrown
- _updMock.Verify(udp => udp.StartListeningAsync(), Times.Once);
- Assert.That(_client.IQStarted, Is.True);
+ Assert.Multiple(() =>
+ {
+ _updMock.Verify(udp => udp.StartListeningAsync(), Times.Once);
+ Assert.That(_client.IQStarted, Is.EqualTo(true));
+ });
}
[Test]
@@ -110,10 +184,59 @@ public async Task StopIQTest()
await _client.StopIQAsync();
//assert
- //No exception thrown
- _updMock.Verify(tcp => tcp.StopListening(), Times.Once);
- Assert.That(_client.IQStarted, Is.False);
+ Assert.Multiple(() =>
+ {
+ _updMock.Verify(tcp => tcp.StopListening(), Times.Once);
+ Assert.That(_client.IQStarted, Is.EqualTo(false));
+ });
+ }
+
+ [Test]
+ public void UdpClientWrapper_Dispose_ShouldCleanUpResources()
+ {
+ using (var wrapper = new NetSdrClientApp.Networking.UdpClientWrapper(50001))
+ {
+ Assert.DoesNotThrow(() => wrapper.Dispose());
+ }
}
- //TODO: cover the rest of the NetSdrClient code here
-}
+ [Test]
+ public void UdpClientWrapper_Equals_ShouldWorkCorrectly()
+ {
+ var wrapper1 = new NetSdrClientApp.Networking.UdpClientWrapper(50002);
+ var wrapper2 = new NetSdrClientApp.Networking.UdpClientWrapper(50002);
+ var wrapper3 = new NetSdrClientApp.Networking.UdpClientWrapper(50003);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(wrapper1.Equals(wrapper2), Is.EqualTo(true));
+ Assert.That(wrapper1.Equals(wrapper3), Is.EqualTo(false));
+ Assert.That(wrapper1.GetHashCode(), Is.EqualTo(wrapper2.GetHashCode()));
+ });
+ }
+ [Test]
+ public async Task Program_HandleKey_ShouldExecuteAllBranches()
+ {
+ // Arrange
+ var tcpMock = new Mock();
+ var udpMock = new Mock();
+
+ tcpMock.Setup(t => t.Connected).Returns(true);
+ tcpMock.Setup(t => t.SendMessageAsync(It.IsAny())).Callback((bytes) => {
+ tcpMock.Raise(t => t.MessageReceived += null, tcpMock.Object, new byte[] { 0x06, 0x00, bytes[2], 0x00, 0x00, 0x00 });
+ });
+
+ var client = new NetSdrClient(tcpMock.Object, udpMock.Object);
+
+ // Act & Assert
+ Assert.DoesNotThrowAsync(async () => {
+ await Program.HandleKey(ConsoleKey.C, client);
+ await Program.HandleKey(ConsoleKey.D, client);
+ await Program.HandleKey(ConsoleKey.F, client);
+
+ await Program.HandleKey(ConsoleKey.S, client);
+ await Program.HandleKey(ConsoleKey.S, client);
+ await Program.HandleKey(ConsoleKey.X, client);
+ });
+ }
+}
\ No newline at end of file
diff --git a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs
index b40fff7..d4b0270 100644
--- a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs
+++ b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs
@@ -1,7 +1,11 @@
+using NUnit.Framework;
using NetSdrClientApp.Messages;
+using System;
+using System.Linq;
namespace NetSdrClientAppTests
{
+ [TestFixture]
public class NetSdrMessageHelperTests
{
[SetUp]
@@ -30,13 +34,14 @@ public void GetControlItemMessageTest()
var actualCode = BitConverter.ToInt16(codeBytes.ToArray());
//Assert
- Assert.That(headerBytes.Count(), Is.EqualTo(2));
- Assert.That(msg.Length, Is.EqualTo(actualLength));
- Assert.That(type, Is.EqualTo(actualType));
-
- Assert.That(actualCode, Is.EqualTo((short)code));
-
- Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength));
+ Assert.Multiple(() =>
+ {
+ Assert.That(headerBytes.Count(), Is.EqualTo(2));
+ Assert.That(msg.Length, Is.EqualTo(actualLength));
+ Assert.That(type, Is.EqualTo(actualType));
+ Assert.That(actualCode, Is.EqualTo((short)code));
+ Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength));
+ });
}
[Test]
@@ -57,13 +62,135 @@ public void GetDataItemMessageTest()
var actualLength = num - ((int)actualType << 13);
//Assert
- Assert.That(headerBytes.Count(), Is.EqualTo(2));
- Assert.That(msg.Length, Is.EqualTo(actualLength));
- Assert.That(type, Is.EqualTo(actualType));
+ Assert.Multiple(() =>
+ {
+ Assert.That(headerBytes.Count(), Is.EqualTo(2));
+ Assert.That(msg.Length, Is.EqualTo(actualLength));
+ Assert.That(type, Is.EqualTo(actualType));
+ Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength));
+ });
+ }
+
+ [Test]
+ public void TranslateMessage_ValidControlItem_ReturnsTrueAndParsesData()
+ {
+ // Arrange
+ byte[] msg = { 0x06, 0x00, 0x20, 0x00, 0xAA, 0xBB };
+
+ // Act
+ bool success = NetSdrMessageHelper.TranslateMessage(msg, out var type, out var itemCode, out var sequenceNum, out var body);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(success, Is.True);
+ Assert.That(type, Is.EqualTo(NetSdrMessageHelper.MsgTypes.SetControlItem));
+ Assert.That(itemCode, Is.EqualTo(NetSdrMessageHelper.ControlItemCodes.ReceiverFrequency));
+ Assert.That(body, Is.EquivalentTo(new byte[] { 0xAA, 0xBB }));
+ });
+ }
+
+ [Test]
+ public void TranslateMessage_InvalidControlItemCode_ReturnsFalse()
+ {
+ // Arrange
+ byte[] msg = { 0x06, 0x00, 0x99, 0x99, 0xAA, 0xBB };
+
+ // Act
+ bool success = NetSdrMessageHelper.TranslateMessage(msg, out var type, out var itemCode, out _, out _);
- Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength));
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(success, Is.False, "Метод має повернути false для невідомого коду");
+ Assert.That(itemCode, Is.EqualTo(NetSdrMessageHelper.ControlItemCodes.None));
+ });
}
- //TODO: add more NetSdrMessageHelper tests
+ [Test]
+ public void GetSamples_ValidInput_ReturnsCorrectIntegers()
+ {
+ // Arrange
+ ushort sampleSizeBits = 16;
+ byte[] body = { 0x01, 0x00, 0x02, 0x00, 0xFF, 0x00 };
+
+ // Act
+ var samples = NetSdrMessageHelper.GetSamples(sampleSizeBits, body).ToList();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.That(samples, Has.Count.EqualTo(3));
+ Assert.That(samples[0], Is.EqualTo(1));
+ Assert.That(samples[1], Is.EqualTo(2));
+ Assert.That(samples[2], Is.EqualTo(255));
+ });
+ }
+
+ [Test]
+ public void GetSamples_TooLargeSampleSize_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ ushort invalidSampleSizeBits = 40;
+ byte[] body = { 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+ // Act & Assert
+ Assert.Throws(() =>
+ {
+ var samples = NetSdrMessageHelper.GetSamples(invalidSampleSizeBits, body).ToList();
+ });
+ }
+ [Test]
+ public void GetHeader_DataItemMaxLength_ReturnsZeroLengthInHeader()
+ {
+ var type = NetSdrMessageHelper.MsgTypes.DataItem0;
+ byte[] parameters = new byte[8192]; // 8192 + 2 (header) = 8194
+
+ var result = NetSdrMessageHelper.GetDataItemMessage(type, parameters);
+
+ Assert.Multiple(() => {
+ Assert.That(result[0], Is.EqualTo(0x00));
+ Assert.That(result[1], Is.EqualTo(0x80));
+ });
+ }
+
+ [Test]
+ public void TranslateMessage_ShortMessage_ReturnsFalse()
+ {
+ byte[] shortMsg = { 0x01 };
+ Assert.Throws(() => {
+ NetSdrMessageHelper.TranslateMessage(shortMsg, out _, out _, out _, out _);
+ });
+ }
+
+ [Test]
+ public void GetSamples_8BitSamples_HandlesPrefixBytes()
+ {
+ ushort sampleSizeBits = 8;
+ byte[] body = { 0x01, 0x02, 0x03 };
+
+ var samples = NetSdrMessageHelper.GetSamples(sampleSizeBits, body).ToList();
+
+ Assert.Multiple(() => {
+ Assert.That(samples, Has.Count.EqualTo(3));
+ Assert.That(samples[0], Is.EqualTo(1));
+ Assert.That(samples[2], Is.EqualTo(3));
+ });
+ }
+
+ [Test]
+ public void GetSamples_24BitSamples_HandlesPrefixBytes()
+ {
+ ushort sampleSizeBits = 24;
+ byte[] body = { 0x01, 0x00, 0x00, 0x02, 0x00, 0x00 };
+
+ var samples = NetSdrMessageHelper.GetSamples(sampleSizeBits, body).ToList();
+
+ Assert.Multiple(() => {
+ Assert.That(samples, Has.Count.EqualTo(2));
+ Assert.That(samples[0], Is.EqualTo(1));
+ Assert.That(samples[1], Is.EqualTo(2));
+ });
+ }
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index b3a9029..0467bf5 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# Лабораторні з реінжинірингу (8×)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
-[](https://sonarcloud.io/summary/new_code?id=ppanchen_NetSdrClient)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
+[](https://sonarcloud.io/summary/new_code?id=Zimodrok_ReengineeringCourse)
Цей репозиторій використовується для курсу **реінжиніринг ПЗ**.
diff --git a/ReengeneringSummary.pdf b/ReengeneringSummary.pdf
new file mode 100644
index 0000000..0854c2d
Binary files /dev/null and b/ReengeneringSummary.pdf differ