From 78707e88cf210112dd2f29012d11a6323c8c4817 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Fri, 24 Apr 2026 19:12:26 +0300 Subject: [PATCH 01/18] upload files --- .github/workflows/build.yml | 19 +++++++++++++++++++ sonar-project.properties | 14 ++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 sonar-project.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..383510e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,19 @@ +name: Build +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] +jobs: + sonarqube: + name: SonarQube + runs-on: windows-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@fd88b7d7ccbaefd23d8f36f73b59db7a3d246602 # v6.0.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..efd48e7 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,14 @@ +sonar.projectKey=VlasenkoMykola_ReengineeringCourse +sonar.organization=vlasenkomykola + + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=ReengineeringCourse +#sonar.projectVersion=1.0 + + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +#sonar.sources=. + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 \ No newline at end of file From 60c9c61578418859b72a16c195328751c5db032e Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Fri, 24 Apr 2026 21:15:13 +0300 Subject: [PATCH 02/18] adjust files --- sonar-project.properties | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 sonar-project.properties diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index efd48e7..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,14 +0,0 @@ -sonar.projectKey=VlasenkoMykola_ReengineeringCourse -sonar.organization=vlasenkomykola - - -# This is the name and version displayed in the SonarCloud UI. -#sonar.projectName=ReengineeringCourse -#sonar.projectVersion=1.0 - - -# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. -#sonar.sources=. - -# Encoding of the source code. Default is default system encoding -#sonar.sourceEncoding=UTF-8 \ No newline at end of file From b19eabbf403c7444962c46f08ebc0ac7d2ca4cde Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sat, 25 Apr 2026 15:56:27 +0300 Subject: [PATCH 03/18] adjust files --- .github/workflows/sonarcloud.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index e784069..1e989f1 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -56,8 +56,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:"VlasenkoMykola_ReengineeringCourse" ` + /o:"VlasenkoMykola" ` /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` /d:sonar.cs.opencover.reportsPaths="**/coverage.xml" ` /d:sonar.cpd.cs.minimumTokens=40 ` @@ -80,4 +80,4 @@ jobs: # 3) END: SonarScanner - name: SonarScanner End run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" - shell: pwsh + shell: pwsh \ No newline at end of file From 68ff125beeced5804e88e19d87edced41c342b1e Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sat, 25 Apr 2026 17:03:30 +0300 Subject: [PATCH 04/18] adjust files --- .github/workflows/build.yml | 19 ------------------- .github/workflows/sonarcloud.yml | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 383510e..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Build -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened] -jobs: - sonarqube: - name: SonarQube - runs-on: windows-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@fd88b7d7ccbaefd23d8f36f73b59db7a3d246602 # v6.0.0 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 1e989f1..8ec3648 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -57,7 +57,7 @@ jobs: echo "$env:USERPROFILE\.dotnet\tools" >> $env:GITHUB_PATH dotnet sonarscanner begin ` /k:"VlasenkoMykola_ReengineeringCourse" ` - /o:"VlasenkoMykola" ` + /o:"vlasenkomykola" ` /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` /d:sonar.cs.opencover.reportsPaths="**/coverage.xml" ` /d:sonar.cpd.cs.minimumTokens=40 ` From b5ed3021fa019877d3534ceb8948054a71799c53 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sat, 25 Apr 2026 18:56:53 +0300 Subject: [PATCH 05/18] suppress warnings --- .github/workflows/sonarcloud.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 8ec3648..7adc316 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -44,6 +44,8 @@ jobs: runs-on: windows-latest # безпечно для будь-яких .NET проектів steps: - uses: actions/checkout@v4 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true with: { fetch-depth: 0 } - uses: actions/setup-dotnet@v4 @@ -63,7 +65,7 @@ jobs: /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.qualitygate.wait=false shell: pwsh # 2) BUILD & TEST - name: Restore From 5ae04948bf1f44d00cdf11db96e93998fbeca923 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Tue, 28 Apr 2026 18:13:15 +0300 Subject: [PATCH 06/18] fix: make _tcpClient and _udpClient readonly --- NetSdrClientApp/NetSdrClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NetSdrClientApp/NetSdrClient.cs b/NetSdrClientApp/NetSdrClient.cs index b0a7c05..873e64f 100644 --- a/NetSdrClientApp/NetSdrClient.cs +++ b/NetSdrClientApp/NetSdrClient.cs @@ -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; } @@ -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}")); } } -} +} \ No newline at end of file From 10526db95e7afd0dffb82bf6436fc613c56d8236 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Tue, 28 Apr 2026 18:18:00 +0300 Subject: [PATCH 07/18] fix: make _host and _port readonly --- NetSdrClientApp/Networking/TcpClientWrapper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs index 1f37e2e..a05de48 100644 --- a/NetSdrClientApp/Networking/TcpClientWrapper.cs +++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs @@ -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; @@ -137,4 +137,4 @@ private async Task StartListeningAsync() } } -} +} \ No newline at end of file From 2cec185a324108f832583763bfd5d57b78e4b9ee Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Tue, 28 Apr 2026 18:26:33 +0300 Subject: [PATCH 08/18] fix: remove empty statement and unused variables --- NetSdrClientApp/NetSdrClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NetSdrClientApp/NetSdrClient.cs b/NetSdrClientApp/NetSdrClient.cs index 873e64f..f582c10 100644 --- a/NetSdrClientApp/NetSdrClient.cs +++ b/NetSdrClientApp/NetSdrClient.cs @@ -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; @@ -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}")); From cafd14b250832557f853038b8e6e4a4e1966ceb5 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Thu, 30 Apr 2026 17:57:28 +0300 Subject: [PATCH 09/18] fix: move IUdpClient and UdpClientWrapper into named namespace --- NetSdrClientApp/Networking/IUdpClient.cs | 17 ++- .../Networking/UdpClientWrapper.cs | 117 +++++++++--------- 2 files changed, 71 insertions(+), 63 deletions(-) diff --git a/NetSdrClientApp/Networking/IUdpClient.cs b/NetSdrClientApp/Networking/IUdpClient.cs index 1b9f931..d04706f 100644 --- a/NetSdrClientApp/Networking/IUdpClient.cs +++ b/NetSdrClientApp/Networking/IUdpClient.cs @@ -1,10 +1,15 @@ - -public interface IUdpClient +using System; +using System.Threading.Tasks; + +namespace NetSdrClientApp.Networking { - event EventHandler? MessageReceived; + public interface IUdpClient + { + event EventHandler? MessageReceived; - Task StartListeningAsync(); + Task StartListeningAsync(); - void StopListening(); - void Exit(); + void StopListening(); + void Exit(); + } } \ No newline at end of file diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs index 31e0b79..fe52572 100644 --- a/NetSdrClientApp/Networking/UdpClientWrapper.cs +++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs @@ -6,80 +6,83 @@ 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? 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? 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 ex) + { + //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(); + _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)); + using var md5 = MD5.Create(); + var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload)); - return BitConverter.ToInt32(hash, 0); + return BitConverter.ToInt32(hash, 0); + } } } \ No newline at end of file From 02988d40c1075b9770833f2f4191255916700b5b Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Thu, 30 Apr 2026 18:10:16 +0300 Subject: [PATCH 10/18] fix: dispose CancellationTokenSource before reassignment --- NetSdrClientApp/Networking/TcpClientWrapper.cs | 1 + NetSdrClientApp/Networking/UdpClientWrapper.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs index a05de48..1e62520 100644 --- a/NetSdrClientApp/Networking/TcpClientWrapper.cs +++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs @@ -40,6 +40,7 @@ public void Connect() try { + _cts?.Dispose(); _cts = new CancellationTokenSource(); _tcpClient.Connect(_host, _port); _stream = _tcpClient.GetStream(); diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs index fe52572..ecb5a9d 100644 --- a/NetSdrClientApp/Networking/UdpClientWrapper.cs +++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs @@ -23,6 +23,7 @@ public UdpClientWrapper(int port) public async Task StartListeningAsync() { + _cts?.Dispose(); _cts = new CancellationTokenSource(); Console.WriteLine("Start listening for UDP messages..."); From 3575ca7231cdafd34428a0cc995fcfc7c7b257d4 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Thu, 30 Apr 2026 18:13:27 +0300 Subject: [PATCH 11/18] fix: remove unused ex variables in catch blocks --- NetSdrClientApp/Networking/TcpClientWrapper.cs | 2 +- NetSdrClientApp/Networking/UdpClientWrapper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs index 1e62520..a0e2cc7 100644 --- a/NetSdrClientApp/Networking/TcpClientWrapper.cs +++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs @@ -118,7 +118,7 @@ private async Task StartListeningAsync() } } } - catch (OperationCanceledException ex) + catch (OperationCanceledException) { //empty } diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs index ecb5a9d..33b9b4c 100644 --- a/NetSdrClientApp/Networking/UdpClientWrapper.cs +++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs @@ -38,7 +38,7 @@ public async Task StartListeningAsync() Console.WriteLine($"Received from {result.RemoteEndPoint}"); } } - catch (OperationCanceledException ex) + catch (OperationCanceledException) { //empty } From 1284888cf537d053356fceb1b3c77e5285d239b2 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Thu, 30 Apr 2026 18:15:39 +0300 Subject: [PATCH 12/18] fix: add meaningful message to ArgumentOutOfRangeException --- NetSdrClientApp/Messages/NetSdrMessageHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs index 0d69b4d..b6041d2 100644 --- a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs +++ b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs @@ -111,7 +111,7 @@ public static IEnumerable 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; @@ -158,4 +158,4 @@ private static void TranslateHeader(byte[] header, out MsgTypes type, out int ms } } } -} +} \ No newline at end of file From ad7b4cf8c9cb9f66dfcdbea225e435624d07091a Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sun, 3 May 2026 19:58:40 +0300 Subject: [PATCH 13/18] added new tests --- .../NetSdrClientAppTests.csproj | 3 +- NetSdrClientAppTests/NetSdrClientTests.cs | 59 +++++++++++++++++- .../NetSdrMessageHelperTests.cs | 62 ++++++++++++++++++- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/NetSdrClientAppTests/NetSdrClientAppTests.csproj b/NetSdrClientAppTests/NetSdrClientAppTests.csproj index 3cbc46a..7c2567c 100644 --- a/NetSdrClientAppTests/NetSdrClientAppTests.csproj +++ b/NetSdrClientAppTests/NetSdrClientAppTests.csproj @@ -11,6 +11,7 @@ + @@ -26,4 +27,4 @@ - + \ No newline at end of file diff --git a/NetSdrClientAppTests/NetSdrClientTests.cs b/NetSdrClientAppTests/NetSdrClientTests.cs index ad00c4f..caed179 100644 --- a/NetSdrClientAppTests/NetSdrClientTests.cs +++ b/NetSdrClientAppTests/NetSdrClientTests.cs @@ -115,5 +115,60 @@ public async Task StopIQTest() Assert.That(_client.IQStarted, Is.False); } - //TODO: cover the rest of the NetSdrClient code here -} + [Test] + public async Task StopIQNoConnectionTest() + { + //act + await _client.StopIQAsync(); + + //assert — no message sent, IQStarted stays false + _tcpMock.Verify(tcp => tcp.SendMessageAsync(It.IsAny()), Times.Never); + Assert.That(_client.IQStarted, Is.False); + } + + [Test] + public async Task ChangeFrequencyAsyncTest() + { + //Arrange + await _client.ConnectAsync(); + long frequency = 14_250_000; // 14.25 MHz + int channel = 1; + + //Act + await _client.ChangeFrequencyAsync(frequency, channel); + + //Assert — Connect sends 3 setup messages, ChangeFrequency sends 1 more + _tcpMock.Verify(tcp => tcp.SendMessageAsync(It.IsAny()), Times.Exactly(4)); + } + + [Test] + public async Task ConnectAsync_AlreadyConnected_DoesNotReconnect() + { + //Arrange — connect first + await _client.ConnectAsync(); + + //Act — try connecting again + await _client.ConnectAsync(); + + //Assert — Connect() called only once, not twice + _tcpMock.Verify(tcp => tcp.Connect(), Times.Once); + } + + [Test] + public async Task StartIQ_Then_StopIQ_Toggles_IQStarted() + { + //Arrange + await _client.ConnectAsync(); + + //Act — start then stop + await _client.StartIQAsync(); + Assert.That(_client.IQStarted, Is.True); + + await _client.StopIQAsync(); + Assert.That(_client.IQStarted, Is.False); + + //Assert — UDP listener started once and stopped once + _updMock.Verify(udp => udp.StartListeningAsync(), Times.Once); + _updMock.Verify(udp => udp.StopListening(), Times.Once); + } +} \ No newline at end of file diff --git a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs index b40fff7..a59f3e7 100644 --- a/NetSdrClientAppTests/NetSdrMessageHelperTests.cs +++ b/NetSdrClientAppTests/NetSdrMessageHelperTests.cs @@ -64,6 +64,66 @@ public void GetDataItemMessageTest() Assert.That(parametersBytes.Count(), Is.EqualTo(parametersLength)); } - //TODO: add more NetSdrMessageHelper tests + [Test] + public void TranslateMessage_RoundTrip_ControlItem() + { + //Arrange + var type = NetSdrMessageHelper.MsgTypes.SetControlItem; + var code = NetSdrMessageHelper.ControlItemCodes.ReceiverFrequency; + var parameters = new byte[] { 0x01, 0xA0, 0x86, 0x01, 0x00, 0x00 }; + + //Act + byte[] msg = NetSdrMessageHelper.GetControlItemMessage(type, code, parameters); + bool success = NetSdrMessageHelper.TranslateMessage(msg, out var parsedType, out var parsedCode, out var seqNum, out var parsedBody); + + //Assert + Assert.That(success, Is.True); + Assert.That(parsedType, Is.EqualTo(type)); + Assert.That(parsedCode, Is.EqualTo(code)); + Assert.That(seqNum, Is.EqualTo((ushort)0)); + Assert.That(parsedBody, Is.EqualTo(parameters)); + } + + [Test] + public void GetSamples_Returns_Correct_16bit_Samples() + { + //Arrange — two 16-bit little-endian samples: 0x0102 and 0x0304 + var body = new byte[] { 0x02, 0x01, 0x04, 0x03 }; + + //Act + var samples = NetSdrMessageHelper.GetSamples(16, body).ToList(); + + //Assert + Assert.That(samples.Count, Is.EqualTo(2)); + Assert.That(samples[0], Is.EqualTo(0x0102)); + Assert.That(samples[1], Is.EqualTo(0x0304)); + } + + [Test] + public void GetSamples_Throws_On_Oversized_SampleSize() + { + //Arrange + var body = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; + + //Act & Assert — sampleSize=40 exceeds 32-bit limit + Assert.Throws(() => + { + NetSdrMessageHelper.GetSamples(40, body).ToList(); + }); + } + + [Test] + public void GetSamples_Returns_Correct_24bit_Samples() + { + //Arrange — one 24-bit little-endian sample: bytes 0x03, 0x02, 0x01 → value 0x010203 + var body = new byte[] { 0x03, 0x02, 0x01 }; + + //Act + var samples = NetSdrMessageHelper.GetSamples(24, body).ToList(); + + //Assert + Assert.That(samples.Count, Is.EqualTo(1)); + Assert.That(samples[0], Is.EqualTo(0x010203)); + } } } \ No newline at end of file From acbf2f5d45f0348aa92bdd92cf9c258dfbdea8ef Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sun, 3 May 2026 20:42:24 +0300 Subject: [PATCH 14/18] adjust file --- .github/workflows/sonarcloud.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 7adc316..b2d41f2 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -44,8 +44,6 @@ jobs: runs-on: windows-latest # безпечно для будь-яких .NET проектів steps: - uses: actions/checkout@v4 - env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true with: { fetch-depth: 0 } - uses: actions/setup-dotnet@v4 @@ -65,20 +63,20 @@ jobs: /d:sonar.cpd.cs.minimumTokens=40 ` /d:sonar.cpd.cs.minimumLines=5 ` /d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml ` - /d:sonar.qualitygate.wait=false + /d:sonar.qualitygate.wait=true 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.xml ` + /p:CoverletOutputFormat=opencover + shell: pwsh # 3) END: SonarScanner - name: SonarScanner End run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 73895a67975461586a19471f7bda4d522e6ea05c Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sun, 3 May 2026 21:26:53 +0300 Subject: [PATCH 15/18] adjust file --- .github/workflows/sonarcloud.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index b2d41f2..9575eb4 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -59,7 +59,7 @@ jobs: /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 ` @@ -74,7 +74,7 @@ jobs: run: | dotnet test NetSdrClientAppTests/NetSdrClientAppTests.csproj -c Release --no-build ` /p:CollectCoverage=true ` - /p:CoverletOutput=TestResults/coverage.xml ` + /p:CoverletOutput=TestResults/coverage ` /p:CoverletOutputFormat=opencover shell: pwsh # 3) END: SonarScanner From d882148f53f49052616f89df853dc174951d0cdf Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Sun, 3 May 2026 21:51:42 +0300 Subject: [PATCH 16/18] adjust file --- .github/workflows/sonarcloud.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 9575eb4..354d5e2 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -36,7 +36,7 @@ on: workflow_dispatch: permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results + pull-requests: read # allows SonarCloud to decorate PRs with analysis results. jobs: sonar-check: From 26437bea5eac8c6564471c3252fb7c5bca615771 Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Mon, 4 May 2026 10:35:49 +0300 Subject: [PATCH 17/18] fix: added int cast --- .github/workflows/sonarcloud.yml | 2 +- NetSdrClientApp/Messages/NetSdrMessageHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 354d5e2..9575eb4 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -36,7 +36,7 @@ on: workflow_dispatch: permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results. + pull-requests: read # allows SonarCloud to decorate PRs with analysis results jobs: sonar-check: diff --git a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs index b6041d2..34e6434 100644 --- a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs +++ b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs @@ -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; } From a7e5c26b9531b276f6eadb42973a6c595b1bfb6a Mon Sep 17 00:00:00 2001 From: VlasenkoMykola Date: Mon, 4 May 2026 12:15:25 +0300 Subject: [PATCH 18/18] refactor: remove duplicate code in UdpClientWrapper and TcpClientWrapper --- NetSdrClientApp/Networking/TcpClientWrapper.cs | 10 +--------- NetSdrClientApp/Networking/UdpClientWrapper.cs | 11 +---------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/NetSdrClientApp/Networking/TcpClientWrapper.cs b/NetSdrClientApp/Networking/TcpClientWrapper.cs index a0e2cc7..17c6a2c 100644 --- a/NetSdrClientApp/Networking/TcpClientWrapper.cs +++ b/NetSdrClientApp/Networking/TcpClientWrapper.cs @@ -88,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() diff --git a/NetSdrClientApp/Networking/UdpClientWrapper.cs b/NetSdrClientApp/Networking/UdpClientWrapper.cs index 33b9b4c..a255162 100644 --- a/NetSdrClientApp/Networking/UdpClientWrapper.cs +++ b/NetSdrClientApp/Networking/UdpClientWrapper.cs @@ -64,16 +64,7 @@ public void StopListening() public void Exit() { - try - { - _cts?.Cancel(); - _udpClient?.Close(); - Console.WriteLine("Stopped listening for UDP messages."); - } - catch (Exception ex) - { - Console.WriteLine($"Error while stopping: {ex.Message}"); - } + StopListening(); } public override int GetHashCode()