namespace Modbus.Data
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Unme.Common;
///
/// Object simulation of device memory map.
/// The underlying collections are thread safe when using the ModbusMaster API to read/write values.
/// You can use the SyncRoot property to synchronize direct access to the DataStore collections.
///
public class DataStore
{
private readonly object _syncRoot = new object();
///
/// Initializes a new instance of the class.
///
public DataStore()
{
CoilDiscretes = new ModbusDataCollection { ModbusDataType = ModbusDataType.Coil };
InputDiscretes = new ModbusDataCollection { ModbusDataType = ModbusDataType.Input };
HoldingRegisters = new ModbusDataCollection { ModbusDataType = ModbusDataType.HoldingRegister };
InputRegisters = new ModbusDataCollection { ModbusDataType = ModbusDataType.InputRegister };
}
///
/// Initializes a new instance of the class.
///
/// List of discrete coil values.
/// List of discrete input values
/// List of holding register values.
/// List of input register values.
internal DataStore(
IList coilDiscretes,
IList inputDiscretes,
IList holdingRegisters,
IList inputRegisters)
{
CoilDiscretes = new ModbusDataCollection(coilDiscretes) { ModbusDataType = ModbusDataType.Coil };
InputDiscretes = new ModbusDataCollection(inputDiscretes) { ModbusDataType = ModbusDataType.Input };
HoldingRegisters = new ModbusDataCollection(holdingRegisters) { ModbusDataType = ModbusDataType.HoldingRegister };
InputRegisters = new ModbusDataCollection(inputRegisters) { ModbusDataType = ModbusDataType.InputRegister };
}
///
/// Occurs when the DataStore is written to via a Modbus command.
///
public event EventHandler DataStoreWrittenTo;
///
/// Occurs when the DataStore is read from via a Modbus command.
///
public event EventHandler DataStoreReadFrom;
///
/// Gets the discrete coils.
///
public ModbusDataCollection CoilDiscretes { get; }
///
/// Gets the discrete inputs.
///
public ModbusDataCollection InputDiscretes { get; }
///
/// Gets the holding registers.
///
public ModbusDataCollection HoldingRegisters { get; }
///
/// Gets the input registers.
///
public ModbusDataCollection InputRegisters { get; }
///
/// An object that can be used to synchronize direct access to the DataStore collections.
///
public object SyncRoot
{
get { return _syncRoot; }
}
///
/// Retrieves subset of data from collection.
///
/// The collection type.
/// The type of elements in the collection.
internal static T ReadData(
DataStore dataStore,
ModbusDataCollection dataSource,
ushort startAddress,
ushort count,
object syncRoot)
where T : Collection, new()
{
DataStoreEventArgs dataStoreEventArgs;
int startIndex = startAddress + 1;
if (startIndex < 0 || dataSource.Count < startIndex + count)
{
throw new InvalidModbusRequestException(Modbus.IllegalDataAddress);
}
U[] dataToRetrieve;
lock (syncRoot)
{
dataToRetrieve = dataSource.Slice(startIndex, count).ToArray();
}
T result = new T();
for (int i = 0; i < count; i++)
{
result.Add(dataToRetrieve[i]);
}
dataStoreEventArgs = DataStoreEventArgs.CreateDataStoreEventArgs(startAddress, dataSource.ModbusDataType, result);
dataStore.DataStoreReadFrom?.Invoke(dataStore, dataStoreEventArgs);
return result;
}
///
/// Write data to data store.
///
/// The type of the data.
internal static void WriteData(
DataStore dataStore,
IEnumerable items,
ModbusDataCollection destination,
ushort startAddress,
object syncRoot)
{
DataStoreEventArgs dataStoreEventArgs;
int startIndex = startAddress + 1;
if (startIndex < 0 || destination.Count < startIndex + items.Count())
{
throw new InvalidModbusRequestException(Modbus.IllegalDataAddress);
}
lock (syncRoot)
{
Update(items, destination, startIndex);
}
dataStoreEventArgs = DataStoreEventArgs.CreateDataStoreEventArgs(
startAddress,
destination.ModbusDataType,
items);
dataStore.DataStoreWrittenTo?.Invoke(dataStore, dataStoreEventArgs);
}
///
/// Updates subset of values in a collection.
///
internal static void Update(IEnumerable items, IList destination, int startIndex)
{
if (startIndex < 0 || destination.Count < startIndex + items.Count())
{
throw new InvalidModbusRequestException(Modbus.IllegalDataAddress);
}
int index = startIndex;
foreach (T item in items)
{
destination[index] = item;
++index;
}
}
}
}