public class Data
{
private const double DefaultCollectionPeriod = 1.0;
private readonly Controller controller;
private ConfiguredRecord[] configuredRecords;
private double configuredRate;
private DataCollectionConfiguration configuration = new DataCollectionConfiguration();
private readonly ContinuousDataCollectionPoller continuousDataPoller;
private readonly AxesDiagPacketRetriever axesDiagRetriever;
public DataCollectionStatus Status
{
get
{
DATACOLLECTION_STATUS pDataCollectionStatus_ = default(DATACOLLECTION_STATUS);
ExceptionResolver.ResolveThrow(controller, Wrapper.getDataCollectionStatus(controller.Handle.Value, ref pDataCollectionStatus_));
bool isContinuousMode = (pDataCollectionStatus_.dwStatus & 0x200) != 0;
return new DataCollectionStatus(pDataCollectionStatus_.dwAllocated, pDataCollectionStatus_.dwCollected, pDataCollectionStatus_.dwRetrieved, ((pDataCollectionStatus_.dwStatus & 4) | (pDataCollectionStatus_.dwStatus & 0x10000)) != 0, isContinuousMode, (pDataCollectionStatus_.dwStatus & 0x10) != 0, (pDataCollectionStatus_.dwStatus & 0x800) != 0, pDataCollectionStatus_.dwScopeTrigID);
}
}
public DataCollectionConfiguration Configuration => configuration;
internal ContinuousDataCollectionPoller Poller => continuousDataPoller;
internal AxesDiagPacketRetriever AxesDiagPacketRetriever => axesDiagRetriever;
public event EventHandler<ContinuousDataSamplesRetrievedEventArgs> ContinuousDataSamplesRetrieved
{
add
{
continuousDataPoller.ContinuousDataSamplesRetrieved += value;
}
remove
{
continuousDataPoller.ContinuousDataSamplesRetrieved -= value;
}
}
internal Data(Controller controller)
{
this.controller = controller;
axesDiagRetriever = new AxesDiagPacketRetriever(controller);
continuousDataPoller = new ContinuousDataCollectionPoller(controller);
}
public ControllerDiagPacket RetrieveDiagnostics()
{
return RetrieveDiagnostics(new UnitInformation(DistanceUnit.Primary, TimeUnit.Seconds));
}
public ControllerDiagPacket RetrieveDiagnostics(UnitInformation units)
{
KeyValuePair<DiagPacket[], double[]> axesDiagnostics = axesDiagRetriever.GetAxesDiagnostics(units);
return new ControllerDiagPacket(controller, units, axesDiagnostics.Key, axesDiagnostics.Value);
}
public void ApplyConfiguration()
{
ApplyConfiguration(configuration);
}
public void ApplyConfiguration(DataCollectionConfiguration configuration)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
setCollectionConfiguration(configuration);
}
public DataCollectionResults GetData()
{
return GetData(Status.PointsAllocated);
}
public DataCollectionResults GetData(ProgressChangedEventHandler progressChangedEventHandler)
{
return GetData(Status.PointsAllocated, progressChangedEventHandler);
}
public DataCollectionResults GetData(int pointsToRetrieve)
{
if (pointsToRetrieve < 0)
{
throw new ArgumentOutOfRangeException("pointsToRetrieve");
}
return getResults(pointsToRetrieve);
}
public DataCollectionResults GetData(int pointsToRetrieve, ProgressChangedEventHandler progressChangedEventHandler)
{
if (pointsToRetrieve < 0)
{
throw new ArgumentOutOfRangeException("pointsToRetrieve");
}
bool flag = false;
DataCollectionStatus status = Status;
Action<int> action = delegate (int progress)
{
if (progressChangedEventHandler != null)
{
progressChangedEventHandler(controller, new ProgressChangedEventArgs(progress, null));
}
};
while (status.PointsRetrieved < pointsToRetrieve && pointsToRetrieve <= status.PointsAllocated)
{
if (!status.IsCollecting)
{
flag = true;
break;
}
action(Math.Min(100, (int)(100.0 * (double)status.PointsRetrieved / (double)pointsToRetrieve)));
Thread.Sleep(10);
status = Status;
}
if (!flag)
{
action(100);
}
return GetData(pointsToRetrieve);
}
public void Start()
{
Start(configuration);
}
public void Start(DataCollectionConfiguration configuration)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
setCollectionConfiguration(configuration);
triggerDataCollection(DataCollectionTriggerType.Immediate);
}
public void StartContinuous(int numberOfPoints)
{
StartContinuous(configuration, numberOfPoints);
}
public void StartContinuous(DataCollectionConfiguration configuration, int numberOfPoints)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
if (numberOfPoints < 0 || numberOfPoints > configuration.PointsToCollect)
{
throw new ArgumentException("numberOfPoints");
}
setCollectionConfiguration(configuration);
if (numberOfPoints > 0)
{
continuousDataPoller.Samples = numberOfPoints;
continuousDataPoller.Start();
}
triggerDataCollection(DataCollectionTriggerType.Continuous);
}
public void WaitForData(int points)
{
WaitForData(points, null);
}
public void WaitForData(int points, ProgressChangedEventHandler waiter)
{
if (points <= 0)
{
throw new ArgumentOutOfRangeException(string.Format(Resources.ArgumentMustBePositive, "points"));
}
if (points > Status.PointsAllocated)
{
throw new InvalidOperationException(string.Format(Resources.ArgumentGreaterThanPointsAllocated, "points"));
}
int num = 0;
int num2 = 0;
bool flag = false;
DataCollectionStatus status = Status;
while (num < points)
{
if (!status.IsCollecting)
{
flag = true;
break;
}
if (waiter != null && num2 != 100 * num / points)
{
num2 = 100 * num / points;
waiter(controller, new ProgressChangedEventArgs(num2, null));
}
Thread.Sleep(10);
status = Status;
num = status.PointsCollected;
}
if (waiter != null && !flag)
{
waiter(controller, new ProgressChangedEventArgs(100, null));
}
}
public void Stop()
{
ExceptionResolver.ResolveThrow(controller, Wrapper.dataCollectionHalt(controller.Handle.Value));
continuousDataPoller.Stop();
}
private void setCollectionConfiguration(DataCollectionConfiguration configuration)
{
ErrorData err = new ErrorData(92, 5);
int num = (int)((configuration.SampleTrigger.Time == null) ? configuration.SampleTrigger.Change.Trigger : ((DataCollectionSampleTrigger)0));
int num2 = ((configuration.SampleTrigger.Time == null) ? configuration.SampleTrigger.Change.Signal : 0);
int num3 = ((configuration.SampleTrigger.Time != null) ? (-1) : configuration.SampleTrigger.Change.Index);
int sampleTriggerOptional_ = ((configuration.SampleTrigger.Time == null) ? configuration.SampleTrigger.Change.Optional : 0);
if (configuration.SampleTrigger.Change != null)
{
if (configuration.SampleTrigger.Change.VariableName != null)
{
Variable variable = null;
if (controller.Variables[configuration.SampleTrigger.Change.VariableName] != null)
{
variable = controller.Variables[configuration.SampleTrigger.Change.VariableName];
}
else
{
if (controller.Variables.Tasks[configuration.SampleTrigger.Change.Index][configuration.SampleTrigger.Change.VariableName] == null)
{
throw new ArgumentOutOfRangeException("variable", Resources.ErrorVariableNameNotFound, variable.Name);
}
variable = controller.Variables.Tasks[configuration.SampleTrigger.Change.Index][configuration.SampleTrigger.Change.VariableName];
}
if (variable.ValueType == VariableType.String)
{
ExceptionResolver.ResolveThrow(controller, err);
}
VariableDescriptor[] array = new VariableDescriptor[1];
array[0].name = variable.Name;
array[0].taskId = (TaskId)variable.ContextKey;
num2 = CoreVariableHelper.CreateVariableSignalRecords(controller, array)[0].itemCode;
num3 = variable.ContextKey;
sampleTriggerOptional_ = variable.Number;
}
else if (configuration.SampleTrigger.Change.AxisName != null)
{
num3 = controller.Information.Axes[configuration.SampleTrigger.Change.AxisName].Number;
}
}
ConfiguredRecord[] array2 = DataCollectionHelper.ConvertConfigurationToRecords(controller, configuration);
List<DATACOLLECTION_RECORD> list = new List<DATACOLLECTION_RECORD>();
ConfiguredRecord[] array3 = array2;
for (int i = 0; i < array3.Length; i++)
{
ConfiguredRecord configuredRecord = array3[i];
list.Add(configuredRecord.coreRecord);
}
double msec = configuration.SampleTrigger.getMsec();
DataCollectionRate collectionRate = DataCollectionHelper.GetCollectionRate(msec);
ExceptionResolver.ResolveThrow(controller, Wrapper.setDataCollectionConfiguration(controller.Handle.Value, list.ToArray(), (short)array2.Length, configuration.PointsToCollect, collectionRate.msecBetweenSamples, collectionRate.samplesPerMsec, (short)num, (short)num2, (short)num3, sampleTriggerOptional_));
configuredRecords = array2;
configuredRate = msec;
}
private void triggerDataCollection(DataCollectionTriggerType triggerType)
{
ExceptionResolver.ResolveThrow(controller, Wrapper.setDataCollectionTriggerMode(controller.Handle.Value, (short)triggerType));
}
private DataCollectionResults getResults(int numRequested)
{
if (configuredRecords == null)
{
throw new InvalidOperationException();
}
double[] array = new double[configuredRecords.Length * numRequested];
if (numRequested > 0 && configuredRecords.Length != 0)
{
if (!Status.IsContinuousMode)
{
ExceptionResolver.ResolveThrow(controller, Wrapper.getDataSamples(controller.Handle.Value, 0, numRequested, array));
}
else
{
ExceptionResolver.ResolveThrow(controller, Wrapper.getQueueSamples(controller.Handle.Value, numRequested, array));
}
}
double[][] array2 = new double[configuredRecords.Length][];
for (int i = 0; i < configuredRecords.Length; i++)
{
array2[i] = new double[numRequested];
}
for (int j = 0; j < configuredRecords.Length * numRequested; j++)
{
int num = j % configuredRecords.Length;
int num2 = j / configuredRecords.Length;
array2[num][num2] = array[j];
}
List<CollectedAxisDataSignalEntry> list = new List<CollectedAxisDataSignalEntry>();
List<CollectedAxisExtendedDataSignalEntry> list2 = new List<CollectedAxisExtendedDataSignalEntry>();
List<CollectedSystemDataSignalEntry> list3 = new List<CollectedSystemDataSignalEntry>();
List<CollectedTaskDataSignalEntry> list4 = new List<CollectedTaskDataSignalEntry>();
List<CollectedVariableDataSignalEntry> list5 = new List<CollectedVariableDataSignalEntry>();
for (int k = 0; k < configuredRecords.Length; k++)
{
ConfiguredRecord configuredRecord = configuredRecords[k];
switch (configuredRecord.context)
{
case DataSignalContext.Axis:
list.Add(new CollectedAxisDataSignalEntry((AxisDataSignal)configuredRecord.coreRecord.dataID, controller.Information.Axes[configuredRecord.coreRecord.index].Name, configuredRecord.coreRecord.index, configuredRecord.coreRecord.additionalInfo, array2[k], string.Empty));
break;
case DataSignalContext.AxisExtended:
list2.Add(new CollectedAxisExtendedDataSignalEntry((AxisExtendedDataSignal)configuredRecord.coreRecord.dataID, controller.Information.Axes[configuredRecord.coreRecord.index].Name, configuredRecord.coreRecord.index, configuredRecord.coreRecord.additionalInfo, array2[k], string.Empty));
break;
case DataSignalContext.System:
list3.Add(new CollectedSystemDataSignalEntry((SystemDataSignal)configuredRecord.coreRecord.dataID, configuredRecord.coreRecord.additionalInfo, array2[k], string.Empty));
break;
case DataSignalContext.Task:
list4.Add(new CollectedTaskDataSignalEntry((TaskDataSignal)configuredRecord.coreRecord.dataID, (TaskId)configuredRecord.coreRecord.index, configuredRecord.coreRecord.index, configuredRecord.coreRecord.additionalInfo, array2[k], string.Empty));
break;
case DataSignalContext.Variable:
{
_PTR_DATA meta = (_PTR_DATA)configuredRecord.metadata;
if (configuredRecord.coreRecord.dataID == 51 || configuredRecord.coreRecord.dataID == 52)
{
_ = ref meta.arrayDimension[0];
array2[k] = Array.ConvertAll(array2[k], (Converter<double, double>)((double point) => (((int)point & (1 << meta.arrayDimension[0].dwValue % 16)) != 0) ? 1 : 0));
}
list5.Add(new CollectedVariableDataSignalEntry(configuredRecord.recordName, (TaskId)meta.taskIndex, meta.taskIndex, array2[k], string.Empty));
break;
}
}
}
return new DataCollectionResults(controller, new UnitInformation(DistanceUnit.Counts, TimeUnit.Seconds), new SystemDataResults(list3), new AxisDataResults(list), new AxisExtendedDataResults(list2), new TaskDataResults(list4), new VariableDataResults(list5), numRequested, configuredRate).ConvertUnits(new UnitInformation(DistanceUnit.Counts, TimeUnit.Seconds));
}
}
这个 Data
类看起来是一个用于数据采集和管理的核心类,主要用于控制器的诊断数据收集、配置和检索。以下是代码的分析、优化建议和关键点说明:
代码分析
1. 主要功能
-
数据采集配置:通过
DataCollectionConfiguration
设置采集参数(如采样率、触发条件等)。 -
数据采集控制:支持立即采集 (
Start
)、连续采集 (StartContinuous
) 和停止采集 (Stop
)。 -
数据检索:提供同步 (
GetData
) 和带进度回调 (GetData with ProgressChangedEventHandler
) 的数据获取方式。 -
诊断数据:通过
RetrieveDiagnostics
获取控制器和轴的状态信息。
2. 关键组件
-
ContinuousDataCollectionPoller
:处理连续数据采集的后台轮询。 -
AxesDiagPacketRetriever
:负责轴诊断数据的获取。 -
DataCollectionStatus
:封装数据采集的当前状态(如已分配点数、已采集点数等)。
3. 外部依赖
-
Wrapper
类:包含与底层硬件交互的 Native 方法(如setDataCollectionConfiguration
、getDataSamples
)。 -
ExceptionResolver
:统一处理硬件调用中的错误。
优化建议
1. 线程安全改进
-
问题:
WaitForData
和GetData
中的Thread.Sleep
可能导致不必要的阻塞。 -
改进:改用
Task.Delay
+async/await
(需升级到支持异步的 API):
public async Task<DataCollectionResults> GetDataAsync(int pointsToRetrieve,
IProgress<int> progress = null,
CancellationToken cancellationToken = default)
{
while (status.PointsRetrieved < pointsToRetrieve)
{
cancellationToken.ThrowIfCancellationRequested();
progress?.Report(CalculateProgress());
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
}
return GetData(pointsToRetrieve);
}
配置验证
-
问题:
setCollectionConfiguration
中部分验证逻辑分散。 -
改进:集中验证配置参数:
private void ValidateConfiguration(DataCollectionConfiguration config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
if (config.PointsToCollect <= 0) throw new ArgumentOutOfRangeException(...);
// 其他验证...
}
3. 事件暴露
-
问题:
ContinuousDataSamplesRetrieved
事件直接委托给continuousDataPoller
。 -
改进:添加中间层以便扩展:
public event EventHandler<ContinuousDataSamplesRetrievedEventArgs> ContinuousDataSamplesRetrieved
{
add => continuousDataPoller.ContinuousDataSamplesRetrieved += value;
remove => continuousDataPoller.ContinuousDataSamplesRetrieved -= value;
}
4. 资源释放
-
问题:未实现
IDisposable
,但持有ContinuousDataCollectionPoller
。 -
改进:实现
IDisposable
:
public class Data : IDisposable
{
public void Dispose()
{
continuousDataPoller?.Dispose();
axesDiagRetriever?.Dispose();
}
}
关键代码片段说明
1. 数据采集触发
public void StartContinuous(DataCollectionConfiguration configuration, int numberOfPoints)
{
// 参数验证
setCollectionConfiguration(configuration);
if (numberOfPoints > 0)
{
continuousDataPoller.Samples = numberOfPoints;
continuousDataPoller.Start(); // 启动后台轮询
}
triggerDataCollection(DataCollectionTriggerType.Continuous); // 通知硬件开始采集
}
2. 数据结果转换
private DataCollectionResults getResults(int numRequested)
{
// 从硬件读取原始数据
double[] rawData = new double[configuredRecords.Length * numRequested];
Wrapper.getDataSamples(..., rawData);
// 按信号类型分类数据
var axisData = new List<CollectedAxisDataSignalEntry>();
foreach (var record in configuredRecords)
{
axisData.Add(new CollectedAxisDataSignalEntry(
record.coreRecord.dataID,
controller.Information.Axes[record.coreRecord.index].Name,
record.coreRecord.index,
record.coreRecord.additionalInfo,
extractedData,
unitDescription));
}
return new DataCollectionResults(...); // 封装结果
}
兼容性注意事项
-
.NET 版本
:-
若需支持旧版 .NET Framework,避免使用
IProgress<T>
和ValueTask
。
-
-
硬件依赖:
-
Wrapper
类中的 Native 方法需要确保与硬件驱动版本兼容。
-
-
线程模型:
-
如果
ContinuousDataCollectionPoller
使用多线程,需确保对configuredRecords
的访问是线程安全的。
-
总结
-
当前实现:功能完整,但存在阻塞调用和资源管理风险。
-
推荐改进:
-
异步化长时间操作(
async/await
)。 -
显式资源释放(
IDisposable
)。 -
集中配置验证逻辑。
-
-
扩展性:可通过继承
DataCollectionConfiguration
支持更多数据采集模式。