update motor

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-07-13 16:28:07 +07:00
parent bccfb156d7
commit dddf3fc594
9 changed files with 2142 additions and 48 deletions

View File

@@ -12,6 +12,9 @@ public class SdoClient : IDisposable
private readonly ICanBus _canBus;
private readonly byte _nodeId;
private readonly ConcurrentDictionary<string, TaskCompletionSource<SdoResponse>> _pendingRequests;
// CANopen chỉ cho phép 1 giao dịch SDO tại một thời điểm trên mỗi node.
// Hai facade trái/phải của ZLAC8015D dùng chung SdoClient nên phải xếp hàng ở đây.
private readonly SemaphoreSlim _transactionLock = new(1, 1);
private readonly TimeSpan _timeout;
private readonly ILogger<SdoClient>? _logger;
private bool _disposed;
@@ -31,26 +34,37 @@ public class SdoClient : IDisposable
{
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
var request = SdoRequest.CreateUpload(index, subIndex);
var response = await SendRequestAsync(request, cancellationToken);
if (response.IsAbort)
await _transactionLock.WaitAsync(cancellationToken);
try
{
var abortCode = (SdoAbortCode)(uint)response.AbortCode;
var message = $"SDO Upload failed: {abortCode.GetDescription()}";
throw new SdoException(_nodeId, index, subIndex, (uint)response.AbortCode, message);
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
var request = SdoRequest.CreateUpload(index, subIndex);
var response = await SendRequestAsync(request, cancellationToken);
if (response.IsAbort)
{
var abortCode = (SdoAbortCode)(uint)response.AbortCode;
var message = $"SDO Upload failed: {abortCode.GetDescription()}";
throw new SdoException(_nodeId, index, subIndex, (uint)response.AbortCode, message);
}
// Check if expedited or segmented
if (response.IsExpedited)
{
return response.GetDataBytes();
}
else
{
// Segmented transfer
return await UploadSegmentedAsync(index, subIndex, response, cancellationToken);
}
}
// Check if expedited or segmented
if (response.IsExpedited)
finally
{
return response.GetDataBytes();
}
else
{
// Segmented transfer
return await UploadSegmentedAsync(index, subIndex, response, cancellationToken);
_transactionLock.Release();
}
}
@@ -126,27 +140,81 @@ public class SdoClient : IDisposable
{
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
if (data.Length <= 4)
await _transactionLock.WaitAsync(cancellationToken);
try
{
// Expedited transfer (≤4 bytes)
var request = SdoRequest.CreateDownload(index, subIndex, data);
var response = await SendRequestAsync(request, cancellationToken);
if (response.IsAbort)
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
if (data.Length <= 4)
{
var abortCode = (SdoAbortCode)(uint)response.AbortCode;
var message = $"SDO Download failed: {abortCode.GetDescription()}";
throw new SdoException(_nodeId, index, subIndex, (uint)response.AbortCode, message);
// Expedited transfer (≤4 bytes)
var request = SdoRequest.CreateDownload(index, subIndex, data);
var response = await SendRequestAsync(request, cancellationToken);
if (response.IsAbort)
{
var abortCode = (SdoAbortCode)(uint)response.AbortCode;
var message = $"SDO Download failed: {abortCode.GetDescription()}";
throw new SdoException(_nodeId, index, subIndex, (uint)response.AbortCode, message);
}
}
else
{
// Segmented transfer (>4 bytes)
await DownloadSegmentedAsync(index, subIndex, data, cancellationToken);
}
}
else
finally
{
// Segmented transfer (>4 bytes)
await DownloadSegmentedAsync(index, subIndex, data, cancellationToken);
_transactionLock.Release();
}
}
/// <summary>
/// Ghi nhiều object liên tiếp trong MỘT lần giữ transaction lock.
/// Dùng khi các lệnh phải đi liền nhau không bị giao dịch SDO khác chen giữa,
/// ví dụ lệnh vận tốc hai bánh của ZLAC8015D khi không có RPDO: nếu ghi bằng hai
/// DownloadAsync riêng, poll feedback có thể chiếm kênh giữa hai lệnh làm bánh sau
/// nhận lệnh muộn hơn hẳn. Chỉ hỗ trợ expedited transfer (data ≤ 4 byte mỗi mục).
/// </summary>
public async Task DownloadBatchAsync(IReadOnlyList<(ushort Index, byte SubIndex, byte[] Data)> items, CancellationToken cancellationToken = default)
{
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
foreach (var item in items)
{
if (item.Data.Length > 4)
throw new NotSupportedException("DownloadBatchAsync chỉ hỗ trợ expedited transfer (≤4 byte mỗi mục)");
}
await _transactionLock.WaitAsync(cancellationToken);
try
{
if (_disposed)
throw new ObjectDisposedException(nameof(SdoClient));
foreach (var (index, subIndex, data) in items)
{
var request = SdoRequest.CreateDownload(index, subIndex, data);
var response = await SendRequestAsync(request, cancellationToken);
if (response.IsAbort)
{
var abortCode = (SdoAbortCode)(uint)response.AbortCode;
var message = $"SDO Download failed: {abortCode.GetDescription()}";
throw new SdoException(_nodeId, index, subIndex, (uint)response.AbortCode, message);
}
}
}
finally
{
_transactionLock.Release();
}
}
/// <summary>
/// Download data using segmented transfer (for data > 4 bytes)
/// </summary>