Skip to content

Commit 092cd44

Browse files
authored
fix: v1 firmware crashing (#207)
* Fixed a bug where v2 trackers would not be detected * Update to use vars, primary constructors, typos * Fixed a bug where closed ports would crash on probe * Fixed incorrectly formatted serial data * Update firmwareSession null checks * Fixed erroneous device names crashing tracker selector * Convert exception blocks to switch statements
1 parent 9c59b6f commit 092cd44

File tree

5 files changed

+170
-113
lines changed

5 files changed

+170
-113
lines changed

src/Baballonia/Helpers/JsonExtractor.cs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ public JsonDocument ReadUntilValidJson(Func<string> readLineFunction, TimeSpan t
1818
if (DateTime.Now - startTime > timeout)
1919
throw new TimeoutException("Timeout reached");
2020

21-
string content = _buffer.ToString();
21+
var content = _buffer.ToString();
2222

23-
int start = -1;
24-
int braceDepth = 0;
23+
var start = -1;
24+
var braceDepth = 0;
2525

26-
for (int i = _lastScannedIndex; i < content.Length; i++)
26+
for (var i = _lastScannedIndex; i < content.Length; i++)
2727
{
2828
if (content[i] == '{')
2929
{
@@ -34,32 +34,28 @@ public JsonDocument ReadUntilValidJson(Func<string> readLineFunction, TimeSpan t
3434
else if (content[i] == '}')
3535
{
3636
braceDepth--;
37-
if (braceDepth == 0 && start != -1)
38-
{
39-
int lenghh = i - start + 1;
40-
string candidatestr = content.Substring(start, lenghh);
37+
if (braceDepth != 0 || start == -1) continue;
4138

42-
var candidate = TryParseJson(candidatestr);
43-
if(candidate != null)
44-
{
45-
_buffer.Remove(0, i + 1);
46-
_lastScannedIndex = 0;
47-
return candidate;
48-
}
39+
var lenghh = i - start + 1;
40+
var candidatestr = content.Substring(start, lenghh);
4941

50-
}
42+
var candidate = TryParseJson(candidatestr);
43+
if (candidate == null) continue;
44+
45+
_buffer.Remove(0, i + 1);
46+
_lastScannedIndex = 0;
47+
return candidate;
5148
}
5249

5350
}
5451
_lastScannedIndex = Math.Max(0, content.Length - 1);
5552

5653
// Only read if buffer was processed and still no JSON
57-
string line = readLineFunction();
58-
if (!string.IsNullOrWhiteSpace(line))
59-
{
60-
_buffer.Append(line);
61-
_lastScannedIndex = 0;
62-
}
54+
var line = readLineFunction();
55+
if (string.IsNullOrWhiteSpace(line)) continue;
56+
57+
_buffer.Append(line);
58+
_lastScannedIndex = 0;
6359
}
6460
}
6561

src/Baballonia/Helpers/SerialCommandSender.cs

Lines changed: 81 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,78 @@
55
using System.Threading;
66
using Baballonia.Contracts;
77

8-
namespace Baballonia.Helpers
8+
namespace Baballonia.Helpers;
9+
10+
public class SerialCommandSender : ICommandSender
911
{
10-
public class SerialCommandSender : ICommandSender
12+
private const int DefaultBaudRate = 115200; // esptool-rs: Setting baud rate higher than 115,200 can cause issues
13+
private readonly SerialPort _serialPort;
14+
15+
public SerialCommandSender(string port)
1116
{
12-
private const int DefaultBaudRate = 115200; // esptool-rs: Setting baud rate higher than 115,200 can cause issues
13-
private readonly SerialPort _serialPort;
17+
_serialPort = new SerialPort(port, DefaultBaudRate);
18+
19+
// Set serial port parameters
20+
_serialPort.DataBits = 8;
21+
_serialPort.StopBits = StopBits.One;
22+
_serialPort.Parity = Parity.None;
23+
_serialPort.Handshake = Handshake.None;
24+
25+
// Set read/write timeouts
26+
_serialPort.ReadTimeout = 30000;
27+
_serialPort.WriteTimeout = 30000;
28+
_serialPort.Encoding = Encoding.UTF8;
1429

15-
public SerialCommandSender(string port)
30+
int maxRetries = 5;
31+
const int sleepTimeInMs = 50;
32+
while (maxRetries > 0)
1633
{
17-
_serialPort = new SerialPort(port, DefaultBaudRate);
18-
19-
// Set serial port parameters
20-
_serialPort.DataBits = 8;
21-
_serialPort.StopBits = StopBits.One;
22-
_serialPort.Parity = Parity.None;
23-
_serialPort.Handshake = Handshake.None;
24-
25-
// Set read/write timeouts
26-
_serialPort.ReadTimeout = 30000;
27-
_serialPort.WriteTimeout = 30000;
28-
_serialPort.Encoding = Encoding.UTF8;
29-
30-
int maxRetries = 5;
31-
const int sleepTimeInMs = 50;
32-
while (maxRetries > 0)
34+
try
3335
{
34-
try
35-
{
36-
_serialPort.Open();
37-
_serialPort.DiscardInBuffer();
38-
_serialPort.DiscardOutBuffer();
39-
break;
40-
}
41-
catch (IOException)
42-
{
43-
// Timeout
44-
maxRetries = 0;
45-
}
46-
catch (Exception ex)
36+
_serialPort.Open();
37+
_serialPort.DiscardInBuffer();
38+
_serialPort.DiscardOutBuffer();
39+
break;
40+
}
41+
catch (Exception ex)
42+
{
43+
switch (ex)
4744
{
48-
if (ex is not FileNotFoundException && ex is not UnauthorizedAccessException) throw;
49-
maxRetries--;
50-
Thread.Sleep(sleepTimeInMs);
45+
case FileNotFoundException:
46+
case UnauthorizedAccessException:
47+
maxRetries--;
48+
Thread.Sleep(sleepTimeInMs);
49+
break;
50+
51+
case IOException:
52+
case InvalidOperationException:
53+
maxRetries = 0;
54+
break;
55+
56+
default:
57+
throw;
5158
}
5259
}
5360
}
61+
}
5462

55-
public void Dispose()
63+
public void Dispose()
64+
{
65+
try
5666
{
5767
if (_serialPort.IsOpen)
5868
_serialPort.Close();
5969

6070
_serialPort.Dispose();
6171
}
72+
catch (IOException) { }
73+
}
6274

63-
public string ReadLine(TimeSpan timeout)
75+
public string ReadLine(TimeSpan timeout)
76+
{
77+
string data;
78+
try
6479
{
65-
string data;
66-
6780
// Read available data
6881
if (_serialPort.BytesToRead > 0)
6982
{
@@ -74,25 +87,38 @@ public string ReadLine(TimeSpan timeout)
7487
{
7588
return "";
7689
}
90+
}
91+
catch (Exception ex)
92+
{
93+
switch (ex)
94+
{
95+
case IOException:
96+
case InvalidOperationException:
97+
case OperationCanceledException:
98+
return ""; // Port is closed
7799

78-
return data;
100+
default:
101+
throw;
102+
}
79103
}
80104

81-
public void WriteLine(string payload)
82-
{
83-
_serialPort.DiscardInBuffer();
105+
return data;
106+
}
84107

85-
// Convert the payload to bytes
86-
byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);
108+
public void WriteLine(string payload)
109+
{
110+
_serialPort.DiscardInBuffer();
87111

88-
// Write the payload to the serial port
89-
const int chunkSize = 256;
90-
for (int i = 0; i < payloadBytes.Length; i += chunkSize)
91-
{
92-
int length = Math.Min(chunkSize, payloadBytes.Length - i);
93-
_serialPort.Write(payloadBytes, i, length);
94-
Thread.Sleep(50); // Small pause between chunks
95-
}
112+
// Convert the payload to bytes
113+
byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);
114+
115+
// Write the payload to the serial port
116+
const int chunkSize = 256;
117+
for (int i = 0; i < payloadBytes.Length; i += chunkSize)
118+
{
119+
int length = Math.Min(chunkSize, payloadBytes.Length - i);
120+
_serialPort.Write(payloadBytes, i, length);
121+
Thread.Sleep(50); // Small pause between chunks
96122
}
97123
}
98124
}

src/Baballonia/Models/FirmwareSession.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private void SendCommand(string command)
7575
}
7676
}
7777

78-
public FirmwareResponses.Heartbeat? WaitForHeartbeat()
78+
private FirmwareResponses.Heartbeat? WaitForHeartbeat()
7979
{
8080
return WaitForHeartbeat(new TimeSpan(5000));
8181
}
@@ -121,11 +121,21 @@ public FirmwareResponse<JsonDocument> SendCommand(IFirmwareRequest request, Time
121121
if (response == null)
122122
return FirmwareResponse<JsonDocument>.Failure("Wtf?");
123123

124-
var result = JsonSerializer.Deserialize<FirmwareResponses.GenericResult>(response.results.First());
125-
126-
return FirmwareResponse<JsonDocument>.Success(JsonSerializer.Deserialize<JsonDocument>(result!.result)!);
124+
try
125+
{
126+
// Attempt to extract inner content
127+
var result = JsonSerializer.Deserialize<FirmwareResponses.GenericResult>(response.results.First());
128+
return FirmwareResponse<JsonDocument>.Success(
129+
JsonSerializer.Deserialize<JsonDocument>(result!.result)!);
130+
}
131+
catch (JsonException)
132+
{
133+
// Attempt to extract outer content
134+
return FirmwareResponse<JsonDocument>.Success(
135+
JsonSerializer.Deserialize<JsonDocument>(response.results.First())!);
136+
}
127137
}
128-
catch (TimeoutException ex)
138+
catch (TimeoutException)
129139
{
130140
return FirmwareResponse<JsonDocument>.Failure("Timeout reached");
131141
}

src/Baballonia/Models/FirmwareSessionFactory.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,9 @@
99

1010
namespace Baballonia.Models;
1111

12-
public class FirmwareSessionFactory
12+
public class FirmwareSessionFactory(ILoggerFactory loggerFactory, ICommandSenderFactory commandSenderFactory)
1313
{
14-
private readonly ILoggerFactory _loggerFactory;
15-
private readonly ILogger<FirmwareSessionFactory> _logger;
16-
private readonly ICommandSenderFactory _commandSenderFactory;
17-
18-
public FirmwareSessionFactory(ILoggerFactory loggerFactory, ICommandSenderFactory commandSenderFactory)
19-
{
20-
_loggerFactory = loggerFactory;
21-
_commandSenderFactory = commandSenderFactory;
22-
_logger = loggerFactory.CreateLogger<FirmwareSessionFactory>();
23-
}
14+
private readonly ILogger<FirmwareSessionFactory> _logger = loggerFactory.CreateLogger<FirmwareSessionFactory>();
2415

2516
private string[] FindAvailableSerialPorts()
2617
{
@@ -86,8 +77,8 @@ public async Task<List<PortToSessionMapping>> TryOpenAllSessionsAsync()
8677
private FirmwareSessionV2? TryOpenV2Session(string port)
8778
{
8879
_logger.LogInformation($"Attempting to open V2 session for {port}");
89-
var sender = _commandSenderFactory.Create(CommandSenderType.Serial, port);
90-
var sessionV2 = new FirmwareSessionV2(sender, _loggerFactory.CreateLogger<FirmwareSessionV2>());
80+
var sender = commandSenderFactory.Create(CommandSenderType.Serial, port);
81+
var sessionV2 = new FirmwareSessionV2(sender, loggerFactory.CreateLogger<FirmwareSessionV2>());
9182
var response = sessionV2.SendCommand(new FirmwareRequests.GetWhoAmIRequest(), TimeSpan.FromSeconds(3));
9283
if (!response.IsSuccess)
9384
{
@@ -111,8 +102,8 @@ public async Task<List<PortToSessionMapping>> TryOpenAllSessionsAsync()
111102
private FirmwareSessionV1? TryOpenV1Session(string port)
112103
{
113104
_logger.LogInformation($"Attempting to open V1 session for {port}");
114-
var sender = _commandSenderFactory.Create(CommandSenderType.Serial, port);
115-
var sessionV1 = new FirmwareSessionV1(sender, _loggerFactory.CreateLogger<FirmwareSessionV1>());
105+
var sender = commandSenderFactory.Create(CommandSenderType.Serial, port);
106+
var sessionV1 = new FirmwareSessionV1(sender, loggerFactory.CreateLogger<FirmwareSessionV1>());
116107
var heartbeat = sessionV1.WaitForHeartbeat(TimeSpan.FromSeconds(3));
117108
if (heartbeat == null)
118109
{

0 commit comments

Comments
 (0)