新增车辆表及OTA权限与逻辑优化
新增车辆任务与报警表,完善实体与DbContext配置。细化OTA权限校验,增强回传会话IP安全。优化OTA上传与设置面板,调度器支持重启恢复。报警采集逻辑支持历史分段。
This commit is contained in:
@@ -16,6 +16,35 @@ public sealed class OtaJobRunner
|
||||
_wd = wd;
|
||||
_vehicles = vehicles;
|
||||
_log = log;
|
||||
RecoverInterruptedJobs();
|
||||
}
|
||||
|
||||
/// <summary>进程重启后把落盘中非终态 job 标为 failed,避免 UI 永久显示 running。</summary>
|
||||
private void RecoverInterruptedJobs()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var job in _store.ListJobs(500))
|
||||
{
|
||||
if (job.Status is not ("running" or "pending")) continue;
|
||||
job.Status = "failed";
|
||||
job.Message = string.IsNullOrWhiteSpace(job.Message)
|
||||
? "进程重启,任务中断"
|
||||
: job.Message;
|
||||
job.FinishedAt = DateTimeOffset.UtcNow;
|
||||
foreach (var step in job.Steps.Where(s => s.Status is "pending" or "running"))
|
||||
{
|
||||
step.Status = "failed";
|
||||
step.Error ??= "进程重启,任务中断";
|
||||
}
|
||||
_store.SaveJob(job);
|
||||
_log.LogWarning("OTA job {Id} marked failed after process restart", job.Id);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogWarning(ex, "OTA interrupted job recovery failed");
|
||||
}
|
||||
}
|
||||
|
||||
public OtaJob EnqueueSync(CreateSyncJobRequest req, string? user)
|
||||
|
||||
@@ -6,9 +6,11 @@ public sealed class OtaSettings
|
||||
public int MaxCar { get; set; } = 2;
|
||||
public bool LatencyEnabled { get; set; }
|
||||
public int RttThresholdMs { get; set; } = 200;
|
||||
/// <summary>skip | confirm</summary>
|
||||
/// <summary>skip | allow(历史值 confirm 视为 allow)</summary>
|
||||
public string OverThreshold { get; set; } = "skip";
|
||||
/// <summary>保留字段:备份尚未实现,仅反序列化兼容。</summary>
|
||||
public int BackupPeriodMinutes { get; set; } = 60;
|
||||
/// <summary>保留字段:备份尚未实现,仅反序列化兼容。</summary>
|
||||
public bool BackupExe { get; set; }
|
||||
public string? NewVersionName { get; set; }
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ public sealed class OtaStore
|
||||
private string? _lastPullId;
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, string> _pullByIp =
|
||||
new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, DateTime> _pullLastReceiveUtc =
|
||||
new(StringComparer.OrdinalIgnoreCase);
|
||||
private long _jobSeq;
|
||||
|
||||
public OtaStore(IWebHostEnvironment env, IOptions<OtaOptions> options, ILogger<OtaStore> log)
|
||||
@@ -144,29 +146,44 @@ public sealed class OtaStore
|
||||
lock (_gate)
|
||||
{
|
||||
var ip = NormalizeIp(sourceIp);
|
||||
// 必须匹配会话 IP;禁止无 IP 时回退到最近一次拉包(可被伪造/误写)。
|
||||
if (ip != null && _pullByIp.TryGetValue(ip, out var byIp))
|
||||
{
|
||||
id = byIp;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ip == null && _lastPullId != null)
|
||||
{
|
||||
id = _lastPullId;
|
||||
return true;
|
||||
}
|
||||
|
||||
id = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void NotePullReceive(string? sourceIp)
|
||||
{
|
||||
var ip = NormalizeIp(sourceIp);
|
||||
if (ip == null) return;
|
||||
_pullLastReceiveUtc[ip] = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public bool TryGetLastReceiveAt(string? sourceIp, out DateTime utc)
|
||||
{
|
||||
var ip = NormalizeIp(sourceIp);
|
||||
if (ip != null && _pullLastReceiveUtc.TryGetValue(ip, out utc))
|
||||
return true;
|
||||
utc = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearActivePull(string? sourceIp = null)
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
var ip = NormalizeIp(sourceIp);
|
||||
if (ip != null) _pullByIp.TryRemove(ip, out _);
|
||||
if (ip != null)
|
||||
{
|
||||
_pullByIp.TryRemove(ip, out _);
|
||||
_pullLastReceiveUtc.TryRemove(ip, out _);
|
||||
}
|
||||
if (_pullByIp.IsEmpty) _lastPullId = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user