481 lines
20 KiB
C#
481 lines
20 KiB
C#
|
|
using System;
|
|||
|
|
using System.Threading;
|
|||
|
|
using DTS.Common.DASResource;
|
|||
|
|
using DTS.Common.ICommunication;
|
|||
|
|
using DTS.DASLib.Command.SLICE;
|
|||
|
|
using DTS.Common.Utilities.Logging;
|
|||
|
|
using DTS.Common.Interface.Connection;
|
|||
|
|
using DTS.Common.Classes.Connection;
|
|||
|
|
using DTS.Common.Interface.DASFactory;
|
|||
|
|
using DTS.Common.Enums.DASFactory;
|
|||
|
|
using static DTS.Common.Enums.DASFactory.DFConstantsAndEnums;
|
|||
|
|
using DTS.Common.Events;
|
|||
|
|
using DTS.Common.Enums;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using DTS.Common.Enums.Sensors;
|
|||
|
|
|
|||
|
|
namespace DTS.DASLib.Service
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
public partial class Slice<T> : Communication<T>,
|
|||
|
|
IDASCommunication,
|
|||
|
|
IConfigurationActions,
|
|||
|
|
IDiagnosticsActions,
|
|||
|
|
ITriggerCheckActions,
|
|||
|
|
IRealTimeActions,
|
|||
|
|
IArmActions,
|
|||
|
|
IDownloadActions where T : IConnection, new()
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns the requested range for an analog channel, making adjustment for nonlinear if needed
|
|||
|
|
/// </summary>
|
|||
|
|
protected static double GetRequestedRange(AnalogInputDASChannel analog, double MvPerEU)
|
|||
|
|
{
|
|||
|
|
if (null != analog.LinearizationFormula && analog.LinearizationFormula.IsValid())
|
|||
|
|
{
|
|||
|
|
switch (analog.LinearizationFormula.NonLinearStyle)
|
|||
|
|
{
|
|||
|
|
case NonLinearStyles.Polynomial:
|
|||
|
|
//polynomial can scale and should bypass this?
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
if (MvPerEU.Equals(1))
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"GetRequestRange HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU} - {InputRangeMV}");
|
|||
|
|
return InputRangeMV;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
APILogger.DebugLog($"GetRequestRange HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU} DesiredRange={analog.DesiredRangeWithHeadroomEU}");
|
|||
|
|
return analog.DesiredRangeWithHeadroomEU * MvPerEU;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public virtual bool SupportsRemoveLeapSeconds { get => false; }
|
|||
|
|
protected void SetRemoveSeconds()
|
|||
|
|
{
|
|||
|
|
//http://manuscript.dts.local/f/cases/31747/Add-support-for-GPS-Time-leap-seconds
|
|||
|
|
if (IsCommandSupported(ProtocolLimitedCommands.RemoveLeapSeconds))
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var set = new SetSystemAttributeSLICE6AIR(this);
|
|||
|
|
set.SetValue(AttributeTypes.SystemAttributesSLICE6AIR.RemoveLeapSeconds, RunTestVariables.RemoveLeapSeconds ? (byte)1 : (byte)0, true);
|
|||
|
|
set.SyncExecute();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
protected virtual QATSExtendedFault[] GetExtendedFaultFlags()
|
|||
|
|
{
|
|||
|
|
var list = new List<QATSExtendedFault>();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!IsCommandSupported(ProtocolLimitedCommands.ExtendedFaultIds)) { return list.ToArray(); }
|
|||
|
|
var qee = new QueryArmAttribute(this) { Key = AttributeTypes.ArmAndEventAttributes.ExtFaultFlags, DeviceID = 0 };
|
|||
|
|
qee.SyncExecute();
|
|||
|
|
if (qee.Value is uint[] uints)
|
|||
|
|
{
|
|||
|
|
var first = uints[0];
|
|||
|
|
for (var i = 0; i < 32; i++)
|
|||
|
|
{
|
|||
|
|
if (0 != (first & (1 << i)))
|
|||
|
|
{
|
|||
|
|
list.Add((QATSExtendedFault)(1 << i));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
return list.ToArray();
|
|||
|
|
}
|
|||
|
|
public virtual bool SupportsADCSamplesPerPacket { get => false; }
|
|||
|
|
protected void SetADCSamplesPerPacket(int adcPerPacket)
|
|||
|
|
{
|
|||
|
|
//http://manuscript.dts.local/f/cases/31754/
|
|||
|
|
if (IsCommandSupported(ProtocolLimitedCommands.ADCSamplesPerPacket))
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var get = new QuerySystemAttribute(this) { Key = AttributeTypes.SystemAttributes.S6A_IrigStreamBufferConfig };
|
|||
|
|
get.SyncExecute();
|
|||
|
|
var getParams = (ushort[])get.Value;
|
|||
|
|
getParams[1] = (ushort)adcPerPacket;
|
|||
|
|
var set = new SetSystemAttribute(this);
|
|||
|
|
set.SetValue(AttributeTypes.SystemAttributes.S6A_IrigStreamBufferConfig, getParams, true);
|
|||
|
|
set.SyncExecute();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
APILogger.Log($"ADC samples per second not supported by firmware on {SerialNumber}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected virtual uint[] GetExtendedFaultFlags(int eventIndex)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var qee = new QueryEventAttribute(this) { Key = AttributeTypes.ArmAndEventAttributes.ExtFaultFlags, DeviceID = 0, EventNumber = Convert.ToUInt16(eventIndex) };
|
|||
|
|
qee.SyncExecute();
|
|||
|
|
return (uint[])qee.Value;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
return new uint[] { 0, 0, 0, 0 };
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// surfaces an error to the application, if applicable
|
|||
|
|
/// http://manuscript.dts.local/f/cases/28312/Surface-Read-does-not-match-write-to-user
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="msg"></param>
|
|||
|
|
protected static void SurfaceApplicationError(string msg)
|
|||
|
|
{
|
|||
|
|
PageErrorEvent.SurfaceApplicationError(msg);
|
|||
|
|
}
|
|||
|
|
public ExcitationStatus ExcitationStatus { get; set; } = ExcitationStatus.Unknown;
|
|||
|
|
public virtual void SetIsStreamingSupported(bool supported = false)
|
|||
|
|
{
|
|||
|
|
IsStreamingSupported = false;
|
|||
|
|
}
|
|||
|
|
public virtual void ReadFirstUseDate()
|
|||
|
|
{
|
|||
|
|
IsFirstUseDateSupported = false;
|
|||
|
|
FirstUseDate = null;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// indicates date of first use
|
|||
|
|
/// null indicates the hardware has not been used since calibration
|
|||
|
|
/// only valid when IsFirstUseDateSupported is true
|
|||
|
|
/// 15524 DAS "First Use Date"
|
|||
|
|
/// </summary>
|
|||
|
|
public DateTime? FirstUseDate { get; set; } = null;
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns whether the hardware supports first use or not
|
|||
|
|
/// for hardware to support first use the hardware must support
|
|||
|
|
/// storage for user attributes in firmware and also have been
|
|||
|
|
/// calibrated by software support hardware first use
|
|||
|
|
/// 15524 DAS "First Use Date"
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsFirstUseDateSupported { get; set; } = false;
|
|||
|
|
/// <summary>
|
|||
|
|
/// indicates whether or not streaming is supported
|
|||
|
|
/// 30429 TSR AIRs can enable/disable streaming via the DISABLE_STREAMING_FEATURE system attribute
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsStreamingSupported { get; set; } = false;
|
|||
|
|
public int RecordId { get; set; } = Common.Enums.Hardware.HardwareConstants.INVALID_IDASCOMMUNICATION_RECORD_ID;
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns the total number of channels in the event
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="eventNum"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
protected virtual uint GetEventTotalChannels(int eventNum)
|
|||
|
|
{
|
|||
|
|
var eventTC = new QueryEventAttribute(this);
|
|||
|
|
eventTC.EventNumber = (ushort)eventNum;
|
|||
|
|
eventTC.Key = AttributeTypes.ArmAndEventAttributes.TotalChannels;
|
|||
|
|
eventTC.SyncExecute();
|
|||
|
|
return (byte)eventTC.Value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string MACAddress { get; set; }
|
|||
|
|
public string[] DownstreamMACAddresses { get; set; }
|
|||
|
|
public virtual bool IsEthernetDistributor() { return false; }
|
|||
|
|
public virtual bool IsSlice6Distributor() { return false; }
|
|||
|
|
public virtual bool IsBattery() { return false; }
|
|||
|
|
public virtual bool IsTSRAIR() { return false; }
|
|||
|
|
public virtual bool IsSlice6Air() { return false; }
|
|||
|
|
public virtual bool IsSlice6AirTc() { return false; }
|
|||
|
|
public virtual bool IsScheduleEventCountSupported() { return false; }
|
|||
|
|
|
|||
|
|
public class SliceServiceQueryConfigAsyncInfo : SliceServiceAsyncInfo
|
|||
|
|
{
|
|||
|
|
public uint CRC { get; set; } = 0;
|
|||
|
|
public string ConfigString { get; set; } = string.Empty;
|
|||
|
|
public bool ReadIds { get; set; } = true;
|
|||
|
|
|
|||
|
|
public bool DeviceScaleFactors { get; set; } = true;
|
|||
|
|
public SliceServiceQueryConfigAsyncInfo(ServiceCallback _callback, object _userData, uint crc,
|
|||
|
|
string strConfig, bool bReadIds, bool bDeviceScaleFactors, bool differentModuleCountsAreOK) : base(_callback, _userData)
|
|||
|
|
{
|
|||
|
|
CRC = crc;
|
|||
|
|
ConfigString = strConfig;
|
|||
|
|
ReadIds = bReadIds;
|
|||
|
|
DeviceScaleFactors = bDeviceScaleFactors;
|
|||
|
|
DifferentModuleCountsAreOK = differentModuleCountsAreOK;
|
|||
|
|
}
|
|||
|
|
public bool DifferentModuleCountsAreOK { get; set; } = false;
|
|||
|
|
}
|
|||
|
|
public class SliceServiceQueryTestSetupAsyncInfo : SliceServiceAsyncInfo
|
|||
|
|
{
|
|||
|
|
public string TestSetupGuid { get; set; }
|
|||
|
|
public SliceServiceQueryTestSetupAsyncInfo(ServiceCallback _callback, object _userData, string testSetupGuid) : base(_callback, _userData)
|
|||
|
|
{
|
|||
|
|
TestSetupGuid = testSetupGuid;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class SliceServiceSetTestSetupAsyncInfo : SliceServiceAsyncInfo
|
|||
|
|
{
|
|||
|
|
public string TestSetupXML { get; set; }
|
|||
|
|
public SliceServiceSetTestSetupAsyncInfo(ServiceCallback _callback, object _userData, string testSetupXML) : base(_callback, _userData)
|
|||
|
|
{
|
|||
|
|
TestSetupXML = testSetupXML;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
public class AutoDetectServiceAsyncInfo : SliceServiceAsyncInfo
|
|||
|
|
{
|
|||
|
|
public bool QueryConfiguration { get; set; } = true;
|
|||
|
|
|
|||
|
|
public AutoDetectServiceAsyncInfo(bool queryConfiguration, ServiceCallback callback, object userData)
|
|||
|
|
: base(callback, userData)
|
|||
|
|
{
|
|||
|
|
QueryConfiguration = queryConfiguration;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
public class SliceServiceAsyncInfo
|
|||
|
|
{
|
|||
|
|
public ServiceCallback callback { get; set; }
|
|||
|
|
public object userData { get; set; }
|
|||
|
|
public object functionData { get; set; }
|
|||
|
|
public PrePostResults PreOrPost { get; set; }
|
|||
|
|
public int? MaxTimeout { get; set; }
|
|||
|
|
|
|||
|
|
public SliceServiceAsyncInfo(ServiceCallback _callback, object _userData)
|
|||
|
|
{
|
|||
|
|
callback = _callback;
|
|||
|
|
userData = _userData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Error(string msg, Exception ex)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var cbData = new ServiceCallbackData();
|
|||
|
|
cbData.Status = ServiceCallbackData.CallbackStatus.Failure;
|
|||
|
|
cbData.ErrorMessage = msg;
|
|||
|
|
cbData.ErrorException = ex;
|
|||
|
|
cbData.UserData = userData;
|
|||
|
|
callback(cbData);
|
|||
|
|
}
|
|||
|
|
catch (Exception eex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoError, eex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Error(string msg)
|
|||
|
|
{
|
|||
|
|
Error(msg, null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Progress(int value)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.ProgressReport;
|
|||
|
|
progressData.ProgressValue = value;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoProgressError, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//public void NewData(IList<short[][]> datas, IList<UInt64> SampleNumbers)
|
|||
|
|
public void NewData(object obj)
|
|||
|
|
{
|
|||
|
|
if (obj is ServiceCallbackData.DiagnosticNewData)
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
progressData.SetNewDiagnosticData((obj as ServiceCallbackData.DiagnosticNewData));
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
else if (obj is ServiceCallbackData.TiltNewData)
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
progressData.SetNewTiltData((obj as ServiceCallbackData.TiltNewData));
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
else if (obj is ServiceCallbackData.UARTNewData)
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
progressData.SetNewUARTData(obj as ServiceCallbackData.UARTNewData);
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
else if (obj is byte[])
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.SetNewByteData((byte[])obj);
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
else if (obj is DFConstantsAndEnums.T0CorrectionStatus status)
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
var i = (int)status;
|
|||
|
|
progressData.SetNewByteData(BitConverter.GetBytes(i));
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var newdatadata = obj as NewDataData;
|
|||
|
|
var datas = newdatadata.datas;
|
|||
|
|
var samplenumbers = newdatadata.SampleNumbers;
|
|||
|
|
var timeStamps = newdatadata.TimeStamps;
|
|||
|
|
var sequenceNumbers = newdatadata.SequenceNumbers;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
var sequenceNumber = 0UL;
|
|||
|
|
for (int i = 0; i < datas.Length && i < samplenumbers.Length; i++)
|
|||
|
|
{
|
|||
|
|
//polling apparently doesn't always populate sequence number
|
|||
|
|
//to prevent an indexing error just use the last one
|
|||
|
|
//18009 DataPRO becomes unusable when trying to put realtime in meter mode
|
|||
|
|
if (i < sequenceNumbers.Length) { sequenceNumber = sequenceNumbers[i]; }
|
|||
|
|
progressData.AddSampleData(datas[i], samplenumbers[i], timeStamps[i], sequenceNumber);
|
|||
|
|
}
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoNewDataError, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
public void NewData(short[][] data, UInt64 samplenumber, ulong timeStamp, ulong sequenceNumber)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var progressData = new ServiceCallbackData();
|
|||
|
|
progressData.Status = ServiceCallbackData.CallbackStatus.NewData;
|
|||
|
|
progressData.ProgressValue = 0;
|
|||
|
|
progressData.UserData = userData;
|
|||
|
|
progressData.AddSampleData(data, samplenumber, timeStamp, sequenceNumber);
|
|||
|
|
callback(progressData);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoNewDataError, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Success()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var success = new ServiceCallbackData();
|
|||
|
|
success.Status = ServiceCallbackData.CallbackStatus.Success;
|
|||
|
|
success.UserData = userData;
|
|||
|
|
callback(success);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoSuccessError, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Cancel()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var cancelReport = new ServiceCallbackData();
|
|||
|
|
cancelReport.Status = ServiceCallbackData.CallbackStatus.Canceled;
|
|||
|
|
cancelReport.ProgressValue = 0;
|
|||
|
|
cancelReport.UserData = userData;
|
|||
|
|
callback(cancelReport);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log("MessageBox", Strings.SLICEAsyncInfoCancelError, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void LaunchAsyncWorker(string Invoker, WaitCallback cb, object asyncInfo)
|
|||
|
|
{
|
|||
|
|
if (!Connected)
|
|||
|
|
{
|
|||
|
|
// "{0}: Not currently connected"
|
|||
|
|
throw new NotConnectedException(string.Format(Strings.Slice_LaunchAsyncWorker_Err1, Invoker));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!ThreadPool.QueueUserWorkItem(cb, asyncInfo))
|
|||
|
|
{
|
|||
|
|
// "{0}: Unable to enqueue function"
|
|||
|
|
throw new Exception(string.Format(Strings.Slice_LaunchAsyncWorker_Err2, Invoker));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// compare to an object to determine equality
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="right"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override bool Equals(object right)
|
|||
|
|
{
|
|||
|
|
if (right == null)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
if (ReferenceEquals(this, right))
|
|||
|
|
return true;
|
|||
|
|
|
|||
|
|
var rightSlice = right as Slice<T>;
|
|||
|
|
if (rightSlice == null)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(SerialNumber))
|
|||
|
|
{
|
|||
|
|
return string.IsNullOrEmpty(rightSlice.SerialNumber);
|
|||
|
|
}
|
|||
|
|
if (string.IsNullOrEmpty(rightSlice.SerialNumber))
|
|||
|
|
return false;
|
|||
|
|
return SerialNumber == rightSlice.SerialNumber;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns identical index for any two 'equal' slice
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override int GetHashCode()
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(SerialNumber))
|
|||
|
|
return 0;
|
|||
|
|
return SerialNumber.GetHashCode();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|