using S7NetErrorCode = global::S7.Net.ErrorCode;
using S7NetPlcException = global::S7.Net.PlcException;
namespace ZB.MOM.WW.OtOpcUa.Driver.S7;
///
/// Thrown by when the post-OpenAsync
/// 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
/// BadDeviceFailure. See docs/v2/s7.md "Pre-flight PUT/GET enablement"
/// section for the full rationale.
///
public sealed class S7PutGetDisabledException : Exception
{
/// The probe address that triggered the typed classification (e.g. MW0).
public string ProbeAddress { get; }
///
/// Construct a typed exception. is the raw
/// from S7netplus that
/// classified positive.
///
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).";
}
///
/// Classifies an 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
/// ModbusPreflight pattern from the Modbus driver.
///
public static class S7PreflightClassifier
{
///
/// Returns true when the exception's ErrorCode 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
/// (S7.Net's primary classification
/// when the response framing isn't a valid PDU because the CPU rejected the
/// request) and (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).
///
///
///
/// 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
/// on the response path or
/// 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.
///
///
/// A non-matching (e.g.
/// ,
/// ) means "transport
/// broke" — let the caller rethrow as-is so other failure shapes don't
/// get silently masked as "PUT/GET disabled".
///
///
public static bool IsPutGetDisabled(S7NetPlcException? exception)
{
if (exception is null) return false;
return exception.ErrorCode is S7NetErrorCode.WrongCPU_Type
or S7NetErrorCode.ReadData;
}
}