1e68121b17
A native notification for MAIN.Flags.3 / .ADSSTATE_RUN.0 was registered on the literal WORD.N path, which ADS has no symbol for: live on the TC 2.11 Elma cleaner Z33856 it failed with DeviceSymbolNotFound, and that registration error tore down the whole subscribe call. The read path already reads the parent word; the notification now does the same - it registers the parent as raw bytes at its resolved width and reads the bit (TwinCATBitAccess), refusing a bit outside the parent or a non-integer parent with BadTypeMismatch.
42 lines
2.3 KiB
C#
42 lines
2.3 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
|
|
|
/// <summary>
|
|
/// Serves a bit-indexed BOOL tag (<c>MAIN.Flags.3</c>, <c>.ADSSTATE_RUN.0</c>) from its PARENT word.
|
|
/// ADS has no symbol for <c>WORD.N</c> — live on a TwinCAT 2.11 target a notification registered on
|
|
/// <c>.ADSSTATE_RUN.0</c> failed with <c>DeviceSymbolNotFound</c> — so a native notification registers
|
|
/// the parent as raw bytes at its resolved width and reads the bit here, exactly as a read already
|
|
/// reads the parent word and extracts the bit.
|
|
/// </summary>
|
|
internal static class TwinCATBitAccess
|
|
{
|
|
/// <summary>The parent word's path: the bit path without its trailing <c>.N</c>.</summary>
|
|
/// <param name="symbolPath">The bit path (<c>MAIN.Flags.3</c>).</param>
|
|
/// <returns>The parent path, or <paramref name="symbolPath"/> when it has no numeric last segment.</returns>
|
|
public static string ParentPath(string symbolPath)
|
|
{
|
|
var lastDot = symbolPath.LastIndexOf('.');
|
|
if (lastDot < 0) return symbolPath;
|
|
return int.TryParse(symbolPath.AsSpan(lastDot + 1), out _) ? symbolPath[..lastDot] : symbolPath;
|
|
}
|
|
|
|
/// <summary>Checks that <paramref name="bit"/> addresses a bit of an integer parent word.</summary>
|
|
/// <param name="bit">The bit index.</param>
|
|
/// <param name="parent">The parent symbol's shape.</param>
|
|
/// <returns><c>null</c> when addressable; otherwise the reason.</returns>
|
|
public static string? CheckParent(int bit, TwinCATPlcSymbolShape parent)
|
|
{
|
|
if (parent.Kind != TwinCATPlcKind.Scalar || parent.IsFloat)
|
|
return $"bit {bit} of {parent.TypeName} ({parent.ByteSize} B) — only an integer word has addressable bits";
|
|
return bit >= 0 && bit < parent.ByteSize * 8
|
|
? null
|
|
: $"bit {bit} is outside the {parent.ByteSize * 8}-bit parent {parent.TypeName}";
|
|
}
|
|
|
|
/// <summary>Reads bit <paramref name="bit"/> of a little-endian parent word.</summary>
|
|
/// <param name="parent">The parent word's raw bytes.</param>
|
|
/// <param name="bit">The bit index (0 = least significant bit of the first byte).</param>
|
|
/// <returns>The bit's value.</returns>
|
|
public static bool ReadBit(ReadOnlySpan<byte> parent, int bit) =>
|
|
(parent[bit / 8] & (1 << (bit % 8))) != 0;
|
|
}
|