Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7ca46f3
lab1
slavik22 May 24, 2026
a701afc
Merge pull request #2 from slavik22/lab1
slavik22 May 24, 2026
4fb8cef
lab2
slavik22 May 24, 2026
b767d8a
Merge pull request #3 from slavik22/lab2
slavik22 May 24, 2026
78b60e6
make private fields readonly
slavik22 May 24, 2026
ef1262c
fix: make private fields readonly (S2933)
slavik22 May 24, 2026
a07a541
fix: dispose CancellationTokenSource to prevent resource leak (S2930)
slavik22 May 24, 2026
e968a0f
fix: remove unused exception variable in empty catch (S2486)
slavik22 May 24, 2026
3ca003c
fix: move IUdpClient and UdpClientWrapper into named namespace (S3903)
slavik22 May 24, 2026
2aafcda
fix: provide message in ArgumentOutOfRangeException (S3928)
slavik22 May 24, 2026
b972a36
fix: use static MD5.HashData instead of ComputeHash (CA1850)
slavik22 May 24, 2026
042aa56
Merge pull request #4 from slavik22/lab2-smells
slavik22 May 24, 2026
4634d91
chore: add coverlet.msbuild for OpenCover report generation
slavik22 May 24, 2026
726d635
ci: enable test coverage step in SonarCloud workflow
slavik22 May 24, 2026
064336d
fix: cast ushort to int before Enum.IsDefined to match enum underlyin…
slavik22 May 24, 2026
f726d15
test: add 5 unit tests for NetSdrClient and NetSdrMessageHelper
slavik22 May 24, 2026
2b303c9
Merge pull request #5 from slavik22/lab3
slavik22 May 24, 2026
13ff71f
test: add architecture rules (NetArchTest) — RED: Messages depends on…
slavik22 May 24, 2026
7d8aba5
fix: remove MessageDispatcher that violated Messages→Networking depen…
slavik22 May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ on:

permissions:
pull-requests: read # allows SonarCloud to decorate PRs with analysis results
contents: read


jobs:
sonar-check:
Expand All @@ -56,8 +58,8 @@ jobs:
dotnet tool install --global dotnet-sonarscanner
echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH
dotnet sonarscanner begin `
/k:"ppanchen_NetSdrClient" `
/o:"ppanchen" `
/k:"slavik22_ReengineeringCourse" `
/o:"slavik22" `
/d:sonar.token="${{ secrets.SONAR_TOKEN }}" `
/d:sonar.cs.opencover.reportsPaths="**/coverage.xml" `
/d:sonar.cpd.cs.minimumTokens=40 `
Expand All @@ -70,13 +72,13 @@ jobs:
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 (OpenCover)
run: |
dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build `
/p:CollectCoverage=true `
/p:CoverletOutput=TestResults/coverage.xml `
/p:CoverletOutputFormat=opencover
shell: pwsh
# 3) END: SonarScanner
- name: SonarScanner End
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
Expand Down
12 changes: 3 additions & 9 deletions NetSdrClientApp/Messages/NetSdrMessageHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;


namespace NetSdrClientApp.Messages
{
//TODO: analyze possible use of [StructLayout] for better performance and readability
Expand Down Expand Up @@ -83,7 +77,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;
}
Expand Down Expand Up @@ -111,7 +105,7 @@ public static IEnumerable<int> GetSamples(ushort sampleSize, byte[] body)
sampleSize /= 8; //to bytes
if (sampleSize > 4)
{
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(nameof(sampleSize), sampleSize, "Sample size must be 8, 16, 24, or 32 bits.");
}

var bodyEnumerable = body as IEnumerable<byte>;
Expand Down
20 changes: 6 additions & 14 deletions NetSdrClientApp/NetSdrClient.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
using NetSdrClientApp.Messages;
using NetSdrClientApp.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using static NetSdrClientApp.Messages.NetSdrMessageHelper;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace NetSdrClientApp
{
public class NetSdrClient
{
private ITcpClient _tcpClient;
private IUdpClient _udpClient;
private readonly ITcpClient _tcpClient;
private readonly IUdpClient _udpClient;

public bool IQStarted { get; set; }

Expand Down Expand Up @@ -66,7 +58,7 @@ public async Task StartIQAsync()
return;
}

; var iqDataMode = (byte)0x80;
var iqDataMode = (byte)0x80;
var start = (byte)0x02;
var fifo16bitCaptureMode = (byte)0x01;
var n = (byte)1;
Expand Down Expand Up @@ -116,7 +108,7 @@ public async Task ChangeFrequencyAsync(long hz, int channel)

private void _udpClient_MessageReceived(object? sender, byte[] e)
{
NetSdrMessageHelper.TranslateMessage(e, out MsgTypes type, out ControlItemCodes code, out ushort sequenceNum, out byte[] body);
NetSdrMessageHelper.TranslateMessage(e, out _, out _, out _, out byte[] body);
var samples = NetSdrMessageHelper.GetSamples(16, body);

Console.WriteLine($"Samples recieved: " + body.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}"));
Expand All @@ -131,9 +123,9 @@ private void _udpClient_MessageReceived(object? sender, byte[] e)
}
}

private TaskCompletionSource<byte[]> responseTaskSource;
private TaskCompletionSource<byte[]>? responseTaskSource;

private async Task<byte[]> SendTcpRequest(byte[] msg)
private async Task<byte[]?> SendTcpRequest(byte[] msg)
{
if (!_tcpClient.Connected)
{
Expand Down
9 changes: 1 addition & 8 deletions NetSdrClientApp/Networking/ITcpClient.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace NetSdrClientApp.Networking
namespace NetSdrClientApp.Networking
{
public interface ITcpClient
{
Expand Down
17 changes: 10 additions & 7 deletions NetSdrClientApp/Networking/IUdpClient.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@

public interface IUdpClient

namespace NetSdrClientApp.Networking
{
event EventHandler<byte[]>? MessageReceived;
public interface IUdpClient
{
event EventHandler<byte[]>? MessageReceived;

Task StartListeningAsync();
Task StartListeningAsync();

void StopListening();
void Exit();
}
void StopListening();
void Exit();
}
}
18 changes: 6 additions & 12 deletions NetSdrClientApp/Networking/TcpClientWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace NetSdrClientApp.Networking
{
public class TcpClientWrapper : ITcpClient
{
private string _host;
private int _port;
private readonly string _host;
private readonly int _port;
private TcpClient? _tcpClient;
private NetworkStream? _stream;
private CancellationTokenSource _cts;
private CancellationTokenSource? _cts;

public bool Connected => _tcpClient != null && _tcpClient.Connected && _stream != null;

Expand Down Expand Up @@ -57,6 +50,7 @@ public void Disconnect()
if (Connected)
{
_cts?.Cancel();
_cts?.Dispose();
_stream?.Close();
_tcpClient?.Close();

Expand Down Expand Up @@ -117,7 +111,7 @@ private async Task StartListeningAsync()
}
}
}
catch (OperationCanceledException ex)
catch (OperationCanceledException)
{
//empty
}
Expand Down
122 changes: 61 additions & 61 deletions NetSdrClientApp/Networking/UdpClientWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,85 +1,85 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class UdpClientWrapper : IUdpClient
namespace NetSdrClientApp.Networking
{
private readonly IPEndPoint _localEndPoint;
private CancellationTokenSource? _cts;
private UdpClient? _udpClient;

public event EventHandler<byte[]>? MessageReceived;

public UdpClientWrapper(int port)
public class UdpClientWrapper : IUdpClient
{
_localEndPoint = new IPEndPoint(IPAddress.Any, port);
}
private readonly IPEndPoint _localEndPoint;
private CancellationTokenSource? _cts;
private UdpClient? _udpClient;

public async Task StartListeningAsync()
{
_cts = new CancellationTokenSource();
Console.WriteLine("Start listening for UDP messages...");
public event EventHandler<byte[]>? MessageReceived;

try
public UdpClientWrapper(int port)
{
_udpClient = new UdpClient(_localEndPoint);
while (!_cts.Token.IsCancellationRequested)
_localEndPoint = new IPEndPoint(IPAddress.Any, port);
}

public async Task StartListeningAsync()
{
_cts = new CancellationTokenSource();
Console.WriteLine("Start listening for UDP messages...");

try
{
UdpReceiveResult result = await _udpClient.ReceiveAsync(_cts.Token);
MessageReceived?.Invoke(this, result.Buffer);
_udpClient = new UdpClient(_localEndPoint);
while (!_cts.Token.IsCancellationRequested)
{
UdpReceiveResult result = await _udpClient.ReceiveAsync(_cts.Token);
MessageReceived?.Invoke(this, result.Buffer);

Console.WriteLine($"Received from {result.RemoteEndPoint}");
Console.WriteLine($"Received from {result.RemoteEndPoint}");
}
}
catch (OperationCanceledException)
{
//empty
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving message: {ex.Message}");
}
}
catch (OperationCanceledException ex)
{
//empty
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving message: {ex.Message}");
}
}

public void StopListening()
{
try
public void StopListening()
{
_cts?.Cancel();
_udpClient?.Close();
Console.WriteLine("Stopped listening for UDP messages.");
}
catch (Exception ex)
{
Console.WriteLine($"Error while stopping: {ex.Message}");
try
{
_cts?.Cancel();
_cts?.Dispose();
_udpClient?.Close();
Console.WriteLine("Stopped listening for UDP messages.");
}
catch (Exception ex)
{
Console.WriteLine($"Error while stopping: {ex.Message}");
}
}
}

public void Exit()
{
try
{
_cts?.Cancel();
_udpClient?.Close();
Console.WriteLine("Stopped listening for UDP messages.");
}
catch (Exception ex)
public void Exit()
{
Console.WriteLine($"Error while stopping: {ex.Message}");
try
{
_cts?.Cancel();
_udpClient?.Close();
Console.WriteLine("Stopped listening for UDP messages.");
}
catch (Exception ex)
{
Console.WriteLine($"Error while stopping: {ex.Message}");
}
}
}

public override int GetHashCode()
{
var payload = $"{nameof(UdpClientWrapper)}|{_localEndPoint.Address}|{_localEndPoint.Port}";
public override int GetHashCode()
{
var payload = $"{nameof(UdpClientWrapper)}|{_localEndPoint.Address}|{_localEndPoint.Port}";

using var md5 = MD5.Create();
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload));
var hash = MD5.HashData(Encoding.UTF8.GetBytes(payload));

return BitConverter.ToInt32(hash, 0);
return BitConverter.ToInt32(hash, 0);
}
}
}
}
Loading