| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| | | } |