Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
26 changes: 13 additions & 13 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,28 @@ jobs:
dotnet tool install --global dotnet-sonarscanner
echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH
dotnet sonarscanner begin `
/k:"ppanchen_NetSdrClient" `
/o:"ppanchen" `
/k:"VlasenkoMykola_ReengineeringCourse" `
/o:"vlasenkomykola" `
/d:sonar.token="${{ secrets.SONAR_TOKEN }}" `
/d:sonar.cs.opencover.reportsPaths="**/coverage.xml" `
/d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.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
/d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml
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 (OpenCover)
run: |
dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build `
/p:CollectCoverage=true `
/p:CoverletOutput=TestResults/coverage `
/p:CoverletOutputFormat=opencover
shell: pwsh
# 3) END: SonarScanner
- name: SonarScanner End
if: always()
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
shell: pwsh
shell: pwsh
6 changes: 3 additions & 3 deletions NetSdrClientApp/Messages/NetSdrMessageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +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;
}
Expand Down Expand Up @@ -111,7 +111,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), "Sample size must not exceed 32 bits.");
}

var bodyEnumerable = body as IEnumerable<byte>;
Expand Down Expand Up @@ -158,4 +158,4 @@ private static void TranslateHeader(byte[] header, out MsgTypes type, out int ms
}
}
}
}
}
10 changes: 5 additions & 5 deletions NetSdrClientApp/NetSdrClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,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; }

Expand Down Expand Up @@ -66,7 +66,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 +116,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 Down Expand Up @@ -162,4 +162,4 @@ private void _tcpClient_MessageReceived(object? sender, byte[] e)
Console.WriteLine("Response recieved: " + e.Select(b => Convert.ToString(b, toBase: 16)).Aggregate((l, r) => $"{l} {r}"));
}
}
}
}
17 changes: 11 additions & 6 deletions NetSdrClientApp/Networking/IUdpClient.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@

public interface IUdpClient
using System;
using System.Threading.Tasks;

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();
}
}
19 changes: 6 additions & 13 deletions NetSdrClientApp/Networking/TcpClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ 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;
Expand All @@ -40,6 +40,7 @@ public void Connect()

try
{
_cts?.Dispose();
_cts = new CancellationTokenSource();
_tcpClient.Connect(_host, _port);
_stream = _tcpClient.GetStream();
Expand Down Expand Up @@ -87,15 +88,7 @@ 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(data);
}

private async Task StartListeningAsync()
Expand All @@ -117,7 +110,7 @@ private async Task StartListeningAsync()
}
}
}
catch (OperationCanceledException ex)
catch (OperationCanceledException)
{
//empty
}
Expand All @@ -137,4 +130,4 @@ private async Task StartListeningAsync()
}
}

}
}
109 changes: 52 additions & 57 deletions NetSdrClientApp/Networking/UdpClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,75 @@
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?.Dispose();
_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
{
_cts?.Cancel();
_udpClient?.Close();
Console.WriteLine("Stopped listening for UDP messages.");
}
catch (Exception ex)
public void StopListening()
{
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 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}");
StopListening();
}
}

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));
using var md5 = MD5.Create();
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload));

return BitConverter.ToInt32(hash, 0);
return BitConverter.ToInt32(hash, 0);
}
}
}
75 changes: 75 additions & 0 deletions NetSdrClientAppTests/ArchitectureTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using NetArchTest.Rules;
using NetSdrClientApp.Messages;

namespace NetSdrClientAppTests
{
public class ArchitectureTests
{
[Test]
public void Messages_Should_Not_Depend_On_Networking()
{
var result = Types.InAssembly(typeof(NetSdrMessageHelper).Assembly)
.That()
.ResideInNamespace("NetSdrClientApp.Messages")
.ShouldNot()
.HaveDependencyOn("NetSdrClientApp.Networking")
.GetResult();

Assert.That(result.IsSuccessful, Is.True,
"Messages layer must not depend on Networking layer. " +
$"Violating types: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
}

[Test]
public void Networking_Should_Not_Depend_On_Messages()
{
var result = Types.InAssembly(typeof(NetSdrMessageHelper).Assembly)
.That()
.ResideInNamespace("NetSdrClientApp.Networking")
.ShouldNot()
.HaveDependencyOn("NetSdrClientApp.Messages")
.GetResult();

Assert.That(result.IsSuccessful, Is.True,
"Networking layer must not depend on Messages layer. " +
$"Violating types: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
}

[Test]
public void Networking_Interfaces_Should_Not_Have_Dependency_On_System_Net_Sockets()
{
var result = Types.InAssembly(typeof(NetSdrMessageHelper).Assembly)
.That()
.ResideInNamespace("NetSdrClientApp.Networking")
.And()
.AreInterfaces()
.ShouldNot()
.HaveDependencyOn("System.Net.Sockets")
.GetResult();

Assert.That(result.IsSuccessful, Is.True,
"Networking interfaces must not depend on System.Net.Sockets directly. " +
$"Violating types: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
}

[Test]
public void MessageHelper_Should_Be_Static()
{
var result = Types.InAssembly(typeof(NetSdrMessageHelper).Assembly)
.That()
.ResideInNamespace("NetSdrClientApp.Messages")
.And()
.HaveNameEndingWith("Helper")
.Should()
.BeAbstract() // static classes are compiled as abstract sealed
.And()
.BeSealed()
.GetResult();

Assert.That(result.IsSuccessful, Is.True,
"Helper classes in Messages should be static. " +
$"Violating types: {string.Join(", ", result.FailingTypeNames ?? Array.Empty<string>())}");
}
}

}
Loading