85 lines
4.4 KiB
C#
85 lines
4.4 KiB
C#
using S7NetErrorCode = global::S7.Net.ErrorCode;
|
|
using S7NetPlcException = global::S7.Net.PlcException;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.S7;
|
|
|
|
/// <summary>
|
|
/// Thrown by <see cref="S7Driver.InitializeAsync"/> when the post-<c>OpenAsync</c>
|
|
/// pre-flight probe receives a response that the driver classifies as
|
|
/// "PUT/GET communication disabled on the PLC". Surfaces a typed exception so
|
|
/// operators see the configuration-fix instructions immediately at init time
|
|
/// instead of waiting for the first per-tag read to fail with
|
|
/// <c>BadDeviceFailure</c>. See <c>docs/v2/s7.md</c> "Pre-flight PUT/GET enablement"
|
|
/// section for the full rationale.
|
|
/// </summary>
|
|
public sealed class S7PutGetDisabledException : Exception
|
|
{
|
|
/// <summary>The probe address that triggered the typed classification (e.g. <c>MW0</c>).</summary>
|
|
public string ProbeAddress { get; }
|
|
|
|
/// <summary>
|
|
/// Construct a typed exception. <paramref name="inner"/> is the raw
|
|
/// <see cref="S7NetPlcException"/> from S7netplus that
|
|
/// <see cref="S7PreflightClassifier.IsPutGetDisabled"/> classified positive.
|
|
/// </summary>
|
|
public S7PutGetDisabledException(string probeAddress, Exception? inner = null)
|
|
: base(BuildMessage(probeAddress), inner)
|
|
{
|
|
ProbeAddress = probeAddress;
|
|
}
|
|
|
|
internal static string BuildMessage(string probeAddress) =>
|
|
"S7 pre-flight probe to '" + probeAddress + "' was rejected by the PLC. " +
|
|
"PUT/GET communication is disabled on the PLC. " +
|
|
"Enable it in TIA Portal: Device → Properties → Protection & Security → " +
|
|
"Connection mechanisms → 'Permit access with PUT/GET communication from " +
|
|
"remote partner'. Re-deploy the hardware config and restart the S7 driver. " +
|
|
"Alternatively set 'Probe.SkipPreflight = true' to defer the check to runtime " +
|
|
"(driver will still surface BadDeviceFailure on every read until PUT/GET is enabled).";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Classifies an <see cref="S7NetPlcException"/> coming back from the pre-flight
|
|
/// probe read into "PUT/GET disabled" vs "everything else". Pulled out as a static
|
|
/// helper so unit tests can drive every branch (matching ErrorCode, non-matching
|
|
/// ErrorCode, null exception) without spinning up an S7 server. Mirrors the
|
|
/// <c>ModbusPreflight</c> pattern from the Modbus driver.
|
|
/// </summary>
|
|
public static class S7PreflightClassifier
|
|
{
|
|
/// <summary>
|
|
/// Returns <c>true</c> when the exception's <c>ErrorCode</c> matches the
|
|
/// S7.Net surface for "PLC refused the read" — which on hardened S7-1200 /
|
|
/// S7-1500 firmware is the wire-level signal for PUT/GET disabled. We match
|
|
/// <see cref="S7NetErrorCode.WrongCPU_Type"/> (S7.Net's primary classification
|
|
/// when the response framing isn't a valid PDU because the CPU rejected the
|
|
/// request) and <see cref="S7NetErrorCode.ReadData"/> (the generic
|
|
/// "couldn't read" code S7.Net falls back to when the PLC sends an S7-level
|
|
/// error byte instead of a normal response).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The S7-1200 / S7-1500 wire response when PUT/GET is disabled is an S7
|
|
/// "Function not allowed in current protection level" header byte
|
|
/// (0xD605 / 0x8500 family). S7netplus surfaces that as PlcException with
|
|
/// <see cref="S7NetErrorCode.ReadData"/> on the response path or
|
|
/// <see cref="S7NetErrorCode.WrongCPU_Type"/> when the CPU drops the
|
|
/// connection before sending a valid response. Matching both ensures the
|
|
/// driver flags the config bug regardless of which path the firmware takes.
|
|
/// </para>
|
|
/// <para>
|
|
/// A non-matching <see cref="S7NetErrorCode"/> (e.g.
|
|
/// <see cref="S7NetErrorCode.ConnectionError"/>,
|
|
/// <see cref="S7NetErrorCode.IPAddressNotAvailable"/>) means "transport
|
|
/// broke" — let the caller rethrow as-is so other failure shapes don't
|
|
/// get silently masked as "PUT/GET disabled".
|
|
/// </para>
|
|
/// </remarks>
|
|
public static bool IsPutGetDisabled(S7NetPlcException? exception)
|
|
{
|
|
if (exception is null) return false;
|
|
return exception.ErrorCode is S7NetErrorCode.WrongCPU_Type
|
|
or S7NetErrorCode.ReadData;
|
|
}
|
|
}
|