wanshenmean
2026-03-13 e5d171107520aa5d40b00b8e3fb8cd26e32531e8
feat: implement M region (Merker memory)

- Add MRegion class with bit/word/dword/int/dint/real operations
- Support individual bit read/write with thread-safety
- Add comprehensive unit tests including concurrent access test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
已添加2个文件
338 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Memory/MRegion.cs 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.UnitTests/Memory/MRegionTests.cs 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Memory/MRegion.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
using System;
using WIDESEAWCS_S7Simulator.Core.Interfaces;
namespace WIDESEAWCS_S7Simulator.Core.Memory
{
    /// <summary>
    /// M区(位存储器/Merker)实现
    /// </summary>
    public class MRegion : MemoryRegion, IMemoryRegion
    {
        /// <summary>
        /// åŒºåŸŸç±»åž‹
        /// </summary>
        public override string RegionType => "M";
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="size">区域大小(字节)</param>
        public MRegion(int size) : base(size)
        {
        }
        /// <summary>
        /// è¯»å–位
        /// </summary>
        /// <param name="byteOffset">字节偏移</param>
        /// <param name="bitOffset">位偏移(0-7)</param>
        /// <returns>位值</returns>
        public bool ReadBit(ushort byteOffset, byte bitOffset)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterReadLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset), "字节偏移超出范围");
                return (_memory[byteOffset] & (1 << bitOffset)) != 0;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
        /// <summary>
        /// å†™å…¥ä½
        /// </summary>
        /// <param name="byteOffset">字节偏移</param>
        /// <param name="bitOffset">位偏移(0-7)</param>
        /// <param name="value">位值</param>
        public void WriteBit(ushort byteOffset, byte bitOffset, bool value)
        {
            if (bitOffset > 7)
                throw new ArgumentOutOfRangeException(nameof(bitOffset), "位偏移必须在0-7之间");
            _lock.EnterWriteLock();
            try
            {
                if (byteOffset >= Size)
                    throw new ArgumentOutOfRangeException(nameof(byteOffset), "字节偏移超出范围");
                if (value)
                    _memory[byteOffset] |= (byte)(1 << bitOffset);
                else
                    _memory[byteOffset] &= (byte)~(1 << bitOffset);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        /// <summary>
        /// è¯»å–字(Word,2字节)
        /// </summary>
        public ushort ReadWord(ushort byteOffset)
        {
            var data = Read(byteOffset, 2);
            return (ushort)((data[0] << 8) | data[1]);
        }
        /// <summary>
        /// å†™å…¥å­—(Word,2字节)
        /// </summary>
        public void WriteWord(ushort byteOffset, ushort value)
        {
            var data = new byte[] { (byte)(value >> 8), (byte)(value & 0xFF) };
            Write(byteOffset, data);
        }
        /// <summary>
        /// è¯»å–双字(DWord,4字节)
        /// </summary>
        public uint ReadDWord(ushort byteOffset)
        {
            var data = Read(byteOffset, 4);
            return (uint)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
        }
        /// <summary>
        /// å†™å…¥åŒå­—(DWord,4字节)
        /// </summary>
        public void WriteDWord(ushort byteOffset, uint value)
        {
            var data = new byte[] {
                (byte)(value >> 24),
                (byte)((value >> 16) & 0xFF),
                (byte)((value >> 8) & 0xFF),
                (byte)(value & 0xFF)
            };
            Write(byteOffset, data);
        }
        /// <summary>
        /// è¯»å–整数(Int,2字节,有符号)
        /// </summary>
        public short ReadInt(ushort byteOffset)
        {
            return (short)ReadWord(byteOffset);
        }
        /// <summary>
        /// å†™å…¥æ•´æ•°ï¼ˆInt,2字节,有符号)
        /// </summary>
        public void WriteInt(ushort byteOffset, short value)
        {
            WriteWord(byteOffset, (ushort)value);
        }
        /// <summary>
        /// è¯»å–双整数(DInt,4字节,有符号)
        /// </summary>
        public int ReadDInt(ushort byteOffset)
        {
            return (int)ReadDWord(byteOffset);
        }
        /// <summary>
        /// å†™å…¥åŒæ•´æ•°ï¼ˆDInt,4字节,有符号)
        /// </summary>
        public void WriteDInt(ushort byteOffset, int value)
        {
            WriteDWord(byteOffset, (uint)value);
        }
        /// <summary>
        /// è¯»å–浮点数(Real,4字节)
        /// </summary>
        public float ReadReal(ushort byteOffset)
        {
            var bytes = Read(byteOffset, 4);
            return BitConverter.ToSingle(bytes, 0);
        }
        /// <summary>
        /// å†™å…¥æµ®ç‚¹æ•°ï¼ˆReal,4字节)
        /// </summary>
        public void WriteReal(ushort byteOffset, float value)
        {
            var bytes = BitConverter.GetBytes(value);
            Write(byteOffset, bytes);
        }
    }
}
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.UnitTests/Memory/MRegionTests.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,170 @@
using Xunit;
using WIDESEAWCS_S7Simulator.Core.Memory;
namespace WIDESEAWCS_S7Simulator.UnitTests.Memory
{
    public class MRegionTests
    {
        [Fact]
        public void Constructor_WithValidSize_CreatesRegion()
        {
            // Arrange & Act
            var region = new MRegion(1024);
            // Assert
            Assert.Equal("M", region.RegionType);
            Assert.Equal(1024, region.Size);
        }
        [Fact]
        public void Read_WithinBounds_ReturnsData()
        {
            // Arrange
            var region = new MRegion(1024);
            var testData = new byte[] { 0x12, 0x34, 0x56, 0x78 };
            region.Write(0, testData);
            // Act
            var result = region.Read(0, 4);
            // Assert
            Assert.Equal(testData, result);
        }
        [Fact]
        public void Read_OutOfBounds_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(100);
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.Read(0, 101));
        }
        [Fact]
        public void Write_WithinBounds_WritesData()
        {
            // Arrange
            var region = new MRegion(1024);
            var testData = new byte[] { 0xAA, 0xBB, 0xCC, 0xDD };
            // Act
            region.Write(100, testData);
            var result = region.Read(100, 4);
            // Assert
            Assert.Equal(testData, result);
        }
        [Fact]
        public void Write_OutOfBounds_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(100);
            var testData = new byte[] { 0x01, 0x02 };
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.Write(99, testData));
        }
        [Fact]
        public void ReadBit_ValidBit_ReturnsCorrectValue()
        {
            // Arrange
            var region = new MRegion(1024);
            region.Write(0, new byte[] { 0xFF }); // æ‰€æœ‰ä½ä¸º1
            // Act
            var result = region.ReadBit(0, 0);
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void WriteBit_ValidBit_SetsCorrectValue()
        {
            // Arrange
            var region = new MRegion(1024);
            // Act
            region.WriteBit(0, 3, true);
            var result = region.ReadBit(0, 3);
            // Assert
            Assert.True(result);
        }
        [Fact]
        public void WriteBit_InvalidBitOffset_ThrowsArgumentOutOfRange()
        {
            // Arrange
            var region = new MRegion(1024);
            // Act & Assert
            Assert.Throws<ArgumentOutOfRangeException>(() => region.WriteBit(0, 8, true));
        }
        [Fact]
        public void Clear_ZerosAllMemory()
        {
            // Arrange
            var region = new MRegion(100);
            region.Write(0, new byte[] { 0xFF, 0xFF, 0xFF });
            // Act
            region.Clear();
            var result = region.Read(0, 3);
            // Assert
            Assert.Equal(new byte[] { 0, 0, 0 }, result);
        }
        [Fact]
        public void ConcurrentReadWrite_ThreadSafe()
        {
            // Arrange
            var region = new MRegion(1024);
            var exceptions = new System.Collections.Concurrent.ConcurrentBag<Exception>();
            var cts = new CancellationTokenSource();
            cts.CancelAfter(1000); // 1秒后取消
            // Act
            var writeTask = Task.Run(() =>
            {
                try
                {
                    var data = new byte[] { 0xAA, 0xBB };
                    while (!cts.Token.IsCancellationRequested)
                    {
                        region.Write(0, data);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }, cts.Token);
            var readTask = Task.Run(() =>
            {
                try
                {
                    while (!cts.Token.IsCancellationRequested)
                    {
                        region.Read(0, 2);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }, cts.Token);
            Task.WaitAll(writeTask, readTask);
            // Assert
            Assert.Empty(exceptions);
        }
    }
}