TimeGridAdvHelper.cs 40.3 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
            LimitPool();
        }
        void LimitPool() 
        {
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
            if (NewestTime == DateTime.MinValue)
                return;

            if (ADPool.Count() == 0)
                return;

            //限制的时间
            var limitTime_ad = NewestTime - TimeSpan.FromMinutes(1);
            if (limitTime_ad - ADPool.First().dt < TimeSpan.FromMinutes(0.2))
            {
                //比限定的多了0.2min 才会动作
                return;
            }



            //限制的时间
            var limitTime_other = NewestTime - TimeSpan.FromMinutes(1.5);

            LimitSimplePool(ADPool, limitTime_ad);
            LimitSimplePool(PosPool, limitTime_other);
            LimitSimplePool(Pos2Pool, limitTime_other);
            LimitSimplePool(DriveStatusPool, limitTime_other);
            LimitSimplePool(IStatusPool, limitTime_other);
125
        }
126

潘栩锋's avatar
潘栩锋 committed
127 128 129 130 131 132
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">IDateTimeUnit</typeparam>
        /// <param name="pool">缓存区</param>
        /// <param name="cap_min">缓存区容量单位min</param>
133 134
        void LimitSimplePool<T>(List<T> pool, DateTime limitTime) where T : IDateTimeUnit 
        {
135

136 137 138
            //删除Pos 数据池
            int removeCnt = 0;
            for (int i = 0; i < pool.Count(); i++)
139
            {
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
                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);
164 165
            }
        }
166

167
        /// <summary>
168
        /// 添加脉冲, 脉冲不一样,返回true
169 170 171
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
172
        public bool AddPos(DateTime time, int pos)
173
        {
174
            return AddSimpleData(PosPool, time, pos);
175 176
        }

潘栩锋's avatar
潘栩锋 committed
177

178
        /// <summary>
179
        /// 添加驱动状态, 返回true,则driveStatus变化了
180 181
        /// </summary>
        /// <param name="time"></param>
潘栩锋's avatar
潘栩锋 committed
182 183 184
        /// <param name="order"></param>
        /// <param name="driveStatus"></param>
        /// <param name="marker"></param>
185
        public bool AddDriveStatus(DateTime time,DRIVE_MAN_ORDER order,  DRIVE_MAN_STATUS driveStatus, int marker)
186
        {
187 188 189 190 191 192 193 194 195 196 197 198 199 200
            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);
                }
            }
201
            
202
            pool.Add(new DateTimeUnit4() { dt = time, order = order, driveStatus = driveStatus, marker = marker });
203 204 205 206 207 208
            
            if (pool.Count() > 1)
            {
                return pool[pool.Count() - 2].driveStatus != driveStatus;
            }
            return false;
209
        }
210 211 212 213 214 215 216 217
        /// <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) {
218 219

            if (pool.Count > 0)
220
            {
221
                if (pool.Last().dt > time)
222
                {
223
                    //异常,把之前的数据删除
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
                    //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) {
                            //什么都不干
240
                            return false;
241 242 243
                        }
                    }
                    //找完了,不插了
244
                }
245
                else if (pool.Last().dt == time) 
246
                {
247
                    //删除之前那个
248 249
                    //pool.RemoveAt(pool.Count() - 1);

250
                    return false;
251 252
                }
            }
253
            pool.Add(new DateTimeUnit() { dt = time, data = data });
254 255 256 257 258 259 260
            if (pool.Count > 1)
            {
                return pool[pool.Count() - 2].data != data;
            }
            else {
                return false;
            }
261
        }
262

263
        /// <summary>
264
        /// 添加脉冲2, 脉冲2不一样,返回true
265 266 267
        /// </summary>
        /// <param name="pos2"></param>
        /// <param name="time"></param>
268
        public bool AddPos2(DateTime time, int pos2)
269
        {
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
            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;
296 297
        }

298 299 300
        /// <summary>
        /// 添加输入口状态
        /// </summary>
301
        /// <param name="time"></param>
302 303
        /// <param name="istatus"></param>
        /// <param name="pos"></param>
304
        /// <param name="pos2"></param>
305
        public bool AddIStatus(DateTime time, UInt16 istatus, int pos, int pos2, out UInt16 ichanged)
306
        {
307 308
            ichanged = 0;

309 310
            var pool = IStatusPool;
            if (pool.Count > 0)
311
            {
312
                if (pool.Last().dt >= time)
313
                {
314 315
                    //异常,把之前的数据删除
                    pool.Clear();
316 317 318
                }
            }

319
            IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus, isPosValid = true,  pos = pos, pos2 = pos2 });
320 321

            
322 323
            AddPos(time, pos);
            AddPos2(time, pos2);
324 325 326 327 328 329 330 331

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

334 335 336 337 338
        /// <summary>
        /// 添加开始位置
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
339
        public void AddPos_Default(DateTime time, int pos)
340 341 342
        {
            if (PosPool.Count() == 0)
            {
343
                AddPos(time,pos);
344 345
            }
        }
346
        
347
        /// <summary>
348
        /// 添加开始位置
349 350 351
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
        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)
382 383 384
        {
            if (IStatusPool.Count() == 0)
            {
385
                IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus});
386 387
            }
        }
388 389 390
        /// <summary>
        /// 
        /// </summary>
391 392 393 394 395
        public void Clear()
        {
            PosPool.Clear();
            ADPool.Clear();
            IStatusPool.Clear();
396 397
            Pos2Pool.Clear();
            DriveStatusPool.Clear();
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 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468

        /// <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
469 470


471 472 473 474
        /// <summary>
        /// 获取最后一次运行中的timegrid;
        /// 找到最后一个 driveStatus!=running的, 最向前找running的
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
475 476
        /// <param name="begin">开始时间</param>
        /// <param name="end">结束时间</param>
477
        /// <param name="reponse_endTime">返回数据的最后一个数据的时间,数据间隔为 TimeGridAdvHelperExt.ts_ad_ms</param>
478
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
479
        public List<int> GetAD(DateTime begin, DateTime end, out DateTime reponse_endTime)
480
        {
潘栩锋's avatar
潘栩锋 committed
481 482
            return TimeGridAdvHelperExt.GetAD(ADPool, begin, end, out reponse_endTime);
        }
483 484 485 486 487
        public List<int> GetAD(DateTime begin)
        {
            return TimeGridAdvHelperExt.GetAD(ADPool, begin );
        }

488 489 490 491 492 493
        /// <summary>
        /// 输出以 1ms 为间隔脉冲列表
        /// </summary>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
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 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
        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;
        
556

潘栩锋's avatar
潘栩锋 committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
        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 
    {
582 583 584
        /// <summary>
        /// 导出的数据间隔是1ms 一个, 导出的数据量肯定比要求给的更多
        /// </summary>
585
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
586 587 588 589
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
        /// <param name="reponse_endTime">实际导出的数据结束时间点</param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
590 591
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin, DateTime end, out DateTime reponse_endTime) 
        {
592 593 594 595 596 597 598 599 600
            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;

601
            return GetAD(reponse2);
潘栩锋's avatar
潘栩锋 committed
602
        }
603 604

        /// <summary>
605
        /// 导出的数据间隔是1ms 一个
606
        /// </summary>
607
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
608
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
609 610
        public static List<int> GetAD(List<DateTimeUnit3> adPool)
        {
611
            return GetAD(adPool, DateTime.MinValue);
612 613
        }

614
        /// <summary>
615
        /// 导出的数据间隔是1ms 一个
616 617 618 619
        /// </summary>
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <returns></returns>
620
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin)
621
        {
潘栩锋's avatar
潘栩锋 committed
622
            List<int> reponse = new List<int>();
623

624 625
            //导出数据的时间
            DateTime idealTime = adPool.Last().dt;
626
            
627

628
            //每个ad包的每个数据不是准确的1ms,肯定比1.28ms大,每次需要准
潘栩锋's avatar
潘栩锋 committed
629 630
            for (int i = 0; i < adPool.Count(); i++)
            {
631
                int idx = adPool.Count() - 1 - i;
632
                var adCell = adPool[idx];
633
                //当前缓存区包,每个数据的时间间隔
634
                //TimeSpan curr_ad_ts;
635

636
                //计算这个包每个数据的时间间隔
637
                DateTime actTime = adCell.dt;//从缓存区获取数据的时间
638

639
                for (int j = 0; j < adCell.datas.Count(); j++)
640
                {
641 642
                    int idx2 = adCell.datas.Count() - 1 - j;
                    int ad = adCell.datas[idx2];
643
                    actTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加
644

645
                    while (idealTime > actTime)
646
                    {
647
                        reponse.Add(ad);//导出数据
648
                        idealTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加
649 650
                    }
                }
651 652
                if (idealTime < begin)
                    goto _end;//完成
653
            }
654 655 656
            _end:
            //反转
            reponse.Reverse();
657 658
            return reponse;

潘栩锋's avatar
潘栩锋 committed
659
        }
660 661 662

        /// <summary>
        /// 获取 时间在 begin~end 的 AD数据包
潘栩锋's avatar
潘栩锋 committed
663
        /// </summary>
664 665 666
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
潘栩锋's avatar
潘栩锋 committed
667 668 669
        /// <returns></returns>
        static List<DateTimeUnit3> GetAD2(List<DateTimeUnit3> adPool, DateTime begin, DateTime end)
        {
670
            //ad 包的总数量不可能多,从头找也没所谓
671
            //TODO, 当ad包,每个包只有一个数据时,包数很庞大。 需要查找加速
潘栩锋's avatar
潘栩锋 committed
672 673
            List<DateTimeUnit3> reponse = new List<DateTimeUnit3>();
            for (int i = 0; i < adPool.Count(); i++)
674
            {
潘栩锋's avatar
潘栩锋 committed
675 676 677 678 679 680 681
                var cell = adPool[i];
                if (cell.dt < begin)
                    continue;
                else if (cell.dt <= end)
                    reponse.Add(cell);
                else
                    break;
682 683 684 685 686
            }
            return reponse;
        }


潘栩锋's avatar
潘栩锋 committed
687 688 689
        /// <summary>
        /// 获取平均速度, 单位 脉冲/min
        /// </summary>
690 691 692
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
潘栩锋's avatar
潘栩锋 committed
693 694 695 696 697 698 699
        /// <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;
        }
700

潘栩锋's avatar
潘栩锋 committed
701
        /// <summary>
702
        /// 输出以 1ms 为间隔脉冲列表
潘栩锋's avatar
潘栩锋 committed
703
        /// </summary>
704 705 706
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
潘栩锋's avatar
潘栩锋 committed
707
        /// <returns></returns>
708
        public static List<int> GetPos(List<DateTimeUnit> posPool, DateTime end_dt, int cnt)
709
        {
710
            //从后面开始找
潘栩锋's avatar
潘栩锋 committed
711
            int searchIdx = posPool.Count() - 1;
712 713 714 715 716 717

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

            DateTime lastTime = end_dt;
            for (int i = 0; i < cnt; i++)
            {
潘栩锋's avatar
潘栩锋 committed
718
                reponse.Add(GetPos(posPool, lastTime, ref searchIdx));
719
                lastTime -= TimeSpan.FromMilliseconds(1);
720 721 722 723 724 725
            }
            //反转
            reponse.Reverse();
            return reponse;
        }

潘栩锋's avatar
潘栩锋 committed
726

727
        /// <summary>
潘栩锋's avatar
潘栩锋 committed
728
        /// 获取时间对应的 位置点
729
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
730
        /// <param name="dt"></param>
潘栩锋's avatar
潘栩锋 committed
731 732
        /// <param name="posPool">脉冲缓存池</param>
        /// <param name="searchIdx">查询开始序号</param>
733
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
734
        public static int GetPos(List<DateTimeUnit> posPool, DateTime dt, ref int searchIdx)
735
        {
潘栩锋's avatar
潘栩锋 committed
736 737 738
            //找到 时间落在 [idx,idx+1] 之间的 序号
            int index = searchIndex(posPool, dt, searchIdx);
            searchIdx = index;
739

740 741 742 743 744 745 746 747 748 749
            if (index == posPool.Count() - 1)
            {
                //发生在未来,返回最后一个脉冲,不进行线性预测
                return posPool[index].data;
            }
            else if (index < 0) {
                //发生在过去,返回第一个脉冲,不进行线性预测
                return posPool[0].data;
            }

潘栩锋's avatar
潘栩锋 committed
750

潘栩锋's avatar
潘栩锋 committed
751
            return GetPosLinear(posPool, dt, index);
752 753 754
        }

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
755
        /// 线性插值算 pos;
756 757
        /// 以 [idx,idx+1] 的比例, 给出 time 计算 pos 
        /// </summary>
758
        /// <param name="posPool"></param>
759 760 761
        /// <param name="time"></param>
        /// <param name="idx"></param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
762
        static int GetPosLinear(List<DateTimeUnit> posPool, DateTime time, int idx)
763
        {
764 765 766 767
            if (posPool.Count() <= 0)//不应该存在
            {
                throw new Exception("没数据,还没准备好,不能获取");
            }
768

769 770
            if (posPool.Count() == 1)
                return posPool.First().data;
771 772 773 774

            if (idx < 0)
                idx = 0;

潘栩锋's avatar
潘栩锋 committed
775
            if (idx >= posPool.Count() - 1)
776 777 778 779 780 781
                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
782

783 784
            TimeSpan ts = cell0.dt - cell1.dt;
            double p = d_pos * (time - cell1.dt).Ticks / ts.Ticks;
785

786
            return (int)(p + cell1.data);
787 788
        }

潘栩锋's avatar
潘栩锋 committed
789 790

        enum SearchState
潘栩锋's avatar
潘栩锋 committed
791 792 793 794 795
        {
            ContinueLastTime,
            Back,
            ForwToCheck
        }
796
        /// <summary>
797
        /// 找到 时间 dt 落在 [idx,idx+1] 之间的 idx;
潘栩锋's avatar
潘栩锋 committed
798
        /// 返回 -1,证明dt 在列表在前面, 或列表数据量为=0,
799
        /// 当dt 大于 列表最后一个数, 返回最后一个数据的id
800
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
801 802
        /// <typeparam name="T"></typeparam>
        /// <param name="list">列表</param>
803
        /// <param name="dt"></param>
潘栩锋's avatar
潘栩锋 committed
804
        /// <param name="searchStartIdx">从列表的某个位置开始向前查找</param>
805
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
806
        public static int searchIndex<T>(List<T> list, DateTime dt, int searchStartIdx) where T : IDateTimeUnit
807
        {
潘栩锋's avatar
潘栩锋 committed
808 809 810 811
            if (list.Count() == 0)
                return -1;

            int max_index = list.Count() - 1;
812 813 814 815 816 817 818 819

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

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


潘栩锋's avatar
潘栩锋 committed
820 821
            int index = searchStartIdx;
            SearchState state = SearchState.ContinueLastTime;
822

潘栩锋's avatar
潘栩锋 committed
823
            if ((index < 0) || (index >= max_index))//重新开始
824
            {
潘栩锋's avatar
潘栩锋 committed
825 826
                index = max_index;
                state = SearchState.Back;
827
            }
潘栩锋's avatar
潘栩锋 committed
828
            else if (state == SearchState.ContinueLastTime)
829
            {
830
                if (list[index].dt <= dt)
潘栩锋's avatar
潘栩锋 committed
831
                {
832 833 834 835 836 837 838
                    //可能找到了
                    //再向正方向看看
                    state = SearchState.ForwToCheck;
                }
                else
                {
                    state = SearchState.Back;
潘栩锋's avatar
潘栩锋 committed
839
                }
840
            }
潘栩锋's avatar
潘栩锋 committed
841

842 843
            if (state == SearchState.ForwToCheck)
            {
潘栩锋's avatar
潘栩锋 committed
844
                while (true)
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
                {
                    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
876
                    if (index < 0)
877 878 879 880 881 882
                    {
                        //不可能发生
                        throw new Exception("之前没有判断出 时间点在列表前,异常!!");
                    }
                }
            }
潘栩锋's avatar
潘栩锋 committed
883 884 885 886
        }



887 888 889 890 891 892 893
        /// <summary>
        /// 输出以 1ms 为间隔 输入口状态 列表
        /// </summary>
        /// <param name="iStatusPool"></param>
        /// <param name="end_dt"></param>
        /// <param name="cnt"></param>
        /// <returns></returns>
潘栩锋's avatar
潘栩锋 committed
894
        public static List<UInt16> GetIStatus(List<DateTimeUnit2> iStatusPool, DateTime end_dt, int cnt) 
潘栩锋's avatar
潘栩锋 committed
895
        {
潘栩锋's avatar
潘栩锋 committed
896 897 898 899 900
            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
901
            {
潘栩锋's avatar
潘栩锋 committed
902
                reponse.Add(GetIStatus(iStatusPool, lastTime, ref searchIdx));
903
                lastTime -= TimeSpan.FromMilliseconds(1);
潘栩锋's avatar
潘栩锋 committed
904
            }
潘栩锋's avatar
潘栩锋 committed
905 906 907
            //反转
            reponse.Reverse();
            return reponse;
潘栩锋's avatar
潘栩锋 committed
908
        }
909

潘栩锋's avatar
潘栩锋 committed
910 911 912 913 914 915 916 917 918
        /// <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
919
        {
潘栩锋's avatar
潘栩锋 committed
920
            int index = searchIndex(iStatusPool, dt, searchIdx);
潘栩锋's avatar
潘栩锋 committed
921

潘栩锋's avatar
潘栩锋 committed
922 923 924 925
            if (index < 0)
                index = 0;
            searchIdx = index;
            return (UInt16)iStatusPool[index].istatus;
潘栩锋's avatar
潘栩锋 committed
926
        }
927

928

潘栩锋's avatar
潘栩锋 committed
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
        /// <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;
946 947
            data0 = new List<Range>();
            data1 = new List<Range>();
潘栩锋's avatar
潘栩锋 committed
948 949 950 951
            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);

952
            DIRECTION direction = endPos > beginPos ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
953

954
            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
955 956 957

            Range r = new Range();

958
            r.Begin = beginPos;
959

潘栩锋's avatar
潘栩锋 committed
960
            for (int i = istatus_search_idx + 1; i < iStatusPool.Count(); i++)
961
            {
潘栩锋's avatar
潘栩锋 committed
962 963 964 965 966 967 968
                istatus = iStatusPool[i].istatus;
                int pos;
                if (isPos1)
                    pos = iStatusPool[i].pos;
                else
                    pos = iStatusPool[i].pos2;

969
                bool b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
潘栩锋's avatar
潘栩锋 committed
970
                if (iStatusPool[i].dt >= endTime)
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
                {
                    //结束,这是最后!!!
                    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;
                    }
                }
            }
990
            r.End = beginPos;
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
            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();
            }
        }
1030

潘栩锋's avatar
潘栩锋 committed
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
        /// <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)
1045
        {
潘栩锋's avatar
潘栩锋 committed
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 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090

            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);


1091 1092 1093
        }


潘栩锋's avatar
潘栩锋 committed
1094 1095

        static void toGrid_step1(int[] grids_sum, int[] grids_cnt, int gridLen, int grid_idx, int ad)
1096
        {
潘栩锋's avatar
潘栩锋 committed
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
            if ((grid_idx < 0) || (grid_idx >= gridLen))
            {
                return;
            }

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

            grids_cnt[grid_idx]++;
            grids_sum[grid_idx] += ad;
1107 1108 1109
        }

        /// <summary>
潘栩锋's avatar
潘栩锋 committed
1110
        /// 输出 grid图。 adList[x-adLag] 对应 posList[x]
1111
        /// </summary>
潘栩锋's avatar
潘栩锋 committed
1112 1113 1114 1115 1116 1117
        /// <param name="adList"></param>
        /// <param name="posList"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridLen"></param>
        /// <param name="adLag"></param>
        /// <returns></returns>
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
        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
1135 1136
            int[] grids_sum = new int[gridLen];
            int[] grids_cnt = new int[gridLen];
1137

潘栩锋's avatar
潘栩锋 committed
1138
            int grid_idx_last = -1;
1139 1140
            grid_start = Misc.MyBase.NULL_VALUE;
            grid_end = Misc.MyBase.NULL_VALUE;
潘栩锋's avatar
潘栩锋 committed
1141 1142 1143 1144 1145 1146
            //grid_idx 应该要连续变化,不是,那就插值
            for (int j = 0; j < adList.Count() && j < posList.Count(); j++)
            {
                int index = j - adLag;
                if (index < 0 || index >= adList.Count())
                    continue;
1147

潘栩锋's avatar
潘栩锋 committed
1148 1149 1150
                int ad = adList[index];
                int pos = posList[j];
                int grid_idx = pos / posOfGrid;
1151 1152 1153
                //grid不能小于0 或 大于等于 gridLen
                if (grid_idx < 0 || grid_idx >= gridLen)
                    continue;
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

                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
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
                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]);
            }

1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
            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
1213 1214 1215 1216 1217
    }
    public class Range_DateTime 
    {
        public DateTime Begin;
        public DateTime End;
1218 1219
    }
}