Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
40f5c8c391
|
@ -4,6 +4,7 @@ using Innovenergy.Backend.Database;
|
||||||
using Innovenergy.Backend.Model;
|
using Innovenergy.Backend.Model;
|
||||||
using Innovenergy.Backend.Model.Relations;
|
using Innovenergy.Backend.Model.Relations;
|
||||||
using Innovenergy.Backend.Utils;
|
using Innovenergy.Backend.Utils;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
||||||
|
|
||||||
|
@ -173,7 +174,8 @@ public class Controller
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
|
|
||||||
var folders = db
|
var folders = db
|
||||||
.GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure
|
.GetDirectlyAccessibleFolders(caller)
|
||||||
|
.Do(f => f.ParentId = 0) // ReSharper disable once AccessToDisposedClosure
|
||||||
.Select(f => PopulateChildren(db, f));
|
.Select(f => PopulateChildren(db, f));
|
||||||
|
|
||||||
var installations = db.GetDirectlyAccessibleInstallations(caller);
|
var installations = db.GetDirectlyAccessibleInstallations(caller);
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,10 @@
|
||||||
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Endianness
|
||||||
|
{
|
||||||
|
LittleEndian32BitIntegers = 0,
|
||||||
|
LittleEndian32Floats = 0,
|
||||||
|
BigEndian32BitIntegers = 1,
|
||||||
|
BigEndian32Floats = 2,
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Threading.Channels;
|
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
|
using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ public abstract class ModbusClient
|
||||||
{
|
{
|
||||||
protected ModbusConnection Connection { get; }
|
protected ModbusConnection Connection { get; }
|
||||||
protected Byte SlaveId { get; }
|
protected Byte SlaveId { get; }
|
||||||
|
protected Endianness Endianness { get; }
|
||||||
|
|
||||||
|
|
||||||
// TODO: add additional functions: coils...
|
// TODO: add additional functions: coils...
|
||||||
|
@ -40,11 +41,11 @@ public abstract class ModbusClient
|
||||||
return WriteRegisters(writeAddress, (IReadOnlyList<UInt16>)values);
|
return WriteRegisters(writeAddress, (IReadOnlyList<UInt16>)values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ModbusClient(ModbusConnection connection, Byte slaveId, Endianness endianness = LittleEndian32BitIntegers | LittleEndian32Floats)
|
||||||
protected ModbusClient(ModbusConnection connection, Byte slaveId)
|
|
||||||
{
|
{
|
||||||
Connection = connection;
|
Connection = connection;
|
||||||
SlaveId = slaveId;
|
SlaveId = slaveId;
|
||||||
|
Endianness = endianness;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseConnection() => Connection.Close();
|
public void CloseConnection() => Connection.Close();
|
||||||
|
|
|
@ -4,13 +4,13 @@ using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Commands;
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Commands;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies;
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Tcp;
|
using InnovEnergy.Lib.Protocols.Modbus.Tcp;
|
||||||
|
using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
using UInt16s = IReadOnlyList<UInt16>;
|
using UInt16s = IReadOnlyList<UInt16>;
|
||||||
using Coils = IReadOnlyList<Boolean>;
|
using Coils = IReadOnlyList<Boolean>;
|
||||||
|
|
||||||
|
|
||||||
public class ModbusTcpClient : ModbusClient
|
public class ModbusTcpClient : ModbusClient
|
||||||
{
|
{
|
||||||
public const UInt16 DefaultPort = 502;
|
public const UInt16 DefaultPort = 502;
|
||||||
|
@ -20,7 +20,7 @@ public class ModbusTcpClient : ModbusClient
|
||||||
private UInt16 NextId() => unchecked(++_Id);
|
private UInt16 NextId() => unchecked(++_Id);
|
||||||
|
|
||||||
|
|
||||||
public ModbusTcpClient(ModbusConnection connection, Byte slaveId) : base(connection, slaveId)
|
public ModbusTcpClient(ModbusConnection connection, Byte slaveId, Endianness endianness = LittleEndian32BitIntegers | LittleEndian32Floats) : base(connection, slaveId, endianness)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ public partial class ModbusRegisters
|
||||||
{
|
{
|
||||||
var offset = index - StartRegister;
|
var offset = index - StartRegister;
|
||||||
|
|
||||||
var byteArray = BitConverter.GetBytes(Registers[offset]).Reverse().ToArray();
|
var byteArray = Registers[offset].Apply(BitConverter.GetBytes).Reverse().ToArray();
|
||||||
var bitArray = new BitArray(byteArray);
|
var bitArray = new BitArray(byteArray);
|
||||||
|
|
||||||
return bitArray.Get(bitIndex);
|
return bitArray.Get(bitIndex);
|
||||||
|
@ -18,7 +19,7 @@ public partial class ModbusRegisters
|
||||||
{
|
{
|
||||||
var offset = index - StartRegister;
|
var offset = index - StartRegister;
|
||||||
|
|
||||||
var byteArray = BitConverter.GetBytes(Registers[offset]).Reverse().ToArray();
|
var byteArray = Registers[offset].Apply(BitConverter.GetBytes).Reverse().ToArray();
|
||||||
var bitArray = new BitArray(byteArray);
|
var bitArray = new BitArray(byteArray);
|
||||||
|
|
||||||
bitArray.Set(bitIndex, value);
|
bitArray.Set(bitIndex, value);
|
||||||
|
|
|
@ -9,7 +9,7 @@ public static class Accessors
|
||||||
public static MbWord WordAt (this ArraySegment<Byte> data, Byte i) => new MbWord(data, i);
|
public static MbWord WordAt (this ArraySegment<Byte> data, Byte i) => new MbWord(data, i);
|
||||||
public static MbAddress AddressAt(this ArraySegment<Byte> data, Byte i) => new MbAddress(data, i);
|
public static MbAddress AddressAt(this ArraySegment<Byte> data, Byte i) => new MbAddress(data, i);
|
||||||
|
|
||||||
public static MbWords WordsAt(this ArraySegment<Byte> data, Byte i) => new MbWords(data, i);
|
public static MbRegisters RegistersAt(this ArraySegment<Byte> data, Byte i) => new MbRegisters(data, i);
|
||||||
public static MbBits BitsAt (this ArraySegment<Byte> data, Byte i) => new MbBits(data, i);
|
public static MbBits BitsAt (this ArraySegment<Byte> data, Byte i) => new MbBits(data, i);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,30 +3,30 @@ using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
|
||||||
|
|
||||||
public readonly struct MbWords : IReadOnlyList<UInt16>
|
public readonly struct MbRegisters : IReadOnlyList<UInt16>
|
||||||
{
|
{
|
||||||
private readonly ArraySegment<Byte> _Data;
|
private readonly ArraySegment<Byte> _Data;
|
||||||
|
|
||||||
internal MbWords(ArraySegment<Byte> data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex))
|
internal MbRegisters(ArraySegment<Byte> data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal MbRegisters(ArraySegment<Byte> data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal MbRegisters(Byte[] data, Byte startIndex, UInt16 wordCount)
|
||||||
|
{
|
||||||
|
_Data = new ArraySegment<Byte>(data, startIndex, wordCount * 2);
|
||||||
|
}
|
||||||
|
|
||||||
private static UInt16 CountWords(ArraySegment<Byte> data, Byte startIndex)
|
private static UInt16 CountWords(ArraySegment<Byte> data, Byte startIndex)
|
||||||
{
|
{
|
||||||
var wordCount = (data.Count - startIndex) / 2;
|
var wordCount = (data.Count - startIndex) / 2;
|
||||||
return wordCount.ConvertTo<UInt16>();
|
return wordCount.ConvertTo<UInt16>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal MbWords(ArraySegment<Byte> data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal MbWords(Byte[] data, Byte startIndex, UInt16 wordCount)
|
|
||||||
{
|
|
||||||
_Data = new ArraySegment<Byte>(data, startIndex, wordCount * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IReadOnlyCollection<UInt16> Set(IReadOnlyCollection<UInt16> values)
|
internal IReadOnlyCollection<UInt16> Set(IReadOnlyCollection<UInt16> values)
|
||||||
{
|
{
|
||||||
if (values.Count != _Data.Count / 2)
|
if (values.Count != _Data.Count / 2)
|
|
@ -16,7 +16,7 @@ internal class ReadWriteRegistersCommandFrame : ModbusFrame
|
||||||
public MbAddress WriteAddress => Data.AddressAt(6);
|
public MbAddress WriteAddress => Data.AddressAt(6);
|
||||||
public MbWord NbToWrite => Data.WordAt(8);
|
public MbWord NbToWrite => Data.WordAt(8);
|
||||||
public MbByte ByteCount => Data.ByteAt(10);
|
public MbByte ByteCount => Data.ByteAt(10);
|
||||||
public MbWords RegistersToWrite => Data.WordsAt(11);
|
public MbRegisters RegistersToWrite => Data.RegistersAt(11);
|
||||||
|
|
||||||
public Int32 ExpectedResponseSize => ReadWriteRegistersResponseFrame.ExpectedSize(NbToRead);
|
public Int32 ExpectedResponseSize => ReadWriteRegistersResponseFrame.ExpectedSize(NbToRead);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ internal class WriteRegistersCommandFrame : ModbusFrame
|
||||||
public MbAddress WriteAddress => Data.AddressAt(2);
|
public MbAddress WriteAddress => Data.AddressAt(2);
|
||||||
public MbWord NbOfRegisters => Data.WordAt(4);
|
public MbWord NbOfRegisters => Data.WordAt(4);
|
||||||
public MbByte ByteCount => Data.ByteAt(6);
|
public MbByte ByteCount => Data.ByteAt(6);
|
||||||
public MbWords RegistersToWrite => Data.WordsAt(7);
|
public MbRegisters RegistersToWrite => Data.RegistersAt(7);
|
||||||
|
|
||||||
public Int32 ExpectedResponseSize => WriteRegistersResponseFrame.ExpectedSize();
|
public Int32 ExpectedResponseSize => WriteRegistersResponseFrame.ExpectedSize();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ internal class ReadHoldingRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
public MbByte ByteCount => Data.ByteAt(2);
|
||||||
public MbWords RegistersRead => Data.WordsAt(3);
|
public MbRegisters RegistersRead => Data.RegistersAt(3);
|
||||||
|
|
||||||
|
|
||||||
public ReadHoldingRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
public ReadHoldingRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
|
|
|
@ -12,7 +12,7 @@ internal class ReadInputRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
public MbByte ByteCount => Data.ByteAt(2);
|
||||||
public MbWords RegistersRead => Data.WordsAt(3);
|
public MbRegisters RegistersRead => Data.RegistersAt(3);
|
||||||
|
|
||||||
public ReadInputRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
public ReadInputRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ internal class ReadWriteRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
public MbByte ByteCount => Data.ByteAt(2);
|
||||||
public MbWords RegistersRead => Data.WordsAt(3);
|
public MbRegisters RegistersRead => Data.RegistersAt(3);
|
||||||
|
|
||||||
public ReadWriteRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
public ReadWriteRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,26 +1,14 @@
|
||||||
using System.Text.Json;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static InnovEnergy.Lib.Units.Units;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record DeviceStatus
|
public abstract record DeviceStatus
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions;
|
|
||||||
|
|
||||||
static DeviceStatus()
|
|
||||||
{
|
|
||||||
JsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
|
|
||||||
JsonConverters.ForEach(JsonSerializerOptions.Converters.Add); // how stupid is that?!!
|
|
||||||
}
|
|
||||||
|
|
||||||
public String DeviceType => GetType()
|
public String DeviceType => GetType()
|
||||||
.Generate(t => t.BaseType!)
|
.Unfold(t => t.BaseType)
|
||||||
.First(t => t.IsAbstract)
|
.First(t => t.IsAbstract)
|
||||||
.Name
|
.Name
|
||||||
.Replace("Status", "");
|
.Replace("Status", "");
|
||||||
|
|
||||||
public String ToJson() => JsonSerializer.Serialize(this, GetType(), JsonSerializerOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Utils\Utils.csproj" />
|
<ProjectReference Include="../Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Angle;
|
using T = Angle;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(AngleConverter))]
|
||||||
public readonly partial struct Angle
|
public readonly partial struct Angle
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = ApparentPower;
|
using T = ApparentPower;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ApparentPowerConverter))]
|
||||||
public readonly partial struct ApparentPower
|
public readonly partial struct ApparentPower
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Current;
|
using T = Current;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(CurrentConverter))]
|
||||||
public readonly partial struct Current
|
public readonly partial struct Current
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Frequency;
|
using T = Frequency;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(FrequencyConverter))]
|
||||||
public readonly partial struct Frequency
|
public readonly partial struct Frequency
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Template;
|
using T = Template;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(TemplateConverter))]
|
||||||
public readonly partial struct Template
|
public readonly partial struct Template
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Number;
|
using T = Number;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(NumberConverter))]
|
||||||
public readonly partial struct Number
|
public readonly partial struct Number
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Power;
|
using T = Power;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(PowerConverter))]
|
||||||
public readonly partial struct Power
|
public readonly partial struct Power
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = ReactivePower;
|
using T = ReactivePower;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ReactivePowerConverter))]
|
||||||
public readonly partial struct ReactivePower
|
public readonly partial struct ReactivePower
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Resistance;
|
using T = Resistance;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ResistanceConverter))]
|
||||||
public readonly partial struct Resistance
|
public readonly partial struct Resistance
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Temperature;
|
using T = Temperature;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(TemperatureConverter))]
|
||||||
public readonly partial struct Temperature
|
public readonly partial struct Temperature
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Utils\Utils.csproj" />
|
<ProjectReference Include="../Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
|
|
|
@ -17,3 +17,4 @@ public readonly partial struct Voltage
|
||||||
// P=UI
|
// P=UI
|
||||||
public static Power operator *(Voltage voltage, Current current) => new Power(current.Value * voltage.Value);
|
public static Power operator *(Voltage voltage, Current current) => new Power(current.Value * voltage.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Voltage;
|
using T = Voltage;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(VoltageConverter))]
|
||||||
public readonly partial struct Voltage
|
public readonly partial struct Voltage
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
|
|
|
@ -243,7 +243,7 @@ public static class EnumerableUtils
|
||||||
return ts.ElementAtOrDefault(index) ?? defaultValue;
|
return ts.ElementAtOrDefault(index) ?? defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<T> Generate<T>(this T seed, Func<T, T> next)
|
public static IEnumerable<T> Unfold<T>(this T seed, Func<T, T?> next)
|
||||||
{
|
{
|
||||||
var value = seed;
|
var value = seed;
|
||||||
while (value is not null)
|
while (value is not null)
|
||||||
|
|
Loading…
Reference in New Issue