wanshenmean
2026-03-17 737dec3c384f394fd6f9849b4480b697d1ba35d5
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Core/Memory/MemoryStore.cs
@@ -62,7 +62,13 @@
            _config = config ?? throw new ArgumentNullException(nameof(config));
            _mRegion = new MRegion(config.MRegionSize);
            _dbRegion = new DBRegion((ushort)config.DBBlockCount, config.DBBlockSize);
            var configuredDbNumbers = ResolveConfiguredDbNumbers();
            var initialDbNumber = configuredDbNumbers.FirstOrDefault();
            _dbRegion = new DBRegion(initialDbNumber == 0 ? (ushort)1 : initialDbNumber, config.DBBlockSize);
            foreach (var dbNumber in configuredDbNumbers)
            {
                _dbRegion.EnsureBlock(dbNumber);
            }
            _iRegion = new IRegion(config.IRegionSize);
            _qRegion = new QRegion(config.QRegionSize);
            _tRegion = new TRegion(config.TRegionCount);
@@ -125,7 +131,7 @@
            return regionType switch
            {
                "M" => ReadMRegion<T>(offset),
                "DB" => ReadDBRegion<T>(dbNumber.Value, offset),
                "DB" => ReadDBRegion<T>(dbNumber.Value, offset, bitOffset),
                "I" => ReadIRegion<T>(offset, bitOffset),
                "Q" => ReadQRegion<T>(offset, bitOffset),
                "T" => ReadTRegion<T>(offset),
@@ -204,7 +210,7 @@
                    WriteMRegion(offset, value);
                    break;
                case "DB":
                    WriteDBRegion(dbNumber.Value, offset, value);
                    WriteDBRegion(dbNumber.Value, offset, bitOffset, value);
                    break;
                case "I":
                    WriteIRegion(offset, bitOffset, value);
@@ -280,12 +286,12 @@
            return new Dictionary<string, byte[]>
            {
                ["M"] = _mRegion.Read(0, (ushort)_mRegion.Size),
                ["M"] = ReadAllFromRegion(_mRegion, _mRegion.Size),
                ["DB"] = ExportDBRegion(),
                ["I"] = _iRegion.Read(0, (ushort)_iRegion.Size),
                ["Q"] = _qRegion.Read(0, (ushort)_qRegion.Size),
                ["T"] = _tRegion.Read(0, (ushort)_tRegion.Size),
                ["C"] = _cRegion.Read(0, (ushort)_cRegion.Size)
                ["I"] = ReadAllFromRegion(_iRegion, _iRegion.Size),
                ["Q"] = ReadAllFromRegion(_qRegion, _qRegion.Size),
                ["T"] = ReadAllFromRegion(_tRegion, _tRegion.Size),
                ["C"] = ReadAllFromRegion(_cRegion, _cRegion.Size)
            };
        }
@@ -381,6 +387,13 @@
                            offset = ushort.Parse(offsetPart.Substring(3));
                        else if (offsetPart.StartsWith("DBB"))
                            offset = ushort.Parse(offsetPart.Substring(3));
                        else if (offsetPart.StartsWith("DBX"))
                        {
                            var dbxParts = offsetPart.Substring(3).Split('.');
                            offset = ushort.Parse(dbxParts[0]);
                            var bit = dbxParts.Length > 1 ? byte.Parse(dbxParts[1]) : (byte)0;
                            return ("DB", offset, dbNumber, bit);
                        }
                        else
                            offset = ushort.Parse(offsetPart);
                    }
@@ -437,11 +450,11 @@
        /// 注意:string类型不受此泛型方法支持(受where T : struct约束)
        /// 请使用WriteBytes/ReadBytes或通过DBRegion直接访问字符串
        /// </summary>
        private T ReadDBRegion<T>(ushort dbNumber, ushort offset) where T : struct
        private T ReadDBRegion<T>(ushort dbNumber, ushort offset, byte? bitOffset) where T : struct
        {
            return typeof(T).Name switch
            {
                "Boolean" => (T)(object)_dbRegion.ReadBool(dbNumber, offset, 0),
                "Boolean" => (T)(object)_dbRegion.ReadBool(dbNumber, offset, bitOffset ?? 0),
                "Int16" => (T)(object)_dbRegion.ReadInt(dbNumber, offset),
                "Int32" => (T)(object)_dbRegion.ReadDInt(dbNumber, offset),
                "Single" => (T)(object)_dbRegion.ReadReal(dbNumber, offset),
@@ -507,12 +520,12 @@
        /// 注意:string类型不受此泛型方法支持(受where T : struct约束)
        /// 请使用WriteBytes/ReadBytes或通过DBRegion直接访问字符串
        /// </summary>
        private void WriteDBRegion<T>(ushort dbNumber, ushort offset, T value) where T : struct
        private void WriteDBRegion<T>(ushort dbNumber, ushort offset, byte? bitOffset, T value) where T : struct
        {
            switch (typeof(T).Name)
            {
                case "Boolean":
                    _dbRegion.WriteBool(dbNumber, offset, 0, (bool)(object)value);
                    _dbRegion.WriteBool(dbNumber, offset, bitOffset ?? 0, (bool)(object)value);
                    break;
                case "Int16":
                    _dbRegion.WriteInt(dbNumber, offset, (short)(object)value);
@@ -671,11 +684,11 @@
        private byte[] ExportDBRegion()
        {
            var result = new List<byte>();
            for (ushort i = 1; i <= _config.DBBlockCount; i++)
            foreach (var dbNumber in ResolveConfiguredDbNumbers())
            {
                try
                {
                    var blockData = _dbRegion.Read(i, 0, (ushort)_config.DBBlockSize);
                    var blockData = ReadAllFromDbBlock(dbNumber, _config.DBBlockSize);
                    result.AddRange(blockData);
                }
                catch (ArgumentException)
@@ -696,19 +709,87 @@
        private void ImportDBRegion(byte[] data)
        {
            int offset = 0;
            for (ushort i = 1; i <= _config.DBBlockCount && offset < data.Length; i++)
            foreach (var dbNumber in ResolveConfiguredDbNumbers())
            {
                if (offset >= data.Length)
                {
                    break;
                }
                var blockSize = Math.Min(_config.DBBlockSize, data.Length - offset);
                var blockData = new byte[blockSize];
                Array.Copy(data, offset, blockData, 0, blockSize);
                _dbRegion.Write(i, 0, blockData);
                _dbRegion.Write(dbNumber, 0, blockData);
                offset += blockSize;
            }
        }
        /// <summary>
        /// 按分片读取整个区域,避免长度超过 ushort 导致溢出。
        /// </summary>
        private static byte[] ReadAllFromRegion(IMemoryRegion region, int totalSize)
        {
            if (totalSize <= 0)
            {
                return Array.Empty<byte>();
            }
            var result = new byte[totalSize];
            var copied = 0;
            while (copied < totalSize)
            {
                var chunkLength = (ushort)Math.Min(ushort.MaxValue, totalSize - copied);
                var chunk = region.Read((ushort)copied, chunkLength);
                Buffer.BlockCopy(chunk, 0, result, copied, chunkLength);
                copied += chunkLength;
            }
            return result;
        }
        /// <summary>
        /// 按分片读取整个 DB 块,避免 DBBlockSize=65536 时 ushort 转换为 0。
        /// </summary>
        private byte[] ReadAllFromDbBlock(ushort dbNumber, int blockSize)
        {
            if (blockSize <= 0)
            {
                return Array.Empty<byte>();
            }
            var result = new byte[blockSize];
            var copied = 0;
            while (copied < blockSize)
            {
                var chunkLength = (ushort)Math.Min(ushort.MaxValue, blockSize - copied);
                var chunk = _dbRegion.Read(dbNumber, (ushort)copied, chunkLength);
                Buffer.BlockCopy(chunk, 0, result, copied, chunkLength);
                copied += chunkLength;
            }
            return result;
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        private List<ushort> ResolveConfiguredDbNumbers()
        {
            if (_config.DBBlockNumbers != null && _config.DBBlockNumbers.Count > 0)
            {
                return _config.DBBlockNumbers
                    .Where(x => x > 0 && x <= ushort.MaxValue)
                    .Distinct()
                    .Select(x => (ushort)x)
                    .ToList();
            }
            return Enumerable
                .Range(1, Math.Max(1, _config.DBBlockCount))
                .Select(x => (ushort)x)
                .ToList();
        }
        public void Dispose()
        {
            Dispose(true);