Class
Sealed
public sealed class TableStore
Namespace: SharpMeter.Emulator
In-memory storage for PSEM table data used by the emulator.
Constructors
| Name | Description |
|---|---|
TableStore(EmulatorConfiguration config) |
Initializes a new TableStore and populates standard tables from config. |
TableStore(EmulatorConfiguration config)
TableStore.TableStore(EmulatorConfiguration config)
Initializes a new TableStore and populates standard tables from config.
Parameters
| Name | Type | Description |
|---|---|---|
config | SharpMeter.Emulator.EmulatorConfiguration | The emulator configuration. |
Properties
| Name | Description |
|---|---|
TableIds |
Gets all table IDs currently stored. |
TableIds
IEnumerable<ushort> TableStore.TableIds { get; }
Gets all table IDs currently stored.
Methods
| Name | Description |
|---|---|
HasTable(ushort tableId) |
Checks whether a table exists in the store. |
ReadTable(ushort tableId) |
Reads the full data for a table. |
ReadTableOffset(…) |
Reads a portion of a table at the given offset. |
WriteTable(ushort tableId, byte[] data) |
Writes full data to a table. |
WriteTableOffset(…) |
Writes data at a specific offset within a table. |
HasTable(ushort tableId)
bool TableStore.HasTable(ushort tableId)
Checks whether a table exists in the store.
ReadTable(ushort tableId)
byte[]? TableStore.ReadTable(ushort tableId)
Reads the full data for a table.
Parameters
| Name | Type | Description |
|---|---|---|
tableId | ushort | The table ID. |
Returns: The table data, or null if the table doesn't exist.
ReadTableOffset(ushort tableId, int offset, int count)
byte[]? TableStore.ReadTableOffset(ushort tableId, int offset, int count)
Reads a portion of a table at the given offset.
Parameters
| Name | Type | Description |
|---|---|---|
tableId | ushort | The table ID. |
offset | int | The byte offset. |
count | int | The number of bytes to read. |
Returns: The partial table data, or null if table doesn't exist or range is invalid.
WriteTable(ushort tableId, byte[] data)
void TableStore.WriteTable(ushort tableId, byte[] data)
Writes full data to a table.
Parameters
| Name | Type | Description |
|---|---|---|
tableId | ushort | The table ID. |
data | byte[] | The data to write. |
WriteTableOffset(ushort tableId, int offset, ReadOnlySpan data)
bool TableStore.WriteTableOffset(ushort tableId, int offset, ReadOnlySpan<byte> data)
Writes data at a specific offset within a table.
Parameters
| Name | Type | Description |
|---|---|---|
tableId | ushort | The table ID. |
offset | int | The byte offset. |
data | ReadOnlySpan<byte> | The data to write. |
Returns: True if successful, false if the table doesn't exist or offset is invalid.
View Source
/// <summary>
/// In-memory storage for PSEM table data used by the emulator.
/// </summary>
public sealed class TableStore
{
#region Constructor
/// <summary>
/// Initializes a new <see cref = "TableStore"/> and populates standard tables from config.
/// </summary>
/// <param name = "config">The emulator configuration.</param>
public TableStore(EmulatorConfiguration config)
{
_config = config;
InitializeStandardTables();
}
#endregion
#region Private Methods
private void InitializeStandardTables()
{
// ST0: General Configuration Table (94 bytes)
var st0 = new byte[94];
st0[4] = (byte)_config.Target; // NAMEPLATE_TYPE
_tables[0] = st0;
// ST1: General Manufacturer ID Table (32 bytes)
var st1 = new byte[32];
Encoding.ASCII.GetBytes(_config.Manufacturer.PadRight(4)[..4]).CopyTo(st1, 0);
Encoding.ASCII.GetBytes(_config.ModelString.PadRight(8)[..8]).CopyTo(st1, 4);
st1[12] = _config.HardwareVersion;
st1[13] = _config.HardwareRevision;
st1[14] = _config.FirmwareVersion.Length > 0 ? _config.FirmwareVersion[0] : (byte)0;
st1[15] = _config.FirmwareVersion.Length > 1 ? _config.FirmwareVersion[1] : (byte)0;
Encoding.ASCII.GetBytes(_config.SerialNumber.PadRight(16)[..16]).CopyTo(st1, 16);
_tables[1] = st1;
// ST3: ED Mode Status Table (5 bytes) - all zeros = normal operation
_tables[3] = new byte[5];
// ST5: Device Identification Table (20 bytes)
var st5 = new byte[20];
Encoding.ASCII.GetBytes(_config.SerialNumber.PadRight(20)[..20]).CopyTo(st5, 0);
_tables[5] = st5;
// ST7: Procedure Initiation Table (placeholder for writes)
_tables[PsemConstants.ProcedureInitiationTable] = new byte[64];
// ST8: Procedure Response Table (16 bytes)
_tables[PsemConstants.ProcedureResponseTable] = new byte[16];
// ST52: Clock Table (7 bytes)
var st52 = new byte[7];
DateTime now = DateTime.UtcNow;
st52[0] = (byte)(now.Year - 2000);
st52[1] = (byte)now.Month;
st52[2] = (byte)now.Day;
st52[3] = (byte)now.Hour;
st52[4] = (byte)now.Minute;
st52[5] = (byte)now.Second;
_tables[52] = st52;
// MT0: Device Table (73 bytes)
var mt0 = new byte[73];
mt0[0] = _config.FirmwareVersion.Length > 0 ? _config.FirmwareVersion[0] : (byte)0;
mt0[1] = _config.FirmwareVersion.Length > 1 ? _config.FirmwareVersion[1] : (byte)0;
mt0[12] = (byte)_config.Model;
mt0[13] = (byte)_config.Mode;
// Write softswitch upgrades as 4-byte bitfield at offset 21
var upgrades = (uint)_config.Upgrades;
mt0[21] = (byte)(upgrades & 0xFF);
mt0[22] = (byte)((upgrades >> 8) & 0xFF);
mt0[23] = (byte)((upgrades >> 16) & 0xFF);
mt0[24] = (byte)((upgrades >> 24) & 0xFF);
_tables[PsemConstants.ManufacturingTableOffset] = mt0; // 2048
}
#endregion
#region Fields
private readonly ConcurrentDictionary<ushort, byte[]> _tables = new();
private readonly EmulatorConfiguration _config;
#endregion
#region Public Methods
/// <summary>Reads the full data for a table.</summary>
/// <param name = "tableId">The table ID.</param>
/// <returns>The table data, or null if the table doesn't exist.</returns>
public byte[]? ReadTable(ushort tableId) => _tables.TryGetValue(tableId, out var data) ? data : null;
/// <summary>Reads a portion of a table at the given offset.</summary>
/// <param name = "tableId">The table ID.</param>
/// <param name = "offset">The byte offset.</param>
/// <param name = "count">The number of bytes to read.</param>
/// <returns>The partial table data, or null if table doesn't exist or range is invalid.</returns>
public byte[]? ReadTableOffset(ushort tableId, int offset, int count)
{
if (!_tables.TryGetValue(tableId, out var data))
return null;
if (offset < 0 || offset + count > data.Length)
return null;
return data.AsSpan(offset, count).ToArray();
}
/// <summary>Writes full data to a table.</summary>
/// <param name = "tableId">The table ID.</param>
/// <param name = "data">The data to write.</param>
public void WriteTable(ushort tableId, byte[] data) => _tables[tableId] = data;
/// <summary>Writes data at a specific offset within a table.</summary>
/// <param name = "tableId">The table ID.</param>
/// <param name = "offset">The byte offset.</param>
/// <param name = "data">The data to write.</param>
/// <returns>True if successful, false if the table doesn't exist or offset is invalid.</returns>
public bool WriteTableOffset(ushort tableId, int offset, ReadOnlySpan<byte> data)
{
if (!_tables.TryGetValue(tableId, out var existing))
return false;
if (offset < 0 || offset + data.Length > existing.Length)
return false;
data.CopyTo(existing.AsSpan(offset));
return true;
}
/// <summary>Checks whether a table exists in the store.</summary>
public bool HasTable(ushort tableId) => _tables.ContainsKey(tableId);
/// <summary>Gets all table IDs currently stored.</summary>
public IEnumerable<ushort> TableIds => _tables.Keys;
#endregion
}