Skip to content

Commit

Permalink
Merge pull request #48 from WildernessLabs/feature/polled-device
Browse files Browse the repository at this point in the history
Feature/polled device
  • Loading branch information
adrianstevens authored Aug 5, 2024
2 parents 64409d7 + 691f44c commit 7814fb6
Show file tree
Hide file tree
Showing 11 changed files with 766 additions and 150 deletions.
79 changes: 4 additions & 75 deletions src/Meadow.Modbus.Unit.Tests/ModbusSerialTStatTests.cs
Original file line number Diff line number Diff line change
@@ -1,87 +1,16 @@
using System;
using Meadow.Modbus.Temco;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;

namespace Meadow.Modbus.Unit.Tests;

public class TStat8 : ModbusPolledDevice
{
private float _currentSetPoint;

private const ushort SetPointRegister = 345;

public TStat8(ModbusRtuClient client, byte modbusAddress, TimeSpan? refreshPeriod = null)
: base(client, modbusAddress, refreshPeriod)
{
MapHoldingRegistersToProperty(
startRegister: 121,
registerCount: 1,
propertyName: nameof(Temperature),
scale: 0.10); // value is in 0.1 deg

// map to a field, not a property as the property setter needs to perform an action
MapHoldingRegistersToField(
startRegister: SetPointRegister,
registerCount: 1,
fieldName: nameof(_currentSetPoint),
scale: 0.10);

MapHoldingRegistersToProperty(
startRegister: 198,
registerCount: 1,
propertyName: nameof(Humidity));

MapHoldingRegistersToProperty(
startRegister: 364, // not scaled by 0.1
registerCount: 1,
propertyName: nameof(PowerUpSetPoint));

MapHoldingRegistersToProperty(
startRegister: 365,
registerCount: 1,
propertyName: nameof(MaxSetPoint));

MapHoldingRegistersToProperty(
startRegister: 366,
registerCount: 1,
propertyName: nameof(MinSetPoint));

MapHoldingRegistersToProperty(
startRegister: 410,
registerCount: 7,
propertyName: nameof(Clock),
conversionFunction: ConvertRegistersToClockTime);
}

private object ConvertRegistersToClockTime(ushort[] data)
{
// data[2] is week, so ignore
return new DateTime(data[0], data[1], data[3], data[4], data[5], data[6]);
}

public DateTime Clock { get; private set; }
public int Humidity { get; private set; }
public float Temperature { get; private set; }
public float MinSetPoint { get; private set; }
public float MaxSetPoint { get; private set; }
public float PowerUpSetPoint { get; private set; }

public float SetPoint
{
get => _currentSetPoint;
set
{
_ = WriteHoldingRegister(SetPointRegister, (ushort)(value * 10));
}
}
}

public class ModbusSerialTStatTests
{
// this class assumes a connected serial Temco Controls TSTAT7 or TSTAT8
[Fact]
public async void PolledDevicetest()
public async void PolledDeviceTest()
{
using (var port = new SerialPortShim("COM3", 19200, Hardware.Parity.None, 8, Hardware.StopBits.One))
{
Expand Down Expand Up @@ -264,4 +193,4 @@ public async void ReadWriteHoldingRegisterTest()
Assert.Equal(newSetpoint, verifySetpoint[0]);
}
}
}
}
47 changes: 47 additions & 0 deletions src/Meadow.Modbus.Unit.Tests/ModbusSerialVoltaicBatteryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Meadow.Modbus.Voltaic;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;

namespace Meadow.Modbus.Unit.Tests;

public class ModbusSerialVoltaicBatteryTests
{
// this class assumes a connected serial Voltaic V10x battery/controller
[Fact]
public async void PolledDeviceTest()
{
using (var port = new SerialPortShim("COM5", V10x.DefaultBaudRate, Hardware.Parity.None, 8, Hardware.StopBits.One))
{
port.ReadTimeout = TimeSpan.FromSeconds(15);
port.Open();

var client = new ModbusRtuClient(port);
var controller = new V10x(client);

controller.CommTimeout += (s, e) => Debug.WriteLine("Read Timeout");
controller.CommError += (s, e) => Debug.WriteLine($"Error: {e.Message}");

controller.StartPolling();

var i = 0;

while (true)
{
await Task.Delay(2000);
Debug.WriteLine($"---------------");
Debug.WriteLine($"Battery voltage: {controller.BatteryVoltage.Volts:N2} V");
Debug.WriteLine($"Input voltage: {controller.InputVoltage.Volts:N2} V");
Debug.WriteLine($"Input current: {controller.InputCurrent.Amps:N2} A");
Debug.WriteLine($"Load voltage: {controller.LoadVoltage.Volts:N2} V");
Debug.WriteLine($"Load current: {controller.LoadCurrent.Amps:N2} A");
Debug.WriteLine($"Environ temp: {controller.EnvironmentTemp.Fahrenheit:N2} F");
Debug.WriteLine($"Controller temp: {controller.ControllerTemp.Fahrenheit:N2} F");
// Debug.WriteLine($"Battery output: {controller.BatteryOutput}");

controller.BatteryOutput = (i % 2 == 0);
}
}
}
}
75 changes: 75 additions & 0 deletions src/Meadow.Modbus.Unit.Tests/TStat8.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;

namespace Meadow.Modbus.Temco;

public class TStat8 : ModbusPolledDevice
{
private float _currentSetPoint;

private const ushort SetPointRegister = 345;

public TStat8(ModbusRtuClient client, byte modbusAddress, TimeSpan? refreshPeriod = null)
: base(client, modbusAddress, refreshPeriod)
{
MapHoldingRegistersToProperty(
startRegister: 121,
registerCount: 1,
propertyName: nameof(Temperature),
scale: 0.10); // value is in 0.1 deg

// map to a field, not a property as the property setter needs to perform an action
MapHoldingRegistersToField(
startRegister: SetPointRegister,
registerCount: 1,
fieldName: nameof(_currentSetPoint),
scale: 0.10);

MapHoldingRegistersToProperty(
startRegister: 198,
registerCount: 1,
propertyName: nameof(Humidity));

MapHoldingRegistersToProperty(
startRegister: 364, // not scaled by 0.1
registerCount: 1,
propertyName: nameof(PowerUpSetPoint));

MapHoldingRegistersToProperty(
startRegister: 365,
registerCount: 1,
propertyName: nameof(MaxSetPoint));

MapHoldingRegistersToProperty(
startRegister: 366,
registerCount: 1,
propertyName: nameof(MinSetPoint));

MapHoldingRegistersToProperty(
startRegister: 410,
registerCount: 7,
propertyName: nameof(Clock),
conversionFunction: ConvertRegistersToClockTime);
}

private object ConvertRegistersToClockTime(ushort[] data)
{
// data[2] is week, so ignore
return new DateTime(data[0], data[1], data[3], data[4], data[5], data[6]);
}

public DateTime Clock { get; private set; }
public int Humidity { get; private set; }
public float Temperature { get; private set; }
public float MinSetPoint { get; private set; }
public float MaxSetPoint { get; private set; }
public float PowerUpSetPoint { get; private set; }

public float SetPoint
{
get => _currentSetPoint;
set
{
_ = WriteHoldingRegister(SetPointRegister, (ushort)(value * 10));
}
}
}
95 changes: 95 additions & 0 deletions src/Meadow.Modbus.Unit.Tests/V10x.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Meadow.Units;
using System;

namespace Meadow.Modbus.Voltaic;

public class V10x : ModbusPolledDevice
{
private double _rawBatteryVoltage;
private double _rawInputVoltage;
private double _rawInputCurrent;
private double _rawLoadVoltage;

Check warning on line 11 in src/Meadow.Modbus.Unit.Tests/V10x.cs

View workflow job for this annotation

GitHub Actions / build

Field 'V10x._rawLoadVoltage' is never assigned to, and will always have its default value 0
private double _rawLoadCurrent;

Check warning on line 12 in src/Meadow.Modbus.Unit.Tests/V10x.cs

View workflow job for this annotation

GitHub Actions / build

Field 'V10x._rawLoadCurrent' is never assigned to, and will always have its default value 0
private double _rawEnvironmentTemp;
private double _rawControllerTemp;

Check warning on line 14 in src/Meadow.Modbus.Unit.Tests/V10x.cs

View workflow job for this annotation

GitHub Actions / build

Field 'V10x._rawControllerTemp' is never assigned to, and will always have its default value 0

public const int DefaultModbusAddress = 1;
public const int DefaultBaudRate = 9600;

private const ushort BatteryOutputSwitchRegister = 0;

public Voltage BatteryVoltage => new Voltage(_rawBatteryVoltage, Voltage.UnitType.Volts);
public Voltage InputVoltage => new Voltage(_rawInputVoltage, Voltage.UnitType.Volts);
public Current InputCurrent => new Current(_rawInputCurrent, Current.UnitType.Amps);
public Voltage LoadVoltage => new Voltage(_rawLoadVoltage, Voltage.UnitType.Volts);
public Current LoadCurrent => new Current(_rawLoadCurrent, Current.UnitType.Amps);
public Temperature EnvironmentTemp => new Temperature(_rawEnvironmentTemp, Temperature.UnitType.Celsius);
public Temperature ControllerTemp => new Temperature(_rawControllerTemp, Temperature.UnitType.Celsius);

public V10x(
ModbusClientBase client,
byte modbusAddress = DefaultModbusAddress,
TimeSpan? refreshPeriod = null)
: base(client, modbusAddress, refreshPeriod)
{
MapInputRegistersToField(
startRegister: 0x30a0,
registerCount: 1,
fieldName: nameof(_rawBatteryVoltage),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x304e,
registerCount: 1,
fieldName: nameof(_rawInputVoltage),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x304f,
registerCount: 1,
fieldName: nameof(_rawInputCurrent),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x304a,
registerCount: 1,
fieldName: nameof(_rawLoadVoltage),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x304b,
registerCount: 1,
fieldName: nameof(_rawLoadCurrent),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x30a2,
registerCount: 1,
fieldName: nameof(_rawEnvironmentTemp),
conversionFunction: ConvertRegisterToRawValue
);

MapInputRegistersToField(
startRegister: 0x3037,
registerCount: 1,
fieldName: nameof(_rawControllerTemp),
conversionFunction: ConvertRegisterToRawValue
);
}

public bool BatteryOutput
{
set => _ = WriteCoil(BatteryOutputSwitchRegister, value);
}

private object ConvertRegisterToRawValue(ushort[] registers)
{
// value is one register in 1/100 of a unit
return registers[0] / 100d;
}
}
Loading

0 comments on commit 7814fb6

Please sign in to comment.