Initial commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Bundles application input and output data
|
||||
/// </summary>
|
||||
public sealed class ApplicationData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the application inputs
|
||||
/// </summary>
|
||||
public ApplicationInputs Inputs { get; init; } = new ApplicationInputs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application outputs
|
||||
/// </summary>
|
||||
public ApplicationOutputs Outputs { get; init; } = new ApplicationOutputs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether application data is empty (not enabled)
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains application inputs from a UDP data packet
|
||||
/// </summary>
|
||||
public sealed class ApplicationInputs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unsafe input sources (bits represent current state of static input sources)
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> UnsafeInputsInputSources { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flags for unsafe input sources (one flag per static input source)
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> UnsafeInputsFlags { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the monitoring case numbers
|
||||
/// </summary>
|
||||
public IReadOnlyList<ushort> MonitoringCases { get; init; } = Array.Empty<ushort>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the monitoring case flags
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> MonitoringCaseFlags { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first linear velocity input
|
||||
/// </summary>
|
||||
public short Velocity0 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second linear velocity input
|
||||
/// </summary>
|
||||
public short Velocity1 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether first linear velocity input is valid
|
||||
/// </summary>
|
||||
public bool IsVelocity0Valid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether second linear velocity input is valid
|
||||
/// </summary>
|
||||
public bool IsVelocity1Valid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether first linear velocity input is transmitted safely
|
||||
/// </summary>
|
||||
public bool IsVelocity0TransmittedSafely { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether second linear velocity input is transmitted safely
|
||||
/// </summary>
|
||||
public bool IsVelocity1TransmittedSafely { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state of the sleep mode input
|
||||
/// </summary>
|
||||
public sbyte SleepModeInput { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the application name from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class ApplicationName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the application name
|
||||
/// </summary>
|
||||
public uint NameLength { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application name string
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains application outputs from a UDP data packet
|
||||
/// </summary>
|
||||
public sealed class ApplicationOutputs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the state of the non-safe cut-off paths (eval out)
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> EvalOut { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a cut-off path from the output paths is safe
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> EvalOutIsSafe { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the output path is valid
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> EvalOutIsValid { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently active monitoring case numbers
|
||||
/// </summary>
|
||||
public IReadOnlyList<ushort> MonitoringCases { get; init; } = Array.Empty<ushort>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the corresponding monitoring case number is valid
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> MonitoringCaseFlags { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state of the sleep mode output
|
||||
/// </summary>
|
||||
public sbyte SleepModeOutput { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a contamination warning is present
|
||||
/// </summary>
|
||||
public bool HostErrorFlagContaminationWarning { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a contamination error is present
|
||||
/// </summary>
|
||||
public bool HostErrorFlagContaminationError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a manipulation error is present
|
||||
/// </summary>
|
||||
public bool HostErrorFlagManipulationError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether glare is present
|
||||
/// </summary>
|
||||
public bool HostErrorFlagGlare { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a reference contour is intruded
|
||||
/// </summary>
|
||||
public bool HostErrorFlagReferenceContourIntruded { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a critical error is present
|
||||
/// </summary>
|
||||
public bool HostErrorFlagCriticalError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first linear velocity output
|
||||
/// </summary>
|
||||
public short Velocity0 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second linear velocity output
|
||||
/// </summary>
|
||||
public short Velocity1 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the first linear velocity output is valid
|
||||
/// </summary>
|
||||
public bool IsVelocity0Valid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the second linear velocity output is valid
|
||||
/// </summary>
|
||||
public bool IsVelocity1Valid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the first linear velocity output is transmitted safely
|
||||
/// </summary>
|
||||
public bool IsVelocity0TransmittedSafely { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the second linear velocity output is transmitted safely
|
||||
/// </summary>
|
||||
public bool IsVelocity1TransmittedSafely { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resulting velocity for each monitoring case table
|
||||
/// </summary>
|
||||
public IReadOnlyList<short> ResultingVelocity { get; init; } = Array.Empty<short>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the resulting velocities are valid
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> ResultingVelocityIsValid { get; init; } = Array.Empty<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the sleep mode is valid
|
||||
/// </summary>
|
||||
public bool FlagsSleepModeOutputIsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the error flags are valid
|
||||
/// </summary>
|
||||
public bool FlagsHostErrorFlagsAreValid { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Communication settings used to configure the SICK Safety Scanner
|
||||
/// This structure is used with ChangeCommSettingsCommand to configure scanner parameters
|
||||
/// </summary>
|
||||
public sealed class CommSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the channel number (0-3)
|
||||
/// </summary>
|
||||
public byte Channel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the publishing frequency (publish every n-th scan)
|
||||
/// Example: 1 = publish every scan, 2 = publish every 2nd scan (half frequency)
|
||||
/// </summary>
|
||||
public ushort PublishingFrequency { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface type
|
||||
/// 0: EFI-pro, 1: EtherNet/IP, 3: Profinet, 4: Non-safe Ethernet
|
||||
/// </summary>
|
||||
public InterfaceType EInterfaceType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start angle in radians
|
||||
/// If start and end angles are equal, all angles are regarded
|
||||
/// </summary>
|
||||
public double StartAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end angle in radians
|
||||
/// If start and end angles are equal, all angles are regarded
|
||||
/// </summary>
|
||||
public double EndAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the enabled features as a bitset (ushort)
|
||||
/// Use SensorDataFeatures constants or SensorDataFeatures.ToFeatureFlags() to set
|
||||
/// </summary>
|
||||
public ushort Features { get; init; } = SensorDataFeatures.All;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether general system state feature is enabled
|
||||
/// </summary>
|
||||
public bool GeneralSystemStateEnabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether derived settings feature is enabled
|
||||
/// </summary>
|
||||
public bool DerivedSettingsEnabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether measurement data feature is enabled
|
||||
/// </summary>
|
||||
public bool MeasurementDataEnabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether intrusion data feature is enabled
|
||||
/// </summary>
|
||||
public bool IntrusionDataEnabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether application data feature is enabled
|
||||
/// </summary>
|
||||
public bool ApplicationDataEnabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the channel is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the host UDP port (0 = auto-assign)
|
||||
/// </summary>
|
||||
public ushort HostUdpPort { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the host IP address as a string (e.g., "192.168.1.100")
|
||||
/// </summary>
|
||||
public string HostIp { get; init; } = "192.168.1.100";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CommSettings with all features enabled
|
||||
/// </summary>
|
||||
public static CommSettings CreateDefault()
|
||||
{
|
||||
return new CommSettings
|
||||
{
|
||||
Channel = 0,
|
||||
PublishingFrequency = 1,
|
||||
EInterfaceType = InterfaceType.NonSafeEthernet,
|
||||
StartAngle = 0.0,
|
||||
EndAngle = 0.0, // Equal to start angle means all angles
|
||||
Features = SensorDataFeatures.All,
|
||||
GeneralSystemStateEnabled = true,
|
||||
DerivedSettingsEnabled = true,
|
||||
MeasurementDataEnabled = true,
|
||||
IntrusionDataEnabled = true,
|
||||
ApplicationDataEnabled = true,
|
||||
Enabled = true,
|
||||
HostUdpPort = 0,
|
||||
HostIp = "192.168.1.100"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CommSettings with custom feature flags
|
||||
/// </summary>
|
||||
public static CommSettings Create(
|
||||
byte channel,
|
||||
string hostIp,
|
||||
ushort hostUdpPort,
|
||||
bool generalSystemState = true,
|
||||
bool derivedSettings = true,
|
||||
bool measurementData = true,
|
||||
bool intrusionData = true,
|
||||
bool applicationData = true,
|
||||
ushort publishingFrequency = 1,
|
||||
double startAngle = 0.0,
|
||||
double endAngle = 0.0,
|
||||
InterfaceType interfaceType = InterfaceType.NonSafeEthernet,
|
||||
bool enabled = true)
|
||||
{
|
||||
return new CommSettings
|
||||
{
|
||||
Channel = channel,
|
||||
PublishingFrequency = publishingFrequency,
|
||||
EInterfaceType = interfaceType,
|
||||
StartAngle = startAngle,
|
||||
EndAngle = endAngle,
|
||||
Features = SensorDataFeatures.ToFeatureFlags(
|
||||
generalSystemState,
|
||||
derivedSettings,
|
||||
measurementData,
|
||||
intrusionData,
|
||||
applicationData),
|
||||
GeneralSystemStateEnabled = generalSystemState,
|
||||
DerivedSettingsEnabled = derivedSettings,
|
||||
MeasurementDataEnabled = measurementData,
|
||||
IntrusionDataEnabled = intrusionData,
|
||||
ApplicationDataEnabled = applicationData,
|
||||
Enabled = enabled,
|
||||
HostUdpPort = hostUdpPort,
|
||||
HostIp = hostIp
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the configuration data from a COLA2 variable command response
|
||||
/// Used for current and persistent sensor configuration
|
||||
/// </summary>
|
||||
public sealed class ConfigData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the host IP address
|
||||
/// </summary>
|
||||
public string HostIp { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the host UDP port
|
||||
/// </summary>
|
||||
public ushort HostUdpPort { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel number (0-3)
|
||||
/// </summary>
|
||||
public byte Channel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the channel is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface type
|
||||
/// </summary>
|
||||
public InterfaceType EInterfaceType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the publishing frequency (publish every n-th scan)
|
||||
/// </summary>
|
||||
public ushort PublishingFrequency { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start angle in radians
|
||||
/// </summary>
|
||||
public double StartAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end angle in radians
|
||||
/// </summary>
|
||||
public double EndAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the enabled features (bit flags)
|
||||
/// Bit 0: GeneralSystemState, Bit 1: DerivedSettings, Bit 2: MeasurementData,
|
||||
/// Bit 3: IntrusionData, Bit 4: ApplicationData
|
||||
/// </summary>
|
||||
public ushort Features { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether general system state feature is enabled
|
||||
/// </summary>
|
||||
public bool GeneralSystemStateEnabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether derived settings feature is enabled
|
||||
/// </summary>
|
||||
public bool DerivedSettingsEnabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether measurement data feature is enabled
|
||||
/// </summary>
|
||||
public bool MeasurementDataEnabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether intrusion data feature is enabled
|
||||
/// </summary>
|
||||
public bool IntrusionDataEnabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether application data feature is enabled
|
||||
/// </summary>
|
||||
public bool ApplicationDataEnabled { get; init; }
|
||||
|
||||
// Derived values (similar to DerivedValues structure)
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the multiplication factor for beam distances
|
||||
/// </summary>
|
||||
public ushort DerivedMultiplicationFactor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of beams
|
||||
/// </summary>
|
||||
public ushort DerivedNumberOfBeams { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scan time in milliseconds
|
||||
/// </summary>
|
||||
public ushort DerivedScanTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the derived start angle in radians
|
||||
/// </summary>
|
||||
public double DerivedStartAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the angular beam resolution in radians
|
||||
/// </summary>
|
||||
public double DerivedAngularBeamResolution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interbeam period in microseconds
|
||||
/// </summary>
|
||||
public uint DerivedInterbeamPeriod { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the configuration metadata from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class ConfigMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the modification time date (days since 1980-01-01)
|
||||
/// </summary>
|
||||
public ushort ModificationTimeDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the modification time (milliseconds since midnight)
|
||||
/// </summary>
|
||||
public uint ModificationTimeTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transfer time date (days since 1980-01-01)
|
||||
/// </summary>
|
||||
public ushort TransferTimeDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transfer time (milliseconds since midnight)
|
||||
/// </summary>
|
||||
public uint TransferTimeTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application checksum
|
||||
/// </summary>
|
||||
public uint AppChecksum { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overall checksum
|
||||
/// </summary>
|
||||
public uint OverallChecksum { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the integrity hash (array of uint32 values)
|
||||
/// </summary>
|
||||
public IReadOnlyList<uint> IntegrityHash { get; init; } = Array.Empty<uint>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the content of the data header of a UDP data packet
|
||||
/// </summary>
|
||||
public sealed class DataHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (capital letter 'V' or 'R' for releases)
|
||||
/// </summary>
|
||||
public byte VersionIndicator { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the release of the version
|
||||
/// </summary>
|
||||
public byte VersionRelease { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the serial number of the device
|
||||
/// </summary>
|
||||
public uint SerialNumberOfDevice { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the serial number of the system plug
|
||||
/// </summary>
|
||||
public uint SerialNumberOfSystemPlug { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel number (0-3)
|
||||
/// </summary>
|
||||
public byte ChannelNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sequence number (increases with each measurement)
|
||||
/// </summary>
|
||||
public uint SequenceNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scan number
|
||||
/// </summary>
|
||||
public uint ScanNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp date
|
||||
/// </summary>
|
||||
public ushort TimestampDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp time
|
||||
/// </summary>
|
||||
public uint TimestampTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the general system state block offset
|
||||
/// </summary>
|
||||
public ushort GeneralSystemStateBlockOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the general system state block size
|
||||
/// </summary>
|
||||
public ushort GeneralSystemStateBlockSize { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the derived values block offset
|
||||
/// </summary>
|
||||
public ushort DerivedValuesBlockOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the derived values block size
|
||||
/// </summary>
|
||||
public ushort DerivedValuesBlockSize { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the measurement data block offset
|
||||
/// </summary>
|
||||
public ushort MeasurementDataBlockOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the measurement data block size
|
||||
/// </summary>
|
||||
public ushort MeasurementDataBlockSize { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the intrusion data block offset
|
||||
/// </summary>
|
||||
public ushort IntrusionDataBlockOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the intrusion data block size
|
||||
/// </summary>
|
||||
public ushort IntrusionDataBlockSize { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application data block offset
|
||||
/// </summary>
|
||||
public ushort ApplicationDataBlockOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application data block size
|
||||
/// </summary>
|
||||
public ushort ApplicationDataBlockSize { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the data header is empty
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the contents of a UDP datagram header
|
||||
/// Used to match datagrams together to form a complete data packet
|
||||
/// </summary>
|
||||
public sealed class DatagramHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Size of the datagram header (24 bytes)
|
||||
/// </summary>
|
||||
public const int HeaderSize = 24;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the datagram marker
|
||||
/// </summary>
|
||||
public uint DatagramMarker { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the protocol
|
||||
/// </summary>
|
||||
public ushort Protocol { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte MajorVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte MinorVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total length of the data packet (excluding headers)
|
||||
/// Total length of the (possibly fragmented) measurement data instance
|
||||
/// </summary>
|
||||
public uint TotalLength { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identification of the data
|
||||
/// Datagrams (fragments) that belong to the same measurement data output instance share the same identifier
|
||||
/// The number increases with each measurement data instance generated per channel
|
||||
/// </summary>
|
||||
public uint Identification { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fragment offset (in bytes)
|
||||
/// Offset of the measurement data carried in this datagram (fragment) relative to the start of the overall measurement data output instance
|
||||
/// </summary>
|
||||
public uint FragmentOffset { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the derived configuration of the measurement data channel
|
||||
/// </summary>
|
||||
public sealed class DerivedValues
|
||||
{
|
||||
/// <summary>
|
||||
/// Angle resolution constant to convert sensor input to the right frame (4194304.0)
|
||||
/// </summary>
|
||||
public const double AngleResolution = 4194304.0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the multiplication factor to be applied to beam distance values to get distance in millimeter
|
||||
/// </summary>
|
||||
public ushort MultiplicationFactor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of beams of the current scan
|
||||
/// </summary>
|
||||
public ushort NumberOfBeams { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time of the scan (ms)
|
||||
/// </summary>
|
||||
public ushort ScanTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start angle of the scan (radians)
|
||||
/// </summary>
|
||||
public double StartAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the angular resolution between beams (radians)
|
||||
/// </summary>
|
||||
public double AngularBeamResolution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time between consecutive beams (microseconds)
|
||||
/// </summary>
|
||||
public uint InterbeamPeriod { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether derived values are empty (not enabled)
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the device name from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class DeviceName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the device name string
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Device status enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum SopasDeviceStatus : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown status
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Start up status
|
||||
/// </summary>
|
||||
StartUp = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Service mode
|
||||
/// </summary>
|
||||
ServiceMode = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Normal operation
|
||||
/// </summary>
|
||||
NormalOperation = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Suspended operation
|
||||
/// </summary>
|
||||
SuspendedOperation = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Service recommended
|
||||
/// </summary>
|
||||
ServiceRecommended = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Service required
|
||||
/// </summary>
|
||||
ServiceRequired = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Recoverable error
|
||||
/// </summary>
|
||||
RecoverableError = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Fatal error
|
||||
/// </summary>
|
||||
FatalError = 8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the device status from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class DeviceStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the device status value
|
||||
/// </summary>
|
||||
public SopasDeviceStatus Status { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains field data for warning and protective fields from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class FieldData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether the field data is valid
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the field data is defined
|
||||
/// </summary>
|
||||
public bool IsDefined { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the evaluation method
|
||||
/// </summary>
|
||||
public byte EvalMethod { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the multiple sampling value
|
||||
/// </summary>
|
||||
public ushort MultiSampling { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object resolution
|
||||
/// </summary>
|
||||
public ushort ObjectResolution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the field set this field belongs to
|
||||
/// </summary>
|
||||
public ushort FieldSetIndex { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the field name
|
||||
/// </summary>
|
||||
public uint NameLength { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the field name string
|
||||
/// </summary>
|
||||
public string FieldName { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this is a warning field
|
||||
/// </summary>
|
||||
public bool IsWarningField { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this is a protective field
|
||||
/// </summary>
|
||||
public bool IsProtectiveField { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the beam distances vector (in mm)
|
||||
/// One distance value per beam
|
||||
/// </summary>
|
||||
public IReadOnlyList<ushort> BeamDistances { get; init; } = Array.Empty<ushort>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start angle in radians
|
||||
/// </summary>
|
||||
public double StartAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end angle in radians
|
||||
/// </summary>
|
||||
public double EndAngle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the angular beam resolution in radians
|
||||
/// </summary>
|
||||
public double AngularBeamResolution { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains field sets data from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class FieldSets
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of each field name (one per field set)
|
||||
/// </summary>
|
||||
public IReadOnlyList<uint> NameLengths { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the field names (one per field set)
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> FieldNames { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether each field set is defined
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> IsDefined { get; init; } = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the firmware version from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class FirmwareVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the firmware version string
|
||||
/// </summary>
|
||||
public string Version { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the general system state including run/standby modes, cut-off paths, monitoring cases, and errors
|
||||
/// </summary>
|
||||
public sealed class GeneralSystemState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether run mode is active
|
||||
/// </summary>
|
||||
public bool IsRunModeActive { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether standby mode is active
|
||||
/// </summary>
|
||||
public bool IsStandbyModeActive { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a contamination warning exists
|
||||
/// </summary>
|
||||
public bool HasContaminationWarning { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a contamination error exists
|
||||
/// </summary>
|
||||
public bool HasContaminationError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the reference contour status is true
|
||||
/// </summary>
|
||||
public bool ReferenceContourStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether manipulation status is set to true
|
||||
/// </summary>
|
||||
public bool ManipulationStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state for all safe cut-off paths
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> SafeCutOffPaths { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state of all non-safe cut-off paths
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> NonSafeCutOffPaths { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a cut-off path has to be reset
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> ResetRequiredCutOffPaths { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current monitoring case number for table 1
|
||||
/// </summary>
|
||||
public byte CurrentMonitoringCaseNoTable1 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current monitoring case number for table 2
|
||||
/// </summary>
|
||||
public byte CurrentMonitoringCaseNoTable2 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current monitoring case number for table 3
|
||||
/// </summary>
|
||||
public byte CurrentMonitoringCaseNoTable3 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current monitoring case number for table 4
|
||||
/// </summary>
|
||||
public byte CurrentMonitoringCaseNoTable4 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an application error exists
|
||||
/// </summary>
|
||||
public bool HasApplicationError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether a device error exists
|
||||
/// </summary>
|
||||
public bool HasDeviceError { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether general system state is empty (not enabled)
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all intrusion data
|
||||
/// </summary>
|
||||
public sealed class IntrusionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets all intrusion datums
|
||||
/// </summary>
|
||||
public IReadOnlyList<IntrusionDatum> IntrusionDatums { get; init; } = Array.Empty<IntrusionDatum>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether intrusion data is empty (not enabled)
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single intrusion datum
|
||||
/// </summary>
|
||||
public sealed class IntrusionDatum
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the flag vector
|
||||
/// </summary>
|
||||
public int Size { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flags vector (one flag per beam indicating intrusion)
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> Flags { get; init; } = Array.Empty<bool>();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all scan points of a single measurement
|
||||
/// </summary>
|
||||
public sealed class MeasurementData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of beams in this measurement
|
||||
/// </summary>
|
||||
public uint NumberOfBeams { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all scan points
|
||||
/// </summary>
|
||||
public IReadOnlyList<ScanPoint> ScanPoints { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether measurement data is empty (not enabled)
|
||||
/// </summary>
|
||||
public bool IsEmpty { get; init; }
|
||||
|
||||
public MeasurementData(uint numberOfBeams, IReadOnlyList<ScanPoint> scanPoints, bool isEmpty = false)
|
||||
{
|
||||
NumberOfBeams = numberOfBeams;
|
||||
ScanPoints = scanPoints ?? Array.Empty<ScanPoint>();
|
||||
IsEmpty = isEmpty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains monitoring case data from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class MonitoringCaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether the monitoring case data is valid
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the monitoring case number
|
||||
/// </summary>
|
||||
public ushort MonitoringCaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the field indices associated with this monitoring case
|
||||
/// </summary>
|
||||
public IReadOnlyList<ushort> FieldIndices { get; init; } = Array.Empty<ushort>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether each field is configured and valid
|
||||
/// </summary>
|
||||
public IReadOnlyList<bool> FieldsValid { get; init; } = Array.Empty<bool>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the order number from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class OrderNumber
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the order number string
|
||||
/// </summary>
|
||||
public string Number { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Packet buffer for COLA2 communication
|
||||
/// Thread-safe wrapper around byte buffer - matches C++ shared_ptr<vector const> semantics
|
||||
/// </summary>
|
||||
public sealed class PacketBuffer : IDisposable
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _length;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum size of packet buffer (matches C++ MAXSIZE = 10000)
|
||||
/// </summary>
|
||||
public const int MaxSize = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new packet buffer from byte array
|
||||
/// </summary>
|
||||
public PacketBuffer(byte[] buffer, int length)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (length < 0 || length > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
if (length > MaxSize)
|
||||
throw new ArgumentException($"Length {length} exceeds MaxSize {MaxSize}", nameof(length));
|
||||
|
||||
// Copy buffer to ensure immutability (like C++ shared_ptr<vector const>)
|
||||
_buffer = new byte[length];
|
||||
Array.Copy(buffer, 0, _buffer, 0, length);
|
||||
_length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new packet buffer from ReadOnlyMemory
|
||||
/// </summary>
|
||||
public PacketBuffer(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (memory.Length > MaxSize)
|
||||
throw new ArgumentException($"Length {memory.Length} exceeds MaxSize {MaxSize}", nameof(memory));
|
||||
|
||||
_buffer = memory.ToArray();
|
||||
_length = _buffer.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new packet buffer from ReadOnlySpan
|
||||
/// </summary>
|
||||
public PacketBuffer(ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span.Length > MaxSize)
|
||||
throw new ArgumentException($"Length {span.Length} exceeds MaxSize {MaxSize}", nameof(span));
|
||||
|
||||
_buffer = span.ToArray();
|
||||
_length = _buffer.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the buffer as ReadOnlyMemory (zero-copy if possible)
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<byte> GetBuffer()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(PacketBuffer));
|
||||
|
||||
return new ReadOnlyMemory<byte>(_buffer, 0, _length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the buffer as ReadOnlySpan (zero-copy)
|
||||
/// </summary>
|
||||
public ReadOnlySpan<byte> GetBufferSpan()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(PacketBuffer));
|
||||
|
||||
return new ReadOnlySpan<byte>(_buffer, 0, _length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the buffer
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(PacketBuffer));
|
||||
|
||||
return _length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the buffer as byte array
|
||||
/// </summary>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(PacketBuffer));
|
||||
|
||||
var result = new byte[_length];
|
||||
Array.Copy(_buffer, 0, result, 0, _length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a PacketBuffer with a parsed DatagramHeader
|
||||
/// Used for merging fragmented UDP packets
|
||||
/// </summary>
|
||||
public sealed class ParsedPacketBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the packet buffer
|
||||
/// </summary>
|
||||
public PacketBuffer PacketBuffer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parsed datagram header
|
||||
/// </summary>
|
||||
public DatagramHeader DatagramHeader { get; init; }
|
||||
|
||||
public ParsedPacketBuffer(PacketBuffer packetBuffer, DatagramHeader datagramHeader)
|
||||
{
|
||||
PacketBuffer = packetBuffer ?? throw new ArgumentNullException(nameof(packetBuffer));
|
||||
DatagramHeader = datagramHeader ?? throw new ArgumentNullException(nameof(datagramHeader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two ParsedPacketBuffer instances by fragment offset for sorting
|
||||
/// </summary>
|
||||
public static int CompareByOffset(ParsedPacketBuffer x, ParsedPacketBuffer y)
|
||||
{
|
||||
if (x == null && y == null) return 0;
|
||||
if (x == null) return -1;
|
||||
if (y == null) return 1;
|
||||
|
||||
return x.DatagramHeader.FragmentOffset.CompareTo(y.DatagramHeader.FragmentOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the project name from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class ProjectName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the project name string
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the required user action information from a COLA2 variable command response
|
||||
/// Provides additional information about the SOPAS state
|
||||
/// </summary>
|
||||
public sealed class RequiredUserAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether the configuration has to be confirmed
|
||||
/// </summary>
|
||||
public bool ConfirmConfiguration { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the configuration has to be checked
|
||||
/// </summary>
|
||||
public bool CheckConfiguration { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the environment has to be checked
|
||||
/// </summary>
|
||||
public bool CheckEnvironment { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the application interfaces have to be checked
|
||||
/// </summary>
|
||||
public bool CheckApplicationInterfaces { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the device has to be checked
|
||||
/// </summary>
|
||||
public bool CheckDevice { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the setup procedure has to be run
|
||||
/// </summary>
|
||||
public bool RunSetupProcedure { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the firmware has to be checked
|
||||
/// </summary>
|
||||
public bool CheckFirmware { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the user has to wait
|
||||
/// </summary>
|
||||
public bool Wait { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single scan point from the SICK Safety Scanner
|
||||
/// </summary>
|
||||
public sealed class ScanPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the angle in sensor coordinates (radians)
|
||||
/// </summary>
|
||||
public double Angle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance of the measured scanpoint (mm)
|
||||
/// </summary>
|
||||
public ushort Distance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reflectivity value (0-255)
|
||||
/// </summary>
|
||||
public byte Reflectivity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the scanpoint is valid
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the scanpoint is infinite (no object detected)
|
||||
/// </summary>
|
||||
public bool IsInfinite { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether there is glare in the scanpoint
|
||||
/// </summary>
|
||||
public bool HasGlare { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the scanpoint detects a reflector
|
||||
/// </summary>
|
||||
public bool IsReflector { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the scanpoint is contaminated
|
||||
/// </summary>
|
||||
public bool IsContaminated { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether there is a contamination warning
|
||||
/// </summary>
|
||||
public bool HasContaminationWarning { get; init; }
|
||||
|
||||
public ScanPoint(double angle, ushort distance, byte reflectivity, bool isValid,
|
||||
bool isInfinite, bool hasGlare, bool isReflector, bool isContaminated, bool hasContaminationWarning)
|
||||
{
|
||||
Angle = angle;
|
||||
Distance = distance;
|
||||
Reflectivity = reflectivity;
|
||||
IsValid = isValid;
|
||||
IsInfinite = isInfinite;
|
||||
HasGlare = hasGlare;
|
||||
IsReflector = isReflector;
|
||||
IsContaminated = isContaminated;
|
||||
HasContaminationWarning = hasContaminationWarning;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Sensor data feature flags constants and helper methods
|
||||
/// Used to configure which data blocks should be included in scan data output
|
||||
/// </summary>
|
||||
public static class SensorDataFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// All features enabled (0b11111)
|
||||
/// </summary>
|
||||
public const ushort All = 0b11111;
|
||||
|
||||
/// <summary>
|
||||
/// No features enabled
|
||||
/// </summary>
|
||||
public const ushort None = 0;
|
||||
|
||||
/// <summary>
|
||||
/// General system state feature (bit 0)
|
||||
/// </summary>
|
||||
public const ushort GeneralSystemState = 1 << 0;
|
||||
|
||||
/// <summary>
|
||||
/// Derived settings feature (bit 1)
|
||||
/// </summary>
|
||||
public const ushort DerivedSettings = 1 << 1;
|
||||
|
||||
/// <summary>
|
||||
/// Measurement data feature (bit 2)
|
||||
/// </summary>
|
||||
public const ushort MeasurementData = 1 << 2;
|
||||
|
||||
/// <summary>
|
||||
/// Intrusion data feature (bit 3)
|
||||
/// </summary>
|
||||
public const ushort IntrusionData = 1 << 3;
|
||||
|
||||
/// <summary>
|
||||
/// Application data feature (bit 4)
|
||||
/// </summary>
|
||||
public const ushort ApplicationData = 1 << 4;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a certain feature flag is set in the bitset
|
||||
/// </summary>
|
||||
/// <param name="bitset">The bitset expressed as ushort</param>
|
||||
/// <param name="flag">The feature flag to check</param>
|
||||
/// <returns>True if the flag is set, false otherwise</returns>
|
||||
public static bool IsFlagSet(ushort bitset, ushort flag)
|
||||
{
|
||||
return (bitset & flag) == flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts boolean indicators (for the sensor features to be streamed) into a bitset
|
||||
/// </summary>
|
||||
/// <param name="generalSystemState">Indicator for the general system state channel</param>
|
||||
/// <param name="derivedSettings">Turn on/off derived values</param>
|
||||
/// <param name="measurementData">Turn on/off measurement data</param>
|
||||
/// <param name="intrusionData">Turn on/off safety field intrusion data</param>
|
||||
/// <param name="applicationData">Turn on/off application data</param>
|
||||
/// <returns>A bitset containing indication flags for each feature channel</returns>
|
||||
public static ushort ToFeatureFlags(
|
||||
bool generalSystemState,
|
||||
bool derivedSettings,
|
||||
bool measurementData,
|
||||
bool intrusionData,
|
||||
bool applicationData)
|
||||
{
|
||||
return (ushort)(
|
||||
(generalSystemState ? GeneralSystemState : 0) +
|
||||
(derivedSettings ? DerivedSettings : 0) +
|
||||
(measurementData ? MeasurementData : 0) +
|
||||
(intrusionData ? IntrusionData : 0) +
|
||||
(applicationData ? ApplicationData : 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the serial number from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class SerialNumber
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the serial number string
|
||||
/// </summary>
|
||||
public string Number { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Device state enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum DeviceState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal state
|
||||
/// </summary>
|
||||
Normal = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Error state
|
||||
/// </summary>
|
||||
Error = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Initialization state
|
||||
/// </summary>
|
||||
Initialization = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Shutdown state
|
||||
/// </summary>
|
||||
Shutdown = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Optics cover calibration state
|
||||
/// </summary>
|
||||
OpticsCoverCalibration = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Config state enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum ConfigState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown config state
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Config required
|
||||
/// </summary>
|
||||
ConfigRequired = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Config in progress
|
||||
/// </summary>
|
||||
ConfigInProgress = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Not verified
|
||||
/// </summary>
|
||||
NotVerified = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Rejected
|
||||
/// </summary>
|
||||
Rejected = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Verified
|
||||
/// </summary>
|
||||
Verified = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Internal error
|
||||
/// </summary>
|
||||
InternalError = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Verification in progress
|
||||
/// </summary>
|
||||
VerificationInProgress = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application state enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum ApplicationState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Application stopped
|
||||
/// </summary>
|
||||
Stopped = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Application starting
|
||||
/// </summary>
|
||||
Starting = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Waiting for partners
|
||||
/// </summary>
|
||||
WaitingForPartners = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Waiting for inputs
|
||||
/// </summary>
|
||||
WaitingForInputs = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Application started
|
||||
/// </summary>
|
||||
Started = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Sleep mode
|
||||
/// </summary>
|
||||
SleepMode = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the status overview from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class StatusOverview
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device state
|
||||
/// </summary>
|
||||
public DeviceState DeviceState { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the config state
|
||||
/// </summary>
|
||||
public ConfigState ConfigState { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application state
|
||||
/// </summary>
|
||||
public ApplicationState ApplicationState { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current time power on count
|
||||
/// </summary>
|
||||
public uint CurrentTimePowerOnCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current time (milliseconds since midnight)
|
||||
/// </summary>
|
||||
public uint CurrentTimeTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current date (days since 1980-01-01)
|
||||
/// </summary>
|
||||
public ushort CurrentTimeDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error info code
|
||||
/// </summary>
|
||||
public uint ErrorInfoCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error info time (milliseconds since midnight)
|
||||
/// </summary>
|
||||
public uint ErrorInfoTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error info date (days since 1980-01-01)
|
||||
/// </summary>
|
||||
public ushort ErrorInfoDate { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Interface type enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum InterfaceType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// EFI-pro interface
|
||||
/// </summary>
|
||||
EfiPro = 0,
|
||||
|
||||
/// <summary>
|
||||
/// EtherNet/IP interface
|
||||
/// </summary>
|
||||
EthernetIp = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Profinet interface
|
||||
/// </summary>
|
||||
Profinet = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Non-safe Ethernet interface
|
||||
/// </summary>
|
||||
NonSafeEthernet = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Range enumeration for SICK Safety Scanner
|
||||
/// </summary>
|
||||
public enum RangeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal range (40m)
|
||||
/// </summary>
|
||||
NormalRange = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Long range (64m)
|
||||
/// </summary>
|
||||
LongRange = 64
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the type code from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class TypeCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type code string
|
||||
/// </summary>
|
||||
public string Code { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface type
|
||||
/// </summary>
|
||||
public InterfaceType InterfaceType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum range in meters
|
||||
/// </summary>
|
||||
public double MaxRange { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all data blocks from a UDP scan data packet
|
||||
/// </summary>
|
||||
public sealed class UdpScanData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the data header
|
||||
/// </summary>
|
||||
public DataHeader Header { get; init; } = new DataHeader();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the general system state
|
||||
/// </summary>
|
||||
public GeneralSystemState? GeneralSystemState { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the derived values (configuration of data output)
|
||||
/// </summary>
|
||||
public DerivedValues? DerivedValues { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the measurement data (scan points)
|
||||
/// </summary>
|
||||
public MeasurementData? MeasurementData { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the intrusion data (field interruption)
|
||||
/// </summary>
|
||||
public IntrusionData? IntrusionData { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application data (inputs and outputs)
|
||||
/// </summary>
|
||||
public ApplicationData? ApplicationData { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp from the header as DateTime (if available)
|
||||
/// Returns UTC DateTime to ensure consistency with Cartographer timestamp handling
|
||||
///
|
||||
/// SICK Timestamp Format (according to official documentation):
|
||||
/// - TimestampDate (ushort, offset 24):
|
||||
/// * If UTC server is time master: Days since 1972-01-01
|
||||
/// * If Safety Designer device is time master: Number of full 24-hour cycles of time master
|
||||
/// * If no time sync: Number of full 24-hour cycles since device was switched on
|
||||
/// - TimestampTime (uint, offset 28): Milliseconds since midnight (or start of 24-hour cycle)
|
||||
///
|
||||
/// Since we cannot detect time sync status from UDP packet, we try both base dates:
|
||||
/// 1. Try 1972-01-01 (UTC server case) - most common in production
|
||||
/// 2. If result is unreasonable, try 1980-01-01 (legacy/fallback)
|
||||
/// 3. If TimestampDate = 0: Use current date as base
|
||||
/// </summary>
|
||||
public DateTime? Timestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Header.TimestampDate == 0 && Header.TimestampTime == 0)
|
||||
return null;
|
||||
|
||||
DateTime baseDate;
|
||||
string baseDateSource = "unknown";
|
||||
|
||||
if (Header.TimestampDate == 0)
|
||||
{
|
||||
// Date not set on device - use current date as base
|
||||
var now = DateTime.UtcNow;
|
||||
baseDate = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0, DateTimeKind.Utc);
|
||||
baseDateSource = "current_date";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try 1972-01-01 first (UTC server case - most common according to documentation)
|
||||
var baseDate1972 = new DateTime(1972, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var time1972 = baseDate1972.AddDays(Header.TimestampDate).AddMilliseconds(Header.TimestampTime);
|
||||
|
||||
// Check if timestamp with 1972 base is reasonable (within 1 day of now)
|
||||
var currentTimeTicks = DateTime.UtcNow.Ticks;
|
||||
var time1972Ticks = time1972.Ticks;
|
||||
var diff1972Ms = Math.Abs(time1972Ticks - currentTimeTicks) / TimeSpan.TicksPerMillisecond;
|
||||
|
||||
if (diff1972Ms <= 86400000) // Within 1 day - reasonable
|
||||
{
|
||||
baseDate = baseDate1972;
|
||||
baseDateSource = "1972-01-01 (UTC server)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try 1980-01-01 as fallback (legacy format or no UTC sync)
|
||||
var baseDate1980 = new DateTime(1980, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var time1980 = baseDate1980.AddDays(Header.TimestampDate).AddMilliseconds(Header.TimestampTime);
|
||||
var time1980Ticks = time1980.Ticks;
|
||||
var diff1980Ms = Math.Abs(time1980Ticks - currentTimeTicks) / TimeSpan.TicksPerMillisecond;
|
||||
|
||||
if (diff1980Ms <= diff1972Ms) // 1980 is closer to now
|
||||
{
|
||||
baseDate = baseDate1980;
|
||||
baseDateSource = "1980-01-01 (legacy/fallback)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1972 is closer, use it even if not perfect
|
||||
baseDate = baseDate1972;
|
||||
baseDateSource = "1972-01-01 (best match)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add days and milliseconds to base date
|
||||
var time = baseDate.AddDays(Header.TimestampDate).AddMilliseconds(Header.TimestampTime);
|
||||
|
||||
// Validate: timestamp should not be too far in the past or future
|
||||
var nowTicks = DateTime.UtcNow.Ticks;
|
||||
var timeTicks = time.Ticks;
|
||||
var diffMs = Math.Abs(timeTicks - nowTicks) / TimeSpan.TicksPerMillisecond;
|
||||
|
||||
// Debug logging for timestamp calculation
|
||||
if (diffMs > 3600000) // Log if more than 1 hour off
|
||||
{
|
||||
var timestampDateStr = Header.TimestampDate == 0 ? "0 (using current date)" : Header.TimestampDate.ToString();
|
||||
var timestampTimeStr = $"{Header.TimestampTime}ms ({Header.TimestampTime / 3600000.0:F2} hours)";
|
||||
var calculatedTime = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"[SICK Timestamp Debug] TimestampDate={timestampDateStr}, TimestampTime={timestampTimeStr}, " +
|
||||
$"BaseDateSource={baseDateSource}, CalculatedTime={calculatedTime}, " +
|
||||
$"DiffFromNow={diffMs / 3600000.0:F2} hours");
|
||||
}
|
||||
|
||||
// If timestamp is more than 1 day off, it's likely incorrect
|
||||
// This can happen if device date is not set correctly or wrong base date
|
||||
if (diffMs > 86400000) // 1 day in milliseconds
|
||||
{
|
||||
// Use current time instead of potentially incorrect device timestamp
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// Ensure the result is marked as UTC
|
||||
return DateTime.SpecifyKind(time, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs for UdpScanDataReceived event
|
||||
/// </summary>
|
||||
public sealed class UdpScanDataEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the timestamp when scan data was received
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parsed UDP scan data
|
||||
/// </summary>
|
||||
public UdpScanData ScanData { get; init; }
|
||||
|
||||
public UdpScanDataEventArgs(DateTime timestamp, UdpScanData scanData)
|
||||
{
|
||||
Timestamp = timestamp;
|
||||
ScanData = scanData ?? throw new ArgumentNullException(nameof(scanData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Sick.SafetyScanners.DataStructures;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the user name from a COLA2 variable command response
|
||||
/// </summary>
|
||||
public sealed class UserName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version indicator (e.g., "V" for version)
|
||||
/// </summary>
|
||||
public string VersionCVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the major version number
|
||||
/// </summary>
|
||||
public byte VersionMajorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minor version number
|
||||
/// </summary>
|
||||
public byte VersionMinorVersionNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version release number
|
||||
/// </summary>
|
||||
public byte VersionReleaseNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the user name
|
||||
/// </summary>
|
||||
public uint NameLength { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user name string
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user