fix(abcip): serialize Read/GetStatus/Decode + Encode/Write/GetStatus per runtime (05/STAB-4)
This commit is contained in:
@@ -42,7 +42,7 @@
|
|||||||
{
|
{
|
||||||
"id": "B2.3",
|
"id": "B2.3",
|
||||||
"subject": "B2: wrap ReadSingleAsync/ReadGroupAsync/WriteAsync critical sections in the per-runtime lock; AbCip suite green",
|
"subject": "B2: wrap ReadSingleAsync/ReadGroupAsync/WriteAsync critical sections in the per-runtime lock; AbCip suite green",
|
||||||
"status": "pending",
|
"status": "completed",
|
||||||
"blockedBy": [
|
"blockedBy": [
|
||||||
"B2.2"
|
"B2.2"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -542,6 +542,13 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var runtime = await EnsureTagRuntimeAsync(device, def, ct).ConfigureAwait(false);
|
var runtime = await EnsureTagRuntimeAsync(device, def, ct).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Serialise Read → GetStatus → Decode against the shared runtime — the cached libplctag
|
||||||
|
// Tag handle is not concurrency-safe and GetStatus returns the LAST op's status (05/STAB-4).
|
||||||
|
var opLock = device.GetRuntimeLock(def.Name);
|
||||||
|
await opLock.WaitAsync(ct).ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
await runtime.ReadAsync(ct).ConfigureAwait(false);
|
await runtime.ReadAsync(ct).ConfigureAwait(false);
|
||||||
|
|
||||||
var status = runtime.GetStatus();
|
var status = runtime.GetStatus();
|
||||||
@@ -574,6 +581,11 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
|||||||
results[fb.OriginalIndex] = new DataValueSnapshot(value, AbCipStatusMapper.Good, now, now);
|
results[fb.OriginalIndex] = new DataValueSnapshot(value, AbCipStatusMapper.Good, now, now);
|
||||||
_health = new DriverHealth(DriverState.Healthy, now, null);
|
_health = new DriverHealth(DriverState.Healthy, now, null);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
opLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
@@ -612,6 +624,13 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var runtime = await EnsureTagRuntimeAsync(device, parent, ct).ConfigureAwait(false);
|
var runtime = await EnsureTagRuntimeAsync(device, parent, ct).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Serialise the whole-UDT Read → GetStatus → per-member DecodeValueAt against the shared
|
||||||
|
// parent runtime — the members decode from that one handle's buffer (05/STAB-4).
|
||||||
|
var opLock = device.GetRuntimeLock(parent.Name);
|
||||||
|
await opLock.WaitAsync(ct).ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
await runtime.ReadAsync(ct).ConfigureAwait(false);
|
await runtime.ReadAsync(ct).ConfigureAwait(false);
|
||||||
|
|
||||||
var status = runtime.GetStatus();
|
var status = runtime.GetStatus();
|
||||||
@@ -636,6 +655,11 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
|||||||
}
|
}
|
||||||
_health = new DriverHealth(DriverState.Healthy, now, null);
|
_health = new DriverHealth(DriverState.Healthy, now, null);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
opLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
@@ -706,10 +730,23 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runtime = await EnsureTagRuntimeAsync(device, def, cancellationToken).ConfigureAwait(false);
|
var runtime = await EnsureTagRuntimeAsync(device, def, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Serialise Encode → Write → GetStatus against the shared runtime — the same cached
|
||||||
|
// Tag handle may be in use by a concurrent ReadAsync or poll loop (05/STAB-4).
|
||||||
|
var opLock = device.GetRuntimeLock(def.Name);
|
||||||
|
await opLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
int status;
|
||||||
|
try
|
||||||
|
{
|
||||||
runtime.EncodeValue(def.DataType, parsedPath?.BitIndex, w.Value);
|
runtime.EncodeValue(def.DataType, parsedPath?.BitIndex, w.Value);
|
||||||
await runtime.WriteAsync(cancellationToken).ConfigureAwait(false);
|
await runtime.WriteAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
status = runtime.GetStatus();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
opLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
var status = runtime.GetStatus();
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
{
|
{
|
||||||
EvictRuntime(device, def.Name);
|
EvictRuntime(device, def.Name);
|
||||||
|
|||||||
Reference in New Issue
Block a user