Skip to content

Commit 3fa7c91

Browse files
committed
Merge branch 'release/v1.0.3'
2 parents 0d3653c + 05dfeb7 commit 3fa7c91

20 files changed

Lines changed: 435 additions & 232 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using NUnit.Framework;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Wissance.Zerial.Common.Utils;
2+
3+
namespace Wissance.Zerial.Common.Tests.Utils
4+
{
5+
public class TestSerialPortNameHelper
6+
{
7+
[TestCase(true, 4, "COM4", true)]
8+
[TestCase(false, 1, "/dev/tty1", false)]
9+
[TestCase(false, 2, "/dev/ttyUSB2", true)]
10+
public void TestBuildSerialDeviceName(bool isWindows, int portNumber, string expectedDeviceName, bool isUsb)
11+
{
12+
if (OperatingSystem.IsWindows() && isWindows || OperatingSystem.IsLinux() && !isWindows)
13+
Assert.That(SerialDeviceHelper.BuildSerialDeviceName(portNumber, isUsb), Is.EqualTo(expectedDeviceName));
14+
}
15+
16+
[TestCase(true, "COM4",4)]
17+
[TestCase(false, "/dev/tty1",1)]
18+
[TestCase(false, "/dev/ttyUSB2",2)]
19+
public void TestGetSerialDeviceNumber(bool isWindows, string deviceName, int expectedPortNumber)
20+
{
21+
if (OperatingSystem.IsWindows() && isWindows || OperatingSystem.IsLinux() && !isWindows)
22+
Assert.That(SerialDeviceHelper.GetSerialDeviceNumber(deviceName), Is.EqualTo(expectedPortNumber));
23+
}
24+
}
25+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/>
13+
<PackageReference Include="NUnit" Version="3.13.3"/>
14+
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
15+
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
16+
<PackageReference Include="coverlet.collector" Version="3.1.2"/>
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Wissance.Zerial.Common\Wissance.Zerial.Common.csproj" />
21+
</ItemGroup>
22+
23+
</Project>

app/Wissance.Zerial/Wissance.Zerial.Common/Rs232/Managers/IRs232DeviceManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ namespace Wissance.Zerial.Common.Rs232.Managers
55
public interface IRs232DeviceManager : IDisposable
66
{
77
Task<bool> OpenAsync(Rs232Settings settings);
8-
Task<bool> CloseAsync(int portNumber);
9-
Task<bool> WriteAsync(int portNumber, byte[] data);
10-
Task<byte[]> ReadAsync(int portNumber);
8+
Task<bool> CloseAsync(string deviceName);
9+
Task<bool> WriteAsync(string deviceName, byte[] data);
10+
Task<byte[]> ReadAsync(string deviceName);
1111
}
1212
}

app/Wissance.Zerial/Wissance.Zerial.Common/Rs232/Managers/MultiDeviceRs232Manager.cs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
using System.Collections.Concurrent;
22
using System.IO.Ports;
3+
using Microsoft.Extensions.Logging;
34
using Wissance.Zerial.Common.Rs232.Settings;
5+
using Wissance.Zerial.Common.Utils;
46

57
namespace Wissance.Zerial.Common.Rs232.Managers
68
{
7-
public class MultiDeviceRs232Manager : IRs232DeviceManager, IDisposable
9+
public class MultiDeviceRs232Manager : IRs232DeviceManager
810
{
9-
public MultiDeviceRs232Manager(SerialDataReceivedEventHandler onDataReceivedHandler /*ILoggerFactory loggerFactory*/)
11+
12+
public MultiDeviceRs232Manager(SerialDataReceivedEventHandler onDataReceivedHandler, ILoggerFactory loggerFactory)
1013
{
1114
_onDataReceivedHandler = onDataReceivedHandler;
15+
_logger = loggerFactory.CreateLogger<MultiDeviceRs232Manager>();
1216
}
1317

1418
public void Dispose()
1519
{
1620
_cancellationSource.Cancel();
17-
foreach (KeyValuePair<int,SerialPort> device in _devices)
21+
foreach (KeyValuePair<string,SerialPort> device in _devices)
1822
{
1923
device.Value.Dispose();
2024
}
@@ -24,16 +28,21 @@ public async Task<bool> OpenAsync(Rs232Settings settings)
2428
{
2529
try
2630
{
31+
// 1. if setting has prepared device name, use it, otherwise we constructing Device name from port number
32+
if (string.IsNullOrEmpty(settings.DeviceName))
33+
return false;
34+
string portName = settings.DeviceName;
35+
2736
SerialPort serialPort = null;
28-
if (_devices.ContainsKey(settings.PortNumber))
29-
serialPort = _devices[settings.PortNumber];
37+
if (_devices.ContainsKey(portName))
38+
serialPort = _devices[portName];
3039

3140
if (serialPort == null)
3241
{
3342
// todo(umv): run timeout parallel to open task
3443
serialPort = new SerialPort()
3544
{
36-
PortName = $"COM{settings.PortNumber}",
45+
PortName = portName,
3746
BaudRate = (int)settings.BaudRate,
3847
StopBits = _stopBitsMapping[settings.StopBits],
3948
DataBits = settings.ByteLength,
@@ -51,11 +60,11 @@ public async Task<bool> OpenAsync(Rs232Settings settings)
5160
// ?
5261
}
5362

54-
_devices[settings.PortNumber] = serialPort;
63+
_devices[portName] = serialPort;
5564

5665
Task openTask = new Task(async _ =>
5766
{
58-
_devices[settings.PortNumber].Open();
67+
_devices[portName].Open();
5968
},
6069
_cancellationSource.Token);
6170
Task delayTask = Task.Delay(DefaultOperationTimeout); // this task starts automatically
@@ -81,13 +90,13 @@ public async Task<bool> OpenAsync(Rs232Settings settings)
8190
}
8291
}
8392

84-
public async Task<bool> CloseAsync(int portNumber)
93+
public async Task<bool> CloseAsync(string deviceName)
8594
{
8695
try
8796
{
88-
if (_devices.ContainsKey(portNumber))
97+
if (_devices.ContainsKey(deviceName))
8998
{
90-
_devices[portNumber].Close();
99+
_devices[deviceName].Close();
91100
}
92101
return true;
93102
}
@@ -98,13 +107,13 @@ public async Task<bool> CloseAsync(int portNumber)
98107
}
99108
}
100109

101-
public async Task<bool> WriteAsync(int portNumber, byte[] data)
110+
public async Task<bool> WriteAsync(string deviceName, byte[] data)
102111
{
103112
try
104113
{
105-
if (_devices.ContainsKey(portNumber))
114+
if (_devices.ContainsKey(deviceName))
106115
{
107-
SerialPort serialDevice = _devices[portNumber];
116+
SerialPort serialDevice = _devices[deviceName];
108117
serialDevice.Write(data, 0, data.Length);
109118
return true;
110119
}
@@ -118,13 +127,13 @@ public async Task<bool> WriteAsync(int portNumber, byte[] data)
118127
}
119128
}
120129

121-
public async Task<byte[]> ReadAsync(int portNumber)
130+
public async Task<byte[]> ReadAsync(string deviceName)
122131
{
123132
try
124133
{
125-
if (_devices.ContainsKey(portNumber))
134+
if (_devices.ContainsKey(deviceName))
126135
{
127-
SerialPort serialDevice = _devices[portNumber];
136+
SerialPort serialDevice = _devices[deviceName];
128137
byte[] buffer = new byte[4096];
129138
int bytesRead = serialDevice.Read(buffer, 0, buffer.Length);
130139
Array.Resize(ref buffer, bytesRead);
@@ -142,7 +151,7 @@ public async Task<byte[]> ReadAsync(int portNumber)
142151

143152
private const int DefaultOperationTimeout = 5000;
144153

145-
private readonly IDictionary<int, SerialPort> _devices = new ConcurrentDictionary<int, SerialPort>();
154+
private readonly IDictionary<string, SerialPort> _devices = new ConcurrentDictionary<string, SerialPort>();
146155

147156
private readonly IDictionary<Rs232StopBits, StopBits> _stopBitsMapping = new Dictionary<Rs232StopBits, StopBits>()
148157
{
@@ -170,5 +179,6 @@ public async Task<byte[]> ReadAsync(int portNumber)
170179

171180
private readonly CancellationTokenSource _cancellationSource = new CancellationTokenSource();
172181
private readonly SerialDataReceivedEventHandler _onDataReceivedHandler;
182+
private ILogger<MultiDeviceRs232Manager> _logger;
173183
}
174184
}

app/Wissance.Zerial/Wissance.Zerial.Common/Rs232/Settings/Rs232Settings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public enum Rs232FlowControl
3535

3636
public class Rs232Settings
3737
{
38-
public int PortNumber { get; set; }
38+
public string DeviceName { get; set; }
3939
public Rs232BaudRate BaudRate { get; set; }
4040
public Rs232StopBits StopBits { get; set; }
4141
public Rs232Parity Parity { get; set; }
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
namespace Wissance.Zerial.Common.Utils
2+
{
3+
public static class SerialDeviceHelper
4+
{
5+
/// <summary>
6+
/// Constructs name depends on Operation System.
7+
/// For Windows
8+
/// </summary>
9+
/// <param name="number">
10+
/// In Windows name is COM4, COM5, therefore number is 4, 5
11+
/// In Linux device name is /dev/tty0 for real COM, /dev/ttyUSB0 for USB-COM, therefore number is 0
12+
/// </param>
13+
/// <param name="isUsbDevice">
14+
/// Don't have any sense for Windows, but have for Linux
15+
/// </param>
16+
public static string BuildSerialDeviceName(int number, bool isUsbDevice = true)
17+
{
18+
if (OperatingSystem.IsWindows())
19+
return string.Format(WindowsComPortNamePattern, number);
20+
if (OperatingSystem.IsLinux())
21+
{
22+
string deviceName = string.Format(isUsbDevice ? LinuxUsbDeviceNamePattern : LinuxComDeviceNamePattern, number);
23+
return string.Format(LinuxSerialDeviceNamePattern, deviceName);
24+
}
25+
26+
return null;
27+
}
28+
29+
public static int GetSerialDeviceNumber(string deviceName)
30+
{
31+
try
32+
{
33+
if (OperatingSystem.IsWindows())
34+
{
35+
int portNumber = 0;
36+
bool portParseResult = int.TryParse(deviceName.Substring(WindowsComDeviceNameSign.Length),
37+
out portNumber);
38+
if (portParseResult)
39+
return portNumber;
40+
return -1;
41+
}
42+
43+
if (OperatingSystem.IsLinux())
44+
{
45+
string deviceNumber = "";
46+
// name is /dev/ttyUSB{N}, where N - number
47+
if (deviceName.Contains(LinuxUsbDeviceNameSign))
48+
{
49+
int nameSignStartIndex = deviceName.IndexOf(LinuxUsbDeviceNameSign);
50+
deviceNumber = deviceName.Substring(nameSignStartIndex + LinuxUsbDeviceNameSign.Length);
51+
}
52+
else
53+
{
54+
if (deviceName.Contains(LinuxComDeviceNameSign))
55+
{
56+
int nameSignStartIndex = deviceName.IndexOf(LinuxComDeviceNameSign);
57+
deviceNumber = deviceName.Substring(nameSignStartIndex + LinuxComDeviceNameSign.Length);
58+
}
59+
}
60+
61+
int portNumber = 0;
62+
bool portParseResult = int.TryParse(deviceNumber, out portNumber);
63+
if (portParseResult)
64+
return portNumber;
65+
return -1;
66+
67+
}
68+
69+
return -2;
70+
}
71+
catch (Exception)
72+
{
73+
return -255;
74+
}
75+
}
76+
77+
private const string WindowsComDeviceNameSign = "COM";
78+
private const string LinuxComDeviceNameSign = "tty";
79+
private const string LinuxUsbDeviceNameSign = "ttyUSB";
80+
private const string WindowsComPortNamePattern = $"{WindowsComDeviceNameSign}{{0}}";
81+
private const string LinuxSerialDeviceNamePattern = "/dev/{0}";
82+
private const string LinuxComDeviceNamePattern = $"{LinuxComDeviceNameSign}{{0}}";
83+
private const string LinuxUsbDeviceNamePattern = $"{LinuxUsbDeviceNameSign}{{0}}";
84+
}
85+
}

app/Wissance.Zerial/Wissance.Zerial.Common/Utils/SerialPortNumberExtractor.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.

app/Wissance.Zerial/Wissance.Zerial.Common/Wissance.Zerial.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
1011
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
1112
</ItemGroup>
1213

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
1+
using System;
2+
using System.IO;
13
using Avalonia;
24
using Avalonia.Controls.ApplicationLifetimes;
35
using Avalonia.Markup.Xaml;
4-
using Wissance.Zerial.Desktop.ViewModels;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.Extensions.DependencyInjection;
58
using Wissance.Zerial.Desktop.Views;
69

7-
namespace Wissance.Zerial.Desktop;
8-
9-
public partial class App : Application
10+
namespace Wissance.Zerial.Desktop
1011
{
11-
public override void Initialize()
12+
public class App : Application
1213
{
13-
AvaloniaXamlLoader.Load(this);
14-
}
14+
static App()
15+
{
16+
IConfigurationBuilder builder = new ConfigurationBuilder()
17+
.SetBasePath(Directory.GetCurrentDirectory())
18+
.AddJsonFile(AppSettingsFile, optional: false, reloadOnChange: true);
1519

16-
public override void OnFrameworkInitializationCompleted()
17-
{
18-
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
20+
Configuration = builder.Build();
21+
22+
ServiceCollection services = new ServiceCollection();
23+
services.AddLogging();
24+
25+
ServiceProvider = services.BuildServiceProvider();
26+
}
27+
28+
public override void Initialize()
29+
{
30+
AvaloniaXamlLoader.Load(this);
31+
}
32+
33+
public override void OnFrameworkInitializationCompleted()
1934
{
20-
desktop.MainWindow = new SplashScreenWindow();
21-
//new MainWindow();
35+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
36+
{
37+
desktop.MainWindow = new SplashScreenWindow();
38+
}
39+
40+
base.OnFrameworkInitializationCompleted();
2241
}
42+
43+
public static IServiceProvider ServiceProvider { get; private set; }
44+
public static IConfiguration Configuration { get; private set; }
2345

24-
base.OnFrameworkInitializationCompleted();
46+
private const string AppSettingsFile = "appsettings.json";
2547
}
2648
}

0 commit comments

Comments
 (0)