//============================================================== // Copyright (C) 2019 Inc. All rights reserved. // //============================================================== // Create by 种道洋 at 2019/12/27 18:45:02. // Version 1.0 // 种道洋 //============================================================== using DBRuntime.His; using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Threading; using System.Linq; using System.Runtime; using System.ComponentModel; using Cdy.Tag.Driver; namespace Cdy.Tag { /// /// 历史数据引擎2 /// public class HisEnginer2 : IHisEngine2, ITagHisValueProduct, IDisposable { #region ... Variables ... /// /// /// private Cdy.Tag.HisDatabase mManager; /// /// /// private Cdy.Tag.RealEnginer mRealEnginer; /// /// /// private LogManager2 mLogManager; /// /// 缓存内存缓存时间,单位:s /// public int CachMemoryTime = 60; /// /// 合并内存存储时间,单位:s /// public int MergeMemoryTime = 5 * 60; /// /// 历史记录时间最短间隔 /// 单位ms /// public const int MemoryTimeTick = 100; /// /// /// private Dictionary mHisTags = new Dictionary(); /// /// 历史记录内存1 /// private HisDataMemoryBlockCollection mCachMemory1; /// /// 历史记录内存2 /// private HisDataMemoryBlockCollection mCachMemory2; /// /// /// private HisDataMemoryBlockCollection mMergeMemory1; /// /// /// private HisDataMemoryBlockCollection mMergeMemory2; /// /// 当前正在使用的内存 /// private HisDataMemoryBlockCollection mCurrentMemory; /// /// /// private HisDataMemoryBlockCollection mCurrentMergeMemory; /// /// /// private HisDataMemoryBlockCollection mWaitForMergeMemory; /// /// 值改变的变量列表 /// private List mValueChangedProcesser = new List(); /// /// /// private List mRecordTimerProcesser = new List(); /// /// /// private TimerMemoryCacheProcesser2 mLastProcesser = new TimerMemoryCacheProcesser2(); /// /// /// private ValueChangedMemoryCacheProcesser2 mLastValueChangedProcesser = new ValueChangedMemoryCacheProcesser2() { Name = "ValueChanged0" }; private System.Timers.Timer mRecordTimer; private DateTime mLastProcessTime; private int mTagCount = 0; private int mLastProcessTick = -1; private bool mIsBusy = false; private ManualResetEvent resetEvent = new ManualResetEvent(false); private Thread mMergeThread; private bool mIsClosed = false; private int mBlockCount = 0; private int mMergeCount = 0; private bool mNeedSnapAllTag=false; private DateTime mSnapAllTagTime = DateTime.Now; private bool mForceSubmiteToCompress = false; private bool mMegerProcessIsClosed = false; private int mStartMergeCount = 0; private Dictionary> mManualHisDataCach = new Dictionary>(); #endregion ...Variables... #region ... Events ... #endregion ...Events... #region ... Constructor... /// /// /// public HisEnginer2() { } /// /// /// /// /// public HisEnginer2(Cdy.Tag.HisDatabase manager, Cdy.Tag.RealEnginer realEnginer) { mManager = manager; mRealEnginer = realEnginer; } #endregion ...Constructor... #region ... Properties ... /// /// 当前工作的内存区域 /// public HisDataMemoryBlockCollection CurrentMemory { get { return mCurrentMemory; } set { mCurrentMemory = value; SwitchMemoryCach(value.Id); //HisRunTag.HisAddr = mCurrentMemory; } } /// /// /// public LogManager2 LogManager { get { return mLogManager; } set { mLogManager = value; if(mLogManager!=null) { mLogManager.TimeLen = (ushort)(CachMemoryTime/60); } } } #endregion ...Properties... #region ... Methods ... /// /// /// /// private void UpdatePerProcesserMaxTagCount(int totalTagCount) { int count = Environment.ProcessorCount / 2; int pcount = totalTagCount / count + count; TimerMemoryCacheProcesser2.MaxTagCount = ValueChangedMemoryCacheProcesser2.MaxTagCount = pcount; } /// /// 初始化 /// public void Init() { Stopwatch sw = new Stopwatch(); sw.Start(); if (mRealEnginer != null) { mLastProcesser = new TimerMemoryCacheProcesser2() { Id = 0 }; mRecordTimerProcesser.Clear(); mRecordTimerProcesser.Add(mLastProcesser); mValueChangedProcesser.Clear(); mValueChangedProcesser.Add(mLastValueChangedProcesser); UpdatePerProcesserMaxTagCount(mManager.HisTags.Count); var count = CachMemoryTime; var realbaseaddr = mRealEnginer.Memory; IntPtr realHandle = mRealEnginer.MemoryHandle; HisRunTag mHisTag = null; Tagbase mRealTag; foreach (var vv in mManager.HisTags) { var realaddr = (int)mRealEnginer.GetDataAddr((int)vv.Value.Id); mRealTag = mRealEnginer.GetTagById(vv.Value.Id); switch (vv.Value.TagType) { case Cdy.Tag.TagType.Bool: case Cdy.Tag.TagType.Byte: mHisTag = new ByteHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr,CompressType = vv.Value.CompressType,Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.Short: case Cdy.Tag.TagType.UShort: mHisTag = new ShortHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.Int: case Cdy.Tag.TagType.UInt: mHisTag = new IntHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.Long: case Cdy.Tag.TagType.ULong: mHisTag = new LongHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr,RealMemoryPtr=realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.Float: mHisTag = new FloatHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters, Precision = (mRealTag as FloatTag).Precision }; break; case Cdy.Tag.TagType.Double: mHisTag = new DoubleHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters, Precision = (mRealTag as DoubleTag).Precision }; break; case Cdy.Tag.TagType.DateTime: mHisTag = new DateTimeHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.String: mHisTag = new StirngHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.UIntPoint: case Cdy.Tag.TagType.IntPoint: mHisTag = new IntPointHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.UIntPoint3: case Cdy.Tag.TagType.IntPoint3: mHisTag = new IntPoint3HisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.ULongPoint: case Cdy.Tag.TagType.LongPoint: mHisTag = new LongPointHisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; case Cdy.Tag.TagType.ULongPoint3: case Cdy.Tag.TagType.LongPoint3: mHisTag = new LongPoint3HisRunTag() { Id = vv.Value.Id, Circle = vv.Value.Circle, Type = vv.Value.Type, TagType = vv.Value.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.Value.CompressType, Parameters = vv.Value.Parameters }; break; } mHisTag.MaxCount = count-1; mHisTags.Add(vv.Key, mHisTag); if (mHisTag.Type == Cdy.Tag.RecordType.Timer) { if(!mLastProcesser.AddTag(mHisTag)) { mLastProcesser = new TimerMemoryCacheProcesser2() { Id = mLastProcesser.Id + 1 }; mLastProcesser.AddTag(mHisTag); mRecordTimerProcesser.Add(mLastProcesser); } } else if(mHisTag.Type == RecordType.ValueChanged) { if(!mLastValueChangedProcesser.AddTag(mHisTag)) { mLastValueChangedProcesser = new ValueChangedMemoryCacheProcesser2() { Name = "ValueChanged"+ mValueChangedProcesser.Count+1 }; mLastValueChangedProcesser.AddTag(mHisTag); mValueChangedProcesser.Add(mLastValueChangedProcesser); } } mTagCount++; } } long ltmp = sw.ElapsedMilliseconds; AllocMemory(); if (LogManager != null) { LogManager.InitHeadData(); } sw.Stop(); LoggerService.Service.Info("HisEnginer", "生成对象耗时:"+ltmp+" 内存分配耗时:"+(sw.ElapsedMilliseconds-ltmp)); } /// /// /// public void Pause() { mRecordTimer.Stop(); } /// /// /// public void Resume() { mRecordTimer.Start(); } /// /// 加载使能新的变量 /// /// /// public void ReLoadTags(IEnumerable tags,HisDatabase mHisDatabase) { UpdatePerProcesserMaxTagCount(mManager.HisTags.Count+tags.Count()); mRecordTimer.Stop(); var realbaseaddr = this.mRealEnginer.Memory; IntPtr realHandle = mRealEnginer.MemoryHandle; HisRunTag mHisTag = null; Tagbase mRealTag; var histags = new List(); //int tcount = 0; //int vcount = 0; foreach (var vv in tags) { var realaddr = (int)mRealEnginer.GetDataAddr((int)vv.Id); mRealTag = mRealEnginer.GetTagById(vv.Id); switch (vv.TagType) { case Cdy.Tag.TagType.Bool: case Cdy.Tag.TagType.Byte: mHisTag = new ByteHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.Short: case Cdy.Tag.TagType.UShort: mHisTag = new ShortHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.Int: case Cdy.Tag.TagType.UInt: mHisTag = new IntHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.Long: case Cdy.Tag.TagType.ULong: mHisTag = new LongHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.Float: mHisTag = new FloatHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters, Precision = (mRealTag as FloatTag).Precision }; break; case Cdy.Tag.TagType.Double: mHisTag = new DoubleHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters, Precision = (mRealTag as DoubleTag).Precision }; break; case Cdy.Tag.TagType.DateTime: mHisTag = new DateTimeHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.String: mHisTag = new StirngHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.UIntPoint: case Cdy.Tag.TagType.IntPoint: mHisTag = new IntPointHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.UIntPoint3: case Cdy.Tag.TagType.IntPoint3: mHisTag = new IntPoint3HisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.ULongPoint: case Cdy.Tag.TagType.LongPoint: mHisTag = new LongPointHisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; case Cdy.Tag.TagType.ULongPoint3: case Cdy.Tag.TagType.LongPoint3: mHisTag = new LongPoint3HisRunTag() { Id = vv.Id, Circle = vv.Circle, Type = vv.Type, TagType = vv.TagType, RealMemoryAddr = realbaseaddr, RealMemoryPtr = realHandle, RealValueAddr = realaddr, CompressType = vv.CompressType, Parameters = vv.Parameters }; break; } mHisTags.Add(vv.Id, mHisTag); histags.Add(mHisTag); mTagCount++; } foreach(var vv in mRecordTimerProcesser) { vv.Stop(); vv.Dispose(); } mRecordTimerProcesser.Clear(); foreach(var vv in mValueChangedProcesser) { vv.Stop(); vv.Dispose(); } mValueChangedProcesser.Clear(); mLastProcesser = new TimerMemoryCacheProcesser2() { Id = 0 }; mRecordTimerProcesser.Clear(); mRecordTimerProcesser.Add(mLastProcesser); mLastValueChangedProcesser = new ValueChangedMemoryCacheProcesser2() { Name = "ValueChanged0" }; mValueChangedProcesser.Clear(); mValueChangedProcesser.Add(mLastValueChangedProcesser); int qulityOffset = 0; int valueOffset = 0; int blockheadsize = 0; foreach (var vv in mHisTags) { var ss = CalMergeBlockSize(vv.Value.TagType, blockheadsize, out valueOffset, out qulityOffset); var abuffer = new HisDataMemoryBlock(ss) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 1 }; var bbuffer = new HisDataMemoryBlock(ss) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 2 }; mMergeMemory1.AddTagAddress(vv.Value.Id, abuffer); mMergeMemory2.AddTagAddress(vv.Value.Id, bbuffer); var css = CalCachDatablockSize(vv.Value.TagType, blockheadsize, out valueOffset, out qulityOffset); var cbuffer = new HisDataMemoryBlock(css) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 1 }; var dbuffer = new HisDataMemoryBlock(css) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 2 }; vv.Value.HisValueMemory1 = cbuffer; vv.Value.HisValueMemory2 = dbuffer; mCachMemory1.AddTagAddress(vv.Value.Id, cbuffer); mCachMemory2.AddTagAddress(vv.Value.Id, dbuffer); abuffer.Clear(); bbuffer.Clear(); cbuffer.Clear(); dbuffer.Clear(); vv.Value.DataSize = css; if (vv.Value.Type == Cdy.Tag.RecordType.Timer) { if (!mLastProcesser.AddTag(vv.Value)) { mLastProcesser = new TimerMemoryCacheProcesser2() { Id = mLastProcesser.Id + 1 }; mLastProcesser.AddTag(vv.Value); mRecordTimerProcesser.Add(mLastProcesser); } } else if(vv.Value.Type == RecordType.ValueChanged) { if (!mLastValueChangedProcesser.AddTag(vv.Value)) { mLastValueChangedProcesser = new ValueChangedMemoryCacheProcesser2() { Name = "ValueChanged" + mValueChangedProcesser.Count + 1 }; mLastValueChangedProcesser.AddTag(vv.Value); mValueChangedProcesser.Add(mLastValueChangedProcesser); } } } this.mManager = mHisDatabase; foreach (var vv in mRecordTimerProcesser) { if (!vv.IsStarted) vv.Start(); } foreach (var vv in mValueChangedProcesser) { if (!vv.IsStarted) vv.Start(); } mLogManager.InitHeadData(); SwitchMemoryCach(mCurrentMemory.Id); mRecordTimer.Start(); } /// /// 计算每个变量数据块的大小 /// /// /// private int CalMergeBlockSize(Cdy.Tag.TagType tagType,int blockHeadSize,out int dataOffset,out int qulityOffset) { //单个数据块内容包括:时间戳(2)+数值+质量戳(1) qulityOffset = 0; int regionHeadSize = blockHeadSize; // int count = MemoryCachTime * 1000 / MemoryTimeTick; int count = MergeMemoryTime; //用于解码时在头尾分别记录前一个区域的值和后一个区域的值 count += 2; //数据区偏移,时间戳占2个字节 dataOffset = regionHeadSize + count * 2; switch (tagType) { case Cdy.Tag.TagType.Byte: case Cdy.Tag.TagType.Bool: qulityOffset = dataOffset + count; return qulityOffset + count; case Cdy.Tag.TagType.Short: case Cdy.Tag.TagType.UShort: qulityOffset = dataOffset + count * 2; return qulityOffset + count; case Cdy.Tag.TagType.Int: case Cdy.Tag.TagType.UInt: case Cdy.Tag.TagType.Float: qulityOffset = dataOffset + count * 4; return qulityOffset + count; case Cdy.Tag.TagType.Long: case Cdy.Tag.TagType.ULong: case Cdy.Tag.TagType.Double: case Cdy.Tag.TagType.DateTime: case Cdy.Tag.TagType.IntPoint: case Cdy.Tag.TagType.UIntPoint: qulityOffset = dataOffset + count * 8; return qulityOffset + count; case Cdy.Tag.TagType.IntPoint3: case Cdy.Tag.TagType.UIntPoint3: qulityOffset = dataOffset + count * 12; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint: case Cdy.Tag.TagType.ULongPoint: qulityOffset = dataOffset + count * 16; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint3: case Cdy.Tag.TagType.ULongPoint3: qulityOffset = dataOffset + count * 24; return qulityOffset + count; case Cdy.Tag.TagType.String: qulityOffset = dataOffset + count * Const.StringSize; return qulityOffset + count; default: return 0; } } /// /// /// /// /// /// /// /// /// private int CalCachDatablockSize(Cdy.Tag.TagType tagType, int headSize, out int dataOffset, out int qulityOffset) { //单个数据块内容包括:时间戳(2)+数值+质量戳(1) qulityOffset = headSize; int count = CachMemoryTime; //数据区偏移,时间戳占2个字节,质量戳占1个字节 dataOffset = headSize + count * 2; switch (tagType) { case Cdy.Tag.TagType.Byte: case Cdy.Tag.TagType.Bool: qulityOffset = dataOffset + count; return qulityOffset + count; case Cdy.Tag.TagType.Short: case Cdy.Tag.TagType.UShort: qulityOffset = dataOffset + count * 2; return qulityOffset + count; case Cdy.Tag.TagType.Int: case Cdy.Tag.TagType.UInt: case Cdy.Tag.TagType.Float: qulityOffset = dataOffset + count * 4; return qulityOffset + count; case Cdy.Tag.TagType.Long: case Cdy.Tag.TagType.ULong: case Cdy.Tag.TagType.Double: case Cdy.Tag.TagType.DateTime: case TagType.UIntPoint: case TagType.IntPoint: qulityOffset = dataOffset + count * 8; return qulityOffset + count; case Cdy.Tag.TagType.IntPoint3: case Cdy.Tag.TagType.UIntPoint3: qulityOffset = dataOffset + count * 12; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint: case Cdy.Tag.TagType.ULongPoint: qulityOffset = dataOffset + count * 16; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint3: case Cdy.Tag.TagType.ULongPoint3: qulityOffset = dataOffset + count * 24; return qulityOffset + count; case Cdy.Tag.TagType.String: qulityOffset = dataOffset + count * Const.StringSize; return qulityOffset + count; default: return 0; } } /// /// /// /// /// /// /// /// /// private int CalCachDatablockSize(Cdy.Tag.TagType tagType, int headSize,int valueCount, out int dataOffset, out int qulityOffset) { //单个数据块内容包括:时间戳(2)+数值+质量戳(1) qulityOffset = headSize; int count = Math.Max(CachMemoryTime, valueCount); //数据区偏移,时间戳占2个字节,质量戳占1个字节 dataOffset = headSize + count * 2; switch (tagType) { case Cdy.Tag.TagType.Byte: case Cdy.Tag.TagType.Bool: qulityOffset = dataOffset + count; return qulityOffset + count; case Cdy.Tag.TagType.Short: case Cdy.Tag.TagType.UShort: qulityOffset = dataOffset + count * 2; return qulityOffset + count; case Cdy.Tag.TagType.Int: case Cdy.Tag.TagType.UInt: case Cdy.Tag.TagType.Float: qulityOffset = dataOffset + count * 4; return qulityOffset + count; case Cdy.Tag.TagType.Long: case Cdy.Tag.TagType.ULong: case Cdy.Tag.TagType.Double: case Cdy.Tag.TagType.DateTime: case TagType.UIntPoint: case TagType.IntPoint: qulityOffset = dataOffset + count * 8; return qulityOffset + count; case Cdy.Tag.TagType.IntPoint3: case Cdy.Tag.TagType.UIntPoint3: qulityOffset = dataOffset + count * 12; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint: case Cdy.Tag.TagType.ULongPoint: qulityOffset = dataOffset + count * 16; return qulityOffset + count; case Cdy.Tag.TagType.LongPoint3: case Cdy.Tag.TagType.ULongPoint3: qulityOffset = dataOffset + count * 24; return qulityOffset + count; case Cdy.Tag.TagType.String: qulityOffset = dataOffset + count * Const.StringSize; return qulityOffset + count; default: return 0; } } /// /// 分配内存 /// private void AllocMemory() { /* 数据块:数据块头+数据 数据块头:质量戳偏移+id 数据:[时间戳]+[值]+[质量戳] */ long storeHeadSize = 0; long cachHeadSize = 0; int blockheadsize = 0; int qulityOffset = 0; int valueOffset = 0; mMergeMemory1 = new HisDataMemoryBlockCollection() { Name = "StoreMemory1", Id = 1 }; mMergeMemory2 = new HisDataMemoryBlockCollection() { Name = "StoreMemory2", Id = 2 }; mCachMemory1 = new HisDataMemoryBlockCollection() { Name = "CachMemory1", Id = 1 }; mCachMemory2 = new HisDataMemoryBlockCollection() { Name = "CachMemory2", Id = 2 }; foreach (var vv in mHisTags) { if (vv.Value.Type != RecordType.Manual) { var ss = CalMergeBlockSize(vv.Value.TagType, blockheadsize, out valueOffset, out qulityOffset); var abuffer = new HisDataMemoryBlock(ss) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 1 }; var bbuffer = new HisDataMemoryBlock(ss) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 2 }; mMergeMemory1.AddTagAddress(vv.Value.Id, abuffer); mMergeMemory2.AddTagAddress(vv.Value.Id, bbuffer); storeHeadSize += ss; var css = CalCachDatablockSize(vv.Value.TagType, blockheadsize, out valueOffset, out qulityOffset); var cbuffer = new HisDataMemoryBlock(css) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 1 }; var dbuffer = new HisDataMemoryBlock(css) { TimerAddress = 0, ValueAddress = valueOffset, QualityAddress = qulityOffset, Id = 2 }; vv.Value.HisValueMemory1 = cbuffer; vv.Value.HisValueMemory2 = dbuffer; mCachMemory1.AddTagAddress(vv.Value.Id, cbuffer); mCachMemory2.AddTagAddress(vv.Value.Id, dbuffer); vv.Value.DataSize = css; cachHeadSize += css; } else { mMergeMemory1.AddTagAddress(vv.Value.Id, null); mMergeMemory2.AddTagAddress(vv.Value.Id, null); mCachMemory1.AddTagAddress(vv.Value.Id, null); mCachMemory2.AddTagAddress(vv.Value.Id, null); } } LoggerService.Service.Info("HisEnginer", "Cal MergeMemory memory size:" + (storeHeadSize/1024.0/1024 *2)+"M", ConsoleColor.Cyan); LoggerService.Service.Info("HisEnginer", "Cal CachMemoryBlock memory size:" + (cachHeadSize / 1024.0 / 1024 *2) + "M", ConsoleColor.Cyan); CurrentMemory = mCachMemory1; } private string FormateDatetime(DateTime datetime) { return datetime.ToString("yyyy-MM-dd HH:mm:ss.fff"); } /// /// /// /// private void SwitchMemoryCach(int id) { if(id==1) { foreach(var vv in mHisTags) { vv.Value.HisValueMemoryStartAddr = vv.Value.HisValueMemory1; } } else { foreach (var vv in mHisTags) { vv.Value.HisValueMemoryStartAddr = vv.Value.HisValueMemory2; } } } /// /// /// public void Start() { LoggerService.Service.Info("HisEnginer", "开始启动"); mIsClosed = false; mMegerProcessIsClosed = false; LoggerService.Service.Info("Record", "历史变量个数: " + this.mHisTags.Count); ClearMemoryHisData(mCachMemory1); ClearMemoryHisData(mCachMemory2); ClearMemoryHisData(mMergeMemory1); ClearMemoryHisData(mMergeMemory2); mCachMemory1.MakeMemoryNoBusy(); mCachMemory2.MakeMemoryNoBusy(); mMergeMemory1.MakeMemoryNoBusy(); mMergeMemory2.MakeMemoryNoBusy(); foreach (var vv in mRecordTimerProcesser) { vv.Start(); } foreach(var vv in mValueChangedProcesser) { vv.Start(); } mLastProcessTime = DateTime.Now; HisRunTag.StartTime = mLastProcessTime; CurrentMemory = mCachMemory1; CurrentMemory.CurrentDatetime = mLastProcessTime; mCurrentMergeMemory = mMergeMemory1; mCurrentMergeMemory.CurrentDatetime = CurrentMemory.CurrentDatetime; if (LogManager != null) { LogManager.Start(); } SnapeAllTag(); RecordAllFirstValue(); mStartMergeCount = (int)((mLastProcessTime - new DateTime(mLastProcessTime.Year, mLastProcessTime.Month, mLastProcessTime.Day, ((mLastProcessTime.Hour / mManager.Setting.FileDataDuration) * mManager.Setting.FileDataDuration), 0, 0)).TotalMinutes % (MergeMemoryTime / CachMemoryTime)); mMergeCount = mStartMergeCount; mRecordTimer = new System.Timers.Timer(MemoryTimeTick); mRecordTimer.Elapsed += MRecordTimer_Elapsed; mRecordTimer.Start(); mMergeThread = new Thread(MergerMemoryProcess); mMergeThread.IsBackground=true; mMergeThread.Start(); } /// /// /// /// private void CheckMemoryIsReady(HisDataMemoryBlockCollection memory) { while (memory.IsBusy()) { LoggerService.Service.Info("Record", "记录出现阻塞 " + memory.Name); System.Threading.Thread.Sleep(1); } } /// /// /// private void MergerMemoryProcess() { int number = MergeMemoryTime / CachMemoryTime; int count = mStartMergeCount; ThreadHelper.AssignToCPU(CPUAssignHelper.Helper.CPUArray1); while (!mIsClosed) { resetEvent.WaitOne(); resetEvent.Reset(); if(mNeedSnapAllTag) { SnapeAllTag(); mNeedSnapAllTag = false; } MemoryMerge(count); //如果合并满了,则提交给压缩流程进行压缩 count++; if(count>=number || mForceSubmiteToCompress) { if (mCurrentMergeMemory != null) { RecordAllLastValue(); //mMergeMemory.Dump(); mCurrentMergeMemory.EndDateTime = mSnapAllTagTime; mCurrentMergeMemory.MakeMemoryBusy(); //提交到数据压缩流程 ServiceLocator.Locator.Resolve().RequestToCompress(mCurrentMergeMemory); LoggerService.Service.Info("HisEnginer", "提交内存 " + mCurrentMergeMemory.Name + " 进行压缩",ConsoleColor.Green); //等待压缩完成 // while(mMergeMemory1.IsBusy()) Thread.Sleep(1); //切换工作合并内存 if(mCurrentMergeMemory == mMergeMemory1) { mCurrentMergeMemory = mMergeMemory2; } else { mCurrentMergeMemory = mMergeMemory1; } LoggerService.Service.Info("HisEnginer", "切换工作 合并内存:" + mCurrentMergeMemory.Name, ConsoleColor.Green); while (mCurrentMergeMemory.IsBusy()) Thread.Sleep(1); RecordAllFirstValue(); } count = 0; } } LoggerService.Service.Info("HisEnginer", "合并线程退出!" , ConsoleColor.Green); mMegerProcessIsClosed = true; } /// /// 进行内存合并 /// private void MemoryMerge(int count) { if (count == 0) { mCurrentMergeMemory.CurrentDatetime = mWaitForMergeMemory.CurrentDatetime; LoggerService.Service.Info("HisEnginer", "MergeMemory 使用新的时间起点:" + mWaitForMergeMemory.Name+" " + FormateDatetime(this.mCurrentMergeMemory.CurrentDatetime), ConsoleColor.Green); } var mcc = mWaitForMergeMemory; LoggerService.Service.Info("Record", "开始内存合并" + mcc.Name, ConsoleColor.Green); Stopwatch sw = new Stopwatch(); sw.Start(); //System.Threading.Tasks.Parallel.ForEach(mHisTags, (tag) => { foreach (var tag in mHisTags) { var taddrs = mCurrentMergeMemory.TagAddress[tag.Value.Id]; var saddrs = mcc.TagAddress[tag.Value.Id]; // if (taddrs == null || saddrs == null) continue; //拷贝时间 var dlen = saddrs.ValueAddress; var vtimeaddr = dlen * count + 2; saddrs.CopyTo(taddrs, 0, vtimeaddr, dlen); //拷贝数值 dlen = saddrs.QualityAddress - saddrs.ValueAddress; vtimeaddr = taddrs.ValueAddress + dlen * count + tag.Value.SizeOfValue; saddrs.CopyTo(taddrs, saddrs.ValueAddress, vtimeaddr, dlen); //拷贝质量戳 dlen = tag.Value.DataSize - saddrs.QualityAddress; vtimeaddr = taddrs.QualityAddress + dlen * count + 1; saddrs.CopyTo(taddrs, saddrs.QualityAddress, vtimeaddr, dlen); } //}); //mCurrentMergeMemory.Dump(); //mcc.Dump(); mcc.MakeMemoryNoBusy(); //ClearMemoryHisData(mcc); sw.Stop(); LoggerService.Service.Info("Record", "合并完成 " + mcc.Name+" 次数:"+(count+1)+" 耗时:"+sw.ElapsedMilliseconds); } /// /// 提交内存数据到合并 /// private void SubmiteMemory(DateTime dateTime) { int number = MergeMemoryTime / CachMemoryTime; var mcc = mCurrentMemory; mMergeCount++; mMergeCount = mMergeCount >= number ? 0 : mMergeCount; //HisRunTag.TimerOffset = mMergeCount * 10 * CachMemoryTime; if (mCurrentMemory == mCachMemory1) { CheckMemoryIsReady(mCachMemory2); CurrentMemory = mCachMemory2; } else { CheckMemoryIsReady(mCachMemory1); CurrentMemory = mCachMemory1; } CurrentMemory.CurrentDatetime = dateTime; if (mMergeCount==0) { mNeedSnapAllTag = true; LoggerService.Service.Info("HisEnginer", "使用新的时间起点:" + CurrentMemory.Name + " " + FormateDatetime(CurrentMemory.CurrentDatetime), ConsoleColor.Cyan); HisRunTag.StartTime = dateTime; } //PrepareForReadyMemory(); foreach (var vv in mHisTags.Values) { vv.Reset(); } if (mcc != null) { mcc.MakeMemoryBusy(); mcc.EndDateTime = dateTime; mWaitForMergeMemory = mcc; //通知进行内存合并 resetEvent.Set(); mLogManager?.RequestToSave(mcc.CurrentDatetime,dateTime, mcc); } } /// /// /// private void SnapeAllTag() { mSnapAllTagTime = DateTime.Now; Stopwatch sw = new Stopwatch(); sw.Start(); foreach(var vv in mHisTags) { vv.Value.Snape(); } sw.Stop(); LoggerService.Service.Info("HisEnginer", "快照记录数值:" + FormateDatetime(mSnapAllTagTime) + " 耗时:" + sw.ElapsedMilliseconds, ConsoleColor.Cyan); } /// /// 在数据区域头部添加数值 /// /// private void RecordAllFirstValue() { foreach(var vv in mCurrentMergeMemory.TagAddress) { //数据内容: 时间戳(time1+time2+...) +数值区(value1+value2+...)+质量戳区(q1+q2+....) //实时数据内存结构为:实时值+时间戳+质量戳 //long timeraddr = vv.Value.Item1; //long valueaddr = vv.Value.Item1 + vv.Value.Item2; //long qaddr = vv.Value.Item1 + vv.Value.Item3; var tag = mHisTags[vv.Key]; vv.Value.WriteShort(0, 0); //写入数值 vv.Value.WriteBytesDirect((int)vv.Value.ValueAddress,tag.ValueSnape); //更新质量戳,在现有质量戳的基础添加100,用于表示这是一个强制更新的值 vv.Value.WriteByteDirect((int)vv.Value.QualityAddress, (byte)(tag.QulitySnape+100)); } } /// /// 在数据区域尾部添加数值 /// /// private void RecordAllLastValue() { DateTime time = mSnapAllTagTime; LoggerService.Service.Info("HisEnginer", "RecordAllLastValue:" + FormateDatetime(time), ConsoleColor.Cyan); ushort timespan = (ushort)((time - mCurrentMergeMemory.CurrentDatetime).TotalMilliseconds / 100); foreach (var vv in mCurrentMergeMemory.TagAddress) { //数据内容: 时间戳(time1+time2+...) +数值区(value1+value2+...)+质量戳区(q1+q2+....) //实时数据内存结构为:实时值+时间戳+质量戳 var tag = mHisTags[vv.Key]; long timeraddr = vv.Value.ValueAddress-2; long valueaddr = vv.Value.QualityAddress-tag.SizeOfValue; long qaddr = vv.Value.AllocSize - 1; vv.Value.WriteUShort((int)timeraddr, timespan); //写入数值 vv.Value.WriteBytesDirect((int)valueaddr,tag.ValueSnape); //更新质量戳 vv.Value.WriteByteDirect((int)qaddr, (byte)(tag.QulitySnape+100)); } } /// /// /// /// /// private void MRecordTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { //内存块的分配,严格按照绝对的时间来执行。例如5分钟一个内存缓冲,而从0点开始每隔5分钟,切换一下内存块; //哪怕自启动时间以来,到一个5分钟节点间不足5分钟。 //这里以最快的固定频率触发事件处理每个内存块;对于定时记录情况,对于没有定时到的情况,不记录,对于定时到的情况,更新实际的时间戳、数值和质量戳。 //对于值改变的情况,同样按照固定的频路定时更新质量戳(没有值),对于值改变的情况,记录实际时间戳、数值和质量戳。由此可以值改变的频路不能够大于我们最大的频率。 //LoggerService.Service.Info("Record", "Thread id:"+Thread.CurrentThread.ManagedThreadId); if (mIsBusy) { mBlockCount++; LoggerService.Service.Warn("RecordTimer", "出现阻塞"+ mBlockCount); return; } mIsBusy = true; mBlockCount = 0; DateTime dt = DateTime.Now; //var mm = (dt.Hour * 24 + dt.Minute * 60 + dt.Second) / CachMemoryTime; var mm = dt.Minute; if (mm!=mLastProcessTick ) { ///处理第一次运行的情况,停止到一秒的开始部分 if (mLastProcessTick == -1 && dt.Millisecond > 400) { mIsBusy = false; return; } LoggerService.Service.Info("Record", "准备新的内存,提交内存 "+ CurrentMemory.Name+ " 到压缩 线程ID:"+ Thread.CurrentThread.ManagedThreadId+" CPU ID:"+ ThreadHelper.GetCurrentProcessorNumber(), ConsoleColor.Green); if (mLastProcessTick != -1) { mLastProcessTime = dt; //将之前的Memory提交到合并流程中 SubmiteMemory(dt); } mLastProcessTick = mm; } foreach (var vv in mRecordTimerProcesser) { vv.Notify(dt); } //值改变的变量,也受内部定时逻辑处理。这样值改变频路高于MemoryTimeTick 的值时,无效。 foreach (var vv in mValueChangedProcesser) { vv.Notify(dt); } mIsBusy = false; } /// /// /// private void SubmitLastDataToSave() { mNeedSnapAllTag = true; mForceSubmiteToCompress = true; mIsClosed = true; SubmiteMemory(DateTime.Now); while (!mMegerProcessIsClosed) Thread.Sleep(1); SaveManualCachData(); } /// /// 保存手动提交历史记录缓存部分 /// private void SaveManualCachData() { foreach(var vv in mManualHisDataCach) { foreach (var vvv in vv.Value) { ServiceLocator.Locator.Resolve().RequestManualToCompress(vvv.Value); } } } /// /// /// public void Stop() { LoggerService.Service.Info("HisEnginer", "开始停止"); if(mRecordTimer!=null) { mRecordTimer.Stop(); mRecordTimer.Elapsed -= MRecordTimer_Elapsed; mRecordTimer.Dispose(); mRecordTimer = null; } foreach (var vv in mRecordTimerProcesser) { vv.Stop(); } foreach (var vv in mValueChangedProcesser) { vv.Stop(); } SubmitLastDataToSave(); if (LogManager != null) LogManager.Stop(); mLastProcessTick = -1; mForceSubmiteToCompress = false; mNeedSnapAllTag = false; } /// /// /// /// public void ClearMemoryHisData(HisDataMemoryBlockCollection memory) { Stopwatch sw = new Stopwatch(); sw.Start(); //while (memory.IsBusy()) ; memory.Clear(); sw.Stop(); LoggerService.Service.Info("Record", memory.Name + "清空数据区耗时:" + sw.ElapsedMilliseconds); } /// /// /// /// public void ClearMemoryHisData(MarshalMemoryBlock memory) { Stopwatch sw = new Stopwatch(); sw.Start(); memory.Clear(); sw.Stop(); LoggerService.Service.Info("Record", memory.Name+ "清空数据区耗时:" + sw.ElapsedMilliseconds); } /// /// /// /// /// public HisRunTag GetHisTag(int id) { if(mHisTags.ContainsKey(id)) { return mHisTags[id]; } return null; } /// /// /// /// public List ListAllTags() { return mHisTags.Values.ToList(); } /// /// /// public void Dispose() { foreach (var vv in mRecordTimerProcesser) { vv.Dispose(); } mRecordTimerProcesser.Clear(); foreach (var vv in mValueChangedProcesser) { vv.Stop(); vv.Dispose(); } mValueChangedProcesser.Clear(); mLastValueChangedProcesser = null; mLastProcesser = null; mHisTags.Clear(); mCachMemory1?.Dispose(); mCachMemory2?.Dispose(); mMergeMemory1?.Dispose(); mMergeMemory2?.Dispose(); } /// /// 手动插入历史数据, /// 数据会累计到整个数据块大小时再提交到后面进行压缩、存储处理 /// /// /// /// 时间最小单位 /// private bool ManualRecordHisValues(long id, IEnumerable values, int timeUnit = 100) { int valueOffset, qulityOffset = 0; DateTime mLastTime = DateTime.MinValue; Dictionary datacach; if (mHisTags.ContainsKey(id)) { if (mManualHisDataCach.ContainsKey(id)) { datacach = mManualHisDataCach[id]; } else { datacach = new Dictionary(); mManualHisDataCach.Add(id, datacach); } var tag = mHisTags[id]; ManualHisDataMemoryBlock hb = null; foreach (var vv in values) { var vdata = vv.Time; var mms = vv.Time.Subtract(vdata).TotalSeconds / MergeMemoryTime; var time = vdata.AddSeconds(mms * MergeMemoryTime); if (datacach.ContainsKey(time)) { hb = datacach[time]; } else { var css = CalCachDatablockSize(tag.TagType, 0, MergeMemoryTime * 1000 / timeUnit, out valueOffset, out qulityOffset); hb = ManualHisDataMemoryBlockPool.Pool.Get(css); hb.Time = time; hb.MaxCount = MergeMemoryTime * 1000 / timeUnit; hb.TimeUnit = timeUnit; hb.TimeLen = 4; hb.TimerAddress = 0; hb.ValueAddress = valueOffset; hb.QualityAddress = qulityOffset; hb.Id = (int)id; datacach.Add(time, hb); } mLastTime = time; if (hb.CurrentCount < hb.MaxCount) { var vtime = (int)((vv.Time - hb.Time).TotalMilliseconds / timeUnit); //写入时间戳 hb.WriteInt(hb.TimerAddress + hb.CurrentCount * 4, vtime); switch (tag.TagType) { case TagType.Bool: hb.WriteByteDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToByte(Convert.ToBoolean(vv.Value))); break; case TagType.Byte: hb.WriteByteDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToByte(vv.Value)); break; case TagType.Short: hb.WriteShortDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToInt16(vv.Value)); break; case TagType.UShort: hb.WriteUShortDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToUInt16(vv.Value)); break; case TagType.Int: hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToInt32(vv.Value)); break; case TagType.UInt: hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToUInt32(vv.Value)); break; case TagType.Long: hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToInt64(vv.Value)); break; case TagType.ULong: hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToUInt64(vv.Value)); break; case TagType.Float: hb.WriteFloatDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToSingle(vv.Value)); break; case TagType.Double: hb.WriteDoubleDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToDouble(vv.Value)); break; case TagType.String: hb.WriteStringDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToString(vv.Value), Encoding.Unicode); break; case TagType.DateTime: hb.WriteDatetime(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, Convert.ToDateTime(vv.Value)); break; case TagType.UIntPoint: UIntPointData data = (UIntPointData)vv.Value; hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, data.X); hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 4, data.Y); break; case TagType.IntPoint: IntPointData idata = (IntPointData)vv.Value; hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, idata.X); hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 4, idata.Y); break; case TagType.UIntPoint3: UIntPoint3Data udata3 = (UIntPoint3Data)vv.Value; hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, udata3.X); hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 4, udata3.Y); hb.WriteUIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 8, udata3.Z); break; case TagType.IntPoint3: IntPoint3Data idata3 = (IntPoint3Data)vv.Value; hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, idata3.X); hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 4, idata3.Y); hb.WriteIntDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 8, idata3.Z); break; case TagType.ULongPoint: ULongPointData udata = (ULongPointData)vv.Value; hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, udata.X); hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 8, udata.Y); break; case TagType.LongPoint: LongPointData lidata = (LongPointData)vv.Value; hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, lidata.X); hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 8, lidata.Y); break; case TagType.ULongPoint3: ULongPoint3Data ludata3 = (ULongPoint3Data)vv.Value; hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, ludata3.X); hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 16, ludata3.Y); hb.WriteULongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 24, ludata3.Z); break; case TagType.LongPoint3: LongPoint3Data lidata3 = (LongPoint3Data)vv.Value; hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue, lidata3.X); hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 16, lidata3.Y); hb.WriteLongDirect(hb.ValueAddress + hb.CurrentCount * tag.SizeOfValue + 24, lidata3.Z); break; } hb.WriteInt(hb.QualityAddress + hb.CurrentCount, vv.Quality); hb.EndTime = vv.Time; hb.CurrentCount++; } } if (values.Count() > 0) { foreach (var vv in datacach.ToArray()) { if (vv.Key < mLastTime || vv.Value.CurrentCount >= vv.Value.MaxCount) { ServiceLocator.Locator.Resolve().RequestManualToCompress(vv.Value); datacach.Remove(vv.Key); } } ServiceLocator.Locator.Resolve().SubmitManualToCompress(); } return true; } else { return false; } } /// /// /// /// /// /// public bool SetTagHisValue(int id, List values, int timeUnit = 100) { return ManualRecordHisValues(id, values, timeUnit); } /// /// /// /// /// public bool SetTagHisValues(Dictionary> values, int timeUnit = 100) { foreach(var vv in values) { ManualRecordHisValues(vv.Key, vv.Value, timeUnit); } return true; } #endregion ...Methods... #region ... Interfaces ... #endregion ...Interfaces... } }