TimeGridAdvHelper.cs 39.9 KB
Newer Older
1 2 3 4 5 6 7 8 9
using Misc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FlyADBase
{
10 11
    /// <summary>
    /// 高级timegrid助手
12
    /// 使用说明:
潘栩锋's avatar
潘栩锋 committed
13
    /// ADPool 只能储存 1min数据, 其它XXXPool 保存1.5min数据
14 15 16 17 18 19
    /// 
    /// 
    /// 获取最后一次运行动作的开始与结束时间
    /// bool ret = mTimeGridAdvHelper.GetLastRunningTime(out DateTime beginTime, out DateTime endTime, out int marker, out DRIVE_MAN_ORDER order);
    /// 
    /// 获取 指定时间范围的 timegrid 数据
20
    /// var adList = mTimeGridAdvHelper.GetAD(beginTime, endTime, out DateTime reponse_endTime);
21 22 23 24 25 26 27 28 29 30 31 32 33 34
    ///
    /// 获取 指定时间范围的 pos 数据
    /// var posList = mTimeGridAdvHelper.GetPos(reponse_endTime,adList.Count());
    /// 
    /// 获取 指定时间范围的 pos2 数据
    /// var pos2List = mTimeGridAdvHelper.GetPos2(reponse_endTime,adList.Count());
    /// 
    /// 获取 指定时间范围的 输入口 数据
    /// var istatusList = mTimeGridAdvHelper.GetIStatus(reponse_endTime,adList.Count());
    /// 
    /// 转为 grid 数据
    /// int posLen = 8900;
    /// var grid = ToGrid(adList, posList, posOfGrid = 10, gridLen = posLen/10);
    /// 
35
    /// </summary>
36 37
    public class TimeGridAdvHelper
    {
38
        NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
39

40
        #region 一分钟数据缓存池, 目标每个AD数据(1ms 都有对应的其它数据
41
        /// <summary>
42 43
        /// AD数据池时间间隔为1ms, 肯定最少有一个数据 
        /// 每个数据包(200个数据)理论间隔是200ms, 但实际上不是。 
44
        /// 数据输出时,输出的每个数据必须是1ms, 需要通过数据包的systick线性填充数据
45
        /// </summary>
46
        public List<DateTimeUnit3> ADPool = new List<DateTimeUnit3>();
47

48 49 50
        /// <summary>
        /// pos 数据池, 肯定最少有一个数据
        /// </summary>
51
        public List<DateTimeUnit> PosPool = new List<DateTimeUnit>();
52 53 54 55

        /// <summary>
        /// 输入端口 数据池, 肯定最少有一个数据
        /// </summary>
56 57 58 59 60 61
        public List<DateTimeUnit2> IStatusPool = new List<DateTimeUnit2>();

        /// <summary>
        /// pos2 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit> Pos2Pool = new List<DateTimeUnit>();
62

63 64 65 66 67 68
        /// <summary>
        /// DriveStatus 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit4> DriveStatusPool = new List<DateTimeUnit4>();
        #endregion

69
        public DateTime NewestTime;
70

71 72 73
        /// <summary>
        /// 
        /// </summary>
74 75 76 77 78 79
        public void Init()
        {
            Clear();
        }


80

81
        /// <summary>
82
        /// 添加ad数据,且判断数据量是否够触发事件, 时间间隔为 1ms
83
        /// </summary>
84 85 86
        /// <param name="end_dt"></param>
        /// <param name="adArray"></param>
        public void AddAD(DateTime end_dt, int[] adArray)
87
        {
88 89
            if (adArray.Count() == 0)
                return;
90 91
            //------------------------------------------------------------------------------------------
            //AD数据添加
92
            //DateTime dt = DateTime.MinValue;
93 94 95
            ADPool.Add(new DateTimeUnit3() { dt = end_dt, datas = adArray});

            //long ticks = (long)((ts_ms * adArray.Count()) * TimeSpan.TicksPerMillisecond);
96
            NewestTime = end_dt;
97 98 99 100 101
            LimitPool();
        }
        void LimitPool() 
        {
            LimitSimplePool(ADPool);
102
            //LimitSimplePool_PosPool();
潘栩锋's avatar
潘栩锋 committed
103 104 105 106
            LimitSimplePool(PosPool,1.5);
            LimitSimplePool(Pos2Pool, 1.5);
            LimitSimplePool(DriveStatusPool, 1.5);
            LimitSimplePool(IStatusPool, 1.5);
107
        }
108

潘栩锋's avatar
潘栩锋 committed
109 110 111 112 113 114 115 116 117
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">IDateTimeUnit</typeparam>
        /// <param name="pool">缓存区</param>
        /// <param name="cap_min">缓存区容量单位min</param>
        void LimitSimplePool<T>(List<T> pool, double cap_min = 1) where T : IDateTimeUnit {
            if (NewestTime == DateTime.MinValue)
                return;
118 119

            //限制的时间
潘栩锋's avatar
潘栩锋 committed
120
            var limitTime = NewestTime - TimeSpan.FromMinutes(cap_min);
121 122 123
            //删除Pos 数据池
            int removeCnt = 0;
            for (int i = 0; i < pool.Count(); i++)
124
            {
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
                var cell = pool[i];
                if (cell.dt < limitTime)
                {
                    //需要删除
                    removeCnt = i + 1;
                }
                else
                {
                    break;
                }
            }

            if (removeCnt > 0)
            {
                //不能全部数据删除完,必须留一个

                if (removeCnt == pool.Count())
                {
                    removeCnt--;
                    if (removeCnt <= 0)
                        return;//不用删除了
                }

                pool.RemoveRange(0, removeCnt);
149 150
            }
        }
151

152
        /// <summary>
153
        /// 添加脉冲, 脉冲不一样,返回true
154 155 156
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
157
        public bool AddPos(DateTime time, int pos)
158
        {
159
            return AddSimpleData(PosPool, time, pos);
160 161
        }

潘栩锋's avatar
潘栩锋 committed
162

163
        /// <summary>
164
        /// 添加驱动状态, 返回true,则driveStatus变化了
165 166
        /// </summary>
        /// <param name="time"></param>
潘栩锋's avatar
潘栩锋 committed
167 168 169
        /// <param name="order"></param>
        /// <param name="driveStatus"></param>
        /// <param name="marker"></param>
170
        public bool AddDriveStatus(DateTime time,DRIVE_MAN_ORDER order,  DRIVE_MAN_STATUS driveStatus, int marker)
171
        {
172 173 174 175 176 177 178 179 180 181 182 183 184 185
            var pool = DriveStatusPool;
            if (pool.Count > 0)
            {
                if (pool.Last().dt > time)
                {
                    //异常,把之前的数据删除
                    pool.Clear();
                }
                else if (pool.Last().dt == time)
                {
                    //删除之前那个
                    pool.RemoveAt(pool.Count() - 1);
                }
            }
186
            
187
            pool.Add(new DateTimeUnit4() { dt = time, order = order, driveStatus = driveStatus, marker = marker });
188 189 190 191 192 193
            
            if (pool.Count() > 1)
            {
                return pool[pool.Count() - 2].driveStatus != driveStatus;
            }
            return false;
194
        }
195 196 197 198 199 200 201 202
        /// <summary>
        /// 添加 基本数据,当数据与上一次不一样,返回true
        /// </summary>
        /// <param name="pool"></param>
        /// <param name="time"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        bool AddSimpleData(List<DateTimeUnit> pool, DateTime time, int data) {
203 204

            if (pool.Count > 0)
205
            {
206
                if (pool.Last().dt > time)
207
                {
208
                    //异常,把之前的数据删除
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
                    //TODO, AD盒有bug, IO 推送, 的systick 比 pos 的systick 要慢, 
                    

                    //正常的操作,全删除
                    //pool.Clear();

                    //数据插入就算了
                    for (int i = 1; i < pool.Count(); i++) {
                        int index = pool.Count() - 1 - i;
                        if (time > pool[index].dt)
                        {
                            //找到了
                            pool.Insert(index, new DateTimeUnit() { dt = time, data = data });
                        }
                        else if (time == pool[index].dt) {
                            //什么都不干
225
                            return false;
226 227 228
                        }
                    }
                    //找完了,不插了
229
                }
230
                else if (pool.Last().dt == time) 
231
                {
232
                    //删除之前那个
233 234
                    //pool.RemoveAt(pool.Count() - 1);

235
                    return false;
236 237
                }
            }
238
            pool.Add(new DateTimeUnit() { dt = time, data = data });
239 240 241 242 243 244 245
            if (pool.Count > 1)
            {
                return pool[pool.Count() - 2].data != data;
            }
            else {
                return false;
            }
246
        }
247

248
        /// <summary>
249
        /// 添加脉冲2, 脉冲2不一样,返回true
250 251 252
        /// </summary>
        /// <param name="pos2"></param>
        /// <param name="time"></param>
253
        public bool AddPos2(DateTime time, int pos2)
254
        {
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
            return AddSimpleData(Pos2Pool, time, pos2);
        }

        /// <summary>
        /// 添加输入口状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="istatus"></param>
        public bool AddIStatus(DateTime time, UInt16 istatus)
        {
            var pool = IStatusPool;
            if (pool.Count > 0)
            {
                if (pool.Last().dt >= time)
                {
                    //异常,把之前的数据删除
                    pool.Clear();
                }
            }

            IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus });

            if (IStatusPool.Count > 1)
                return IStatusPool[IStatusPool.Count - 2].istatus != istatus;
            else
                return false;
281 282
        }

283 284 285
        /// <summary>
        /// 添加输入口状态
        /// </summary>
286
        /// <param name="time"></param>
287 288
        /// <param name="istatus"></param>
        /// <param name="pos"></param>
289
        /// <param name="pos2"></param>
290
        public bool AddIStatus(DateTime time, UInt16 istatus, int pos, int pos2, out UInt16 ichanged)
291
        {
292 293
            ichanged = 0;

294 295
            var pool = IStatusPool;
            if (pool.Count > 0)
296
            {
297
                if (pool.Last().dt >= time)
298
                {
299 300
                    //异常,把之前的数据删除
                    pool.Clear();
301 302 303
                }
            }

304
            IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus, isPosValid = true,  pos = pos, pos2 = pos2 });
305 306

            
307 308
            AddPos(time, pos);
            AddPos2(time, pos2);
309 310 311 312 313 314 315 316

            if (IStatusPool.Count > 1)
            {
                ichanged = (UInt16)(IStatusPool[IStatusPool.Count - 2].istatus ^ istatus);
                return IStatusPool[IStatusPool.Count - 2].istatus != istatus;
            }
            else
                return false;
317 318
        }

319 320 321 322 323
        /// <summary>
        /// 添加开始位置
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
324
        public void AddPos_Default(DateTime time, int pos)
325 326 327
        {
            if (PosPool.Count() == 0)
            {
328
                AddPos(time,pos);
329 330
            }
        }
331
        
332
        /// <summary>
333
        /// 添加开始位置
334 335 336
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        public void AddPos2_Default(DateTime time, int pos)
        {
            if (Pos2Pool.Count() == 0)
            {
                AddPos2(time,pos);
            }
        }


        /// <summary>
        /// 添加开始驱动状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="order"></param>
        /// <param name="dirveStatus"></param>
        /// <param name="marker"></param>
        public void AddDriveStatus_Default(DateTime time, DRIVE_MAN_ORDER order, DRIVE_MAN_STATUS dirveStatus, int marker)
        {
            if (IStatusPool.Count() == 0)
            {
                AddDriveStatus(time, order, dirveStatus, marker);
            }
        }

        /// <summary>
        /// 添加开始状态
        /// </summary>
        /// <param name="istatus"></param>
        /// <param name="time"></param>
        public void AddIStatus_Default(DateTime time, UInt16 istatus)
367 368 369
        {
            if (IStatusPool.Count() == 0)
            {
370
                IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus});
371 372
            }
        }
373 374 375
        /// <summary>
        /// 
        /// </summary>
376 377 378 379 380
        public void Clear()
        {
            PosPool.Clear();
            ADPool.Clear();
            IStatusPool.Clear();
381 382
            Pos2Pool.Clear();
            DriveStatusPool.Clear();
383 384
        }

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

        /// <summary>
        /// 获取最后一次运行的时间段
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <param name="marker"></param>
        /// <param name="order"></param>
        /// <returns></returns>
        public bool GetLastRunningTime(out DateTime begin, out DateTime end, out int marker, out DRIVE_MAN_ORDER order) {
            begin = DateTime.MinValue;
            end = DateTime.MinValue;
            marker = 0;
            order = DRIVE_MAN_ORDER.IDLE;
            //1. 找 最后一个 driveStatus!=running的
            int idxOfStop = -1;
            for (int i = DriveStatusPool.Count() - 1; i >= 0; i--)
            {

                var cell = DriveStatusPool[i];
                if (cell.driveStatus != DRIVE_MAN_STATUS.RUNNING)
                {
                    //对!!它是。但继续向前找找看
                    idxOfStop = i;
                }
                else
                {
                    if (idxOfStop != -1)
                    {
                        //之前已经找到了,
                        break;
                    }
                }
            }
            if (idxOfStop == -1)
            {
                //一直在running,还没停下来
                return false;
            }

            //2. 最向前找running的
            int idxOfRunning = -1;
            for (int i = idxOfStop - 1; i >= 0; i--)
            {
                var cell = DriveStatusPool[i];
                if (cell.driveStatus == DRIVE_MAN_STATUS.RUNNING)
                {
                    //对!!它是。但继续向前找找看
                    idxOfRunning = i;
                }
                else
                {
                    //没了
                    break;
                }
            }

            if (idxOfRunning == -1)
            {
                //一直停着,没有运行过
                return false;
            }

            begin = DriveStatusPool[idxOfRunning].dt;
            end = DriveStatusPool[idxOfStop].dt;
            order = DriveStatusPool[idxOfStop].order;
            marker = DriveStatusPool[idxOfStop].marker;
            return true;
        }
潘栩锋's avatar
潘栩锋 committed
454 455


456 457 458 459
        /// <summary>
        /// 获取最后一次运行中的timegrid;
        /// 找到最后一个 driveStatus!=running的, 最向前找running的
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
460 461
        /// <param name="begin">开始时间</param>
        /// <param name="end">结束时间</param>
462
        /// <param name="reponse_endTime">返回数据的最后一个数据的时间,数据间隔为 TimeGridAdvHelperExt.ts_ad_ms</param>
463
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
464
        public List<int> GetAD(DateTime begin, DateTime end, out DateTime reponse_endTime)
465
        {
潘栩锋's avatar
潘栩锋 committed
466 467
            return TimeGridAdvHelperExt.GetAD(ADPool, begin, end, out reponse_endTime);
        }
468 469 470 471 472
        public List<int> GetAD(DateTime begin)
        {
            return TimeGridAdvHelperExt.GetAD(ADPool, begin );
        }

473 474 475 476 477 478
        /// <summary>
        /// 输出以 1ms 为间隔脉冲列表
        /// </summary>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
        public List<int> GetPos(DateTime end_dt, int cnt) {

            return TimeGridAdvHelperExt.GetPos(PosPool, end_dt, cnt);
        }

        public List<int> GetPos2(DateTime end_dt, int cnt)
        {
            return TimeGridAdvHelperExt.GetPos(Pos2Pool, end_dt, cnt);
        }

        public List<UInt16> GetIStatus(DateTime end_dt, int cnt)
        {
            return TimeGridAdvHelperExt.GetIStatus(IStatusPool, end_dt, cnt);
        }

        public void GetIStatusRange(
            int istatusIndex,
            DateTime beginTime, DateTime endTime,
            out List<Range> data0, out List<Range> data1)
        {
            TimeGridAdvHelperExt.GetIStatusRange(IStatusPool, PosPool, true, istatusIndex, beginTime, endTime, out data0, out data1);
        }

    }
    public interface IDateTimeUnit 
    {
        DateTime dt { get; set; }
    }
    public struct DateTimeUnit: IDateTimeUnit
    {
        public int data;
        public DateTime dt { get; set; }
        public override string ToString()
        {
            return $"{dt.Ticks} | {data}";
        }
    }

    public struct DateTimeUnit4 : IDateTimeUnit
    {
        public DateTime dt { get; set; }
        public DRIVE_MAN_ORDER order;
        public DRIVE_MAN_STATUS driveStatus;
        public int marker;

        public override string ToString()
        {
            return $"{dt.Ticks} | {order} | {driveStatus} | {marker}";
        }
    }
    public struct DateTimeUnit3 : IDateTimeUnit
    {
        /// <summary>
        /// 数据结束点
        /// </summary>
        public DateTime dt { get; set; }

        /// <summary>
        /// 数据列
        /// </summary>
        public int[] datas;
        
541

潘栩锋's avatar
潘栩锋 committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
        public override string ToString()
        {
            return $"{dt.Ticks} | cnt={datas.Count()}";
        }
    }

    public struct DateTimeUnit2: IDateTimeUnit
    {
        public DateTime dt { get; set; }
        public UInt16 istatus;
        public bool isPosValid;
        public int pos;
        public int pos2;
        public override string ToString()
        {
            if(isPosValid)
                return $"{dt.Ticks} | ({istatus:X2}) | 1={pos} | 2={pos2}";
            else
                return $"{dt.Ticks} | ({istatus:X2})";

        }
    }

    public static class TimeGridAdvHelperExt 
    {
567 568 569
        /// <summary>
        /// 导出的数据间隔是1ms 一个, 导出的数据量肯定比要求给的更多
        /// </summary>
570
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
571 572 573 574
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
        /// <param name="reponse_endTime">实际导出的数据结束时间点</param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
575 576
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin, DateTime end, out DateTime reponse_endTime) 
        {
577 578 579 580 581 582 583 584 585
            reponse_endTime = DateTime.MinValue;

            var reponse2 = GetAD2(adPool, begin, end);
            if (reponse2.Count() == 0)
                return new List<int>();

            DateTime lastTime = reponse2.Last().dt;
            reponse_endTime = lastTime;

586
            return GetAD(reponse2);
潘栩锋's avatar
潘栩锋 committed
587
        }
588 589

        /// <summary>
590
        /// 导出的数据间隔是1ms 一个
591
        /// </summary>
592
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
593
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
594 595
        public static List<int> GetAD(List<DateTimeUnit3> adPool)
        {
596
            return GetAD(adPool, DateTime.MinValue);
597 598
        }

599
        /// <summary>
600
        /// 导出的数据间隔是1ms 一个
601 602 603 604
        /// </summary>
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <returns></returns>
605
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin)
606
        {
潘栩锋's avatar
潘栩锋 committed
607
            List<int> reponse = new List<int>();
608

609 610
            //导出数据的时间
            DateTime idealTime = adPool.Last().dt;
611
            
612

613
            //每个ad包的每个数据不是准确的1ms,肯定比1.28ms大,每次需要准
潘栩锋's avatar
潘栩锋 committed
614 615
            for (int i = 0; i < adPool.Count(); i++)
            {
616
                int idx = adPool.Count() - 1 - i;
617
                //当前缓存区包,每个数据的时间间隔
618
                //TimeSpan curr_ad_ts;
619

620
                //计算这个包每个数据的时间间隔
621
                DateTime actTime = adPool[idx].dt;//从缓存区获取数据的时间
622 623 624 625 626

                for (int j = 0; j < adPool[idx].datas.Count(); j++)
                {
                    int idx2 = adPool[idx].datas.Count() - 1 - j;
                    int ad = adPool[idx].datas[idx2];
627
                    actTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加
628

629
                    while (idealTime > actTime)
630
                    {
631
                        reponse.Add(ad);//导出数据
632
                        idealTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加
633 634
                    }
                }
635 636
                if (idealTime < begin)
                    goto _end;//完成
637
            }
638 639 640
            _end:
            //反转
            reponse.Reverse();
641 642
            return reponse;

潘栩锋's avatar
潘栩锋 committed
643
        }
644 645 646

        /// <summary>
        /// 获取 时间在 begin~end 的 AD数据包
潘栩锋's avatar
潘栩锋 committed
647
        /// </summary>
648 649 650
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
潘栩锋's avatar
潘栩锋 committed
651 652 653
        /// <returns></returns>
        static List<DateTimeUnit3> GetAD2(List<DateTimeUnit3> adPool, DateTime begin, DateTime end)
        {
654
            //ad 包的总数量不可能多,从头找也没所谓
655
            //TODO, 当ad包,每个包只有一个数据时,包数很庞大。 需要查找加速
潘栩锋's avatar
潘栩锋 committed
656 657
            List<DateTimeUnit3> reponse = new List<DateTimeUnit3>();
            for (int i = 0; i < adPool.Count(); i++)
658
            {
潘栩锋's avatar
潘栩锋 committed
659 660 661 662 663 664 665
                var cell = adPool[i];
                if (cell.dt < begin)
                    continue;
                else if (cell.dt <= end)
                    reponse.Add(cell);
                else
                    break;
666 667 668 669 670
            }
            return reponse;
        }


潘栩锋's avatar
潘栩锋 committed
671 672 673
        /// <summary>
        /// 获取平均速度, 单位 脉冲/min
        /// </summary>
674 675 676
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
潘栩锋's avatar
潘栩锋 committed
677 678 679 680 681 682 683
        /// <returns></returns>
        public static double GetSpeed(List<DateTimeUnit> posPool, DateTime begin, DateTime end) {
            int searchIdx = posPool.Count() - 1;
            int end_pos = GetPos(posPool, end, ref searchIdx);
            int begin_pos = GetPos(posPool, begin, ref searchIdx);
            return (end_pos - begin_pos) / (end - begin).TotalMinutes;
        }
684

潘栩锋's avatar
潘栩锋 committed
685
        /// <summary>
686
        /// 输出以 1ms 为间隔脉冲列表
潘栩锋's avatar
潘栩锋 committed
687
        /// </summary>
688 689 690
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
潘栩锋's avatar
潘栩锋 committed
691
        /// <returns></returns>
692
        public static List<int> GetPos(List<DateTimeUnit> posPool, DateTime end_dt, int cnt)
693
        {
694
            //从后面开始找
潘栩锋's avatar
潘栩锋 committed
695
            int searchIdx = posPool.Count() - 1;
696 697 698 699 700 701

            List<int> reponse = new List<int>();

            DateTime lastTime = end_dt;
            for (int i = 0; i < cnt; i++)
            {
潘栩锋's avatar
潘栩锋 committed
702
                reponse.Add(GetPos(posPool, lastTime, ref searchIdx));
703
                lastTime -= TimeSpan.FromMilliseconds(1);
704 705 706 707 708 709
            }
            //反转
            reponse.Reverse();
            return reponse;
        }

潘栩锋's avatar
潘栩锋 committed
710

711
        /// <summary>
潘栩锋's avatar
潘栩锋 committed
712
        /// 获取时间对应的 位置点
713
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
714
        /// <param name="dt"></param>
潘栩锋's avatar
潘栩锋 committed
715 716
        /// <param name="posPool">脉冲缓存池</param>
        /// <param name="searchIdx">查询开始序号</param>
717
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
718
        public static int GetPos(List<DateTimeUnit> posPool, DateTime dt, ref int searchIdx)
719
        {
潘栩锋's avatar
潘栩锋 committed
720 721 722
            //找到 时间落在 [idx,idx+1] 之间的 序号
            int index = searchIndex(posPool, dt, searchIdx);
            searchIdx = index;
723

724 725 726 727 728 729 730 731 732 733
            if (index == posPool.Count() - 1)
            {
                //发生在未来,返回最后一个脉冲,不进行线性预测
                return posPool[index].data;
            }
            else if (index < 0) {
                //发生在过去,返回第一个脉冲,不进行线性预测
                return posPool[0].data;
            }

潘栩锋's avatar
潘栩锋 committed
734

潘栩锋's avatar
潘栩锋 committed
735
            return GetPosLinear(posPool, dt, index);
736 737 738
        }

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
739
        /// 线性插值算 pos;
740 741
        /// 以 [idx,idx+1] 的比例, 给出 time 计算 pos 
        /// </summary>
742
        /// <param name="posPool"></param>
743 744 745
        /// <param name="time"></param>
        /// <param name="idx"></param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
746
        static int GetPosLinear(List<DateTimeUnit> posPool, DateTime time, int idx)
747
        {
748 749 750 751
            if (posPool.Count() <= 0)//不应该存在
            {
                throw new Exception("没数据,还没准备好,不能获取");
            }
752

753 754
            if (posPool.Count() == 1)
                return posPool.First().data;
755 756 757 758

            if (idx < 0)
                idx = 0;

潘栩锋's avatar
潘栩锋 committed
759
            if (idx >= posPool.Count() - 1)
760 761 762 763 764 765
                idx = posPool.Count() - 2;
            var cell0 = posPool[idx];
            var cell1 = posPool[idx + 1];
            double d_pos = cell0.data - cell1.data;
            if (d_pos == 0)
                return cell0.data;
潘栩锋's avatar
潘栩锋 committed
766

767 768
            TimeSpan ts = cell0.dt - cell1.dt;
            double p = d_pos * (time - cell1.dt).Ticks / ts.Ticks;
769

770
            return (int)(p + cell1.data);
771 772
        }

潘栩锋's avatar
潘栩锋 committed
773 774

        enum SearchState
潘栩锋's avatar
潘栩锋 committed
775 776 777 778 779
        {
            ContinueLastTime,
            Back,
            ForwToCheck
        }
780
        /// <summary>
781
        /// 找到 时间 dt 落在 [idx,idx+1] 之间的 idx;
潘栩锋's avatar
潘栩锋 committed
782
        /// 返回 -1,证明dt 在列表在前面, 或列表数据量为=0,
783
        /// 当dt 大于 列表最后一个数, 返回最后一个数据的id
784
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
785 786
        /// <typeparam name="T"></typeparam>
        /// <param name="list">列表</param>
787
        /// <param name="dt"></param>
潘栩锋's avatar
潘栩锋 committed
788
        /// <param name="searchStartIdx">从列表的某个位置开始向前查找</param>
789
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
790
        public static int searchIndex<T>(List<T> list, DateTime dt, int searchStartIdx) where T : IDateTimeUnit
791
        {
潘栩锋's avatar
潘栩锋 committed
792 793 794 795
            if (list.Count() == 0)
                return -1;

            int max_index = list.Count() - 1;
796 797 798 799 800 801 802 803

            if (dt >= list[max_index].dt)
                return max_index;

            if (dt < list[0].dt)
                return -1;


潘栩锋's avatar
潘栩锋 committed
804 805
            int index = searchStartIdx;
            SearchState state = SearchState.ContinueLastTime;
806

潘栩锋's avatar
潘栩锋 committed
807
            if ((index < 0) || (index >= max_index))//重新开始
808
            {
潘栩锋's avatar
潘栩锋 committed
809 810
                index = max_index;
                state = SearchState.Back;
811
            }
潘栩锋's avatar
潘栩锋 committed
812
            else if (state == SearchState.ContinueLastTime)
813
            {
814
                if (list[index].dt <= dt)
潘栩锋's avatar
潘栩锋 committed
815
                {
816 817 818 819 820 821 822
                    //可能找到了
                    //再向正方向看看
                    state = SearchState.ForwToCheck;
                }
                else
                {
                    state = SearchState.Back;
潘栩锋's avatar
潘栩锋 committed
823
                }
824
            }
潘栩锋's avatar
潘栩锋 committed
825

826 827
            if (state == SearchState.ForwToCheck)
            {
潘栩锋's avatar
潘栩锋 committed
828
                while (true)
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
                {
                    DateTime time = list[index].dt;
                    if (time > dt)
                    {
                        //就是之前那个
                        index--;
                        return index;
                    }
                    else
                    {
                        index++;
                    }
                    if (index > max_index)
                    {
                        //最后一个就是了
                        throw new Exception("之前没有判断出 时间点在列表后,异常!!");
                        //return max_index;
                    }
                }
            }
            else//SearchState.Back
            {
                while (true)
                {
                    DateTime time = list[index].dt;
                    if (time <= dt)
                    {
                        //就是它
                        return index;
                    }
                    index--;
潘栩锋's avatar
潘栩锋 committed
860
                    if (index < 0)
861 862 863 864 865 866
                    {
                        //不可能发生
                        throw new Exception("之前没有判断出 时间点在列表前,异常!!");
                    }
                }
            }
潘栩锋's avatar
潘栩锋 committed
867 868 869 870
        }



871 872 873 874 875 876 877
        /// <summary>
        /// 输出以 1ms 为间隔 输入口状态 列表
        /// </summary>
        /// <param name="iStatusPool"></param>
        /// <param name="end_dt"></param>
        /// <param name="cnt"></param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
878
        public static List<UInt16> GetIStatus(List<DateTimeUnit2> iStatusPool, DateTime end_dt, int cnt) 
潘栩锋's avatar
潘栩锋 committed
879
        {
潘栩锋's avatar
潘栩锋 committed
880 881 882 883 884
            int searchIdx = iStatusPool.Count() - 1;

            List<UInt16> reponse = new List<UInt16>();
            DateTime lastTime = end_dt;
            for (int i = 0; i < cnt; i++)
潘栩锋's avatar
潘栩锋 committed
885
            {
潘栩锋's avatar
潘栩锋 committed
886
                reponse.Add(GetIStatus(iStatusPool, lastTime, ref searchIdx));
887
                lastTime -= TimeSpan.FromMilliseconds(1);
潘栩锋's avatar
潘栩锋 committed
888
            }
潘栩锋's avatar
潘栩锋 committed
889 890 891
            //反转
            reponse.Reverse();
            return reponse;
潘栩锋's avatar
潘栩锋 committed
892
        }
893

潘栩锋's avatar
潘栩锋 committed
894 895 896 897 898 899 900 901 902
        /// <summary>
        /// 根据时间点,获取输入口状态;
        /// 时间 dt 落在 [searchIdx,searchIdx+1]
        /// </summary>
        /// <param name="iStatusPool"></param>
        /// <param name="dt"></param>
        /// <param name="searchIdx"></param>
        /// <returns></returns>
        public static UInt16 GetIStatus(List<DateTimeUnit2> iStatusPool, DateTime dt, ref int searchIdx)
潘栩锋's avatar
潘栩锋 committed
903
        {
潘栩锋's avatar
潘栩锋 committed
904
            int index = searchIndex(iStatusPool, dt, searchIdx);
潘栩锋's avatar
潘栩锋 committed
905

潘栩锋's avatar
潘栩锋 committed
906 907 908 909
            if (index < 0)
                index = 0;
            searchIdx = index;
            return (UInt16)iStatusPool[index].istatus;
潘栩锋's avatar
潘栩锋 committed
910
        }
911

912

潘栩锋's avatar
潘栩锋 committed
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
        /// <summary>
        /// data0 的 输入口=false 脉冲范围;data1 的 输入口=true 脉冲范围;
        /// </summary>
        /// <param name="iStatusPool">输入口池</param>
        /// <param name="posPool">脉冲池</param>
        /// <param name="isPos1">是pos1?</param>
        /// <param name="istatusIndex">输入口序号</param>
        /// <param name="beginTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="data0">false脉冲范围</param>
        /// <param name="data1">true脉冲范围</param>
        public static void GetIStatusRange( 
            List<DateTimeUnit2> iStatusPool, List<DateTimeUnit> posPool, bool isPos1,
            int istatusIndex, DateTime beginTime, DateTime endTime, out List<Range> data0, out List<Range> data1)
        {
            int istatus_search_idx = iStatusPool.Count - 1;
            int pos_search_idx = posPool.Count - 1;
930 931
            data0 = new List<Range>();
            data1 = new List<Range>();
潘栩锋's avatar
潘栩锋 committed
932 933 934 935
            UInt16 istatus = GetIStatus(iStatusPool, beginTime, ref istatus_search_idx);
            int beginPos = GetPos(posPool, beginTime, ref pos_search_idx);
            int endPos = GetPos(posPool, endTime, ref pos_search_idx);

936
            DIRECTION direction = endPos > beginPos ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
937

938
            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
939 940 941

            Range r = new Range();

942
            r.Begin = beginPos;
943

潘栩锋's avatar
潘栩锋 committed
944
            for (int i = istatus_search_idx + 1; i < iStatusPool.Count(); i++)
945
            {
潘栩锋's avatar
潘栩锋 committed
946 947 948 949 950 951 952
                istatus = iStatusPool[i].istatus;
                int pos;
                if (isPos1)
                    pos = iStatusPool[i].pos;
                else
                    pos = iStatusPool[i].pos2;

953
                bool b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
潘栩锋's avatar
潘栩锋 committed
954
                if (iStatusPool[i].dt >= endTime)
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
                {
                    //结束,这是最后!!!
                    break;
                }
                else
                {
                    if (b != istatus_no_b)
                    {
                        r.End = pos;
                        if (istatus_no_b)
                            data1.Add(r);
                        else
                            data0.Add(r);
                        istatus_no_b = b;
                        r = new Range();
                        r.Begin = pos;
                    }
                }
            }
974
            r.End = beginPos;
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
            if (istatus_no_b)
                data1.Add(r);
            else
                data0.Add(r);

            if (direction == DIRECTION.FORWARD)
            {
                for (int i = 0; i < data0.Count(); i++)
                {
                    data0[i].End -= 1;
                }
                for (int i = 0; i < data1.Count(); i++)
                {
                    data1[i].End -= 1;
                }
            }
            else
            {
                for (int i = 0; i < data0.Count(); i++)
                {

                    data0[i].End += 1;
                    int t = data0[i].End;
                    data0[i].End = data0[i].Begin;
                    data0[i].Begin = t;
                }
                data0.Reverse();


                for (int i = 0; i < data1.Count(); i++)
                {
                    data1[i].End += 1;
                    int t = data1[i].End;
                    data1[i].End = data1[i].Begin;
                    data1[i].Begin = t;
                }
                data1.Reverse();
            }
        }
1014

潘栩锋's avatar
潘栩锋 committed
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
        /// <summary>
        /// data0 的 输入口=false 序号范围;data1 的 输入口=true 序号范围;
        /// timeOffset 为时间偏移,用于多AD盒情况,iStatusPool内的全部时间都会+timeOffset
        /// </summary>
        /// <param name="iStatusPool">输入口池</param>
        /// <param name="istatusIndex">输入口序号</param>
        /// <param name="beginTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="data0">false脉冲范围</param>
        /// <param name="data1">true脉冲范围</param>
        public static void GetIStatusRange(
            List<DateTimeUnit2> iStatusPool, 
            int istatusIndex, DateTime beginTime, DateTime endTime,
            out List<Range_DateTime> data0, out List<Range_DateTime> data1)
1029
        {
潘栩锋's avatar
潘栩锋 committed
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074

            int istatus_search_idx = iStatusPool.Count - 1;
            data0 = new List<Range_DateTime>();
            data1 = new List<Range_DateTime>();
            UInt16 istatus = GetIStatus(iStatusPool, beginTime, ref istatus_search_idx);

            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);

            Range_DateTime r = new Range_DateTime();
            r.Begin = beginTime;
            int i = istatus_search_idx + 1;
            while (i < iStatusPool.Count()) {

                istatus = iStatusPool[i].istatus;

                bool b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
                if (iStatusPool[i].dt >= endTime)
                {
                    //结束,这是最后!!!

                    break;
                }
                else
                {
                    if (b != istatus_no_b)
                    {
                        r.End = iStatusPool[i].dt;
                        if (istatus_no_b)
                            data1.Add(r);
                        else
                            data0.Add(r);
                        istatus_no_b = b;
                        r = new Range_DateTime();
                        r.Begin = iStatusPool[i].dt;
                    }
                }
                i++;
            }
            r.End = endTime;
            if (istatus_no_b)
                data1.Add(r);
            else
                data0.Add(r);


1075 1076 1077
        }


潘栩锋's avatar
潘栩锋 committed
1078 1079

        static void toGrid_step1(int[] grids_sum, int[] grids_cnt, int gridLen, int grid_idx, int ad)
1080
        {
潘栩锋's avatar
潘栩锋 committed
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
            if ((grid_idx < 0) || (grid_idx >= gridLen))
            {
                return;
            }

            if (!Misc.MyBase.ISVALIDATA(ad))
                return;

            grids_cnt[grid_idx]++;
            grids_sum[grid_idx] += ad;
1091 1092 1093
        }

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
1094
        /// 输出 grid图。 adList[x-adLag] 对应 posList[x]
1095
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
1096 1097 1098 1099 1100 1101
        /// <param name="adList"></param>
        /// <param name="posList"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridLen"></param>
        /// <param name="adLag"></param>
        /// <returns></returns>
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
        public static int[] ToGrid(List<int> adList, List<int> posList, int posOfGrid, int gridLen, int adLag=0) 
        {
            return ToGrid(adList, posList, posOfGrid, gridLen, out int grid_start, out int grid_end, adLag);
        }


        /// <summary>
        /// 输出 grid图。 adList[x-adLag] 对应 posList[x]
        /// </summary>
        /// <param name="adList"></param>
        /// <param name="posList"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridLen"></param>
        /// <param name="adLag"></param>
        /// <returns></returns>
        public static int[] ToGrid(List<int> adList, List<int> posList, int posOfGrid, int gridLen, out int grid_start, out int grid_end, int adLag = 0, bool isMiniGrid=false)
        {
潘栩锋's avatar
潘栩锋 committed
1119 1120
            int[] grids_sum = new int[gridLen];
            int[] grids_cnt = new int[gridLen];
1121

潘栩锋's avatar
潘栩锋 committed
1122
            int grid_idx_last = -1;
1123 1124
            grid_start = Misc.MyBase.NULL_VALUE;
            grid_end = Misc.MyBase.NULL_VALUE;
潘栩锋's avatar
潘栩锋 committed
1125 1126 1127 1128 1129 1130
            //grid_idx 应该要连续变化,不是,那就插值
            for (int j = 0; j < adList.Count() && j < posList.Count(); j++)
            {
                int index = j - adLag;
                if (index < 0 || index >= adList.Count())
                    continue;
1131

潘栩锋's avatar
潘栩锋 committed
1132 1133 1134
                int ad = adList[index];
                int pos = posList[j];
                int grid_idx = pos / posOfGrid;
1135 1136 1137
                //grid不能小于0 或 大于等于 gridLen
                if (grid_idx < 0 || grid_idx >= gridLen)
                    continue;
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150

                if (!Misc.MyBase.ISVALIDATA(grid_start))
                {
                    grid_start = grid_idx;
                    grid_end = grid_idx;
                }
                else
                {
                    if (grid_idx < grid_start)
                        grid_start = grid_idx;
                    if (grid_idx > grid_end)
                        grid_end = grid_idx;
                }
潘栩锋's avatar
潘栩锋 committed
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
                if (grid_idx_last != -1 && (Math.Abs(grid_idx_last - grid_idx) > 1))
                {
                    //步子太大,需要插值
                    if (grid_idx_last < grid_idx)
                    {
                        for (int i = grid_idx_last; i < grid_idx; i++)
                        {
                            toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad);
                        }
                    }
                    else
                    {
                        for (int i = grid_idx_last; i > grid_idx; i--)
                        {
                            toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad);
                        }
                    }
                }
                toGrid_step1(grids_sum, grids_cnt, gridLen, grid_idx, ad);
                grid_idx_last = grid_idx;
            }

            for (int i = 0; i < gridLen; i++)
            {
                if (grids_cnt[i] == 0)
                    grids_sum[i] = Misc.MyBase.NULL_VALUE;
                else
                    grids_sum[i] = (int)(grids_sum[i] / grids_cnt[i]);
            }

1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
            if (isMiniGrid)
            {
                //只导出 grid_start~grid_end 数据
                if (Misc.MyBase.ISVALIDATA(grid_start))
                {
                    return grids_sum.Skip(grid_start).Take(grid_end - grid_start + 1).ToArray();
                }
                else {
                    return null;//一个数据都没有
                }
            }
            else
            {
                return grids_sum;
            }
        }
潘栩锋's avatar
潘栩锋 committed
1197 1198 1199 1200 1201
    }
    public class Range_DateTime 
    {
        public DateTime Begin;
        public DateTime End;
1202 1203
    }
}