using System;
|
using System.Collections.Generic;
|
using System.Text;
|
using WIDESEAWCS_S7Simulator.Core.Interfaces;
|
|
namespace WIDESEAWCS_S7Simulator.Core.Memory
|
{
|
/// <summary>
|
/// DB区(数据块)- 支持多个DB块
|
/// </summary>
|
public class DBRegion : IMemoryRegion
|
{
|
/// <summary>
|
/// DB块字典,键为DB编号,值为字节数组
|
/// </summary>
|
private readonly Dictionary<ushort, byte[]> _blocks;
|
|
/// <summary>
|
/// 读写锁(支持并发访问)
|
/// </summary>
|
private readonly ReaderWriterLockSlim _lock;
|
|
/// <summary>
|
/// 释放状态标志
|
/// </summary>
|
private bool _disposed = false;
|
|
/// <summary>
|
/// 单个DB块的大小(字节)
|
/// </summary>
|
private readonly int _blockSize;
|
|
/// <summary>
|
/// 默认DB编号
|
/// </summary>
|
private const ushort DEFAULT_DB_NUMBER = 1;
|
|
/// <summary>
|
/// 区域类型
|
/// </summary>
|
public string RegionType => "DB";
|
|
/// <summary>
|
/// 区域大小(字节)- 返回默认DB块的大小
|
/// </summary>
|
public int Size => _blockSize;
|
|
/// <summary>
|
/// 构造函数
|
/// </summary>
|
/// <param name="defaultDBNumber">默认DB编号</param>
|
/// <param name="blockSize">单个DB块的大小</param>
|
public DBRegion(ushort defaultDBNumber, int blockSize)
|
{
|
_blockSize = blockSize;
|
_blocks = new Dictionary<ushort, byte[]>();
|
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
|
// 创建默认DB块
|
CreateBlock(defaultDBNumber);
|
}
|
|
/// <summary>
|
/// 创建DB块
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
private void CreateBlock(ushort dbNumber)
|
{
|
if (!_blocks.ContainsKey(dbNumber))
|
{
|
_blocks[dbNumber] = new byte[_blockSize];
|
}
|
}
|
|
/// <summary>
|
/// 获取DB块
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <returns>DB块字节数组</returns>
|
private byte[] GetBlock(ushort dbNumber)
|
{
|
if (!_blocks.ContainsKey(dbNumber))
|
{
|
throw new ArgumentException($"DB块不存在: DB{dbNumber}");
|
}
|
return _blocks[dbNumber];
|
}
|
|
/// <summary>
|
/// 读取字节数据(使用默认DB1)
|
/// </summary>
|
/// <param name="offset">偏移量</param>
|
/// <param name="length">长度</param>
|
/// <returns>字节数组</returns>
|
public byte[] Read(ushort offset, ushort length)
|
{
|
return Read(DEFAULT_DB_NUMBER, offset, length);
|
}
|
|
/// <summary>
|
/// 读取字节数据(指定DB编号)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="length">长度</param>
|
/// <returns>字节数组</returns>
|
public byte[] Read(ushort dbNumber, ushort offset, ushort length)
|
{
|
_lock.EnterReadLock();
|
try
|
{
|
var block = GetBlock(dbNumber);
|
if (offset + length > block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"读取超出DB{dbNumber}区范围: offset={offset}, length={length}, size={block.Length}");
|
}
|
|
byte[] result = new byte[length];
|
Array.Copy(block, offset, result, 0, length);
|
return result;
|
}
|
finally
|
{
|
_lock.ExitReadLock();
|
}
|
}
|
|
/// <summary>
|
/// 写入字节数据(使用默认DB1)
|
/// </summary>
|
/// <param name="offset">偏移量</param>
|
/// <param name="data">字节数组</param>
|
public void Write(ushort offset, byte[] data)
|
{
|
Write(DEFAULT_DB_NUMBER, offset, data);
|
}
|
|
/// <summary>
|
/// 写入字节数据(指定DB编号)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="data">字节数组</param>
|
public void Write(ushort dbNumber, ushort offset, byte[] data)
|
{
|
if (data == null)
|
{
|
throw new ArgumentNullException(nameof(data));
|
}
|
|
_lock.EnterWriteLock();
|
try
|
{
|
// 如果DB块不存在,自动创建
|
if (!_blocks.ContainsKey(dbNumber))
|
{
|
CreateBlock(dbNumber);
|
}
|
|
var block = _blocks[dbNumber];
|
if (offset + data.Length > block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"写入超出DB{dbNumber}区范围: offset={offset}, length={data.Length}, size={block.Length}");
|
}
|
|
Array.Copy(data, 0, block, offset, data.Length);
|
}
|
finally
|
{
|
_lock.ExitWriteLock();
|
}
|
}
|
|
/// <summary>
|
/// 读取布尔值
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="byteOffset">字节偏移量</param>
|
/// <param name="bitOffset">位偏移量(0-7)</param>
|
/// <returns>布尔值</returns>
|
public bool ReadBool(ushort dbNumber, ushort byteOffset, byte bitOffset)
|
{
|
if (bitOffset > 7)
|
{
|
throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移量必须在0-7之间");
|
}
|
|
_lock.EnterReadLock();
|
try
|
{
|
var block = GetBlock(dbNumber);
|
if (byteOffset >= block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"读取超出DB{dbNumber}区范围: byteOffset={byteOffset}, size={block.Length}");
|
}
|
|
return (block[byteOffset] & (1 << bitOffset)) != 0;
|
}
|
finally
|
{
|
_lock.ExitReadLock();
|
}
|
}
|
|
/// <summary>
|
/// 写入布尔值
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="byteOffset">字节偏移量</param>
|
/// <param name="bitOffset">位偏移量(0-7)</param>
|
/// <param name="value">布尔值</param>
|
public void WriteBool(ushort dbNumber, ushort byteOffset, byte bitOffset, bool value)
|
{
|
if (bitOffset > 7)
|
{
|
throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移量必须在0-7之间");
|
}
|
|
_lock.EnterWriteLock();
|
try
|
{
|
// 如果DB块不存在,自动创建
|
if (!_blocks.ContainsKey(dbNumber))
|
{
|
CreateBlock(dbNumber);
|
}
|
|
var block = _blocks[dbNumber];
|
if (byteOffset >= block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"写入超出DB{dbNumber}区范围: byteOffset={byteOffset}, size={block.Length}");
|
}
|
|
if (value)
|
{
|
block[byteOffset] |= (byte)(1 << bitOffset);
|
}
|
else
|
{
|
block[byteOffset] &= (byte)~(1 << bitOffset);
|
}
|
}
|
finally
|
{
|
_lock.ExitWriteLock();
|
}
|
}
|
|
/// <summary>
|
/// 读取16位整数(Int,大端序)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <returns>16位整数</returns>
|
public short ReadInt(ushort dbNumber, ushort offset)
|
{
|
var data = Read(dbNumber, offset, 2);
|
return (short)((data[0] << 8) | data[1]);
|
}
|
|
/// <summary>
|
/// 写入16位整数(Int,大端序)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="value">16位整数</param>
|
public void WriteInt(ushort dbNumber, ushort offset, short value)
|
{
|
var data = new byte[2];
|
data[0] = (byte)((value >> 8) & 0xFF);
|
data[1] = (byte)(value & 0xFF);
|
Write(dbNumber, offset, data);
|
}
|
|
/// <summary>
|
/// 读取32位整数(DInt,大端序)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <returns>32位整数</returns>
|
public int ReadDInt(ushort dbNumber, ushort offset)
|
{
|
var data = Read(dbNumber, offset, 4);
|
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
}
|
|
/// <summary>
|
/// 写入32位整数(DInt,大端序)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="value">32位整数</param>
|
public void WriteDInt(ushort dbNumber, ushort offset, int value)
|
{
|
var data = new byte[4];
|
data[0] = (byte)((value >> 24) & 0xFF);
|
data[1] = (byte)((value >> 16) & 0xFF);
|
data[2] = (byte)((value >> 8) & 0xFF);
|
data[3] = (byte)(value & 0xFF);
|
Write(dbNumber, offset, data);
|
}
|
|
/// <summary>
|
/// 读取32位浮点数(Real,IEEE 754)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <returns>32位浮点数</returns>
|
public float ReadReal(ushort dbNumber, ushort offset)
|
{
|
var data = Read(dbNumber, offset, 4);
|
// 需要转换字节序(大端序转小端序)
|
if (BitConverter.IsLittleEndian)
|
{
|
Array.Reverse(data);
|
}
|
return BitConverter.ToSingle(data, 0);
|
}
|
|
/// <summary>
|
/// 写入32位浮点数(Real,IEEE 754)
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="value">32位浮点数</param>
|
public void WriteReal(ushort dbNumber, ushort offset, float value)
|
{
|
var data = BitConverter.GetBytes(value);
|
// 需要转换字节序(小端序转大端序)
|
if (BitConverter.IsLittleEndian)
|
{
|
Array.Reverse(data);
|
}
|
Write(dbNumber, offset, data);
|
}
|
|
/// <summary>
|
/// 读取字符串(S7字符串格式)
|
/// S7字符串格式:[最大长度(1字节)][实际长度(1字节)][字符数据]
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="maxLength">最大长度</param>
|
/// <returns>字符串</returns>
|
public string ReadString(ushort dbNumber, ushort offset, byte maxLength)
|
{
|
_lock.EnterReadLock();
|
try
|
{
|
var block = GetBlock(dbNumber);
|
if (offset + 2 + maxLength > block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"读取字符串超出DB{dbNumber}区范围: offset={offset}, maxLength={maxLength}, size={block.Length}");
|
}
|
|
// 读取实际长度
|
byte actualLength = block[offset + 1];
|
|
// 读取字符数据
|
if (actualLength == 0)
|
{
|
return string.Empty;
|
}
|
|
var chars = new char[actualLength];
|
for (int i = 0; i < actualLength; i++)
|
{
|
chars[i] = (char)block[offset + 2 + i];
|
}
|
|
return new string(chars);
|
}
|
finally
|
{
|
_lock.ExitReadLock();
|
}
|
}
|
|
/// <summary>
|
/// 写入字符串(S7字符串格式)
|
/// S7字符串格式:[最大长度(1字节)][实际长度(1字节)][字符数据]
|
/// </summary>
|
/// <param name="dbNumber">DB编号</param>
|
/// <param name="offset">偏移量</param>
|
/// <param name="value">字符串值</param>
|
/// <param name="maxLength">最大长度</param>
|
public void WriteString(ushort dbNumber, ushort offset, string value, byte maxLength)
|
{
|
if (value == null)
|
{
|
throw new ArgumentNullException(nameof(value));
|
}
|
|
if (value.Length > maxLength)
|
{
|
throw new ArgumentException(
|
$"字符串长度{value.Length}超过最大长度{maxLength}");
|
}
|
|
_lock.EnterWriteLock();
|
try
|
{
|
// 如果DB块不存在,自动创建
|
if (!_blocks.ContainsKey(dbNumber))
|
{
|
CreateBlock(dbNumber);
|
}
|
|
var block = _blocks[dbNumber];
|
if (offset + 2 + maxLength > block.Length)
|
{
|
throw new ArgumentOutOfRangeException(
|
$"写入字符串超出DB{dbNumber}区范围: offset={offset}, maxLength={maxLength}, size={block.Length}");
|
}
|
|
// 写入最大长度
|
block[offset] = maxLength;
|
|
// 写入实际长度
|
block[offset + 1] = (byte)value.Length;
|
|
// 写入字符数据
|
for (int i = 0; i < value.Length; i++)
|
{
|
block[offset + 2 + i] = (byte)value[i];
|
}
|
|
// 填充剩余空间为0
|
for (int i = value.Length; i < maxLength; i++)
|
{
|
block[offset + 2 + i] = 0;
|
}
|
}
|
finally
|
{
|
_lock.ExitWriteLock();
|
}
|
}
|
|
/// <summary>
|
/// 清空所有DB块
|
/// </summary>
|
public void Clear()
|
{
|
_lock.EnterWriteLock();
|
try
|
{
|
foreach (var block in _blocks.Values)
|
{
|
Array.Clear(block, 0, block.Length);
|
}
|
}
|
finally
|
{
|
_lock.ExitWriteLock();
|
}
|
}
|
|
/// <summary>
|
/// 释放资源
|
/// </summary>
|
public void Dispose()
|
{
|
Dispose(true);
|
GC.SuppressFinalize(this);
|
}
|
|
/// <summary>
|
/// 释放资源
|
/// </summary>
|
/// <param name="disposing">是否正在释放托管资源</param>
|
protected virtual void Dispose(bool disposing)
|
{
|
if (!_disposed)
|
{
|
if (disposing)
|
{
|
_lock?.Dispose();
|
_blocks.Clear();
|
}
|
_disposed = true;
|
}
|
}
|
}
|
}
|