You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.2 KiB
133 lines
4.2 KiB
|
1 week ago
|
using Peak.Can.Basic;
|
||
|
|
using IOModuleTestBlazor.Models;
|
||
|
|
|
||
|
|
namespace IOModuleTestBlazor.Services;
|
||
|
|
|
||
|
|
public class CanService : ICanService
|
||
|
|
{
|
||
|
|
private PcanChannel _channel;
|
||
|
|
private readonly List<CanFilter> _filters = [];
|
||
|
|
private readonly List<CanBitmask> _bitmasks = [];
|
||
|
|
private readonly object _lock = new();
|
||
|
|
|
||
|
|
public bool IsConnected { get; private set; }
|
||
|
|
public string ChannelName => _channel.ToString();
|
||
|
|
|
||
|
|
public event Action<CanMessageDto>? MessageReceived;
|
||
|
|
|
||
|
|
public void PublishMessage(CanMessageDto dto) => MessageReceived?.Invoke(dto);
|
||
|
|
|
||
|
|
// ── Filters ───────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
public IReadOnlyList<CanFilter> Filters
|
||
|
|
{
|
||
|
|
get { lock (_lock) return _filters.ToList(); }
|
||
|
|
}
|
||
|
|
|
||
|
|
public CanFilter AddFilter(CanFilter filter)
|
||
|
|
{
|
||
|
|
lock (_lock) _filters.Add(filter);
|
||
|
|
return filter;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool RemoveFilter(Guid id)
|
||
|
|
{
|
||
|
|
lock (_lock) return _filters.RemoveAll(f => f.Id == id) > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ClearFilters()
|
||
|
|
{
|
||
|
|
lock (_lock) _filters.Clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Bitmasks ──────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
public IReadOnlyList<CanBitmask> Bitmasks
|
||
|
|
{
|
||
|
|
get { lock (_lock) return _bitmasks.ToList(); }
|
||
|
|
}
|
||
|
|
|
||
|
|
public CanBitmask AddBitmask(CanBitmask bitmask)
|
||
|
|
{
|
||
|
|
lock (_lock) _bitmasks.Add(bitmask);
|
||
|
|
return bitmask;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool RemoveBitmask(Guid id)
|
||
|
|
{
|
||
|
|
lock (_lock) return _bitmasks.RemoveAll(b => b.Id == id) > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ClearBitmasks()
|
||
|
|
{
|
||
|
|
lock (_lock) _bitmasks.Clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
public bool PassesFilter(uint messageId)
|
||
|
|
{
|
||
|
|
lock (_lock)
|
||
|
|
{
|
||
|
|
if (_filters.Count == 0) return true;
|
||
|
|
return _filters.Any(f => (messageId & f.Mask) == (f.MessageId & f.Mask));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public IReadOnlyDictionary<string, double> ExtractSignals(uint messageId, byte[] data)
|
||
|
|
{
|
||
|
|
List<CanBitmask> applicable;
|
||
|
|
lock (_lock)
|
||
|
|
applicable = _bitmasks.Where(b => b.MessageId == messageId).ToList();
|
||
|
|
|
||
|
|
if (applicable.Count == 0)
|
||
|
|
return new Dictionary<string, double>();
|
||
|
|
|
||
|
|
Span<byte> padded = stackalloc byte[8];
|
||
|
|
var len = Math.Min(data.Length, 8);
|
||
|
|
for (int i = 0; i < len; i++)
|
||
|
|
padded[i] = data[i];
|
||
|
|
|
||
|
|
ulong raw = 0;
|
||
|
|
for (int i = 0; i < 8; i++)
|
||
|
|
raw = (raw << 8) | padded[i];
|
||
|
|
|
||
|
|
var signals = new Dictionary<string, double>(applicable.Count);
|
||
|
|
foreach (var bm in applicable)
|
||
|
|
{
|
||
|
|
ulong masked = (raw & bm.DataMask) >> bm.RightShift;
|
||
|
|
signals[bm.SignalName] = masked * bm.Scale + bm.Offset;
|
||
|
|
}
|
||
|
|
return signals;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── PCAN passthrough ──────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
public void Initialize(PcanChannel channel, Bitrate bitrate)
|
||
|
|
{
|
||
|
|
_channel = channel;
|
||
|
|
var result = Api.Initialize(channel, bitrate);
|
||
|
|
if (result != PcanStatus.OK)
|
||
|
|
throw new InvalidOperationException($"CAN init failed: {GetErrorText(result)}");
|
||
|
|
IsConnected = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Uninitialize()
|
||
|
|
{
|
||
|
|
Api.Uninitialize(_channel);
|
||
|
|
IsConnected = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public PcanStatus Read(out PcanMessage msg, out ulong timestamp)
|
||
|
|
=> Api.Read(_channel, out msg, out timestamp);
|
||
|
|
|
||
|
|
public PcanStatus Write(PcanMessage msg)
|
||
|
|
=> Api.Write(_channel, msg);
|
||
|
|
|
||
|
|
private static string GetErrorText(PcanStatus status)
|
||
|
|
{
|
||
|
|
try { Api.GetErrorText(status, out var text); return text; }
|
||
|
|
catch (PcanBasicException) { return status.ToString(); }
|
||
|
|
}
|
||
|
|
}
|