Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#67 Functions for unpacking based on byte arrays #71

Merged
merged 14 commits into from
Aug 27, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 64 additions & 60 deletions DbcParserLib/Packer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using uint8_T = System.Byte;
using uint16_T = System.UInt16;
using int64_T = System.Int64;
using uint64_T = System.UInt64;
using System;
using System.Runtime.InteropServices;
using DbcParserLib.Model;
Expand All @@ -12,16 +16,16 @@ public static class Packer
/// <param name="value">Value to be packed</param>
/// <param name="signal">Signal containing dbc information</param>
/// <returns>Returns a 64 bit unsigned data message</returns>
public static ulong TxSignalPack(double value, Signal signal)
public static uint64_T TxSignalPack(double value, Signal signal)
{
long iVal = TxPackApplySignAndScale(value, signal);
ulong bitMask = signal.BitMask();
int64_T iVal = TxPackApplySignAndScale(value, signal);
uint64_T bitMask = signal.BitMask();

// Pack signal
if (signal.Intel()) // Little endian (Intel)
return (((ulong)iVal & bitMask) << signal.StartBit);
return (((uint64_T)iVal & bitMask) << signal.StartBit);
else // Big endian (Motorola)
return MirrorMsg(((ulong)iVal & bitMask) << GetStartBitLE(signal));
return MirrorMsg(((uint64_T)iVal & bitMask) << GetStartBitLE(signal));
}

/// <summary>
Expand All @@ -32,25 +36,25 @@ public static ulong TxSignalPack(double value, Signal signal)
/// <param name="signal">Signal containing dbc information</param>
/// <remarks>Due to needing to reverse the byte array when handling BigEndian(Motorola) format this method can not be called in parallel for multiple signals in one message.
/// To make this obvios the message is a ref and is actually reassigned after handling BigEndian format.</remarks>
public static void TxSignalPack(ref byte[] message, double value, Signal signal)
public static void TxSignalPack(ref uint8_T[] message, double value, Signal signal)
{
long iVal = TxPackApplySignAndScale(value, signal);
int64_T iVal = TxPackApplySignAndScale(value, signal);

// Pack signal
if (!signal.Intel())
{
var tempArray = new byte[message.Length];
var tempArray = new uint8_T[message.Length];
Array.Copy(message, tempArray, message.Length);
Array.Reverse(tempArray);

WriteBits(tempArray, unchecked((ulong)iVal), GetStartBitLE(signal, message.Length), signal.Length);
WriteBits(tempArray, unchecked((uint64_T)iVal), GetStartBitLE(signal, message.Length), signal.Length);

Array.Reverse(tempArray);

message = tempArray;
return;
}
WriteBits(message, unchecked((ulong)iVal), signal.StartBit, signal.Length);
WriteBits(message, unchecked((uint64_T)iVal), signal.StartBit, signal.Length);
}

/// <summary>
Expand All @@ -59,9 +63,9 @@ public static void TxSignalPack(ref byte[] message, double value, Signal signal)
/// <param name="value">Value to be packed</param>
/// <param name="signal">Signal containing dbc information</param>
/// <returns>Returns a 64 bit unsigned data message</returns>
public static ulong TxStatePack(ulong value, Signal signal)
public static uint64_T TxStatePack(uint64_T value, Signal signal)
{
ulong bitMask = signal.BitMask();
uint64_T bitMask = signal.BitMask();

// Apply overflow protection
value = CLAMP(value, 0UL, bitMask);
Expand All @@ -79,10 +83,10 @@ public static ulong TxStatePack(ulong value, Signal signal)
/// <param name="RxMsg64">The 64 bit unsigned data message</param>
/// <param name="signal">Signal containing dbc information</param>
/// <returns>Returns a double value representing the unpacked signal</returns>
public static double RxSignalUnpack(ulong RxMsg64, Signal signal)
public static double RxSignalUnpack(uint64_T RxMsg64, Signal signal)
{
ulong iVal;
ulong bitMask = signal.BitMask();
uint64_T iVal;
uint64_T bitMask = signal.BitMask();

// Unpack signal
if (signal.Intel()) // Little endian (Intel)
Expand All @@ -99,14 +103,14 @@ public static double RxSignalUnpack(ulong RxMsg64, Signal signal)
/// <param name="receiveMessage">The message data</param>
/// <param name="signal">Signal containing dbc information</param>
/// <returns>Returns a double value representing the unpacked signal</returns>
public static double RxSignalUnpack(byte[] receiveMessage, Signal signal)
public static double RxSignalUnpack(uint8_T[] receiveMessage, Signal signal)
{
var startBit = signal.StartBit;
var message = receiveMessage;

if (!signal.Intel())
{
var tempArray = new byte[message.Length];
var tempArray = new uint8_T[message.Length];
Array.Copy(message, tempArray, message.Length);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is bittersweet. It's easy and readable but allocating to just read backward it's a pity.
Maybe if we change the ExtractBits signature into something like

private static uint64_T ExtractBits(uint8_T[] data, int startBit, int endBit, int step = 1) // Or -1 if backward

we could keep the same iteration without assuming that length is the limit. Just food for thoughts.

...or... (this is a bit outside the scope of this PR, but worth mentioning)
Today the Packer is a static class so it needs Signal and message info. If we optimize the Signal class (or if we adda a sort of .GetReader() on it) then we could optimize some stuff, removing ifs, and give a forward/backward reader depending on endianness

Array.Reverse(tempArray);

Expand All @@ -125,10 +129,10 @@ public static double RxSignalUnpack(byte[] receiveMessage, Signal signal)
/// <param name="RxMsg64">The 64 bit unsigned data message</param>
/// <param name="signal">Signal containing dbc information</param>
/// <returns>Returns an unsigned integer representing the unpacked state</returns>
public static ulong RxStateUnpack(ulong RxMsg64, Signal signal)
public static uint64_T RxStateUnpack(uint64_T RxMsg64, Signal signal)
{
ulong iVal;
ulong bitMask = signal.BitMask();
uint64_T iVal;
uint64_T bitMask = signal.BitMask();

// Unpack signal
if (signal.Intel()) // Little endian (Intel)
Expand All @@ -139,10 +143,10 @@ public static ulong RxStateUnpack(ulong RxMsg64, Signal signal)
return iVal;
}

private static long TxPackApplySignAndScale(double value, Signal signal)
private static int64_T TxPackApplySignAndScale(double value, Signal signal)
{
long iVal;
ulong bitMask = signal.BitMask();
int64_T iVal;
uint64_T bitMask = signal.BitMask();

// Apply scaling
var rawValue = (value - signal.Offset) / signal.Factor;
Expand All @@ -151,18 +155,18 @@ private static long TxPackApplySignAndScale(double value, Signal signal)
else if (signal.ValueType == DbcValueType.IEEEDouble)
iVal = DoubleConverter.AsInteger(rawValue);
else
iVal = (long)Math.Round(rawValue);
iVal = (int64_T)Math.Round(rawValue);

// Apply overflow protection
if (signal.ValueType == DbcValueType.Signed)
iVal = CLAMP(iVal, -(long)(bitMask >> 1) - 1, (long)(bitMask >> 1));
iVal = CLAMP(iVal, -(int64_T)(bitMask >> 1) - 1, (int64_T)(bitMask >> 1));
else if (signal.ValueType == DbcValueType.Unsigned)
iVal = CLAMP(iVal, 0L, (long)bitMask);
iVal = CLAMP(iVal, 0L, (int64_T)bitMask);

// Manage sign bit (if signed)
if (signal.ValueType == DbcValueType.Signed && iVal < 0)
{
iVal += (long)(1UL << signal.Length);
iVal += (int64_T)(1UL << signal.Length);
}

return iVal;
Expand All @@ -173,10 +177,10 @@ private static double RxUnpackApplySignAndScale(Signal signal, ulong value)
switch (signal.ValueType)
{
case DbcValueType.Signed:
long signedValue;
int64_T signedValue;
if (signal.Length == 64)
{
signedValue = unchecked((long)value);
signedValue = unchecked((int64_T)value);
}
else
{
Expand All @@ -190,58 +194,58 @@ private static double RxUnpackApplySignAndScale(Signal signal, ulong value)
case DbcValueType.IEEEFloat:
return FloatConverter.AsFloatingPoint((int)value) * signal.Factor + signal.Offset;
case DbcValueType.IEEEDouble:
return DoubleConverter.AsFloatingPoint(unchecked((long)value)) * signal.Factor + signal.Offset;
return DoubleConverter.AsFloatingPoint(unchecked((int64_T)value)) * signal.Factor + signal.Offset;
default:
return (double)(value * (decimal)signal.Factor + (decimal)signal.Offset);
}
}

private static long CLAMP(long x, long low, long high)
private static int64_T CLAMP(int64_T x, int64_T low, int64_T high)
{
return Math.Max(low, Math.Min(x, high));
}

private static ulong CLAMP(ulong x, ulong low, ulong high)
private static uint64_T CLAMP(uint64_T x, uint64_T low, uint64_T high)
{
return Math.Max(low, Math.Min(x, high));
}

/// <summary>
/// Mirror data message. It is used to convert Big endian to Little endian and vice-versa
/// </summary>
private static ulong MirrorMsg(ulong msg)
private static uint64_T MirrorMsg(uint64_T msg)
{
byte[] v =
uint8_T[] v =
{
(byte)msg,
(byte)(msg >> 8),
(byte)(msg >> 16),
(byte)(msg >> 24),
(byte)(msg >> 32),
(byte)(msg >> 40),
(byte)(msg >> 48),
(byte)(msg >> 56)
(uint8_T)msg,
(uint8_T)(msg >> 8),
(uint8_T)(msg >> 16),
(uint8_T)(msg >> 24),
(uint8_T)(msg >> 32),
(uint8_T)(msg >> 40),
(uint8_T)(msg >> 48),
(uint8_T)(msg >> 56)
};
return (((ulong)v[0] << 56)
| ((ulong)v[1] << 48)
| ((ulong)v[2] << 40)
| ((ulong)v[3] << 32)
| ((ulong)v[4] << 24)
| ((ulong)v[5] << 16)
| ((ulong)v[6] << 8)
| (ulong)v[7]);
return (((uint64_T)v[0] << 56)
| ((uint64_T)v[1] << 48)
| ((uint64_T)v[2] << 40)
| ((uint64_T)v[3] << 32)
| ((uint64_T)v[4] << 24)
| ((uint64_T)v[5] << 16)
| ((uint64_T)v[6] << 8)
| (uint64_T)v[7]);
}

/// <summary>
/// Get start bit Little Endian
/// </summary>
private static byte GetStartBitLE(Signal signal, int messageByteCount = 8)
private static uint16_T GetStartBitLE(Signal signal, int messageByteCount = 8)
{
byte startByte = (byte)(signal.StartBit / 8);
return (byte)(8 * messageByteCount - (signal.Length + 8 * startByte + (8 * (startByte + 1) - (signal.StartBit + 1)) % 8));
uint16_T startByte = (uint16_T)(signal.StartBit / 8);
return (uint16_T)(8 * messageByteCount - (signal.Length + 8 * startByte + (8 * (startByte + 1) - (signal.StartBit + 1)) % 8));
}

private static void WriteBits(byte[] data, ulong value, int startBit, int length)
private static void WriteBits(uint8_T[] data, uint64_T value, int startBit, int length)
{
for (int bitIndex = 0; bitIndex < length; bitIndex++)
{
Expand All @@ -250,16 +254,16 @@ private static void WriteBits(byte[] data, ulong value, int startBit, int length
int bitInBytePosition = bitPosition % 8;

// Extract the bit from the signal value
ulong bitValue = (value >> bitIndex) & 1;
uint64_T bitValue = (value >> bitIndex) & 1;

// Set the bit in the message
data[byteIndex] |= (byte)(bitValue << bitInBytePosition);
}
}

private static ulong ExtractBits(byte[] data, int startBit, int length)
private static uint64_T ExtractBits(uint8_T[] data, int startBit, int length)
{
ulong result = 0;
uint64_T result = 0;
int bitIndex = 0;

for (int bitPos = startBit; bitPos < startBit + length; bitPos++)
Expand Down Expand Up @@ -303,15 +307,15 @@ public static float AsFloatingPoint(int value)
[StructLayout(LayoutKind.Explicit)]
public class DoubleConverter
{
[FieldOffset(0)] public long Integer;
[FieldOffset(0)] public int64_T Integer;
[FieldOffset(0)] public double Float;

public static long AsInteger(double value)
public static int64_T AsInteger(double value)
{
return new DoubleConverter() { Float = value }.Integer;
}

public static double AsFloatingPoint(long value)
public static double AsFloatingPoint(int64_T value)
{
return new DoubleConverter() { Integer = value }.Float;
}
Expand Down