借助网络配置,控制Modbus工具的日志显示,避免卡死界面
智能大石头 authored at 2021-12-11 22:09:19
14.07 KiB
XCoder
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using NewLife.Collections;

namespace ALib
{
    /// <summary>环形队列接口</summary>
    public interface ICircularQueue<T>
    {
        /// <summary>进队</summary>
        /// <param name="item"></param>
        /// <param name="expan">是否扩容</param>
        /// <returns></returns>
        public bool Enqueue(T item, bool expan = false);

        /// <summary>出队</summary>
        /// <returns></returns>
        public T Dequeue();

        /// <summary>设置容量</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public bool SetCapacity(int size);

        /// <summary>丢弃,单向的。</summary>
        /// <remarks>环形队列的特殊性,seek出来的位置会被head写入。必须不能倒退</remarks>
        /// <param name="capacity">负值清空所有</param>
        public void Seek(int capacity);

        /// <summary>序列化为数组,队列内容不丢失</summary>
        /// <returns></returns>
        public T[] ToArray();

        /// <summary>进队多个</summary>
        /// <param name="item"></param>
        /// <param name="expan">是否扩容</param>
        /// <returns></returns>
        public bool WriteArray(T[] item, bool expan = false);

        /// <summary>出队多个</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public T[] ReadArray(int size = -1);
    }

    /// <summary>环形队列,不带锁</summary>
    /// <remarks>写、读 不冲突。多读/多写 冲突。</remarks>
    /// <typeparam name="T"></typeparam>
    public class CircularQueue<T> : ICircularQueue<T>
    {
        /// <summary>内部缓冲</summary>
        protected T[] _queue;
        /// <summary>头,写入下标</summary>
        protected volatile int _head;
        /// <summary>尾,读取下标</summary>
        protected volatile int _tail;

        // <summary>不使用 count 变量维护内部数量,避免读写操作count变量造成竞争</summary>
        // protected int _count;

        /// <summary>是否为基元类型,决定是否可用 Buffer.BlockCopy</summary>
        protected static readonly bool _isPrimitive = typeof(T).IsPrimitive;

        /// <summary>每个元素的字节大小,用于 Buffer.BlockCopy</summary>
        protected static readonly int _sizeOfT = _isPrimitive ? System.Runtime.InteropServices.Marshal.SizeOf<T>() : -1;

        /// <summary>复制数组,根据类型选择最佳方式</summary>
        private void CopyArray(T[] src, int srcIdx, T[] dst, int dstIdx, int len)
        {
            if (_isPrimitive)
            {
                Buffer.BlockCopy(src, srcIdx * _sizeOfT, dst, dstIdx * _sizeOfT, len * _sizeOfT);
            }
            else
            {
                Array.Copy(src, srcIdx, dst, dstIdx, len);
            }
        }

        /// <summary>构造</summary>
        /// <param name="capacity">最少32个吧</param>
        public CircularQueue(int capacity = -1)
        {
            if (capacity < 8) capacity = 8;

            // 必须留一个空位置,否则无法使用 _head  _tial 区分空和满。
            _queue = new T[capacity + 1];
            _head = 0;
            _tail = 0;
        }

        /// <summary>清空内容</summary>
        public void Clear()
        {
            lock (this)
            {
                Array.Clear(_queue, 0, _queue.Length);

                _head = 0;
                _tail = 0;
            }
        }

        /// <summary>队列内容量</summary>
        /// <returns></returns>
        public int Count()
        {
            // 变量copy处理,避免计算过程中其他线程修改。
            var head = _head;
            var tail = _tail;

            if (head >= tail) return head - tail;

            return head + _queue.Length - tail;
        }

        /// <summary>剩余空间</summary>
        /// <returns></returns>
        public int Remain()
        {
            // 必须留一个空位置,否则无法使用 _head  _tial 区分空和满。
            return _queue.Length - Count() - 1;
        }

        /// <summary>空</summary>
        public bool IsEmpty()
        {
            return _tail == _head;
        }

        /// <summary>满的</summary>
        public bool IsFull()
        {
            return Remain() == 0;
        }

        /// <summary>进队</summary>
        /// <param name="item"></param>
        /// <param name="expan">是否扩容</param>
        /// <returns></returns>
        public virtual bool Enqueue(T item, bool expan = false)
        {
            if (IsFull())
            {
                if (!expan) return false;
                // 扩容一倍
                SetCapacity((_queue.Length - 1) * 2);
            }

            _queue[_head] = item;

            var head = _head + 1;
            // _head = head % _queue.Length;
            // 语法上看,取余是最好的写法。但是执行效率不一定合适。
            // 如果是ARM处理器,取余/除法 执行效率都不高。
            // 还是加减法吧。
            if (head < _queue.Length)
                _head = head;
            else
                _head = head - _queue.Length;

            return true;
        }

        /// <summary>出队</summary>
        /// <returns></returns>
        public virtual T Dequeue()
        {
            if (IsEmpty()) return default(T);

            var temp = _queue[_tail];
            _queue[_tail] = default(T);

            var tail = _tail + 1;
            // _tail = tail % _queue.Length;
            // 语法上看,取余是最好的写法。但是执行效率不一定合适。
            // 如果是ARM处理器,取余/除法 执行效率都不高。
            // 还是加减法吧。
            if (tail < _queue.Length)
                _tail = tail;
            else
                _tail = tail - _queue.Length;

            return temp;
        }

        /// <summary>读一个,但不出队</summary>
        /// <returns></returns>
        public T Peek()
        {
            if (IsEmpty()) return default(T);

            return _queue[_tail];
        }

        /// <summary>读多个,但不出队列</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public T[] PeekArray(int size = -1)
        {
            var cnt = Count();
            var len = (size == -1) ? cnt : size;
            if (len > cnt) len = cnt;

            if (len == 0) return new T[0];

            // 变量copy处理,避免计算过程中其他线程修改。
            var tail = _tail;
            var result = new T[len];

            if (tail + len <= _queue.Length)
            {
                CopyArray(_queue, tail, result, 0, len);
            }
            else
            {
                // _queue 后半截
                var cplen = _queue.Length - tail;
                CopyArray(_queue, tail, result, 0, cplen);

                // _queue 前半截
                var cplen2 = len - cplen;
                CopyArray(_queue, 0, result, cplen, cplen2);
            }

            return result;
        }


        /// <summary>读最后写入的一个,不出队(不允许从_head出队)</summary>
        /// <returns></returns>
        public T PeekHead()
        {
            if (IsEmpty()) return default(T);

            var n = _queue.Length;
            var idx = _head - 1;
            if (idx < 0) idx += n;

            return _queue[idx];
        }

        /// <summary>读最后最后写入的多个,不出队(不允许从_head出队)</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public T[] PeekHeadArray(int size = -1)
        {
            var cnt = Count();
            var len = (size == -1) ? cnt : size;
            if (len > cnt) len = cnt;
            if (len == 0) return new T[0];

            var n = _queue.Length;
            // 最近写入的元素在 (_head - 1),窗口起点(最旧的一个)为 (_head - len)
            var start = _head - len;
            if (start < 0) start += n;

            var result = new T[len];

            if (start + len <= n)
            {
                CopyArray(_queue, start, result, 0, len);
            }
            else
            {
                var cplen = n - start;
                CopyArray(_queue, start, result, 0, cplen);

                var cplen2 = len - cplen;
                CopyArray(_queue, 0, result, cplen, cplen2);
            }

            return result;
        }

        /// <summary>进队多个</summary>
        /// <param name="item"></param>
        /// <param name="expan">是否扩容</param>
        /// <returns></returns>
        public virtual bool WriteArray(T[] item, bool expan = false)
        {
            var remian = Remain();
            var wrlen = item.Length;
            if (remian < wrlen)
            {
                if (!expan) return false;

                // 计算扩容到多少才够
                var needlen = Count() + wrlen;
                var len = _queue.Length - 1;
                while (true)
                {
                    len *= 2;
                    if (needlen < len) break;
                }

                SetCapacity(len);
            }

            var head = _head;
            if (head + wrlen <= _queue.Length)
            {
                CopyArray(item, 0, _queue, head, wrlen);
            }
            else
            {
                // 后半部
                var cplen = _queue.Length - head;
                CopyArray(item, 0, _queue, head, cplen);
                // 前半部
                var cplen2 = wrlen - cplen;
                CopyArray(item, cplen, _queue, 0, cplen2);
            }

            head += wrlen;
            // _head = head % _queue.Length;
            // 语法上看,取余是最好的写法。但是执行效率不一定合适。
            // 如果是ARM处理器,取余/除法 执行效率都不高。
            // 还是加减法吧。
            if (head < _queue.Length)
                _head = head;
            else
                _head = head - _queue.Length;

            return true;
        }

        /// <summary>出队多个</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public virtual T[] ReadArray(int size = -1)
        {
            var result = PeekArray(size);

            var tail = _tail;
            var len = result.Length;

            tail += len;
            // _tail = tail % _queue.Length;
            // 语法上看,取余是最好的写法。但是执行效率不一定合适。
            // 如果是ARM处理器,取余/除法 执行效率都不高。
            // 还是加减法吧。
            if (tail < _queue.Length)
                _tail = tail;
            else
                _tail = tail - _queue.Length;

            return result;
        }

        /// <summary>设置容量</summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public bool SetCapacity(int size)
        {
            lock (this)
            {
                var cnt = Count();
                // 设置容量不能小于当前队列内容大小。
                if (size < cnt) return false;

                // 读出内容
                var all = ToArray();

                // 新建缓冲
                // 必须+1留一个空位置,否则无法使用 _head  _tial 区分空和满。
                var queue = new T[size + 1];
                // 放入新缓冲
                CopyArray(all, 0, queue, 0, all.Length);

                // 更新 queue tail head
                _queue = queue;
                _tail = 0;
                _head = all.Length;
            }

            return true;
        }

        /// <summary>丢弃,单向的。</summary>
        /// <remarks>环形队列的特殊性,seek出来的位置会被head写入。必须不能倒退</remarks>
        /// <param name="capacity">负值清空所有</param>
        public virtual void Seek(int capacity)
        {
            if (capacity < 0)
            {
                Clear();
                return;
            }

            var cnt = Count();
            var len = cnt > capacity ? capacity : cnt;
            if (len == 0) return;

            // 变量copy处理,避免计算过程中其他线程修改。
            var tail = _tail;
            if (tail + len <= _queue.Length)
            {
                Array.Clear(_queue, tail, len);
            }
            else
            {
                // _queue 后半截
                var cplen = _queue.Length - tail;
                Array.Clear(_queue, tail, cplen);

                // _queue 前半截
                var cplen2 = len - cplen;
                Array.Clear(_queue, 0, cplen2);
            }

            tail += len;
            // _tail = tail % _queue.Length;
            // 语法上看,取余是最好的写法。但是执行效率不一定合适。
            // 如果是ARM处理器,取余/除法 执行效率都不高。
            // 还是加减法吧。
            if (tail < _queue.Length)
                _tail = tail;
            else
                _tail = tail - _queue.Length;
        }

        /// <summary>序列化为数组,队列内容不丢失</summary>
        /// <returns></returns>
        public virtual T[] ToArray()
        {
            var len = Count();
            if (len == 0) return new T[0];

            // 变量copy处理,避免计算过程中其他线程修改。
            var tail = _tail;
            var result = new T[len];

            if (tail + len <= _queue.Length)
            {
                CopyArray(_queue, tail, result, 0, len);
            }
            else
            {
                // _queue 后半截
                var cplen = _queue.Length - tail;
                CopyArray(_queue, tail, result, 0, cplen);

                // _queue 前半截
                var cplen2 = len - cplen;
                CopyArray(_queue, 0, result, cplen, cplen2);
            }

            return result;
        }
    }
}