using Microsoft.AspNetCore.Mvc;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using Siger.ApiQMS.Utility;
using Siger.Middlelayer.QmsRepository.Repositories.Interface;
using Siger.Middlelayer.QmsRepository.Request;
using Siger.Middlelayer.QmsRepository.Response;
using Siger.Middlelayer.Repository.Repositories.Interface;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Repository.Response;
using Siger.Middlelayer.QmsRepository.Entities;
using Siger.Middlelayer.Utility.Helpers;
using Siger.ApiCommon.Utilities;
using Siger.Middlelayer.Common.AppSettings;
using System.IO;
using Siger.Middlelayer.Utility.ImportEntities;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Log;

namespace Siger.ApiQMS.Controllers
{
    public class StatisticalController : BaseController
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly ISigerProjectLevelRepository _levelRepository;
        private readonly ISigerProjectLevelSectionRepository _levelSectionRepository;
        private readonly ISigerProjectProductRepository _productRepository;
        private readonly IProductRouteRepository _routeRepository;
        private readonly ICheckSnTraceInspectionRepository _inspectionRepository;
        private readonly ICheckSnTraceMaterialRepository _traceMaterialRepository;
        private readonly ISigerTrMaterialsRepository _materialsRepository;
        private readonly IInspectStandardRepository _inspectStandardRepository;
        private readonly ICheckSnTraceDetailRepository _traceDetailRepository;
        private readonly IQmsTypeCountSettingRepository _typeCountSettingRepository;
        private readonly IAbnormalRuleRepository _ruleRepository;

        public StatisticalController(IUnitOfWork unitOfWork,
            ISigerProjectLevelRepository levelRepository, ISigerProjectLevelSectionRepository levelSectionRepository,
            ISigerProjectProductRepository productRepository, IProductRouteRepository routeRepository,
            ICheckSnTraceInspectionRepository inspectionRepository, ICheckSnTraceMaterialRepository traceMaterialRepository,
            ISigerTrMaterialsRepository materialsRepository, IInspectStandardRepository inspectStandardRepository,
            ICheckSnTraceDetailRepository traceDetailRepository, IQmsTypeCountSettingRepository typeCountSettingRepository,
            IAbnormalRuleRepository ruleRepository)
        {
            _unitOfWork = unitOfWork;
            _levelRepository = levelRepository;
            _levelSectionRepository = levelSectionRepository;
            _productRepository = productRepository;
            _routeRepository = routeRepository;
            _inspectionRepository = inspectionRepository;
            _traceMaterialRepository = traceMaterialRepository;
            _materialsRepository = materialsRepository;
            _inspectStandardRepository = inspectStandardRepository;
            _traceDetailRepository = traceDetailRepository;
            _typeCountSettingRepository = typeCountSettingRepository;
            _ruleRepository = ruleRepository;
        }

        [HttpPost]
        public IActionResult GetQualifiedRate([FromBody]RequestGetQualifiedRate req)
        {
            if (string.IsNullOrWhiteSpace(req.starttime) || string.IsNullOrWhiteSpace(req.endtime))
            {
                throw new ServerException(200099);
            }
            var res = new ResponseStatisticalChart();
            var axisList = new List<axis>();
            var levelSections = _levelSectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid).ToList();
            var sections = new List<siger_project_level_section>();
            var section = levelSections.FirstOrDefault(t => t.id == req.sectionid.ToInt() && t.status == (int)RowState.Valid);
            if(section == null && req.sectionid.ToInt() > 0)
            {
                throw new ServerException(1950580);
            }
            var maxLevel = _levelRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid)
                .OrderByDescending(t => t.id).FirstOrDefault();
            if (req.x_id.ToInt() == 0)
            {
                var sectionIds = new List<int>();
                if (maxLevel != null && section != null && maxLevel.id == section.levelid)
                {
                    sections.Add(section);
                    sectionIds = sections.Select(m => m.id).ToList();
                }
                else
                {
                    sections = levelSections.Where(t => t.parentid == req.sectionid.ToInt()).ToList();
                    sectionIds = ChannelSectionHelper.GetSonLevelSectionList(req.sectionid.ToInt(), levelSections).Select(m => m.id).ToList();
                }

                var datas = _traceDetailRepository.GetSnTraceList(sectionIds, req.productid.ToInt()
                    , req.materialid.ToInt(), req.checktype.ToInt(), req.starttime, req.endtime, ProjectId).ToList();
                foreach (var sec in sections)
                {
                    var lastSections = ChannelSectionHelper.GetSonLevelSections(sec, levelSections)
                        .Where(t => t.levelid == maxLevel.id).Select(m => m.id).ToList();
                    var data = datas.Where(q => lastSections.Contains(q.SectionID));
                    var ngNum = data.Count(t => t.Result.ToUpper() == "NG");
                    var okNum = data.Count(t => t.Result.ToUpper() == "OK");
                    var total = ngNum + okNum;
                    axisList.Add(new axis
                    {
                        x = sec.title,
                        y1 = Math.Round(total > 0 ? (okNum / (double)total) * 100 : 100, 2),
                        y2 = ngNum
                    });
                }
            }
            else
            {
                var product = _productRepository.Get(t => t.projectid == ProjectId && t.status == (int)RowState.Valid && t.id == req.productid.ToInt());
                var lastSections = section == null ? levelSections.Where(t=>t.levelid == maxLevel.id).ToList() : 
                    ChannelSectionHelper.GetSonLevelSections(section, levelSections).Where(t => t.levelid == maxLevel.id).ToList();
                var lastSectionIds = lastSections.Select(t => t.id).ToList();
                var data = _traceDetailRepository.GetSnTraceList(lastSectionIds, product?.id ?? 0, req.materialid.ToInt(), req.checktype.ToInt(),
                        req.starttime, req.endtime, ProjectId).Where(t => t.RouteID > 0).ToList();
                var routeIds = data.Select(t => t.RouteID).ToList();
                var routes = _routeRepository.GetList(t => t.projectId == ProjectId && t.status == (int)RowState.Valid &&
                    routeIds.Contains(t.id)).ToList();
                foreach(var route in routes)
                {
                    var traces = data.Where(t => t.RouteID == route.id).ToList();
                    var ngNum = traces.Count(t => t.Result.ToUpper() == "NG");
                    var okNum = traces.Count(t => t.Result.ToUpper() == "OK");
                    var total = ngNum + okNum;
                    axisList.Add(new axis
                    {
                        x = route.name,
                        y1 = Math.Round(total > 0 ? (okNum / (double)total) * 100 : 100, 2),
                        y2 = ngNum,
                    });
                }
            }
            var axisList1 = req.top.ToInt() > 0 ? axisList.OrderBy(t => t.y).Take(req.top.ToInt()).ToList() :
                    axisList.OrderBy(t => t.y1).ToList();
            foreach(var axis in axisList1)
            {
                res.X1.Add(axis.x);
                res.Y1.Add(axis.y1);
            }
            var axisList2 = req.top.ToInt() > 0 ? axisList.OrderByDescending(t => t.y).Take(req.top.ToInt()).ToList() :
                    axisList.OrderByDescending(t => t.y2).ToList();
            double totalRate = 0;
            var total1 = axisList2.Sum(t => t.y2);
            int i = 1;
            foreach (var axis in axisList2)
            {
                res.X2.Add(axis.x);
                res.Y2.Add(axis.y2);
                var rate = total1 > 0 ? Math.Round(axis.y2 / (double)total1 * 100, 2) : 0;
                totalRate += rate;

                totalRate = totalRate < 100 ? Math.Round(totalRate, 2) : 100;

                if (i == axisList2.Count)
                {
                    res.Y3.Add(100);
                }
                else
                {
                    res.Y3.Add(totalRate);
                }
                i++;
            }

            return new ObjectResult(res);
        }

        [HttpPost]
        public IActionResult GetTestRoomCheckReport([FromBody]RequestGetTestRoomCheckReport req)
        {
            if (string.IsNullOrWhiteSpace(req.starttime) || string.IsNullOrWhiteSpace(req.endtime))
            {
                throw new ServerException(200099);
            }
            var data = _inspectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.check_status == (int)SendCheckStatus.Completed && t.inspection_type == (int)InspectionType.SendTest);
            if (req.productid.ToInt() > 0)
            {
                data = data.Where(t => t.productid == req.productid.ToInt());
            }
            if (!string.IsNullOrEmpty(req.testroom))
            {
                data = data.Where(t => t.testroom == req.testroom);
            }
            if(req.materialid.ToInt() > 0)
            {
                data = data.Where(t => t.materialid == req.materialid.ToInt());
            }
            if(req.testtype.ToInt() > 0)
            {
                data = data.Where(t => t.check_type == req.testtype.ToInt());
            }
            var stime = req.starttime.ToDateTime();
            var etime = req.endtime.ToDateTime();
            var checkData = data.Where(t => t.send_time <= etime && t.send_time >= stime).ToList();
            var xtype = req.xtype.ToInt();
            var dates = DateTimeHelper.GetDateTimes(req.starttime.ToDateTime(), req.endtime.ToDateTime(), 0);
            var res = new ResponseGetTestRoomCheckReport();
            if(xtype == 1 || xtype == 5 || xtype == 2 || xtype == 4)
            {
                var dateList = new List<DateTime>();
                foreach(var date in dates)
                {
                    dateList.Add(DateTimeHelper.GetTimeStartByType(xtype, date));
                }
                dates = dateList.Distinct().ToList();
            }
            if (dates.Any())
            {
                for (var i = 0; i < dates.Count; i++)
                {
                    var endTime = dates[i];
                    var startTime = dates[i];
                    if (xtype == 1 || xtype == 5 || xtype == 2 || xtype == 4)
                    {
                        startTime = DateTimeHelper.GetTimeStartByType(xtype, dates[i]);
                        endTime = DateTimeHelper.GetTimeEndByType(xtype, dates[i]).AddDays(1).AddMilliseconds(-1);
                        if (startTime < stime)
                        {
                            startTime = stime;
                        }
                        if (endTime > etime)
                        {
                            endTime = etime;
                        }
                    }
                    else
                    {
                        if (i < dates.Count - 1)
                        {
                            endTime = dates[i + 1].AddSeconds(-1);
                        }
                        else
                        {
                            endTime = etime;
                        }
                    }
                    var datas = checkData.Where(t => t.send_time <= endTime && t.send_time >= startTime).ToList();
                    res.qualified.Add(datas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified));
                    res.unqualified.Add(datas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified));
                    res.deviationrelease.Add(datas.Count(t => t.result.ToInt() == (int)SendTestType.DeviationRelease));
                    res.date.Add(DateTimeHelper.GetTimeStrByType(xtype, startTime));
                }               
            }
            else
            {
                res.date.Add(stime.ToString(ParameterConstant.YearMonthFormat));
                var checkDatas = checkData.Where(t => t.send_time <= etime && t.send_time >= stime).ToList();
                res.qualified.Add(checkDatas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified));
                res.unqualified.Add(checkDatas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified));
                res.deviationrelease.Add(checkDatas.Count(t => t.result.ToInt() == (int)SendTestType.DeviationRelease));
            }
            return new ObjectResult(res);
        }

        [HttpPost]
        public IActionResult GetTestRoomQualifiedReport([FromBody]RequestGetTestRoomQualifiedReport req)
        {
            var res = new ResponseGetTestRoomQualifiedReport();
            if (string.IsNullOrWhiteSpace(req.starttime) || string.IsNullOrWhiteSpace(req.endtime))
            {
                throw new ServerException(200099);
            }
            
            var materialIds = new List<int>();
            if (req.materialNameId.ToInt() > 0)
            {
                var material = _materialsRepository.Get(t => t.projectId == ProjectId && t.status == (int)RowState.Valid &&
                t.id == req.materialNameId.ToInt());
                if(material != null)
                {
                    materialIds = _materialsRepository.GetList(t => t.projectId == ProjectId && t.status == (int)RowState.Valid &&
                        t.name == material.name).Select(t => t.id).ToList();
                }                
            }
            var etime = req.endtime.ToDateTime();
            var stime = req.starttime.ToDateTime();
            var traceIds = new List<string>();
            if (req.itemid.ToInt() > 0)
            {
                var item = _inspectStandardRepository.Get(t => t.id == req.itemid.ToInt() && t.projectid == ProjectId &&
                    t.status == (int)RowState.Valid);
                if(item == null)
                {
                    throw new BadRequestException(CommonEnum.RecordNotFound);
                }
                var items = _inspectStandardRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                    t.item == item.item).Select(t => t.id).ToList();
                if (!items.Any())
                {
                    throw new BadRequestException(CommonEnum.RecordNotFound);
                }
                traceIds = _traceDetailRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                    t.CreateTime <= req.endtime.ToDateTime() && t.CreateTime >= req.starttime.ToDateTime() && items.Contains(t.ItemID)).
                    Select(t => t.TraceID).ToList();
                if (!traceIds.Any())
                {
                    throw new BadRequestException(CommonEnum.RecordNotFound);
                }
            }
            var data = _inspectionRepository.GetList(t => t.projectid == ProjectId &&
                t.status == (int)RowState.Valid && t.send_time <= etime && t.send_time >= stime && t.inspection_type == (int)InspectionType.SendTest);
            if(req.productid.ToInt() > 0)
            {
                data = data.Where(t => t.productid == req.productid.ToInt());
            }
            if(req.materialid.ToInt() > 0)
            {
                data = data.Where(t => t.materialid == req.materialid.ToInt());
            }
            if (!string.IsNullOrEmpty(req.testroom))
            {
                data = data.Where(t => t.testroom == req.testroom);
            }
            if (traceIds.Any())
            {
                data = data.Where(t => traceIds.Contains(t.trace_id));
            }
            if (req.materialNameId.ToInt() > 0)
            {
                data = data.Where(t => materialIds.Contains(t.materialid));
            }
            var checkData = data.ToList();
            var factorys = GetSectionSelect(3, 0);//取工厂            
            var dates = DateTimeHelper.GetDateTimes(req.starttime.ToDateTime(), req.endtime.ToDateTime(), 0);
            if (req.x_type == 1 || req.x_type == 2 || req.x_type == 4 || req.x_type == 5)
            {
                var dateList = new List<DateTime>();
                foreach (var date in dates)
                {
                    dateList.Add(DateTimeHelper.GetTimeStartByType(req.x_type, date));
                }
                dates = dateList.Distinct().ToList();
            }
            foreach (var factory in factorys)
            {
                var levelSections = new List<siger_project_level_section>();
                var factoryData = new FactoryTestRoomData
                {
                    factory = factory.name
                };
                var channels = GetSectionSelect(2, factory.id);//取通道
                foreach(var channel in channels)
                {
                    var channelData = new ChannelTestRoomData
                    {
                        channel = channel.name
                    };
                    var sections = _levelSectionRepository.GetList(t => t.projectid == ProjectId && t.parentid == channel.id &&
                        t.status == (int)RowState.Valid).ToList();
                    levelSections.AddRange(sections);
                    var checkDatas = checkData.Where(t => sections.Select(q => q.id).Contains(t.sectionid)).ToList();
                    AddChannelData(dates, stime, etime, checkDatas, channelData, req.x_type);
                    factoryData.ChannelData.Add(channelData);
                }
                var factoryCheckData = checkData.Where(q => levelSections.Select(t => t.id).Contains(q.sectionid)).ToList();
                AddFactoryData(dates, stime, etime, factoryCheckData, factoryData, req.x_type);
                res.FactoryData.Add(factoryData);
            }

            return new ObjectResult(res);
        }

        private void AddChannelData(List<DateTime> dates, DateTime stime, DateTime etime, List<siger_check_sn_trace_inspection> checkDatas, ChannelTestRoomData channelData, int xtype)
        {
            if (dates.Any())
            {
                for (var i = 0; i < dates.Count; i++)
                {
                    var endTime = dates[i];
                    var startTime = dates[i];
                    if (xtype == 1 || xtype == 2 || xtype == 4 || xtype == 5)
                    {
                        startTime = DateTimeHelper.GetTimeStartByType(xtype, dates[i]);
                        endTime = DateTimeHelper.GetTimeEndByType(xtype, dates[i]).AddDays(1).AddMilliseconds(-1);
                        if(startTime < stime)
                        {
                            startTime = stime;
                        }
                        if(endTime > etime)
                        {
                            endTime = etime;
                        }
                    }
                    else
                    {
                        if (i < dates.Count - 1)
                        {
                            endTime = dates[i + 1].AddSeconds(-1);
                        }
                        else
                        {
                            endTime = etime;
                        }
                    }                                        
                    var datas = checkDatas.Where(t => t.send_time <= endTime && t.send_time >= startTime).ToList();
                    var total = datas.Count;
                    var qualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified ||
                        t.result.ToInt() == (int)SendTestType.DeviationRelease);
                    var unQualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified);
                    channelData.x.Add(DateTimeHelper.GetTimeStrByType(xtype, startTime));
                    channelData.y.Add(qualifiedNum);
                    channelData.y1.Add(unQualifiedNum);
                    channelData.y2.Add(total > 0 ? Math.Round(qualifiedNum / (double)total * 100, 2) : 100);
                }
            }
            else
            {
                channelData.x.Add(stime.ToString(ParameterConstant.YearMonthFormat));
                var datas = checkDatas.Where(t => t.send_time <= etime && t.send_time >= stime).ToList();                
                var qualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified ||
                    t.result.ToInt() == (int)SendTestType.DeviationRelease);
                var unQualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified);
                var total = qualifiedNum + unQualifiedNum;
                channelData.y.Add(qualifiedNum);
                channelData.y1.Add(unQualifiedNum);
                channelData.y2.Add(total > 0 ? Math.Round(qualifiedNum / (double)total * 100, 2) : 100);
            }
        }
        private void AddFactoryData(List<DateTime> dates, DateTime stime, DateTime etime, List<siger_check_sn_trace_inspection> factoryCheckData, FactoryTestRoomData factoryData, int xtype)
        {
            if (dates.Any())
            {
                for (var i = 0; i < dates.Count; i++)
                {
                    var endTime = dates[i];
                    var startTime = dates[i];
                    if (xtype == 1 || xtype == 2 || xtype == 4 || xtype == 5)
                    {
                        startTime = DateTimeHelper.GetTimeStartByType(xtype, dates[i]);
                        endTime = DateTimeHelper.GetTimeEndByType(xtype, dates[i]).AddDays(1).AddMilliseconds(-1);
                        if (startTime < stime)
                        {
                            startTime = stime;
                        }
                        if (endTime > etime)
                        {
                            endTime = etime;
                        }
                    }
                    else
                    {
                        if (i < dates.Count - 1)
                        {
                            endTime = dates[i + 1].AddSeconds(-1);
                        }
                        else
                        {
                            endTime = etime;
                        }
                    }
                    var datas = factoryCheckData.Where(t => t.send_time <= endTime && t.send_time >= startTime).ToList();
                    var total = datas.Count;
                    var qualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified ||
                        t.result.ToInt() == (int)SendTestType.DeviationRelease);
                    var unQualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified);
                    factoryData.x.Add(DateTimeHelper.GetTimeStrByType(xtype, startTime));
                    factoryData.y.Add(qualifiedNum);
                    factoryData.y1.Add(unQualifiedNum);
                    factoryData.y2.Add(total > 0 ? Math.Round(qualifiedNum / (double)total * 100, 2) : 100);
                }
            }
            else
            {
                factoryData.x.Add(stime.ToString(ParameterConstant.YearMonthFormat));
                var datas = factoryCheckData.Where(t => t.send_time <= etime && t.send_time >= stime).ToList();                
                var qualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Qalified ||
                    t.result.ToInt() == (int)SendTestType.DeviationRelease);
                var unQualifiedNum = datas.Count(t => t.result.ToInt() == (int)SendTestType.Unqualified);
                var total = qualifiedNum + unQualifiedNum;
                factoryData.y.Add(qualifiedNum);
                factoryData.y1.Add(unQualifiedNum);
                factoryData.y2.Add(total > 0 ? Math.Round(qualifiedNum / (double)total * 100, 2) : 100);
            }
        }

        private List<ResponseIdName> GetSectionSelect(int reverselevel, int parentid)
        {
            var levels = _levelRepository.GetList(t => t.status == (int)RowState.Valid &&
                t.projectid == ProjectId).OrderByDescending(t => t.id).ToList();
            if (levels.Count < reverselevel)
            {
                throw new BadRequestException(RequestEnum.LevelNotFound);
            }
            var res = _levelSectionRepository.GetSectionIdNamesByLevel(levels[reverselevel - 1].id,
                    parentid, ProjectId).ToList();
            return res;
        }

        [HttpPost]
        public IActionResult GetConcessionReceptionReport([FromBody]RequestGetConcessionReceptionReport req)
        {
            var res = new ResponseGetConcessionReceptionReport();
            if (string.IsNullOrWhiteSpace(req.starttime) || string.IsNullOrWhiteSpace(req.endtime))
            {
                throw new ServerException(200099);
            }

            var materialNameIds = new List<int>();
            if (req.materialNameId.ToInt() > 0)
            {
                var material = _materialsRepository.Get(t => t.projectId == ProjectId && t.status == (int)RowState.Valid && t.id == req.materialNameId.ToInt());
                if (material != null)
                {
                    materialNameIds = _materialsRepository.GetList(t => t.projectId == ProjectId && t.status == (int)RowState.Valid && t.name == material.name).
                        Select(t => t.id).ToList();
                }
            }

            var stime = req.starttime.ToDateTime();
            var etime = req.endtime.ToDateTime();
            var data = _traceMaterialRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.CreateTime <= etime && t.CreateTime >= stime && t.Result.ToInt() == (int)IncomingMaterialType.ConcessionReception);
            if(req.materialid.ToInt() > 0)
            {
                data = data.Where(t => t.MaterialID == req.materialid.ToInt());
            }
            if(req.supplierid.ToInt() > 0)
            {
                data = data.Where(t => t.SupplierID == req.supplierid.ToInt());
            }
            if (req.materialNameId.ToInt() > 0)
            {
                data = data.Where(t => materialNameIds.Contains(t.MaterialID));
            }

            var axisList = new List<ImcomingAxis>();
            var levelSections = _levelSectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid).ToList();
            var sections = new List<siger_project_level_section>();
            var section = levelSections.FirstOrDefault(t => t.id == req.sectionid.ToInt() && t.status == (int)RowState.Valid);
            if (section == null && req.sectionid.ToInt() > 0)
            {
                throw new ServerException(1950580);
            }
            var maxLevel = _levelRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid)
                    .OrderByDescending(t => t.id).FirstOrDefault();
            if (maxLevel != null && section != null && maxLevel.id == section.levelid)
            {
                sections.Add(section);
            }
            else if(section != null)
            {
                sections = ChannelSectionHelper.GetSonLevelSections(section, levelSections).Where(t => t.levelid == maxLevel.id).ToList();
            }
            else
            {
                sections = levelSections.Where(t => t.levelid == maxLevel.id).ToList();
            }
            var checkDatas = data.Where(t => sections.Select(q => q.id).Contains(t.SectionID)).ToList();
            var materialIds = checkDatas.Select(t => t.MaterialID).Distinct().ToList();
            var materials = _materialsRepository.GetList(t => t.projectId == ProjectId && t.status == (int)RowState.Valid && 
                materialIds.Contains(t.id)).ToList();
            foreach (var materialId in materialIds)
            {
                var material = materials.FirstOrDefault(t => t.id == materialId);
                if(material  == null)
                {
                    continue;
                }
                var checkData = checkDatas.Where(t => t.MaterialID == materialId).ToList();
                axisList.Add(new ImcomingAxis
                {
                    x = material.pn,
                    y1 = checkData.Sum(t => t.TotalQuantity),
                    y2 = checkData.Count,
                });
            }
            var materialNumList = req.top.ToInt() > 0 ? axisList.OrderByDescending(t => t.y1).Take(req.top.ToInt()).ToList() :
                    axisList.OrderByDescending(t => t.y1).ToList();
            foreach (var axis in materialNumList)
            {
                res.X1.Add(axis.x);
                res.Y1.Add(axis.y1);
            }
            var receptionNumList = req.top.ToInt() > 0 ? axisList.OrderByDescending(t => t.y2).Take(req.top.ToInt()).ToList() :
                    axisList.OrderByDescending(t => t.y2).ToList();
            double totalRate = 0;
            var total = receptionNumList.Sum(t => t.y2);
            int i = 1;
            foreach(var axis in receptionNumList)
            {
                res.X2.Add(axis.x);
                res.Y2.Add(axis.y2);
                var rate = total > 0 ? Math.Round(axis.y2 / (double)total * 100, 2) : 0;
                totalRate += rate;
                totalRate = totalRate < 100 ? Math.Round(totalRate, 2) : 100;

                if (i == receptionNumList.Count)
                {
                    res.Y3.Add(100);
                }
                else
                {
                    res.Y3.Add(totalRate);
                }
                i++;
            }

            return new ObjectResult(res);
        }

        [HttpPost]
        public IActionResult GetIncomingCheckType([FromBody]RequestGetIncomingChekType req)
        {
            if(req.sectionid.ToInt() == 0)
            {
                throw new ServerException(1950580);
            }

            if(req.materialid.ToInt() == 0)
            {
                throw new ServerException(1950603);
            }

            if (string.IsNullOrWhiteSpace(req.starttime) || string.IsNullOrWhiteSpace(req.endtime))
            {
                throw new ServerException(200099);
            }

            var res = new ReponseGetIncomingChekType();

            var data = _traceMaterialRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.SectionID == req.sectionid.ToInt() && t.MaterialID == req.materialid.ToInt() &&
                t.CreateTime <= req.endtime.ToDateTime() && t.CreateTime >= req.starttime.ToDateTime()).ToList();

            res.QalifiedNum = data.Count(t => t.Result.ToInt() == (int)IncomingMaterialType.Qalified);
            res.ReworkNum = data.Count(t => t.Result.ToInt() == (int)IncomingMaterialType.Rework);
            res.ConcessionReceptionNum = data.Count(t => t.Result.ToInt() == (int)IncomingMaterialType.ConcessionReception);
            res.RejectionNum = data.Count(t => t.Result.ToInt() == (int)IncomingMaterialType.Rejection);
            double total = res.QalifiedNum + res.RejectionNum + res.ConcessionReceptionNum + res.RejectionNum;
            res.QalifiedRate = total > 0 ? Math.Round(res.QalifiedNum / total * 100, 2) : 0;
            res.ReworkRate = total > 0 ? Math.Round(res.ReworkNum / total * 100, 2) : 0;
            res.ConcessionReceptionRate = total > 0 ? Math.Round(res.ConcessionReceptionNum / total * 100, 2) : 0;
            res.RejectionRate = total > 0 ? Math.Round(res.RejectionNum / total * 100, 2) : 0;

            return new ObjectResult(res);
        }

        [HttpPost]
        public IActionResult GetDeviationReleaseQuantity([FromBody]RequestGetDeviationReleaseQuantity req)
        {
            var datas = _inspectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.result == ((int)SendTestType.DeviationRelease).ToString());
            if (req.productid.ToInt() > 0)
            {
                datas = datas.Where(t => t.productid == req.productid.ToInt());
            }
            var levelIds = _levelRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid)
                .OrderByDescending(t => t.id).Select(t => t.id).ToList();
            if (!levelIds.Any())
            {
                throw new BadRequestException(RequestEnum.LevelNotFound);
            }
            var levelSections = _levelSectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid).ToList();            
            var section = levelSections.FirstOrDefault(t => t.id == req.sectionid.ToInt() && t.status == (int)RowState.Valid);
            var lastSections = section == null ? levelSections.Where(t => t.levelid == levelIds.First()).ToList() :
                    ChannelSectionHelper.GetSonLevelSections(section, levelSections).Where(t => t.levelid == levelIds.First()).ToList();
            var lastSectionIds = lastSections.Select(t => t.id).ToList();
            if (section == null && req.sectionid.ToInt() > 0)
            {
                throw new ServerException(1950580);
            }
            if (req.sectionid.ToInt() > 0)
            {
                datas = datas.Where(t => lastSectionIds.Contains(t.sectionid));
            }
            if (!string.IsNullOrEmpty(req.starttime) && !string.IsNullOrEmpty(req.endtime))
            {
                datas = datas.Where(t => t.check_time <= req.endtime.ToDateTime() && t.check_time >= req.starttime.ToDateTime());
            }

            var dataList = datas.ToList();

            var res = new ReponseGetDeviationReleaseQuantity();

            var sections = new List<siger_project_level_section>();
            
            if(section == null)
            {
                var topSection = levelSections.FirstOrDefault(t => t.parentid == 0);
                sections = levelSections.Where(t => t.parentid == (topSection?.id ?? 0)).ToList();
            }
            else if(section.levelid != levelIds.First())
            {
                sections = levelSections.Where(t => t.parentid == section.id).ToList();
            }
            else
            {
                sections.Add(section);
            }
            var quantityTotal = 0;
            var batchTotal = 0;
            foreach (var _Section in sections)
            {
                var sonLastSectionIds = ChannelSectionHelper.GetSonLevelSections(_Section, levelSections).
                    Where(t => t.levelid == levelIds.First()).Select(t => t.id).ToList();
                var quantity = dataList.Where(t => sonLastSectionIds.Contains(t.sectionid)).Sum(t => t.quantity);
                quantityTotal += quantity;
                var batchQuantity = dataList.Count(t => sonLastSectionIds.Contains(t.sectionid));
                batchTotal += batchQuantity;
                res.datalist.Add(new DeviationReleaseQuantity
                {
                    quantity = quantity,
                    batch_quantity = batchQuantity,
                    channel = _Section.title
                });
            }
            var quantityList = new List<DeviationReleaseQuantity>();
            res.datalist = res.datalist.OrderByDescending(t => t.quantity).ToList();
            var total = 0;
            foreach(var data in res.datalist)
            {
                res.x1.Add(data.channel);                
                res.y1.Add(data.quantity);
                total += data.quantity;
                res.z1.Add(quantityTotal > 0 ? Math.Round(total / (double)quantityTotal * 100, 2) : 100);
                quantityList.Add(new DeviationReleaseQuantity
                {
                    quantity = data.quantity,
                    batch_quantity = data.batch_quantity,
                    channel = data.channel,
                });
            }
            res.datalist = res.datalist.OrderByDescending(t => t.batch_quantity).ToList();
            total = 0;
            foreach (var data in res.datalist)
            {
                res.x2.Add(data.channel);
                res.y2.Add(data.batch_quantity);
                total += data.batch_quantity;
                res.z2.Add(batchTotal > 0 ? Math.Round(total / (double)batchTotal * 100, 2) : 100);
            }
            res.datalist = quantityList;

            if(req.toexcel.ToInt() > 0)
            {
                return DeviationReleaseQuantityExportExcel(res.datalist);
            }
            return new ObjectResult(res);
        }

        private IActionResult DeviationReleaseQuantityExportExcel(List<DeviationReleaseQuantity> data)
        {
            var rootDir = FileSystemHelper.GetPhysicalFolders(FileSystemHelper.CommonFileSetting.PhysicalFolder, FileSystemHelper.ExportFileName);

            if (!data.Any())
            {
                throw new BadRequestException(CommonEnum.RecordNotFound);
            }
            var dataList = new List<DeviationReleaseQuantityList>();
            int index = 1;
            foreach (var item in data)
            {
                var model = Mapper<DeviationReleaseQuantity, DeviationReleaseQuantityList>.Map(item);
                model.index = index;
                dataList.Add(model);
                index++;
            }
            if (dataList.Any())
            {
                EpPlusExcelHelper<DeviationReleaseQuantityList> helper = null;
                try
                {
                    helper = new EpPlusExcelHelper<DeviationReleaseQuantityList>();
                    var temporaryFileName = $"偏差放行统计_DeviationReleaseQuantity_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
                    helper.GenerateExcel(dataList, Path.Combine(rootDir, temporaryFileName));
                    return new ObjectResult($"{FileSystemHelper.CommonFileSetting.RequestPath}/{FileSystemHelper.ExportFileName}/{temporaryFileName}");
                }
                catch (Exception e)
                {
                    Logger.WriteLineError("Export DeviationReleaseQuantity Data failed, error:" + e);
                    throw new BadRequestException(RequestEnum.ExportFailed);
                }
                finally
                {
                    helper?.Dispose();
                }
            }

            throw new BadRequestException(CommonEnum.Fail);
        }

        [HttpPost]
        public IActionResult GetSendTestInspectionTime([FromBody]RequestGetSendTestInspectionTime req)
        {
            var datas = _inspectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.check_status == (int)SendCheckStatus.Completed && t.inspection_type == (int)InspectionType.SendTest &&
                t.check_time.HasValue && t.check_time != DateTime.MinValue);
            if (req.productid.ToInt() > 0)
            {
                datas = datas.Where(t => t.productid == req.productid.ToInt());
            }
            var levelIds = _levelRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid)
                .OrderByDescending(t => t.id).Select(t => t.id).ToList();
            if (!levelIds.Any())
            {
                throw new BadRequestException(RequestEnum.LevelNotFound);
            }
            var levelSections = _levelSectionRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid).ToList();
            var section = levelSections.FirstOrDefault(t => t.id == req.sectionid.ToInt() && t.status == (int)RowState.Valid);
            var lastSections = section == null ? levelSections.Where(t => t.levelid == levelIds.First()).ToList() :
                    ChannelSectionHelper.GetSonLevelSections(section, levelSections).Where(t => t.levelid == levelIds.First()).ToList();
            var lastSectionIds = lastSections.Select(t => t.id).ToList();
            if (section == null && req.sectionid.ToInt() > 0)
            {
                throw new ServerException(1950580);
            }
            if (req.sectionid.ToInt() > 0)
            {
                datas = datas.Where(t => lastSectionIds.Contains(t.sectionid));
            }
            if (req.materialid.ToInt() > 0)
            {
                datas = datas.Where(t => t.materialid == req.materialid.ToInt());
            }
            if (!string.IsNullOrEmpty(req.starttime) && !string.IsNullOrEmpty(req.endtime))
            {
                datas = datas.Where(t => t.check_time <= req.endtime.ToDateTime() && t.check_time >= req.starttime.ToDateTime());
            }

            var dataList = datas.ToList();

            var res = new ReponseGetSendTestInspectionTime();

            var sections = new List<siger_project_level_section>();

            switch (req.x_type.ToInt())
            {
                case 0:
                    if (section == null)
                    {
                        var topSection = levelSections.FirstOrDefault(t => t.parentid == 0);
                        sections = levelSections.Where(t => t.parentid == (topSection?.id ?? 0)).ToList();
                    }
                    else if (section.levelid != levelIds.First())
                    {
                        sections = levelSections.Where(t => t.parentid == section.id).ToList();
                    }
                    else
                    {
                        sections.Add(section);
                    }
                    foreach (var _Section in sections)
                    {
                        var sonLastSectionIds = ChannelSectionHelper.GetSonLevelSections(_Section, levelSections).
                            Where(t => t.levelid == levelIds.First()).Select(t => t.id).ToList();

                        var list = dataList.Where(t => sonLastSectionIds.Contains(t.sectionid));
                        var list1 = list.Where(t => t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue);
                        var list2 = list.Where(t => !(t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue));
                        res.datalist.Add(new SendTestInspectionTime
                        {
                            section = _Section.title,
                            time = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes), 2) : 0,
                            time1 = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes), 2) : 0,
                            time2 = Math.Round(req.type.ToInt() == 0 ?
                                (list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes)) / list.Sum(t => t.number) :
                                list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes), 2)
                        });
                    }
                    break;
                case 1:
                    foreach (var _Section in lastSections)
                    {
                        var list = dataList.Where(t => t.sectionid == _Section.id);
                        var list1 = list.Where(t => t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue);
                        var list2 = list.Where(t => !(t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue));
                        res.datalist.Add(new SendTestInspectionTime
                        {
                            section = _Section.title,
                            time = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes), 2) : 0,
                            time1 = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes), 2) : 0,
                            time2 = Math.Round(req.type.ToInt() == 0 ?
                                (list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes)) / list.Sum(t => t.number) :
                                list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes), 2)
                        });
                    }
                    break;
                case 2:
                    var productIds = dataList.Select(t => t.productid).ToList();
                    var products = _productRepository.GetList(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                        productIds.Contains(t.id)).ToList();
                    foreach(var product in products)
                    {
                        var list = dataList.Where(t => t.productid == product.id);
                        var list1 = list.Where(t => t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue);
                        var list2 = list.Where(t => !(t.recieve_time.HasValue && t.recieve_time != DateTime.MinValue &&
                            t.checking_time.HasValue && t.checking_time != DateTime.MinValue));
                        res.datalist.Add(new SendTestInspectionTime
                        {
                            section = product.name,
                            time = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.recieve_time.Value - t.send_time).TotalMinutes), 2) : 0,
                            time1 = list1.Any() ? Math.Round(req.type.ToInt() == 0 ?
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes) / list1.Sum(t => t.number) :
                                list1.Sum(t => (t.checking_time.Value - t.recieve_time.Value).TotalMinutes), 2) : 0,
                            time2 = Math.Round(req.type.ToInt() == 0 ?
                                (list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes)) / list.Sum(t => t.number) :
                                list1.Sum(t => (t.check_time.Value - t.checking_time.Value).TotalMinutes) +
                                list2.Sum(t => (t.check_time.Value - t.send_time).TotalMinutes), 2)
                        });
                    }
                    break;
            }            

            var timeList = new List<SendTestInspectionTime>();
            var timeDataList = res.datalist.OrderBy(t => t.time2).ToList();
            foreach (var data in timeDataList)
            {
                var checkTime = data.time2 >= 0 ? data.time2 : 0;
                if(checkTime <= 0)
                {
                    continue;
                }
                timeList.Add(new SendTestInspectionTime
                {
                    section = data.section,
                    time = data.time >= 0 ? data.time : 0,
                    time1 = data.time1 >= 0 ? data.time1 : 0,
                    time2 = checkTime
                });
                res.x.Add(data.section);
                res.y.Add(data.time);
                res.y1.Add(data.time1);
                res.y2.Add(checkTime);
            }
            res.datalist = timeList.OrderByDescending(t => t.time2 + t.time1 + t.time).ToList();

            return new ObjectResult(res);
        }

        /// <summary>
        /// SPC实时
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult GetSPCMonitor([FromBody]RequestGetSPCMonitor req)
        {
            var res = new ResponseGetSPCMonitor();
            if (req.sectionid.ToInt() <= 0 || req.productid.ToInt() <= 0 || req.itemid.ToInt() <= 0
                || req.checktype.ToInt() <= 0)
            {
                throw new BadRequestException(RequestEnum.ParameterMiss);
            }

            var checkTypeCount = _typeCountSettingRepository.Get(t => t.type_code == req.checktype.ToInt() && t.projectid == ProjectId &&
                t.status == (int)RowState.Valid);
            if(checkTypeCount == null)
            {
                throw new BadRequestException(RequestEnum.ParameterError);
            }

            var item = _inspectStandardRepository.Get(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.id == req.itemid.ToInt());
            if(item == null || !item.ucl.HasValue || !item.lcl.HasValue)
            {
                throw new BadRequestException(RequestEnum.ParameterError);
            }

            var traceData = _inspectionRepository.GetMonitorTraceDetails(req.sectionid.ToInt(), req.productid.ToInt(),
                req.itemid.ToInt(), req.checktype.ToInt(), req.starttime, req.endtime, ProjectId).Where(t => t.Value.HasValue)
                .OrderBy(t => t.CreateTime).OrderBy(t => t.NumberIndex).OrderBy(t => t.ID)
                .TakeLast(100).Select(t => t.Value.Value).ToList();

            var ucl = item.ucl.Value;
            var lcl = item.lcl.Value;
            res.checktype_count = checkTypeCount.check_count;
            if (checkTypeCount.check_count == 1)//I-MR
            {                
                GetMonitorData(traceData, ucl, lcl, 3, res);
                GetMonitorData(traceData, ucl, lcl, 4, res);
            }
            else if(checkTypeCount.check_count > 1)//X-Bar
            {
                GetMonitorData(traceData, ucl, lcl, 1, res);
                GetMonitorData(traceData, ucl, lcl, 2, res);
            }

            var spcdatas = _inspectionRepository.GetMonitorTraceDetails(req.sectionid.ToInt(), req.productid.ToInt(),
                req.itemid.ToInt(), req.checktype.ToInt(), req.starttime, req.endtime, ProjectId).Where(t => t.Value.HasValue)
                .OrderBy(t => t.CreateTime).OrderBy(t => t.NumberIndex).OrderBy(t => t.ID).TakeLast(10).ToList();
            foreach(var spcdata in spcdatas)
            {
                res.spcdata.Add(new SpcData
                {
                    value = spcdata.Value.Value,
                    isexception = string.IsNullOrWhiteSpace(spcdata.abnomal_status) ? 0 : 1
                });
            }

            var abnormalData = _inspectionRepository.GetMonitorTraceDetails(req.sectionid.ToInt(), req.productid.ToInt(),
                req.itemid.ToInt(), req.checktype.ToInt(), req.starttime, req.endtime, ProjectId).Where(t => !string.IsNullOrEmpty(t.abnomal_status) && t.Value.HasValue)
                .OrderBy(t => t.CreateTime).OrderBy(t => t.NumberIndex).OrderBy(t => t.ID).TakeLast(5).ToList();

            var rules = _ruleRepository.GetList(t => t.projectid == ProjectId && 
                t.status == (int)RowState.Valid).ToList();
            foreach(var abormal in abnormalData)
            {
                var rule = rules.Where(t => abormal.abnomal_status.Contains(t.key.ToString())).ToList();
                var ruleNames = string.Empty;
                if (rule.Any())
                {
                    for(var x=0;x< rule.Count; x++)
                    {
                        ruleNames += $"({x + 1}).{rule[x].rule};";
                    }
                }
                res.abnormalDatas.Add(new AbnormalData
                {
                    value = abormal.Value.Value,
                    rule = ruleNames,
                    time = abormal.CreateTime.ToString(ParameterConstant.DateTimeFormat)
                });
            }

            if (traceData.Any())
            {
                res.SampleSize = traceData.Count.ToString();
                var mean = Math.Round(traceData.Average(), 4);
                res.SampleMean = mean.ToString();
                var std = Math.Round(StdCount(traceData, mean), 4);                
                res.SampleStd = std.ToString();
                res.UpperLimit = item.ucl.HasValue ? item.ucl.Value.ToString() : "";
                res.LowerLimit = item.lcl.HasValue ? item.lcl.Value.ToString() : "";
                var hasValue = item.lcl.HasValue && item.ucl.HasValue;
                res.MidLine = hasValue ? Math.Round((item.lcl.Value + item.ucl.Value) / 2, 4).ToString() : "";
                res.Cp = hasValue ? Math.Round((item.ucl.Value - item.lcl.Value) / (6 * std), 4).ToString() : "";
                var cpu = hasValue ? Math.Round((item.ucl.Value - mean) / (3 * std), 4) : 0;
                var cpl = hasValue ? Math.Round((mean - item.lcl.Value) / (3 * std), 4) : 0;
                res.Cpk = hasValue ? Math.Min(cpu, cpl).ToString() : "";
                res.Cpu = hasValue ? cpu.ToString() : "";
                res.Cpl = hasValue ? cpl.ToString() : "";
                res.USL = (item.max_value.ToString() == QmsLimitValue.MaxValue) ? "" : item.max_value.ToString();
                res.LSL = (item.min_value.ToString() == QmsLimitValue.MinValue) ? "" : item.min_value.ToString();

                GetGroupCount(traceData, ucl, lcl, res);
            }

            var stime = DateTime.Now.ToString(ParameterConstant.MouthFirstDay);
            var etime = DateTime.Now.ToString(ParameterConstant.DateTimeFormat);
            var mouthData = _inspectionRepository.GetMonitorTraceDetails(req.sectionid.ToInt(), req.productid.ToInt(),
                req.itemid.ToInt(), req.checktype.ToInt(), stime, etime, ProjectId).Where(t => t.Value.HasValue)
                .OrderBy(t => t.CreateTime).OrderBy(t => t.NumberIndex).OrderBy(t => t.ID).Select(t => t.Value.Value).ToList();
            if (mouthData.Any())
            {
                var mean = Math.Round(mouthData.Average(), 4);
                var std = Math.Round(StdCount(mouthData, mean), 4);
                var hasValue = item.lcl.HasValue && item.ucl.HasValue;
                res.Cp_mouth = hasValue ? Math.Round((item.ucl.Value - item.lcl.Value) / (6 * std), 4).ToString() : "";
                var cpu = hasValue ? Math.Round((item.ucl.Value - mean) / (3 * std), 4) : 0;
                var cpl = hasValue ? Math.Round((mean - item.lcl.Value) / (3 * std), 4) : 0;
                res.Cpk_mouth = hasValue ? Math.Min(cpu, cpl).ToString() : "";
                res.Cpu_mouth = hasValue ? cpu.ToString() : "";
                res.Cpl_mouth = hasValue ? cpl.ToString() : "";
            }

            return new ObjectResult(res);
        }

        [HttpPost]
        public IActionResult GetSPCMonitoring([FromBody] RequestGetSPCMonitoring req)
        {
            var res = new ResponseGetSPCMonitor();
            if (req.sectionid.ToInt() <= 0 || req.itemid.ToInt() <= 0||req.checktype.ToInt()<=0 )
            {
                throw new BadRequestException(RequestEnum.ParameterMiss);
            }

            var item = _inspectStandardRepository.Get(t => t.projectid == ProjectId && t.status == (int)RowState.Valid &&
                t.id==req.itemid.ToInt());
            if (item == null || !item.ucl.HasValue || !item.lcl.HasValue)
            {
                throw new BadRequestException(RequestEnum.ParameterError);
            }

            var traceData = _inspectionRepository.GetSnTraceDetails(req.sectionid.ToInt(), req.productid,
                req.itemid, req.starttime, req.endtime, ProjectId).Where(t => t.Value.ToDouble() != 0)
                .OrderBy(t => t.TransDateTime).OrderBy(t => t.id)
                .TakeLast(100).Select(t => t.Value.ToDouble()).ToList();

            var ucl = item.ucl.Value;
            var lcl = item.lcl.Value;

            GetMonitorData(traceData, ucl, lcl, 1, res);
            GetMonitorData(traceData, ucl, lcl, 2, res);


            if (traceData.Any())
            {
                res.SampleSize = traceData.Count.ToString();
                var mean = Math.Round(traceData.Average(), 4);
                res.SampleMean = mean.ToString();
                var std = Math.Round(StdCount(traceData, mean), 4);
                res.SampleStd = std.ToString();
                res.UpperLimit = item.ucl.HasValue ? item.ucl.Value.ToString() : "";
                res.LowerLimit = item.lcl.HasValue ? item.lcl.Value.ToString() : "";
                var hasValue = item.lcl.HasValue && item.ucl.HasValue;
                res.MidLine = hasValue ? Math.Round((item.lcl.Value + item.ucl.Value) / 2, 4).ToString() : "";
                res.Cp = hasValue ? Math.Round((item.ucl.Value - item.lcl.Value) / (6 * std), 4).ToString() : "";
                var cpu = hasValue ? Math.Round((item.ucl.Value - mean) / (3 * std), 4) : 0;
                var cpl = hasValue ? Math.Round((mean - item.lcl.Value) / (3 * std), 4) : 0;
                res.Cpk = hasValue ? Math.Min(cpu, cpl).ToString() : "";
                res.Cpu = hasValue ? cpu.ToString() : "";
                res.Cpl = hasValue ? cpl.ToString() : "";
                res.USL = (item.max_value.ToString() == QmsLimitValue.MaxValue) ? "" : item.max_value.ToString();
                res.LSL = (item.min_value.ToString() == QmsLimitValue.MinValue) ? "" : item.min_value.ToString();

                GetGroupCount(traceData, ucl, lcl, res);
            }

            var stime = DateTime.Now.ToString(ParameterConstant.MouthFirstDay);
            var etime = DateTime.Now.ToString(ParameterConstant.DateTimeFormat);
            var mouthData = _inspectionRepository.GetSnTraceDetails(req.sectionid.ToInt(), req.productid,
                req.itemid, stime, etime, ProjectId).Where(t => t.Value.ToDouble()!=0)
                .OrderBy(t => t.TransDateTime).OrderBy(t => t.id).Select(t => t.Value.ToDouble()).ToList();
            if (mouthData.Any())
            {
                var mean = Math.Round(mouthData.Average(), 4);
                var std = Math.Round(StdCount(mouthData, mean), 4);
                var hasValue = item.lcl.HasValue && item.ucl.HasValue;
                res.Cp_mouth = hasValue ? Math.Round((item.ucl.Value - item.lcl.Value) / (6 * std), 4).ToString() : "";
                var cpu = hasValue ? Math.Round((item.ucl.Value - mean) / (3 * std), 4) : 0;
                var cpl = hasValue ? Math.Round((mean - item.lcl.Value) / (3 * std), 4) : 0;
                res.Cpk_mouth = hasValue ? Math.Min(cpu, cpl).ToString() : "";
                res.Cpu_mouth = hasValue ? cpu.ToString() : "";
                res.Cpl_mouth = hasValue ? cpl.ToString() : "";
            }

            return new ObjectResult(res);
        }
        /// <summary>
        /// 计算方差
        /// </summary>
        /// <param name="xList">数据源</param>
        /// <param name="xBar">平均数</param>
        /// <returns></returns>
        private double StdCount(List<double> xList, double xBar)
        {
            double total = 0;
            foreach (var x in xList)
            {
                total += (x - xBar) * (x - xBar);
            }
            return xList.Count > 0 ? Math.Sqrt(total / xList.Count) : 0;
        }
        /// <summary>
        /// 计算组距组数和分组列表
        /// </summary>
        /// <param name="XList">数据源</param>
        /// <param name="UCL">控制上限</param>
        /// <param name="LCL">控制下限</param>
        /// <param name="res"></param>
        private void GetGroupCount(List<double> XList, double UCL, double LCL, ResponseGetSPCMonitor res)
        {
            if (!XList.Any())
            {                
                return;                
            }
            var GroupMin = LCL < XList.Min() ? LCL : XList.Min();
            var GroupMax = UCL > XList.Max() ? UCL : XList.Max();
            var K = 15;//Math.Round(Math.Log10(XList.Count) / Math.Log10(2) + 1);//组数
            var CW = Math.Round((GroupMax - GroupMin) / K, 4);//组距            
            for (var i = 1; i <= K; i++)
            {
                var min = GroupMin + (i - 1) * CW;
                var max = i == K ? GroupMax : GroupMin + i * CW;
                var number = i == K ? XList.Count(t => t <= max && t >= min) :
                    XList.Count(t => t < max && t >= min);
                res.sample_x.Add($"{min}~{max}");
                res.sample_y.Add(number);
            }
        }
        /// <summary>
        /// X-Bar图 I-MR图数据
        /// </summary>
        /// <param name="values">数据源</param>
        /// <param name="ucl">控制上限</param>
        /// <param name="lcl">控制下限</param>
        /// <param name="type">1->X-Bar均值图2->R图3->I-MR均值图4->I-MR R图</param>
        /// <param name="res"></param>
        private void GetMonitorData(List<double> values, double ucl, double lcl, int type, ResponseGetSPCMonitor res)
        {
            if (!values.Any())
            {
                return;
            }
            //2020-06-22 黑线(中间线)的值 改为 所有点的均值
            switch (type)
            {
                case 1://X-Bar均值图 
                    {
                        int j = 1;
                        for (var i = 1; i <= 20; i++)
                        {
                            var valueList = values.Skip((i - 1) * 5).Take(5);
                            if (!valueList.Any())
                            {
                                continue;
                            }
                            var mean = Math.Round(valueList.Average(), 4);
                            res.x.Add(j);
                            res.y.Add(mean);
                            j++;
                        }
                        res.ucl = ucl;
                        res.lcl = lcl;
                        res.sl = Math.Round(res.y.Average(), 4);//Math.Round((ucl + lcl) / 2, 4);

                        res.Std = (res.ucl - res.lcl) / 6;
                        var std1 = Math.Round(res.ucl - res.Std * 2, 4);
                        var std2 = Math.Round(res.ucl - res.Std, 4);
                        var std3 = res.ucl;
                        var std4 = Math.Round(res.lcl + res.Std * 2, 4);
                        var std5 = Math.Round(res.lcl + res.Std, 4);
                        var std6 = res.lcl;
                        for (var i = 0; i < res.y.Count; i++)
                        {
                            res.std1.Add(std1);
                            res.std2.Add(std2);
                            res.std3.Add(std3);
                            res.std4.Add(std4);
                            res.std5.Add(std5);
                            res.std6.Add(std6);
                        }
                        break;
                    }                    
                case 2://R图
                    {
                        int j = 1;
                        for (var i = 1; i <= 20; i++)
                        {
                            var valueList = values.Skip((i - 1) * 5).Take(5);
                            if (!valueList.Any())
                            {
                                continue;
                            }
                            var range = Math.Round(valueList.Max() - valueList.Min(), 2);
                            res.xr.Add(j);
                            res.yr.Add(range);                            
                            j++;
                        }
                        res.slr = Math.Round(res.yr.Average(), 4);
                        res.uclr = Math.Round(SpcHelper.D4[5] * res.slr, 4);
                        res.lclr = Math.Round(SpcHelper.D3[5] * res.slr, 4);

                        res.Stdr = (res.uclr - res.lclr) / 6;
                        var stdr1 = Math.Round(res.uclr - res.Stdr * 2, 4);
                        var stdr2 = Math.Round(res.uclr - res.Stdr, 4);
                        var stdr3 = res.uclr;
                        var stdr4 = Math.Round(res.slr + res.Stdr * 2, 4);
                        var stdr5 = Math.Round(res.slr + res.Stdr, 4);
                        var stdr6 = res.lclr;
                        for (var i = 0; i < res.yr.Count; i++)
                        {
                            res.stdr1.Add(stdr1);
                            res.stdr2.Add(stdr2);
                            res.stdr3.Add(stdr3);
                            res.stdr4.Add(stdr4);
                            res.stdr5.Add(stdr5);
                            res.stdr6.Add(stdr6);
                        }
                        break;
                    }
                case 3:
                    {
                        res.ucl = ucl;
                        res.lcl = lcl;                        
                        int i = 1;
                        foreach(var value in values)
                        {
                            res.x.Add(i);
                            res.y.Add(value);
                            i++;
                        }
                        res.sl = Math.Round(res.y.Average(), 4);//Math.Round((ucl + lcl) / 2, 4);

                        res.Std = (res.ucl - res.lcl) / 6;
                        var std1 = Math.Round(res.ucl - res.Std * 2, 4);
                        var std2 = Math.Round(res.ucl - res.Std, 4);
                        var std3 = res.ucl;
                        var std4 = Math.Round(res.lcl + res.Std * 2, 4);
                        var std5 = Math.Round(res.lcl + res.Std, 4);
                        var std6 = res.lcl;
                        for (var x = 0; x < res.y.Count; x++)
                        {
                            res.std1.Add(std1);
                            res.std2.Add(std2);
                            res.std3.Add(std3);
                            res.std4.Add(std4);
                            res.std5.Add(std5);
                            res.std6.Add(std6);
                        }
                        break;
                    }
                case 4://多个值I-MR图
                    {
                        for (var i = 1; i < values.Count; i++)
                        {
                            var value = Math.Abs(Math.Round(values[i] - values[i - 1], 4));
                            res.xr.Add(i);
                            res.yr.Add(value);
                        }
                        res.slr = Math.Round(res.yr.Average(), 4);//Math.Round(res.yr.Sum() / values.Count, 4);
                        res.uclr = Math.Round(3.267 * res.slr, 4);
                        res.lclr = 0;

                        res.Stdr = (res.uclr - res.lclr) / 6;
                        var stdr1 = Math.Round(res.uclr - res.Stdr * 2, 4);
                        var stdr2 = Math.Round(res.uclr - res.Stdr, 4);
                        var stdr3 = res.uclr;
                        var stdr4 = Math.Round(res.slr + res.Stdr * 2, 4);
                        var stdr5 = Math.Round(res.slr + res.Stdr, 4);
                        var stdr6 = res.lclr;
                        for (var i = 0; i < res.yr.Count; i++)
                        {
                            res.stdr1.Add(stdr1);
                            res.stdr2.Add(stdr2);
                            res.stdr3.Add(stdr3);
                            res.stdr4.Add(stdr4);
                            res.stdr5.Add(stdr5);
                            res.stdr6.Add(stdr6);
                        }
                        break;
                    }
            }
        }
    }
}