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

fixes and improvements #50

Merged
merged 4 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
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
36 changes: 35 additions & 1 deletion src/Meadow.Modbus/Clients/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public static class Extensions
/// Converts a set of Modbus registers (ushort[]) to integers (int[]) assuming little-endina ordering
/// </summary>
/// <param name="registers"></param>
/// <returns></returns>
public static int[] ConvertRegistersToInt32(this ushort[] registers)
{
var values = new int[registers.Length / 2];
Expand All @@ -28,6 +27,17 @@ public static int[] ConvertRegistersToInt32(this ushort[] registers)
return values;
}

/// <summary>
/// Converts ushort registers to a single Int32, starting at a specific offset
/// </summary>
/// <param name="registers">The registers</param>
/// <param name="startOffset">The offset in the registers to begine extraction</param>
/// <param name="swappedWords">True to convert from big-endian words</param>
public static int ExtractInt32(this ushort[] registers, int startOffset = 0, bool swappedWords = false)
{
return registers.AsSpan().ExtractInt32(startOffset, swappedWords);
}

/// <summary>
/// Converts ushort registers to a single Int32, starting at a specific offset
/// </summary>
Expand Down Expand Up @@ -140,6 +150,17 @@ public static ulong ExtractUInt64(this Span<ushort> registers, int startOffset =
}
}

/// <summary>
/// Converts 4 ushort registers to a IEEE 64 floating point, starting at a specific offset
/// </summary>
/// <param name="registers">The registers</param>
/// <param name="startOffset">The offset in the registers to begine extraction</param>
/// <param name="swappedWords">True to convert from big-endian words</param>
public static double ExtractDouble(this ushort[] registers, int startOffset = 0, bool swappedWords = false)
{
return registers.AsSpan().ExtractDouble(startOffset, swappedWords);
}

/// <summary>
/// Converts 4 ushort registers to a IEEE 64 floating point, starting at a specific offset
/// </summary>
Expand All @@ -148,6 +169,8 @@ public static ulong ExtractUInt64(this Span<ushort> registers, int startOffset =
/// <param name="swappedWords">True to convert from big-endian words</param>
public static double ExtractDouble(this Span<ushort> registers, int startOffset = 0, bool swappedWords = false)
{
if (registers.Length < 4) throw new ArgumentException("registers does not contain enough data to extract a double");

if (swappedWords)
{
byte[] value = BitConverter.GetBytes(registers[startOffset + 0])
Expand All @@ -170,6 +193,17 @@ public static double ExtractDouble(this Span<ushort> registers, int startOffset
}
}

/// <summary>
/// Converts 2 ushort registers to a IEEE 32 floating point, starting at a specific offset
/// </summary>
/// <param name="registers">The registers</param>
/// <param name="startOffset">The offset in the registers to begine extraction</param>
/// <param name="swappedWords">True to convert from big-endian words</param>
public static float ExtractSingle(this ushort[] registers, int startOffset = 0, bool swappedWords = false)
{
return registers.AsSpan().ExtractSingle(startOffset, swappedWords);
}

/// <summary>
/// Converts 2 ushort registers to a IEEE 32 floating point, starting at a specific offset
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Meadow.Modbus/Clients/ModbusRtuClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ protected override async Task<byte[]> ReadResult(ModbusFunction function)

switch (function)
{
case ModbusFunction.WriteMultipleRegisters:
case ModbusFunction.WriteRegister:
case ModbusFunction.WriteMultipleCoils:
case ModbusFunction.WriteCoil:
bufferLen = 8; //fixed length
resultLen = 0; //no result data
break;
case ModbusFunction.WriteRegister:
case ModbusFunction.WriteMultipleRegisters:
bufferLen = 7 + header[headerLen - 1];
resultLen = header[2];
break;
Expand Down
41 changes: 39 additions & 2 deletions src/Meadow.Modbus/ModbusPolledDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public abstract class ModbusPolledDevice
/// </summary>
public event EventHandler<Exception>? CommError;

/// <summary>
/// Raised when data has been read from the device
/// </summary>
public event EventHandler? DataUpdated;

/// <summary>
/// Represents the possible formats of source registers
/// </summary>
Expand Down Expand Up @@ -83,7 +88,8 @@ private class RegisterMapping
/// </summary>
public virtual void StartPolling()
{
_timer.Change(_refreshPeriosMs, -1);
// trigger first read immediately - subsequent reads will be at the desired frequency
_timer.Change(0, -1);
}

/// <summary>
Expand Down Expand Up @@ -126,6 +132,17 @@ protected async Task WriteHoldingRegister(ushort startRegister, params ushort[]
}
}

/// <summary>
/// Writes one or more values to the holding registers of the Modbus device.
/// </summary>
/// <param name="startRegister">The starting register address.</param>
/// <param name="count">The number of registers to read.</param>
/// <returns>A task representing the asynchronous write operation.</returns>
protected Task<ushort[]> ReadHoldingRegisters(ushort startRegister, int count)
{
return _client.ReadHoldingRegisters(_address, startRegister, count);
}

/// <summary>
/// Reads a value from a coil register of the Modbus device.
/// </summary>
Expand Down Expand Up @@ -361,6 +378,8 @@ private async void RefreshTimerProc(object _)
{
await MoveHoldingRegistersToProperties();
await MoveInputRegistersToProperties();

DataUpdated?.Invoke(this, EventArgs.Empty);
}
finally
{
Expand Down Expand Up @@ -517,17 +536,27 @@ private void UpdateField(ushort[] data, RegisterMapping mapping)
}
else if (
mapping.FieldInfo!.FieldType == typeof(byte) ||
mapping.FieldInfo!.FieldType == typeof(ushort) ||
mapping.FieldInfo!.FieldType == typeof(short) ||
mapping.FieldInfo!.FieldType == typeof(int) ||
mapping.FieldInfo!.FieldType == typeof(uint) ||
mapping.FieldInfo!.FieldType == typeof(long))
{
UpdateIntegerField(data, mapping);
}
else if (
mapping.PropertyInfo!.PropertyType == typeof(bool))
mapping.FieldInfo!.FieldType == typeof(bool))
{
UpdateBooleanField(data, mapping);
}
else if (
mapping.FieldInfo!.FieldType == typeof(ushort[]) ||
mapping.FieldInfo!.FieldType == typeof(short[]))
{
// the field is a ref value, so this is valid
ushort[] f = (ushort[])mapping.FieldInfo.GetValue(this);
Array.Copy(data, f, Math.Min(data.Length, f.Length));
}
else
{
throw new NotSupportedException();
Expand Down Expand Up @@ -670,10 +699,18 @@ private void UpdateIntegerField(ushort[] data, RegisterMapping mapping)
{
mapping.FieldInfo!.SetValue(this, Convert.ToInt16(final));
}
else if (mapping.FieldInfo!.FieldType == typeof(ushort))
{
mapping.FieldInfo!.SetValue(this, Convert.ToUInt16(final));
}
else if (mapping.FieldInfo!.FieldType == typeof(int))
{
mapping.FieldInfo!.SetValue(this, Convert.ToInt32(final));
}
else if (mapping.FieldInfo!.FieldType == typeof(uint))
{
mapping.FieldInfo!.SetValue(this, Convert.ToUInt32(final));
}
else if (mapping.FieldInfo!.FieldType == typeof(long))
{
mapping.FieldInfo!.SetValue(this, final);
Expand Down
Loading