Skip to content

Commit

Permalink
refactor: Make DataBlockDataItem immutable except for it's value
Browse files Browse the repository at this point in the history
  • Loading branch information
mycroes committed Jun 9, 2023
1 parent a6bab04 commit 935168b
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 71 deletions.
4 changes: 2 additions & 2 deletions Sally7.Tests/Protocol/CommunicationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public async Task Verify_Read_Single()
{
var sourceTsap = new Tsap(201, 202);
var destinationTsap = new Tsap(203, 204);
var dataItem = new DataBlockDataItem<short> { DbNumber = 9, StartByte = 6 };
var dataItem = new DataBlockDataItem<short>(9, 6);

var communication = new CommunicationSequence(output)
.AddConnectRequest(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap)
Expand All @@ -63,7 +63,7 @@ public async Task Verify_Write_Single()
{
var sourceTsap = new Tsap(201, 202);
var destinationTsap = new Tsap(203, 204);
var dataItem = new DataBlockDataItem<short> { DbNumber = 9, StartByte = 6, Value = 513 };
var dataItem = new DataBlockDataItem<short>(9, 6) { Value = 513 };

var communication = new CommunicationSequence(output)
.AddConnectRequest(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap).AddCommunicationSetup()
Expand Down
111 changes: 46 additions & 65 deletions Sally7/DataBlockDataItem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Sally7.Internal;
using Sally7.Protocol;
using Sally7.Protocol.S7;
using Sally7.ValueConversion;
Expand All @@ -7,95 +8,75 @@ namespace Sally7
{
public class DataBlockDataItem<TValue> : IDataItem<TValue>
{
private readonly VariableType variableType;
private readonly TransportSize transportSize;
private readonly int elementSize;
private readonly ConvertToS7<TValue> toS7Converter;
private readonly ConvertFromS7<TValue> fromS7Converter;

public DataBlockDataItem() : this(ConverterFactory.GetToPlcConverter<TValue>(),
ConverterFactory.GetFromPlcConverter<TValue>(), ConversionHelper.GetElementSize<TValue>())
{
if (typeof(TValue) == typeof(bool))
{
variableType = VariableType.Bit;
transportSize = TransportSize.Bit;
}
else
{
variableType = VariableType.Byte;
transportSize = TransportSize.Byte;
}
}

private DataBlockDataItem(ConvertToS7<TValue> toS7Converter, ConvertFromS7<TValue> fromS7Converter, int elementSize)
{
this.toS7Converter = toS7Converter;
this.fromS7Converter = fromS7Converter;
this.elementSize = elementSize;

if (typeof(TValue).IsValueType) SetLength(1);
}

private Address address;
private int startByte;
private int bit;
private TValue? value;
private int length;

public BigEndianShort DbNumber { get; set; }
public BigEndianShort ReadCount { get; private set; }

public int Length
{
get => length;
set => SetLength(value);
}

public TValue? Value
{
get => value;
set => this.value = value;
}

public int StartByte
public DataBlockDataItem(BigEndianShort dbNumber, int startByte, int length = 1) : this(dbNumber, startByte, 0,
length)
{
get => startByte;
set => address.FromStartByteAndBit(startByte = value, bit);
}

public int Bit
public DataBlockDataItem(BigEndianShort dbNumber, int startByte, int bit, int length = 1)
{
get => bit;
set => address.FromStartByteAndBit(startByte, bit = value);
}
Assertions.AssertDataItemLengthIsValidForType(length, typeof(TValue));
Assertions.AssertBitIsValidForType(bit, typeof(TValue));

Address IDataItem.Address => address;
Area IDataItem.Area => Area.DataBlock;
TransportSize IDataItem.TransportSize => transportSize;
VariableType IDataItem.VariableType => variableType;
toS7Converter = ConverterFactory.GetToPlcConverter<TValue>();
fromS7Converter = ConverterFactory.GetFromPlcConverter<TValue>();
var elementSize = ConversionHelper.GetElementSize<TValue>();

int IDataItem.WriteValue(Span<byte> output) => toS7Converter.Invoke(value, Length, output);
DbNumber = dbNumber;
StartByte = startByte;
Bit = bit;
Length = length;

void IDataItem.ReadValue(ReadOnlySpan<byte> input) => fromS7Converter.Invoke(ref value, input, Length);
if (typeof(TValue) == typeof(bool))
{
VariableType = VariableType.Bit;
TransportSize = TransportSize.Bit;
}
else
{
VariableType = VariableType.Byte;
TransportSize = TransportSize.Byte;
}

private void SetLength(int newLength)
{
if (length == newLength) return;
Address = Address.FromStartByteAndBit(startByte, bit);

length = newLength;
if (typeof(TValue) == typeof(string))
{
ReadCount = length + 2;
}
else if (typeof(TValue) == typeof(bool[]))
{
ReadCount = (length + 7) >> 3; // bit-hack for (length + 7) / 8
ReadCount = (length + 7) >> 3; // Round to bytes
}
else
{
ReadCount = length * elementSize;
}
}

public BigEndianShort DbNumber { get; }
public int StartByte { get; }
public int Bit { get; }
public int Length { get; }

public TValue? Value
{
get => value;
set => this.value = value;
}

public Address Address { get; }
public BigEndianShort ReadCount { get; }
public Area Area => Area.DataBlock;
public TransportSize TransportSize { get; }
public VariableType VariableType { get; }

int IDataItem.WriteValue(Span<byte> output) => toS7Converter.Invoke(Value, Length, output);

void IDataItem.ReadValue(ReadOnlySpan<byte> input) => fromS7Converter.Invoke(ref value, input, Length);
}
}
41 changes: 41 additions & 0 deletions Sally7/Internal/Assertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,46 @@ public static void AssertTimeoutIsValid(TimeSpan value)
private static void ThrowTimeoutIsInvalid(TimeSpan value) =>
throw new ArgumentOutOfRangeException(nameof(value),
$"The timeout {value.TotalMilliseconds}ms is less than -1 or greater than Int32.MaxValue.");

public static void AssertDataItemLengthIsValidForType(int length, Type type)
{
if (length < 1)
{
throw new ArgumentOutOfRangeException(nameof(length),
$"Length must be greater or equal to 1. Value provided was: {length}.");
}

if (type.IsValueType && length != 1)
{
throw new ArgumentOutOfRangeException(nameof(length),
$"Length can't be set for value of type {type} because it is of constant size. Value provided was: {length}.");
}

if (type == typeof(string) && length > byte.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(length),
$"Length of a string can't be greater than 255. Value provided was: {length}.");
}
}

public static void AssertBitIsValidForType(int bit, Type type)
{
if (bit != 0 && type != typeof(bool))
{
throw new ArgumentException("Bit can only be set when value type is boolean.", nameof(bit));
}

if (bit < 0)
{
throw new ArgumentOutOfRangeException(nameof(bit),
$"Bit value can't be less than 0. Value provided was: {bit}.");
}

if (bit > 7)
{
throw new ArgumentOutOfRangeException(nameof(bit),
$"Bit value can't be greater than 7 (increment {nameof(DataBlockDataItem<bool>.StartByte)} instead). Value provided was: {bit}.");
}
}
}
}
6 changes: 2 additions & 4 deletions Sally7/Protocol/S7/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ public struct Address
public byte Mid;
public byte Low;

public void FromStartByteAndBit(int startByte, int bit)
public static Address FromStartByteAndBit(int startByte, int bit)
{
var value = startByte * 8 + bit;
Low = (byte) value;
Mid = (byte) (value >> 8);
High = (byte) (value >> 16);
return new Address { Low = (byte)value, Mid = (byte)(value >> 8), High = (byte)(value >> 16) };
}

public static implicit operator Address(int value)
Expand Down

0 comments on commit 935168b

Please sign in to comment.