Skip to content

Commit

Permalink
Merge pull request #49 from mycroes/hotfix/conversion
Browse files Browse the repository at this point in the history
Hotfix conversion of float/double
  • Loading branch information
mycroes authored Mar 13, 2024
2 parents 3d1f572 + d865239 commit f2caba4
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Sally7.Tests
{
public class ValueTypeConversionTests
public class FromPlcConverterTests
{
[Theory]
[InlineData(0)]
Expand Down
106 changes: 106 additions & 0 deletions Sally7.Tests/TestValues.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace Sally7.Tests;

internal static class TestValues
{
public static IEnumerable<byte> ByteData =>
Enumerable.Range(byte.MinValue, byte.MaxValue).Select(x => (byte)x);

public static IEnumerable<sbyte> SByteData =>
Enumerable.Range(sbyte.MinValue, sbyte.MaxValue).Select(x => (sbyte)x);


public static IEnumerable<short> Int16Data => ShortValues;

public static IEnumerable<ushort> UInt16Data =>
ShortValues.Select(x => (ushort)x);

public static IEnumerable<int> Int32Data => UIntValues.Select(x => (int)x);

public static IEnumerable<uint> UInt32Data => UIntValues;

public static IEnumerable<float> SingleData
{
get
{
float[] values =
[
0,
1,
0.1f,
123.45f,
float.MinValue,
float.MaxValue,
];

return values;
}
}

public static IEnumerable<long> Int64Data => ULongValues.Select(x => (long) x);

public static IEnumerable<ulong> UInt64Data => ULongValues;

public static IEnumerable<double> DoubleData
{
get
{
double[] values =
[
0,
1,
0.1,
123.45,
0.0000001,
(double) ulong.MaxValue * 33,
double.MinValue,
double.MaxValue,
];

return values;
}
}

private static readonly short[] ShortValues =
[
0,
1,
123,
12345,
-1,
-123,
-12345,
1 << 8,
1 | 2 << 8,
short.MinValue,
short.MaxValue
];

private static readonly uint[] UIntValues =
[
uint.MinValue,
uint.MaxValue,
1,
123,
12345,
1 << 8,
1 << 16,
1 << 24,
1 | 2 << 8,
1 | 2 << 8 | 3 << 16,
1 | 2 << 8 | 3 << 24,
1 | 2 << 8 | 3 << 16 | 4 << 24,
0xf,
0xf << 8,
0xf << 16,
0xf << 24,
];

private static readonly ulong[] ULongValues = UIntValues.Select(x => (ulong)x)
.SelectMany(x => new[] { x, x << 8, x << 16, x << 24, x << 32 })
.Concat(UIntValues.SelectMany(_ => UIntValues, (a, b) => (ulong)a << 32 | b))
.Concat(UIntValues.SelectMany(_ => UIntValues, (a, b) => a | (ulong)b << 32)).Distinct().ToArray();
}
22 changes: 22 additions & 0 deletions Sally7.Tests/TestValuesDataAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Xunit.Sdk;

namespace Sally7.Tests;

internal sealed class TestValuesDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var paramType = testMethod.GetParameters()[0].ParameterType;
var dataProperty = typeof(TestValues).GetProperty($"{paramType.Name}Data") ??
throw new ArgumentException($"No data available for type {paramType}");

var data = dataProperty.GetValue(null) as IEnumerable ??
throw new NotSupportedException($"Data from {dataProperty} could not be converted to {nameof(IEnumerable)}.");

foreach (var value in data) yield return [value];
}
}
134 changes: 134 additions & 0 deletions Sally7.Tests/ToPlcConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using Sally7.ValueConversion;
using Xunit;

namespace Sally7.Tests;

public static class ToPlcConverterTests
{
public class Elements
{
[Theory]
[TestValuesData]
public void ConvertByteToPlc(byte value)
{
// Arrange
var converter = ConverterFactory.GetToPlcConverter<byte>(1);
var buffer = new byte[sizeof(byte)];

// Act
converter(value, 1, buffer);

// Assert
Assert.Equal([value], buffer);
}

[Theory]
[TestValuesData]
public void ConvertSByteToPlc(sbyte value)
{
// Arrange
var converter = ConverterFactory.GetToPlcConverter<sbyte>(1);
var buffer = new byte[sizeof(sbyte)];

// Act
converter(value, 1, buffer);

// Assert
Assert.Equal([(byte)value], buffer);
}

[Theory]
[TestValuesData]
public void ConvertInt16ToPlc(short value)
{
TestConvertToPlc(value, sizeof(short), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertUInt16ToPlc(ushort value)
{
TestConvertToPlc(value, sizeof(ushort), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertInt32ToPlc(int value)
{
TestConvertToPlc(value, sizeof(int), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertUInt32ToPlc(uint value)
{
TestConvertToPlc(value, sizeof(uint), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertSingleToPlc(float value)
{
TestConvertToPlc(value, sizeof(float), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertInt64ToPlc(long value)
{
TestConvertToPlc(value, sizeof(long), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertUInt64ToPlc(ulong value)
{
TestConvertToPlc(value, sizeof(ulong), BitConverter.GetBytes);
}

[Theory]
[TestValuesData]
public void ConvertDoubleToPlc(double value)
{
TestConvertToPlc(value, sizeof(double), BitConverter.GetBytes);
}

private static void TestConvertToPlc<T>(T value, int size, Func<T, byte[]> getBytes)
{
// Arrange
var converter = ConverterFactory.GetToPlcConverter<T>(1);
var buffer = new byte[size];

// Act
converter(value, 1, buffer);

// Assert
var bytes = getBytes.Invoke(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);

Assert.Equal(bytes, buffer);
}
}

public class Arrays
{
[Theory]
[TestValuesData]
public void ConvertFloatArrToPlc(float value)
{
// Arrange
var converter = ConverterFactory.GetToPlcConverter<float[]>(1);
var buffer = new byte[sizeof(float)];

// Act
converter([value], 1, buffer);

// Assert
var bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);

Assert.Equal(bytes, buffer);
}
}
}
2 changes: 1 addition & 1 deletion Sally7/ValueConversion/ConverterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Sally7.ValueConversion
{
public delegate int ConvertToS7<TValue>(TValue? value, int length, Span<byte> output);
public delegate int ConvertToS7<TValue>(in TValue? value, int length, Span<byte> output);

public delegate void ConvertFromS7<TValue>(ref TValue? value, ReadOnlySpan<byte> input, int length);

Expand Down
26 changes: 13 additions & 13 deletions Sally7/ValueConversion/ToS7Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,56 +50,56 @@ public static Delegate GetConverter<TValue>(int length)
throw new NotImplementedException();
}

private static int ConvertFromLong(long value, int length, Span<byte> output)
private static int ConvertFromLong(in long value, int length, Span<byte> output)
{
BinaryPrimitives.WriteInt64BigEndian(output, value);

return sizeof(long);
}

private static int ConvertFromLongArray(long[]? value, int length, Span<byte> output)
private static int ConvertFromLongArray(in long[]? value, int length, Span<byte> output)
{
if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null.");

return BufferHelper.CopyAndAlign64Bit(Unsafe.As<long[], byte[]>(ref value), output, length);
return BufferHelper.CopyAndAlign64Bit(Unsafe.As<long[], byte[]>(ref Unsafe.AsRef(value)), output, length);
}

private static int ConvertFromInt(int value, int length, Span<byte> output)
private static int ConvertFromInt(in int value, int length, Span<byte> output)
{
BinaryPrimitives.WriteInt32BigEndian(output, value);

return sizeof(int);
}

private static int ConvertFromIntArray(int[]? value, int length, Span<byte> output)
private static int ConvertFromIntArray(in int[]? value, int length, Span<byte> output)
{
if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null.");

return BufferHelper.CopyAndAlign32Bit(Unsafe.As<int[], byte[]>(ref value), output, length);
return BufferHelper.CopyAndAlign32Bit(Unsafe.As<int[], byte[]>(ref Unsafe.AsRef(value)), output, length);
}

private static int ConvertFromShort(short value, int length, Span<byte> output)
private static int ConvertFromShort(in short value, int length, Span<byte> output)
{
BinaryPrimitives.WriteInt16BigEndian(output, value);

return sizeof(short);
}

private static int ConvertFromShortArray(short[]? value, int length, Span<byte> output)
private static int ConvertFromShortArray(in short[]? value, int length, Span<byte> output)
{
if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null.");

return BufferHelper.CopyAndAlign16Bit(Unsafe.As<short[], byte[]>(ref value), output, length);
return BufferHelper.CopyAndAlign16Bit(Unsafe.As<short[], byte[]>(ref Unsafe.AsRef(value)), output, length);
}

private static int ConvertFromByte(byte value, int length, Span<byte> output)
private static int ConvertFromByte(in byte value, int length, Span<byte> output)
{
output[0] = value;

return sizeof(byte);
}

private static int ConvertFromByteArray(byte[]? value, int length, Span<byte> output)
private static int ConvertFromByteArray(in byte[]? value, int length, Span<byte> output)
{
if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null.");

Expand All @@ -108,7 +108,7 @@ private static int ConvertFromByteArray(byte[]? value, int length, Span<byte> ou
return length;
}

private static int ConvertFromBoolArray(bool[]? value, int length, Span<byte> output)
private static int ConvertFromBoolArray(in bool[]? value, int length, Span<byte> output)
{
if (value is null)
{
Expand Down Expand Up @@ -145,7 +145,7 @@ private static int ConvertFromBoolArray(bool[]? value, int length, Span<byte> ou
return length;
}

private static int ConvertFromString(string? value, int length, Span<byte> output)
private static int ConvertFromString(in string? value, int length, Span<byte> output)
{
if (value == null)
{
Expand Down

0 comments on commit f2caba4

Please sign in to comment.