Merge #574/#576/#577/#584: AB forms preserve unknown keys + write factory ms keys, /raw device-name + prerender fixes, CLI read prints Bad status, docs dot-form
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
@page "/raw"
|
||||
@attribute [Authorize(Policy = AdminUiPolicies.ConfigEditor)]
|
||||
@rendermode RenderMode.InteractiveServer
|
||||
@* prerender: false (#577 "first tree click ignored"): with prerender on, the server-rendered tree is shown
|
||||
before the circuit attaches, so a click on its (handler-less) twists is silently dropped, and the
|
||||
component then re-runs OnInitializedAsync and swaps in a second, freshly loaded node set. The tree
|
||||
is the only content here and needs the circuit anyway, so render it once, interactively. *@
|
||||
@rendermode @(new InteractiveServerRenderMode(prerender: false))
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Uns
|
||||
@using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Raw
|
||||
@inject IRawTreeService Svc
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="row g-3 mb-2">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Device name</label>
|
||||
<input class="form-control form-control-sm mono" @bind="_name" placeholder="Device1" />
|
||||
<input class="form-control form-control-sm mono" @bind="_name" placeholder="e.g. plc1" />
|
||||
<div class="form-text">A RawPath segment — letters, digits, dash, underscore.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@@ -145,7 +145,7 @@
|
||||
{
|
||||
_resolvedDriverInstanceId = DriverInstanceId;
|
||||
_driverType = DriverType ?? "";
|
||||
_name = "Device1";
|
||||
_name = ""; // placeholder only: a pre-filled value made typing append to it (#577); the name is validated on save
|
||||
_enabled = true;
|
||||
_deviceConfigJson = "{}";
|
||||
_rowVersion = [];
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers;
|
||||
|
||||
/// <summary>
|
||||
/// Preserve-unknown save for the typed driver forms (#576). A form deserialises the stored DriverConfig
|
||||
/// into a typed options object and re-serialises it on save, which erases every key the type does not
|
||||
/// model (a newer option, a hand-edited key, <c>rawTags</c>, a device's extra keys…). This overlays only
|
||||
/// the paths the form OWNS onto the ORIGINAL JSON object: an owned path is replaced by the form's value,
|
||||
/// or removed when the form emits nothing/null ("Default"); every other key in the original survives.
|
||||
/// </summary>
|
||||
internal static class DriverConfigJsonOverlay
|
||||
{
|
||||
/// <summary>Overlays the <paramref name="ownedPaths"/> of <paramref name="typedJson"/> onto <paramref name="originalJson"/>.</summary>
|
||||
/// <param name="originalJson">The stored config as loaded; blank or non-object input yields <paramref name="typedJson"/> unchanged.</param>
|
||||
/// <param name="typedJson">The form's own serialised options.</param>
|
||||
/// <param name="ownedPaths">Dot-separated, case-insensitive property paths the form edits (e.g. <c>transport</c>, <c>probe.enabled</c>).</param>
|
||||
/// <param name="timeSpanToMs">
|
||||
/// (TimeSpan-string path, integer-millisecond path) pairs. The typed options serialise durations as
|
||||
/// <c>"00:00:05"</c> but the driver factories bind <c>*Ms</c> integers (#576); each pair is rewritten
|
||||
/// in the typed output (and the legacy TimeSpan path becomes owned, so a stale one is removed).
|
||||
/// </param>
|
||||
public static string Apply(string? originalJson, string typedJson, IEnumerable<string> ownedPaths,
|
||||
IReadOnlyList<(string TimeSpanPath, string MsPath)>? timeSpanToMs = null)
|
||||
{
|
||||
if (JsonNode.Parse(typedJson) is not JsonObject typed) return typedJson;
|
||||
var owned = ownedPaths.ToList();
|
||||
foreach (var (tsPath, msPath) in timeSpanToMs ?? [])
|
||||
{
|
||||
owned.Add(tsPath);
|
||||
owned.Add(msPath);
|
||||
if (Get(typed, tsPath) is JsonValue v && v.TryGetValue<string>(out var text)
|
||||
&& TimeSpan.TryParse(text, System.Globalization.CultureInfo.InvariantCulture, out var ts))
|
||||
{
|
||||
RemovePath(typed, tsPath);
|
||||
Set(typed, msPath, (int)ts.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject? original = null;
|
||||
if (!string.IsNullOrWhiteSpace(originalJson))
|
||||
{
|
||||
try { original = JsonNode.Parse(originalJson) as JsonObject; }
|
||||
catch (JsonException) { /* unusable original: fall through to the typed output */ }
|
||||
}
|
||||
if (original is null) return typed.ToJsonString();
|
||||
|
||||
foreach (var path in owned)
|
||||
{
|
||||
var segments = path.Split('.');
|
||||
JsonObject? from = typed;
|
||||
JsonObject? to = original;
|
||||
for (var i = 0; i < segments.Length - 1 && to is not null; i++)
|
||||
{
|
||||
from = from is null ? null : Find(from, segments[i]) as JsonObject;
|
||||
var next = Find(to, segments[i]) as JsonObject;
|
||||
if (next is null && from is not null)
|
||||
{
|
||||
next = [];
|
||||
to[segments[i]] = next;
|
||||
}
|
||||
to = next;
|
||||
}
|
||||
if (to is null) continue;
|
||||
|
||||
var leaf = segments[^1];
|
||||
var value = from is null ? null : Find(from, leaf);
|
||||
Remove(to, leaf);
|
||||
if (value is not null) to[leaf] = value.DeepClone();
|
||||
}
|
||||
return original.ToJsonString();
|
||||
}
|
||||
|
||||
private static JsonNode? Get(JsonObject root, string path)
|
||||
{
|
||||
JsonNode? cur = root;
|
||||
foreach (var seg in path.Split('.'))
|
||||
cur = cur is JsonObject o ? Find(o, seg) : null;
|
||||
return cur;
|
||||
}
|
||||
|
||||
private static void RemovePath(JsonObject root, string path)
|
||||
{
|
||||
var segs = path.Split('.');
|
||||
JsonObject? cur = root;
|
||||
for (var i = 0; i < segs.Length - 1 && cur is not null; i++)
|
||||
cur = Find(cur, segs[i]) as JsonObject;
|
||||
if (cur is not null) Remove(cur, segs[^1]);
|
||||
}
|
||||
|
||||
private static void Set(JsonObject root, string path, JsonNode value)
|
||||
{
|
||||
var segs = path.Split('.');
|
||||
var cur = root;
|
||||
for (var i = 0; i < segs.Length - 1; i++)
|
||||
{
|
||||
if (Find(cur, segs[i]) is not JsonObject next) { next = []; cur[segs[i]] = next; }
|
||||
cur = next;
|
||||
}
|
||||
cur[segs[^1]] = value;
|
||||
}
|
||||
|
||||
private static JsonNode? Find(JsonObject o, string key)
|
||||
{
|
||||
foreach (var kv in o)
|
||||
if (string.Equals(kv.Key, key, StringComparison.OrdinalIgnoreCase)) return kv.Value;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void Remove(JsonObject o, string key)
|
||||
{
|
||||
var hit = o.Select(kv => kv.Key).FirstOrDefault(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase));
|
||||
if (hit is not null) o.Remove(hit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers;
|
||||
|
||||
/// <summary>
|
||||
/// Shared millisecond helpers for the AB forms (#576): the driver factories bind <c>*Ms</c> integers
|
||||
/// while the forms edit whole seconds, so a sub-second stored value must survive an edit of an
|
||||
/// unrelated field.
|
||||
/// </summary>
|
||||
internal static class DriverConfigMs
|
||||
{
|
||||
/// <summary>Reads an integer-millisecond key at <paramref name="path"/> from <paramref name="json"/>, or null.</summary>
|
||||
public static int? Read(string? json, string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return null;
|
||||
try
|
||||
{
|
||||
JsonNode? cur = JsonNode.Parse(json);
|
||||
foreach (var seg in path.Split('.'))
|
||||
{
|
||||
if (cur is not JsonObject o) return null;
|
||||
cur = o.FirstOrDefault(kv => string.Equals(kv.Key, seg, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
}
|
||||
return cur is JsonValue v && v.TryGetValue<int>(out var ms) ? ms : null;
|
||||
}
|
||||
catch (JsonException) { return null; }
|
||||
}
|
||||
|
||||
/// <summary>Whole seconds for display (truncating, like the pre-#576 forms).</summary>
|
||||
public static int ToSeconds(TimeSpan t) => (int)t.TotalSeconds;
|
||||
|
||||
/// <summary>
|
||||
/// The duration to save: the loaded value when the operator left the seconds field unchanged
|
||||
/// (keeps 500 ms from collapsing to 0 s), else the edited whole seconds.
|
||||
/// </summary>
|
||||
public static TimeSpan Save(int seconds, TimeSpan? loaded)
|
||||
=> loaded is { } l && ToSeconds(l) == seconds ? l : TimeSpan.FromSeconds(seconds);
|
||||
}
|
||||
+66
-8
@@ -18,6 +18,11 @@
|
||||
<InputNumber @bind-Value="_form.TimeoutSeconds" @bind-Value:after="EmitAsync" class="form-control form-control-sm" />
|
||||
<div class="form-text">Default libplctag call timeout. Default 2 s.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Max concurrent reads</label>
|
||||
<InputNumber @bind-Value="_form.MaxConcurrentReads" @bind-Value:after="EmitAsync" class="form-control form-control-sm" />
|
||||
<div class="form-text">Read work items in flight at once. Default 8; 1 = sequential.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-check form-switch mt-4">
|
||||
<InputCheckbox @bind-Value="_form.EnableControllerBrowse" @bind-Value:after="EmitAsync" class="form-check-input" id="enableBrowse" />
|
||||
@@ -124,6 +129,27 @@
|
||||
|
||||
private FormModel _form = new();
|
||||
private string? _lastParsed;
|
||||
// The config exactly as loaded; the save overlays the form-owned keys onto it so keys this form does not
|
||||
// model (unknown/newer options, rawTags, per-device extras) survive an unrelated edit (#576).
|
||||
private string? _originalJson;
|
||||
|
||||
private static bool IsBlankConfig(string? json)
|
||||
=> string.IsNullOrWhiteSpace(json) || json.AsSpan().Trim().SequenceEqual("{}");
|
||||
|
||||
/// <summary>Top-level/nested JSON paths this form edits; everything else in the stored config is preserved.</summary>
|
||||
internal static readonly string[] OwnedPaths =
|
||||
[
|
||||
"transport", "enableControllerBrowse", "enableDeclarationOnlyUdtGrouping",
|
||||
"enableAlarmProjection", "maxConcurrentReads", "probeTimeoutSeconds",
|
||||
"probe.enabled", "probe.probeTagPath",
|
||||
];
|
||||
|
||||
/// <summary>The factory binds integer-millisecond keys, not the TimeSpan strings the typed options serialise (#576).</summary>
|
||||
internal static readonly (string TimeSpanPath, string MsPath)[] TimeSpanToMs =
|
||||
[
|
||||
("timeout", "timeoutMs"), ("alarmPollInterval", "alarmPollIntervalMs"),
|
||||
("probe.interval", "probe.intervalMs"), ("probe.timeout", "probe.timeoutMs"),
|
||||
];
|
||||
|
||||
// Preserve the driver's existing Devices across a load→save (the device form authors the real endpoints;
|
||||
// the merge reconstructs the Devices array from the device rows at deploy/probe time).
|
||||
@@ -135,7 +161,12 @@
|
||||
if (!string.Equals(_lastParsed, DriverConfigJson, StringComparison.Ordinal))
|
||||
{
|
||||
var opts = TryDeserialize(DriverConfigJson) ?? new AbCipDriverOptions();
|
||||
_form = FormModel.FromOptions(opts);
|
||||
_form = FormModel.FromJson(DriverConfigJson, opts);
|
||||
// #577: a brand-new driver (nothing authored yet) must not default the probe ON with no tag path —
|
||||
// that logs an operator warning per driver and runs no probe. An existing config keeps whatever
|
||||
// it stores (an absent "probe" key still means "enabled" to the driver, so don't flip it on edit).
|
||||
if (IsBlankConfig(DriverConfigJson)) _form.ProbeEnabled = false;
|
||||
_originalJson = DriverConfigJson;
|
||||
_preservedDevices = opts.Devices;
|
||||
_lastParsed = DriverConfigJson;
|
||||
}
|
||||
@@ -145,16 +176,17 @@
|
||||
/// Devices array forward (the device form authors the real endpoints; the merge reconstructs the array
|
||||
/// from the device rows at deploy/probe time).</summary>
|
||||
public string GetConfigJson()
|
||||
=> Serialize(_form.ToOptions(_preservedDevices));
|
||||
=> Serialize(_form.ToOptions(_preservedDevices), _originalJson);
|
||||
|
||||
/// <summary>Serializes options with the form's JSON settings (camelCase, enums as strings).</summary>
|
||||
internal static string Serialize(AbCipDriverOptions options)
|
||||
=> JsonSerializer.Serialize(options, _jsonOpts);
|
||||
internal static string Serialize(AbCipDriverOptions options, string? originalJson = null)
|
||||
=> DriverConfigJsonOverlay.Apply(originalJson, JsonSerializer.Serialize(options, _jsonOpts), OwnedPaths, TimeSpanToMs);
|
||||
|
||||
private async Task EmitAsync()
|
||||
{
|
||||
var json = GetConfigJson();
|
||||
_lastParsed = json;
|
||||
_originalJson = json;
|
||||
await DriverConfigJsonChanged.InvokeAsync(json);
|
||||
}
|
||||
|
||||
@@ -180,6 +212,7 @@
|
||||
// PLC-5 / LogixPccc exclusion still applies). Never re-emit a concrete default here — that would pin
|
||||
// every edited driver to it.
|
||||
public AbCipTransport? Transport { get; set; }
|
||||
public int MaxConcurrentReads { get; set; } = 8;
|
||||
public bool EnableControllerBrowse { get; set; } = false;
|
||||
public bool EnableDeclarationOnlyUdtGrouping { get; set; } = false;
|
||||
|
||||
@@ -196,10 +229,34 @@
|
||||
// Admin UI probe timeout
|
||||
public int AdminProbeTimeoutSeconds { get; set; } = 5;
|
||||
|
||||
// The loaded durations, so an unchanged seconds field saves the exact stored value (sub-second safe).
|
||||
private TimeSpan? _loadedTimeout, _loadedAlarmPoll, _loadedProbeInterval, _loadedProbeTimeout;
|
||||
|
||||
/// <summary>Loads from the stored JSON: the factory's <c>*Ms</c> keys win; a legacy TimeSpan-string key is the fallback.</summary>
|
||||
public static FormModel FromJson(string? json, AbCipDriverOptions o)
|
||||
{
|
||||
static TimeSpan? Ms(string? j, string path) => DriverConfigMs.Read(j, path) is int ms ? TimeSpan.FromMilliseconds(ms) : null;
|
||||
var f = FromOptions(o);
|
||||
f._loadedTimeout = Ms(json, "timeoutMs") ?? f._loadedTimeout;
|
||||
f._loadedAlarmPoll = Ms(json, "alarmPollIntervalMs") ?? f._loadedAlarmPoll;
|
||||
f._loadedProbeInterval = Ms(json, "probe.intervalMs") ?? f._loadedProbeInterval;
|
||||
f._loadedProbeTimeout = Ms(json, "probe.timeoutMs") ?? f._loadedProbeTimeout;
|
||||
f.TimeoutSeconds = DriverConfigMs.ToSeconds(f._loadedTimeout ?? o.Timeout);
|
||||
f.AlarmPollIntervalSeconds = DriverConfigMs.ToSeconds(f._loadedAlarmPoll ?? o.AlarmPollInterval);
|
||||
f.ProbeIntervalSeconds = DriverConfigMs.ToSeconds(f._loadedProbeInterval ?? o.Probe.Interval);
|
||||
f.ProbeTimeoutSeconds = DriverConfigMs.ToSeconds(f._loadedProbeTimeout ?? o.Probe.Timeout);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static FormModel FromOptions(AbCipDriverOptions o) => new()
|
||||
{
|
||||
_loadedTimeout = o.Timeout,
|
||||
_loadedAlarmPoll = o.AlarmPollInterval,
|
||||
_loadedProbeInterval = o.Probe.Interval,
|
||||
_loadedProbeTimeout = o.Probe.Timeout,
|
||||
TimeoutSeconds = (int)o.Timeout.TotalSeconds,
|
||||
Transport = o.Transport,
|
||||
MaxConcurrentReads = o.MaxConcurrentReads,
|
||||
EnableControllerBrowse = o.EnableControllerBrowse,
|
||||
EnableDeclarationOnlyUdtGrouping = o.EnableDeclarationOnlyUdtGrouping,
|
||||
EnableAlarmProjection = o.EnableAlarmProjection,
|
||||
@@ -218,15 +275,16 @@
|
||||
Probe = new AbCipProbeOptions
|
||||
{
|
||||
Enabled = ProbeEnabled,
|
||||
Interval = TimeSpan.FromSeconds(ProbeIntervalSeconds),
|
||||
Timeout = TimeSpan.FromSeconds(ProbeTimeoutSeconds),
|
||||
Interval = DriverConfigMs.Save(ProbeIntervalSeconds, _loadedProbeInterval),
|
||||
Timeout = DriverConfigMs.Save(ProbeTimeoutSeconds, _loadedProbeTimeout),
|
||||
ProbeTagPath = string.IsNullOrWhiteSpace(ProbeTagPath) ? null : ProbeTagPath,
|
||||
},
|
||||
Timeout = TimeSpan.FromSeconds(TimeoutSeconds),
|
||||
Timeout = DriverConfigMs.Save(TimeoutSeconds, _loadedTimeout),
|
||||
Transport = Transport,
|
||||
MaxConcurrentReads = MaxConcurrentReads,
|
||||
EnableControllerBrowse = EnableControllerBrowse,
|
||||
EnableAlarmProjection = EnableAlarmProjection,
|
||||
AlarmPollInterval = TimeSpan.FromSeconds(AlarmPollIntervalSeconds),
|
||||
AlarmPollInterval = DriverConfigMs.Save(AlarmPollIntervalSeconds, _loadedAlarmPoll),
|
||||
EnableDeclarationOnlyUdtGrouping = EnableDeclarationOnlyUdtGrouping,
|
||||
ProbeTimeoutSeconds = AdminProbeTimeoutSeconds,
|
||||
};
|
||||
|
||||
+44
-7
@@ -90,6 +90,22 @@
|
||||
|
||||
private FormModel _form = new();
|
||||
private string? _lastParsed;
|
||||
// The config exactly as loaded; the save overlays the form-owned keys onto it so keys this form does not
|
||||
// model (unknown/newer options, rawTags, per-device extras) survive an unrelated edit (#576).
|
||||
private string? _originalJson;
|
||||
|
||||
/// <summary>JSON paths this form edits; everything else in the stored config is preserved.</summary>
|
||||
internal static readonly string[] OwnedPaths =
|
||||
[
|
||||
"transport", "probeTimeoutSeconds",
|
||||
"probe.enabled", "probe.probeAddress",
|
||||
];
|
||||
|
||||
/// <summary>The factory binds integer-millisecond keys, not the TimeSpan strings the typed options serialise (#576).</summary>
|
||||
internal static readonly (string TimeSpanPath, string MsPath)[] TimeSpanToMs =
|
||||
[
|
||||
("timeout", "timeoutMs"), ("probe.interval", "probe.intervalMs"), ("probe.timeout", "probe.timeoutMs"),
|
||||
];
|
||||
|
||||
// Devices are separate v3 entities — this form doesn't edit them, but preserves the driver's existing
|
||||
// Devices array verbatim across a load→save (the merge rebuilds it at deploy/probe time).
|
||||
@@ -101,7 +117,8 @@
|
||||
if (!string.Equals(_lastParsed, DriverConfigJson, StringComparison.Ordinal))
|
||||
{
|
||||
var opts = TryDeserialize(DriverConfigJson) ?? new AbLegacyDriverOptions();
|
||||
_form = FormModel.FromOptions(opts);
|
||||
_form = FormModel.FromJson(DriverConfigJson, opts);
|
||||
_originalJson = DriverConfigJson;
|
||||
_preservedDevices = opts.Devices;
|
||||
_lastParsed = DriverConfigJson;
|
||||
}
|
||||
@@ -110,16 +127,17 @@
|
||||
/// <summary>Serializes the current channel/protocol config to camelCase JSON, preserving the driver's
|
||||
/// existing Devices array (authored as separate v3 entities).</summary>
|
||||
public string GetConfigJson()
|
||||
=> Serialize(_form.ToOptions(_preservedDevices));
|
||||
=> Serialize(_form.ToOptions(_preservedDevices), _originalJson);
|
||||
|
||||
/// <summary>Serializes options with the form's JSON settings (camelCase, enums as strings).</summary>
|
||||
internal static string Serialize(AbLegacyDriverOptions options)
|
||||
=> JsonSerializer.Serialize(options, _jsonOpts);
|
||||
internal static string Serialize(AbLegacyDriverOptions options, string? originalJson = null)
|
||||
=> DriverConfigJsonOverlay.Apply(originalJson, JsonSerializer.Serialize(options, _jsonOpts), OwnedPaths, TimeSpanToMs);
|
||||
|
||||
private async Task EmitAsync()
|
||||
{
|
||||
var json = GetConfigJson();
|
||||
_lastParsed = json;
|
||||
_originalJson = json;
|
||||
await DriverConfigJsonChanged.InvokeAsync(json);
|
||||
}
|
||||
|
||||
@@ -159,8 +177,27 @@
|
||||
public string? ResilienceConfig { get; set; }
|
||||
public byte[] RowVersion { get; set; } = [];
|
||||
|
||||
private TimeSpan? _loadedTimeout, _loadedProbeInterval, _loadedProbeTimeout;
|
||||
|
||||
/// <summary>Loads from the stored JSON: the factory's <c>*Ms</c> keys win; a legacy TimeSpan-string key is the fallback.</summary>
|
||||
public static FormModel FromJson(string? json, AbLegacyDriverOptions o)
|
||||
{
|
||||
static TimeSpan? Ms(string? j, string path) => DriverConfigMs.Read(j, path) is int ms ? TimeSpan.FromMilliseconds(ms) : null;
|
||||
var f = FromOptions(o);
|
||||
f._loadedTimeout = Ms(json, "timeoutMs") ?? f._loadedTimeout;
|
||||
f._loadedProbeInterval = Ms(json, "probe.intervalMs") ?? f._loadedProbeInterval;
|
||||
f._loadedProbeTimeout = Ms(json, "probe.timeoutMs") ?? f._loadedProbeTimeout;
|
||||
f.TimeoutSeconds = DriverConfigMs.ToSeconds(f._loadedTimeout ?? o.Timeout);
|
||||
f.ProbeIntervalSeconds = DriverConfigMs.ToSeconds(f._loadedProbeInterval ?? o.Probe.Interval);
|
||||
f.ProbeTimeoutSeconds = DriverConfigMs.ToSeconds(f._loadedProbeTimeout ?? o.Probe.Timeout);
|
||||
return f;
|
||||
}
|
||||
|
||||
public static FormModel FromOptions(AbLegacyDriverOptions o) => new()
|
||||
{
|
||||
_loadedTimeout = o.Timeout,
|
||||
_loadedProbeInterval = o.Probe.Interval,
|
||||
_loadedProbeTimeout = o.Probe.Timeout,
|
||||
TimeoutSeconds = (int)o.Timeout.TotalSeconds,
|
||||
Transport = o.Transport,
|
||||
ProbeEnabled = o.Probe.Enabled,
|
||||
@@ -177,11 +214,11 @@
|
||||
Probe = new AbLegacyProbeOptions
|
||||
{
|
||||
Enabled = ProbeEnabled,
|
||||
Interval = TimeSpan.FromSeconds(ProbeIntervalSeconds),
|
||||
Timeout = TimeSpan.FromSeconds(ProbeTimeoutSeconds),
|
||||
Interval = DriverConfigMs.Save(ProbeIntervalSeconds, _loadedProbeInterval),
|
||||
Timeout = DriverConfigMs.Save(ProbeTimeoutSeconds, _loadedProbeTimeout),
|
||||
ProbeAddress = string.IsNullOrWhiteSpace(ProbeAddress) ? null : ProbeAddress,
|
||||
},
|
||||
Timeout = TimeSpan.FromSeconds(TimeoutSeconds),
|
||||
Timeout = DriverConfigMs.Save(TimeoutSeconds, _loadedTimeout),
|
||||
Transport = Transport,
|
||||
ProbeTimeoutSeconds = AdminProbeTimeoutSeconds,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user