diff --git a/Program.cs b/Program.cs index a258a89..7184a19 100644 --- a/Program.cs +++ b/Program.cs @@ -26,24 +26,29 @@ { try { - var reading = GetLatestReading(Port, Location, led: true); + var reading = GetLatestReadingWithRetry(Port, Location, led: true); + if (reading is null) + { + return 2; + } + + var json = JsonSerializer.Serialize(reading, SensorReadingsJsonContext.Default.Jciebu2); if (HasArg(args, "--dry")) { - var json = JsonSerializer.Serialize(reading, SensorReadingsJsonContext.Default.Jciebu2); Console.WriteLine(json); return 0; } - var (statusCode, body) = PostODataAsync(reading).GetAwaiter().GetResult(); + var (statusCode, _) = PostODataAsync(json).GetAwaiter().GetResult(); if (statusCode >= 200 && statusCode < 300) { - Console.WriteLine("ok"); + Console.WriteLine(json); return 0; } WritePayloadToVarLog(reading); - Console.Error.WriteLine($"http {statusCode}: {body}"); + Console.Error.WriteLine($"http {statusCode}"); return 2; } catch (Exception ex) @@ -54,18 +59,47 @@ } /// + /// 从传感器读取数据;如果响应太短,随机等待 10 到 30 秒后重试,最多重试 3 次。 + /// + /// 串口设备路径。 + /// 位置标识,若为空则使用默认值。 + /// 是否开启读数时的指示灯。 + /// 解析后的传感器数据。 + private static Jciebu2? GetLatestReadingWithRetry(string port, string? location, bool led) + { + const int maxRetries = 3; + + for (var attempt = 0; ; attempt++) + { + if (TryGetLatestReading(port, location, led, out var reading, out var actualBytes)) + { + return reading; + } + + if (attempt >= maxRetries) + { + Console.Error.WriteLine($"error: response too short: {actualBytes} bytes (need >= 56)"); + return null; + } + + var delaySeconds = Random.Shared.Next(10, 31); + Console.Error.WriteLine($"error: response too short: {actualBytes} bytes (need >= 56); retrying in {delaySeconds}s"); + Thread.Sleep(TimeSpan.FromSeconds(delaySeconds)); + } + } + + /// /// 将传感器数据发送到配置的 OData 端点。 /// - /// 要发送的传感器数据。 + /// 要发送的 JSON payload。 /// HTTP 状态码与响应内容。 - private static async Task<(int StatusCode, string Body)> PostODataAsync(Jciebu2 payload) + private static async Task<(int StatusCode, string Body)> PostODataAsync(string json) { using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(TimeoutSeconds) }; - var json = JsonSerializer.Serialize(payload, SensorReadingsJsonContext.Default.Jciebu2); using var content = new StringContent(json, Encoding.UTF8, "application/json"); if (!string.IsNullOrEmpty(ODataToken)) @@ -107,9 +141,14 @@ /// 串口设备路径。 /// 位置标识,若为空则使用默认值。 /// 是否开启读数时的指示灯。 - /// 解析后的传感器数据。 - private static Jciebu2 GetLatestReading(string port, string? location, bool led) + /// 解析成功时的传感器数据。 + /// 响应太短时的实际字节数。 + /// 解析成功则为 true;响应太短则为 false。 + private static bool TryGetLatestReading(string port, string? location, bool led, out Jciebu2 reading, out int actualBytes) { + reading = null!; + actualBytes = 0; + using var serial = new SerialPort(port, 115200, Parity.None, 8, StopBits.One) { ReadTimeout = 1000, @@ -135,14 +174,17 @@ WriteWithCrc(serial, latest); Thread.Sleep(100); var data = ReadAvailable(serial, TimeSpan.FromSeconds(1)); - var reading = ParseLatestDataLong(data); + if (!TryParseLatestDataLong(data, out reading, out actualBytes)) + { + return false; + } var now = DateTimeOffset.UtcNow; var trimmed = now.AddTicks(-(now.Ticks % TimeSpan.TicksPerSecond)); reading.Ts = trimmed.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.InvariantCulture); reading.Location = string.IsNullOrEmpty(location) ? Location : location; - return reading; + return true; } finally { @@ -232,16 +274,20 @@ /// 解析“latest data long”响应为传感器数据。 /// /// 设备返回的原始数据。 - /// 解析后的传感器数据。 - private static Jciebu2 ParseLatestDataLong(byte[] data) + /// 解析成功时的传感器数据。 + /// 响应太短时的实际字节数。 + /// 解析成功则为 true;响应太短则为 false。 + private static bool TryParseLatestDataLong(byte[] data, out Jciebu2 reading, out int actualBytes) { + reading = null!; + actualBytes = data.Length; + if (data.Length < 56) { - Console.Error.WriteLine($"error: response too short: {data.Length} bytes (need >= 56)"); - Environment.Exit(2); + return false; } - return new Jciebu2 + reading = new Jciebu2 { TemperatureC = S16Le(data, 8) / 100.0, HumidityRh = U16Le(data, 10) / 100.0, @@ -269,6 +315,8 @@ PgaFlag = data[54], SeismicIntensityFlag = data[55] }; + + return true; } ///