2200205 6 месяцев назад
Родитель
Сommit
6f61d957ee
100 измененных файлов с 9822 добавлено и 0 удалено
  1. 9 0
      Core/Core.csproj
  2. 14 0
      Core/Device.cs
  3. 13 0
      Core/Line.cs
  4. 6 0
      GarnetClient/App.config
  5. 99 0
      GarnetClient/Form1.Designer.cs
  6. 46 0
      GarnetClient/Form1.cs
  7. 120 0
      GarnetClient/Form1.resx
  8. 90 0
      GarnetClient/GarnetClient.csproj
  9. 22 0
      GarnetClient/Program.cs
  10. 33 0
      GarnetClient/Properties/AssemblyInfo.cs
  11. 71 0
      GarnetClient/Properties/Resources.Designer.cs
  12. 117 0
      GarnetClient/Properties/Resources.resx
  13. 30 0
      GarnetClient/Properties/Settings.Designer.cs
  14. 7 0
      GarnetClient/Properties/Settings.settings
  15. 5 0
      GarnetClient/packages.config
  16. 15 0
      PCore/Device.cs
  17. 30 0
      PCore/HourData.cs
  18. 13 0
      PCore/Line.cs
  19. 9 0
      PCore/PCore.csproj
  20. 15 0
      PCore/ProcessData.cs
  21. 17 0
      ProductionLineMonitor.Application/ProductionLineMonitor.Application.csproj
  22. 428 0
      ProductionLineMonitor.Application/Services/AdminService/AdminService.cs
  23. 14 0
      ProductionLineMonitor.Application/Services/AdminService/Dtos/zNode.cs
  24. 39 0
      ProductionLineMonitor.Application/Services/AdminService/IAdminService.cs
  25. 124 0
      ProductionLineMonitor.Application/Services/CimService/CimService.cs
  26. 13 0
      ProductionLineMonitor.Application/Services/CimService/Dtos/CimPasswordDto.cs
  27. 15 0
      ProductionLineMonitor.Application/Services/CimService/ICimService.cs
  28. 27 0
      ProductionLineMonitor.Application/Services/Dtos/FactoryShift.cs
  29. 33 0
      ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/ElectricEnergyMeterDataDto.cs
  30. 23 0
      ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/EnergyConsumptionDto.cs
  31. 165 0
      ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/EnergyConsumptionReportDto.cs
  32. 13 0
      ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/HourElectricEnergy.cs
  33. 150 0
      ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/MachineElectricEnergyDto.cs
  34. 489 0
      ProductionLineMonitor.Application/Services/ExcelService.cs
  35. 10 0
      ProductionLineMonitor.Application/Services/FaultService/Dtos/FaultDto.cs
  36. 19 0
      ProductionLineMonitor.Application/Services/FaultService/Dtos/LineAccumulatedFaultDto.cs
  37. 12 0
      ProductionLineMonitor.Application/Services/FaultService/Dtos/LineFaultDto.cs
  38. 47 0
      ProductionLineMonitor.Application/Services/FaultService/Dtos/MachineFaultDto.cs
  39. 805 0
      ProductionLineMonitor.Application/Services/FaultService/FaultService.cs
  40. 23 0
      ProductionLineMonitor.Application/Services/FaultService/IFaultService.cs
  41. 16 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/LineDto.cs
  42. 137 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/LineModuleTypeOverview.cs
  43. 550 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/ModuleTypeOverview.cs
  44. 18 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthModuleTypeCreateAndUpdateDto.cs
  45. 143 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthModuleTypeDto.cs
  46. 459 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthOverview.cs
  47. 22 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/ProductionPlanDto.cs
  48. 14 0
      ProductionLineMonitor.Application/Services/HomeService/Dtos/UpdateRemarkDto.cs
  49. 148 0
      ProductionLineMonitor.Application/Services/HomeService/HomeService.cs
  50. 23 0
      ProductionLineMonitor.Application/Services/HomeService/IHomeService.cs
  51. 18 0
      ProductionLineMonitor.Application/Services/HomeService/Models/MonthModuleType.cs
  52. 11 0
      ProductionLineMonitor.Application/Services/IExcelService.cs
  53. 12 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/CapaDto.cs
  54. 86 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/KeyInInfo.cs
  55. 14 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/LineKeyInInfo.cs
  56. 313 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/LineMonthData.cs
  57. 43 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/LineOverviewDto.cs
  58. 73 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/MachineDayOutPutPerHour.cs
  59. 77 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/MachineFaultViewModel.cs
  60. 491 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/MachineViewModel.cs
  61. 95 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionLineFaultViewModel.cs
  62. 438 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionLineViewModel.cs
  63. 28 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionPlan.cs
  64. 171 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionPlanDto.cs
  65. 13 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/QueryCapa.cs
  66. 17 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/QueryMachineKeyInFaultParam.cs
  67. 19 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/QueryMachineParam.cs
  68. 12 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/QueryPlanParam.cs
  69. 16 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/ReplaceConsumable.cs
  70. 97 0
      ProductionLineMonitor.Application/Services/LineService/Dtos/Statistic.cs
  71. 15 0
      ProductionLineMonitor.Application/Services/LineService/ILineService.cs
  72. 135 0
      ProductionLineMonitor.Application/Services/LineService/LineService.cs
  73. 584 0
      ProductionLineMonitor.Application/Services/MesApiService/MesApiService.cs
  74. 103 0
      ProductionLineMonitor.Application/Services/OEEService/Dtos/MachineOEEInfo.cs
  75. 12 0
      ProductionLineMonitor.Application/Services/OEEService/IOEEService.cs
  76. 56 0
      ProductionLineMonitor.Application/Services/OEEService/OEEService.cs
  77. 18 0
      ProductionLineMonitor.Application/Services/RecipeService/IRecipeService.cs
  78. 121 0
      ProductionLineMonitor.Application/Services/RecipeService/RecipeService.cs
  79. 76 0
      ProductionLineMonitor.Application/Services/Result.cs
  80. 12 0
      ProductionLineMonitor.Core/Dtos/ChangeShiftDto.cs
  81. 20 0
      ProductionLineMonitor.Core/Dtos/CimDto.cs
  82. 12 0
      ProductionLineMonitor.Core/Dtos/CimListDto.cs
  83. 21 0
      ProductionLineMonitor.Core/Dtos/ECharts.cs
  84. 18 0
      ProductionLineMonitor.Core/Dtos/EChartsDto.cs
  85. 1304 0
      ProductionLineMonitor.Core/Dtos/EQPData.cs
  86. 144 0
      ProductionLineMonitor.Core/Dtos/EQPDataDto.cs
  87. 39 0
      ProductionLineMonitor.Core/Dtos/EQPDataFaultRecordDto.cs
  88. 12 0
      ProductionLineMonitor.Core/Dtos/EapDto.cs
  89. 13 0
      ProductionLineMonitor.Core/Dtos/HomeDto.cs
  90. 27 0
      ProductionLineMonitor.Core/Dtos/MachineDto.cs
  91. 18 0
      ProductionLineMonitor.Core/Dtos/MachineEnergyConsumptionDto.cs
  92. 20 0
      ProductionLineMonitor.Core/Dtos/MachineFaultComparisonDto.cs
  93. 111 0
      ProductionLineMonitor.Core/Dtos/MachineFaultRecordDto.cs
  94. 11 0
      ProductionLineMonitor.Core/Dtos/MachineOutPutPerHourAlarmDto.cs
  95. 12 0
      ProductionLineMonitor.Core/Dtos/MachineOutPutPerHourDto.cs
  96. 34 0
      ProductionLineMonitor.Core/Dtos/MachineStatisticsDto.cs
  97. 17 0
      ProductionLineMonitor.Core/Dtos/MenuDto.cs
  98. 12 0
      ProductionLineMonitor.Core/Dtos/MqttClientStateDto.cs
  99. 18 0
      ProductionLineMonitor.Core/Dtos/PageDto.cs
  100. 23 0
      ProductionLineMonitor.Core/Dtos/ProductionLineDto.cs

+ 9 - 0
Core/Core.csproj

@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>

+ 14 - 0
Core/Device.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Core
+{
+    public class Device
+    {
+        public string Name { get; set; } = string.Empty;
+        
+    }
+}

+ 13 - 0
Core/Line.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Core
+{
+    public class Line
+    {
+        public string Name { get; set; } = string.Empty;
+    }
+}

+ 6 - 0
GarnetClient/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
+    </startup>
+</configuration>

+ 99 - 0
GarnetClient/Form1.Designer.cs

@@ -0,0 +1,99 @@
+namespace GarnetClient
+{
+    partial class Form1
+    {
+        /// <summary>
+        /// 必需的设计器变量。
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// 清理所有正在使用的资源。
+        /// </summary>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows 窗体设计器生成的代码
+
+        /// <summary>
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.button1 = new System.Windows.Forms.Button();
+            this.button2 = new System.Windows.Forms.Button();
+            this.button3 = new System.Windows.Forms.Button();
+            this.button4 = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // button1
+            // 
+            this.button1.Location = new System.Drawing.Point(12, 12);
+            this.button1.Name = "button1";
+            this.button1.Size = new System.Drawing.Size(101, 35);
+            this.button1.TabIndex = 0;
+            this.button1.Text = "连接";
+            this.button1.UseVisualStyleBackColor = true;
+            this.button1.Click += new System.EventHandler(this.button1_Click);
+            // 
+            // button2
+            // 
+            this.button2.Location = new System.Drawing.Point(12, 83);
+            this.button2.Name = "button2";
+            this.button2.Size = new System.Drawing.Size(101, 35);
+            this.button2.TabIndex = 1;
+            this.button2.Text = "发送数据";
+            this.button2.UseVisualStyleBackColor = true;
+            this.button2.Click += new System.EventHandler(this.button2_Click);
+            // 
+            // button3
+            // 
+            this.button3.Location = new System.Drawing.Point(141, 12);
+            this.button3.Name = "button3";
+            this.button3.Size = new System.Drawing.Size(101, 35);
+            this.button3.TabIndex = 2;
+            this.button3.Text = "断开连接";
+            this.button3.UseVisualStyleBackColor = true;
+            // 
+            // button4
+            // 
+            this.button4.Location = new System.Drawing.Point(417, 83);
+            this.button4.Name = "button4";
+            this.button4.Size = new System.Drawing.Size(101, 35);
+            this.button4.TabIndex = 3;
+            this.button4.Text = "读取数据";
+            this.button4.UseVisualStyleBackColor = true;
+            this.button4.Click += new System.EventHandler(this.button4_Click);
+            // 
+            // Form1
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(800, 450);
+            this.Controls.Add(this.button4);
+            this.Controls.Add(this.button3);
+            this.Controls.Add(this.button2);
+            this.Controls.Add(this.button1);
+            this.Name = "Form1";
+            this.Text = "Form1";
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Button button1;
+        private System.Windows.Forms.Button button2;
+        private System.Windows.Forms.Button button3;
+        private System.Windows.Forms.Button button4;
+    }
+}
+

+ 46 - 0
GarnetClient/Form1.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using HslCommunication;
+using HslCommunication.Enthernet;
+
+namespace GarnetClient
+{
+    public partial class Form1 : Form
+    {
+        public Form1()
+        {
+            InitializeComponent();
+        }
+
+        NetSimplifyClient client;
+
+        private void button1_Click(object sender, EventArgs e)
+        {
+            client = new NetSimplifyClient();
+            client.IpAddress = "127.0.0.1";
+            client.Port = 6379;
+            var rev = client.ConnectServer();
+            if (!rev.IsSuccess)
+            {
+                MessageBox.Show($"{rev.Message}");
+            }
+        }
+
+        private void button2_Click(object sender, EventArgs e)
+        {
+            
+        }
+
+        private void button4_Click(object sender, EventArgs e)
+        {
+            NetHandle netHandle = new NetHandle();
+        }
+    }
+}

+ 120 - 0
GarnetClient/Form1.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 90 - 0
GarnetClient/GarnetClient.csproj

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{2DDD350B-16FE-433A-89A3-E5F4F3628485}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>GarnetClient</RootNamespace>
+    <AssemblyName>GarnetClient</AssemblyName>
+    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="HslCommunication, Version=12.1.2.0, Culture=neutral, PublicKeyToken=3d72ad3b6b5ec0e3, processorArchitecture=MSIL">
+      <HintPath>..\packages\HslCommunication.12.1.2\lib\net451\HslCommunication.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Form1.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Form1.Designer.cs">
+      <DependentUpon>Form1.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <EmbeddedResource Include="Form1.resx">
+      <DependentUpon>Form1.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 22 - 0
GarnetClient/Program.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace GarnetClient
+{
+    internal static class Program
+    {
+        /// <summary>
+        /// 应用程序的主入口点。
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new Form1());
+        }
+    }
+}

+ 33 - 0
GarnetClient/Properties/AssemblyInfo.cs

@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("GarnetClient")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("GarnetClient")]
+[assembly: AssemblyCopyright("Copyright ©  2024")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("2ddd350b-16fe-433a-89a3-e5f4f3628485")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
GarnetClient/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本: 4.0.30319.42000
+//
+//     对此文件的更改可能导致不正确的行为,如果
+//     重新生成代码,则所做更改将丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GarnetClient.Properties
+{
+
+
+    /// <summary>
+    ///   强类型资源类,用于查找本地化字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   返回此类使用的缓存 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GarnetClient.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
GarnetClient/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
GarnetClient/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace GarnetClient.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
GarnetClient/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 5 - 0
GarnetClient/packages.config

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="HslCommunication" version="12.1.2" targetFramework="net48" />
+  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
+</packages>

+ 15 - 0
PCore/Device.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PCore
+{
+    public class Device
+    {
+        public string Name { get; set; } = string.Empty;
+        public List<HourData> HourDatas { get; set; } = [];
+        public List<ProcessData> ProcessDatas { get; set; } = [];
+    }
+}

+ 30 - 0
PCore/HourData.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PCore
+{
+    public class HourData
+    {
+        /// <summary>
+        /// 时段 例:8:00~9:00
+        /// </summary>
+        public string Period { get; set; } = string.Empty;
+
+        public DateTime StartTime { get; set; }
+        public DateTime EndTime { get; set; }
+        public int RunTime { get; set; }
+        public int IdelTime { get; set; }
+        public int DownTime { get; set; }
+        public int Outputs { get; set; }
+        public double TT
+        {
+            get
+            {
+                return Outputs == 0 ? 0 : Math.Round(RunTime * 1.0 / Outputs, 2);
+            }
+        }
+    }
+}

+ 13 - 0
PCore/Line.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PCore
+{
+    public class Line
+    {
+        public string Name { get; set; } = string.Empty;
+    }
+}

+ 9 - 0
PCore/PCore.csproj

@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+</Project>

+ 15 - 0
PCore/ProcessData.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PCore
+{
+    public class ProcessData
+    {
+        public int No { get; set; }
+        public string Name { get; set; } = string.Empty;
+        public string Value { get; set; } = string.Empty;
+    }
+}

+ 17 - 0
ProductionLineMonitor.Application/ProductionLineMonitor.Application.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Remove="Services\HomeService\Dtos\zwvsybvn.irv~" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\ProductionLineMonitor.Core\ProductionLineMonitor.Core.csproj" />
+    <ProjectReference Include="..\ProductionLineMonitor.EntityFramework\ProductionLineMonitor.EntityFramework.csproj" />
+  </ItemGroup>
+
+</Project>

+ 428 - 0
ProductionLineMonitor.Application/Services/AdminService/AdminService.cs

@@ -0,0 +1,428 @@
+using AutoMapper;
+using ProductionLineMonitor.Application.Services.AdminService.Dtos;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Core.Utils;
+using ProductionLineMonitor.EntityFramework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
+
+namespace ProductionLineMonitor.Application.Services.AdminService
+{
+    public class AdminService : IAdminService
+    {
+        protected readonly IUnitOfWork _unitOfWork;
+        protected readonly IMapper _mapper;
+        public AdminService(IUnitOfWork unitOfWork, IMapper mapper)
+        {
+            _unitOfWork = unitOfWork;
+            _mapper = mapper;
+        }
+
+        public ResultDto<UserDto> CreateUser(UserCreateOrUpdateDto dto)
+        {
+            bool rev = _unitOfWork.UserRepository.Any(x => x.JobNo == dto.JobNo);
+            if (rev)
+            {
+                return ResultDto<UserDto>.Fail("工号已存在!");
+            }
+            var user = _mapper.Map<User>(dto);
+            user.CreateTime = DateTime.Now;
+            user.Password = $"{dto.JobNo}&123456".To32MD5();
+            _unitOfWork.UserRepository.Create(user);
+            _unitOfWork.SaveChanges();
+            var userDto = _mapper.Map<UserDto>(user);
+            return ResultDto<UserDto>.Success(userDto);
+        }
+
+        public ResultDto ChangePassword(UserChangePasswordDto dto)
+        {
+            if (!Regex.IsMatch(dto.NewPassword, @"^(?:(?=.*[0-9].*)(?=.*[A-Za-z].*)(?=.*[^0-9A-Za-z].*)).{3,}"))
+            {
+                return ResultDto.Fail("密码请使用数字、字母、符号组成!");
+            }
+            if (dto.NewPassword != dto.AgainNewPassword)
+            {
+                return ResultDto.Fail("两次输入新密码不一致!");
+            }
+            var user = _unitOfWork.UserRepository.FirstOrDefault(x => x.JobNo == dto.JobNo);
+            if (user == null)
+            {
+                return ResultDto.Fail("用户不存在!");
+            }
+            string passwordMD5 = $"{dto.JobNo}&{dto.Password}".To32MD5();
+            if (user.Password != passwordMD5)
+            {
+                return ResultDto.Fail("原密码不正确!");
+            }
+            user.Password = $"{dto.JobNo}&{dto.NewPassword}".To32MD5();
+            user.UpdateTime = DateTime.Now;
+            _unitOfWork.UserRepository.Update(user);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public UserLoginResultInfoDto? Login(string jobNo, string password)
+        {
+            var user = _unitOfWork.UserRepository.FirstOrDefault(
+                x => x.JobNo == jobNo && x.Password == $"{jobNo}&{password}".To32MD5());
+
+            if (user == null)
+                return null;
+
+            UserLoginResultInfoDto userLoginResultInfo = new UserLoginResultInfoDto
+            {
+                JobNo = user.JobNo,
+                Name = user.Name,
+                RoleId = user.RoleId,
+                CreateTime = user.CreateTime
+            };
+
+            var role = _unitOfWork.RoleRepository.GetById(user.RoleId);
+            if (role != null)
+            {
+                userLoginResultInfo.RoleName = role.Name;
+                userLoginResultInfo.MenuDtos = GetPermissions(role.Id);
+            }
+
+            return userLoginResultInfo;
+        }
+
+        public ResultDto UpdateUser(string id, UserCreateOrUpdateDto dto)
+        {
+            var user = _unitOfWork.UserRepository.GetById(id);
+            if (user == null)
+            {
+                return ResultDto.Fail("用户不存在!");
+            }
+
+            _mapper.Map(dto, user);
+            _unitOfWork.UserRepository.Update(user);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public ResultDto DeleteUser(string id)
+        {
+            var user = _unitOfWork.UserRepository.GetById(id);
+            if (user == null)
+            {
+                return ResultDto.Fail("用户不存在!");
+            }
+            _unitOfWork.UserRepository.Delete(user);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public ResultDto<IEnumerable<UserDto>> GetUserList(string? keyword = null)
+        {
+            var userDtos = new List<UserDto>();
+            var users = _unitOfWork.UserRepository.GetList(x => x.Name.Contains(keyword) || x.JobNo.Contains(keyword));
+            if (users == null)
+            {
+                return ResultDto<IEnumerable<UserDto>>.Success(userDtos);
+            }
+
+            foreach (var user in users)
+            {
+                var userDto = _mapper.Map<UserDto>(user);
+                var role = _unitOfWork.RoleRepository.GetById(user.RoleId);
+                if (role != null)
+                {
+                    userDto.RoleName = role.Name;
+                }
+                userDtos.Add(userDto);
+            }
+            return ResultDto<IEnumerable<UserDto>>.Success(userDtos);
+        }
+
+        public PageDto<IEnumerable<UserDto>> GetUserPageList(int pageIndex, int pageSize, string? keyword = null)
+        {
+            var userDtos = new List<UserDto>();
+            var users = _unitOfWork.UserRepository.GetPageList(
+                out int total, pageIndex, pageSize, o => o.OrderByDescending(o => o.CreateTime),
+                x => x.Name.Contains(keyword) || x.JobNo.Contains(keyword));
+            if (users != null)
+            {
+                foreach (var user in users)
+                {
+                    var userDto = _mapper.Map<UserDto>(user);
+                    var role = _unitOfWork.RoleRepository.GetById(user.RoleId);
+                    if (role != null)
+                    {
+                        userDto.RoleName = role.Name;
+                    }
+                    userDtos.Add(userDto);
+                }
+            }
+            return new PageDto<IEnumerable<UserDto>>(total, userDtos);
+        }
+
+        public ResultDto<IEnumerable<Role>> GetAllRoles()
+        {
+            var roles = _unitOfWork.RoleRepository.GetList();
+            return ResultDto<IEnumerable<Role>>.Success(roles);
+        }
+
+        public PageDto<IEnumerable<Role>> GetRolePageList(int pageIndex, int pageSize, string? keyword = null)
+        {
+            var roles = _unitOfWork.RoleRepository.GetPageList(out int total, pageIndex, pageSize,
+                o => o.OrderByDescending(o => o.CreateTime),
+                x => x.Name.Contains(keyword));
+            return new PageDto<IEnumerable<Role>>(total, roles);
+        }
+
+        public ResultDto<UserDto> GetUserById(string id)
+        {
+            var user = _unitOfWork.UserRepository.GetById(id);
+            if (user == null)
+            {
+                return ResultDto<UserDto>.Fail("资源不存在!");
+            }
+            var userDto = _mapper.Map<UserDto>(user);
+            var role = _unitOfWork.RoleRepository.GetById(user.RoleId);
+            if (role != null)
+            {
+                userDto.RoleName = role.Name;
+            }
+            return ResultDto<UserDto>.Success(userDto);
+        }
+
+        public ResultDto<Role> GetRoleById(string id)
+        {
+            var role = _unitOfWork.RoleRepository.GetById(id);
+            return ResultDto<Role>.Success(role);
+        }
+
+        public ResultDto<Role> CreateRole(RoleCreateOrUpdateDto dto)
+        {
+            bool rev = _unitOfWork.RoleRepository.Any(x => x.Name == dto.Name);
+            if (rev)
+            {
+                return ResultDto<Role>.Fail("角色名称已存在!");
+            }
+            var role = _mapper.Map<Role>(dto);
+            role.CreateTime = DateTime.Now;
+            _unitOfWork.RoleRepository.Create(role);
+            _unitOfWork.SaveChanges();
+            return ResultDto<Role>.Success(role);
+        }
+
+        public ResultDto UpdateRole(string id, RoleCreateOrUpdateDto dto)
+        {
+            var role = _unitOfWork.RoleRepository.GetById(id);
+            if (role == null)
+            {
+                return ResultDto.Fail("角色不存在!");
+            }
+            _mapper.Map(dto, role);
+            role.UpdateTime = DateTime.Now;
+            _unitOfWork.RoleRepository.Update(role);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public ResultDto DeleteRole(string id)
+        {
+            var role = _unitOfWork.RoleRepository.GetById(id);
+            if (role == null)
+            {
+                return ResultDto.Fail("角色不存在!");
+            }
+
+            bool isUse = _unitOfWork.UserRepository.Any(x => x.RoleId == id);
+            if (isUse)
+            {
+                return ResultDto.Fail("已有用户使用该角色,请先解除用户与角色绑定!");
+            }
+
+            _unitOfWork.RoleRepository.Delete(role);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public TreeDto GetMenus()
+        {
+            TreeDto treeDto = new TreeDto();
+
+            var menus = _unitOfWork.MenuRepository.GetList().OrderBy(o => o.Id);
+            foreach (var menu in menus)
+            {
+                treeDto.Nodes.Add(new Node()
+                {
+                    Id = menu.Id,
+                    Name = menu.Name
+                });
+            }
+
+            return treeDto;
+        }
+
+        public virtual ResultDto SetPermissions(string roleId, IEnumerable<MenuDto> menuDtos)
+        {
+            var role = _unitOfWork.RoleRepository.GetById(roleId);
+            if (role == null)
+            {
+                return ResultDto.Fail("角色不存在!");
+            }
+
+            var roleMenus = _unitOfWork.RoleMenuRepository.GetList(x => x.RoleId == roleId);
+            _unitOfWork.RoleMenuRepository.Delete(roleMenus);
+            foreach (var menu in menuDtos)
+            {
+                _unitOfWork.RoleMenuRepository.Create(new RoleMenu()
+                {
+                    RoleId = roleId,
+                    MenuId = menu.Id
+                });
+            }
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public IList<MenuDto> GetPermissions(string roleId)
+        {
+            IList<MenuDto> menuDtos = new List<MenuDto>();
+
+            var role = _unitOfWork.RoleRepository.GetById(roleId);
+            if (role == null)
+            {
+                return menuDtos;
+            }
+
+            IList<Menu> menus = new List<Menu>();
+
+            if (role.Name == "管理员")
+            {
+                menus = _unitOfWork.MenuRepository.GetList().OrderBy(o => o.Id).ToList();
+            }
+            else
+            {
+                var context = _unitOfWork.GetDbContext() as ProductionLineContext;
+                if (context == null)
+                {
+                    return menuDtos;
+                }
+
+                var query = from roleMenu in context.Set<RoleMenu>()
+                            join menu in context.Set<Menu>()
+                                on roleMenu.MenuId equals menu.Id
+                            where
+                            roleMenu.RoleId == roleId
+                            orderby
+                            menu.Id
+                            select new { roleMenu, menu };
+                foreach (var item in query)
+                {
+                    menus.Add(item.menu);
+                }
+            }
+            
+            foreach (var menu in menus)
+            {
+                if (menu.Level == 1)
+                {
+                    menuDtos.Add(new MenuDto()
+                    {
+                        Id = menu.Id,
+                        Name = menu.Name,
+                        Icon = menu.Icon,
+                        Url = menu.Url,
+                    });
+                }
+            }
+
+            foreach (var menuDto in menuDtos)
+            {
+                var temps = menus.Where(x => x.FatherId == menuDto.Id).OrderBy(o => o.Id);
+                foreach (var item in temps)
+                {
+                    menuDto.Items.Add(new MenuDto
+                    {
+                        Icon = item.Icon,
+                        Url = item.Url,
+                        Name = item.Name,
+                        Id = item.Id
+                    });
+                }
+            }
+            
+            return menuDtos;
+        }
+
+        public bool CheckIsInitialPassword(string jobNo)
+        {
+            var user = _unitOfWork.UserRepository.FirstOrDefault(x => x.JobNo == jobNo);
+            if (user != null)
+            {
+                if (user.Password == $"{jobNo}&{123456}".To32MD5())
+                {
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public IEnumerable<zNode> GetZNodes()
+        {
+            IList<zNode> zNodes = new List<zNode>();
+
+            var menus = _unitOfWork.MenuRepository.GetList().OrderBy(o => o.Id);
+            foreach (var menu in menus)
+            {
+                zNode zNode = new zNode
+                {
+                    Id = menu.Id,
+                    Name = menu.Name,
+                    PId = menu.FatherId ?? "0"
+                };
+                int count = menus.Where(x => x.FatherId == menu.Id).Count();
+                if (count > 0)
+                    zNode.Open = true;
+                else
+                    zNode.Open = false;
+                zNodes.Add(zNode);
+            }
+
+            return zNodes;
+        }
+
+        public UserLoginResultInfoDto? Login(string jobNo)
+        {
+            var user = _unitOfWork.UserRepository.FirstOrDefault(
+                x => x.JobNo == jobNo);
+
+            if (user == null)
+                return null;
+
+            UserLoginResultInfoDto userLoginResultInfo = new UserLoginResultInfoDto
+            {
+                JobNo = user.JobNo,
+                Name = user.Name,
+                RoleId = user.RoleId,
+                CreateTime = user.CreateTime
+            };
+
+            var role = _unitOfWork.RoleRepository.GetById(user.RoleId);
+            if (role != null)
+            {
+                userLoginResultInfo.RoleName = role.Name;
+                userLoginResultInfo.MenuDtos = GetPermissions(role.Id);
+            }
+
+            return userLoginResultInfo;
+        }
+    }
+}

+ 14 - 0
ProductionLineMonitor.Application/Services/AdminService/Dtos/zNode.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.AdminService.Dtos
+{
+    public class zNode
+    {
+        public string Id { get; set; }
+        public string PId { get; set; }
+        public string Name { get; set; }
+        public bool Open { get; set; }
+    }
+}

+ 39 - 0
ProductionLineMonitor.Application/Services/AdminService/IAdminService.cs

@@ -0,0 +1,39 @@
+using ProductionLineMonitor.Application.Services.AdminService.Dtos;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.AdminService
+{
+    public interface IAdminService
+    {
+        UserLoginResultInfoDto? Login(string jobNo, string password);
+        UserLoginResultInfoDto? Login(string jobNo);
+
+        ResultDto ChangePassword(UserChangePasswordDto dto);
+
+        ResultDto<UserDto> GetUserById(string id);
+        ResultDto<UserDto> CreateUser(UserCreateOrUpdateDto dto);
+        ResultDto UpdateUser(string id, UserCreateOrUpdateDto dto);
+        ResultDto DeleteUser(string id);
+        ResultDto<IEnumerable<UserDto>> GetUserList(string keyword);
+        PageDto<IEnumerable<UserDto>> GetUserPageList(int pageIndex, int pageSize, string keyword);
+
+        PageDto<IEnumerable<Role>> GetRolePageList(int pageIndex, int pageSize, string keyword);
+        ResultDto<IEnumerable<Role>> GetAllRoles();
+        ResultDto<Role> GetRoleById(string id);
+        ResultDto<Role> CreateRole(RoleCreateOrUpdateDto dto);
+        ResultDto UpdateRole(string id, RoleCreateOrUpdateDto dto);
+        ResultDto DeleteRole(string id);
+
+        TreeDto GetMenus();
+        ResultDto SetPermissions(string roleId, IEnumerable<MenuDto> menuDtos);
+        IList<MenuDto> GetPermissions(string roleId);
+
+        bool CheckIsInitialPassword(string jobNo);
+
+        IEnumerable<zNode> GetZNodes();
+    }
+}

+ 124 - 0
ProductionLineMonitor.Application/Services/CimService/CimService.cs

@@ -0,0 +1,124 @@
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.CimService
+{
+    public class CimService : ICimService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        public CimService(IUnitOfWork unitOfWork)
+        {
+            _unitOfWork = unitOfWork;
+        }
+        public void CreateOrUpdate(CimDto dto)
+        {
+            var cim = _unitOfWork.CimRepository.FirstOrDefault(x => x.Topic == dto.Topic);
+            if (cim == null)
+            {
+                _unitOfWork.CimRepository.Create(new Cim()
+                {
+                    Topic = dto.Topic,
+                    IpAddress = dto.IpAddress,
+                    User = dto.User,
+                    Time = dto.Time,
+                    PLCIsConnected = dto.PLCConnect,
+                    EAPIsConnected = dto.EAPConnect,
+                    IsConnected = true,
+                    Version = dto.Version,
+                    EAPIP = dto.EAPIP,
+                    EAPPort= dto.EAPPort,
+                    CreateTime = DateTime.Now
+                });
+                _unitOfWork.SaveChanges();
+            }
+            else
+            {
+                cim.IsConnected = true;
+                cim.PLCIsConnected = dto.PLCConnect;
+                
+                cim.EAPIsConnected = dto.EAPConnect;
+                if (cim.EAPIsConnected)
+                {
+                    cim.IpAddress = dto.IpAddress;
+                }
+
+                cim.Topic = dto.Topic;
+                cim.User = dto.User;
+                cim.Time = dto.Time;
+                cim.Version = dto.Version;
+                cim.EAPIP= dto.EAPIP;
+                cim.EAPPort= dto.EAPPort;
+                cim.UpdateTime = DateTime.Now;
+                
+                _unitOfWork.CimRepository.Update(cim);
+                _unitOfWork.SaveChanges();
+            }
+        }
+        public ResultDto<IEnumerable<Cim>> GetCims(bool isShowError)
+        {
+            if (isShowError == false)
+            {
+                var cims = _unitOfWork.CimRepository.GetList();
+                if (cims != null)
+                    foreach (var item in cims)
+                    {
+                        int index = item.Topic.LastIndexOf('#');
+                        if (index != -1)
+                        {
+                            item.Topic = item.Topic.Substring(index + 1, item.Topic.Length - index - 1);
+                        }
+                        if (item.IpAddress != null)
+                        {
+                            index = item.IpAddress.LastIndexOf(':');
+                            if (index != -1)
+                            {
+                                item.IpAddress = item.IpAddress[..index];
+                            }
+                        }
+                    }
+                return ResultDto<IEnumerable<Cim>>.Success(cims.OrderBy(o => o.Topic.Substring(0, 2)));
+            }
+            else
+            {
+                var cims = _unitOfWork.CimRepository.GetList(
+                    x => x.EAPIsConnected == false 
+                    || x.PLCIsConnected == false
+                    || x.IsConnected == false).OrderBy(o => o.Topic);
+                if (cims != null)
+                    foreach (var item in cims)
+                    {
+                        int index = item.Topic.LastIndexOf('#');
+                        if (index != -1)
+                        {
+                            item.Topic = item.Topic.Substring(index + 1, item.Topic.Length - index - 1);
+                        }
+                        if (item.IpAddress != null)
+                        {
+                            index = item.IpAddress.LastIndexOf(':');
+                            if (index != -1)
+                            {
+                                item.IpAddress = item.IpAddress[..index];
+                            }
+                        }
+                    }
+                return ResultDto<IEnumerable<Cim>>.Success(cims.OrderBy(o => o.Topic.Substring(0, 2)));
+            }
+        }
+        public void UpdateMqttClientState(MqttClientStateDto dto)
+        {
+            var cim = _unitOfWork.CimRepository.FirstOrDefault(x => x.Topic == dto.ClientId);
+            if (cim != null)
+            {
+                cim.IsConnected = dto.IsConnected;
+                _unitOfWork.CimRepository.Update(cim);
+                _unitOfWork.SaveChanges();
+            }
+        }
+    }
+}

+ 13 - 0
ProductionLineMonitor.Application/Services/CimService/Dtos/CimPasswordDto.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.CimService.Dtos
+{
+    public class CimPasswordDto
+    {
+        public int No { get; set; }
+        public bool Enabled { get; set; }
+        public string Password { get; set; } = string.Empty;
+    }
+}

+ 15 - 0
ProductionLineMonitor.Application/Services/CimService/ICimService.cs

@@ -0,0 +1,15 @@
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.CimService
+{
+    public interface ICimService
+    {
+        void CreateOrUpdate(CimDto dto);
+        void UpdateMqttClientState(MqttClientStateDto dto);
+        ResultDto<IEnumerable<Cim>> GetCims(bool isShowError);
+    }
+}

+ 27 - 0
ProductionLineMonitor.Application/Services/Dtos/FactoryShift.cs

@@ -0,0 +1,27 @@
+using System;
+
+namespace ProductionLineMonitor.Application.Services.Dtos
+{
+    /// <summary>
+    /// 工厂班别
+    /// </summary>
+    public class FactoryShift
+    {
+        /// <summary>
+        /// 日期
+        /// </summary>
+        public string Date { get; set; } = string.Empty;
+        /// <summary>
+        /// 班次
+        /// </summary>
+        public string Shifts { get; set; } = string.Empty;
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public DateTime StartTime { get; set; }
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public DateTime EndTime { get; set; }
+    }
+}

+ 33 - 0
ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/ElectricEnergyMeterDataDto.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos
+{
+    public class ElectricEnergyMeterDataDto
+    {
+        public string SerialNumber { get; set; } = string.Empty;
+        public DateTime DataTime { get; set; }
+        public double ElectricEnergy { get; set; }
+        public double Ua { get; set; }
+        public double Ub { get; set; }
+        public double Uc { get; set; }
+        public double Iab { get; set; }
+        public double Ibc { get; set; }
+        public double Ica { get; set; }
+        public double Uab { get; set; }
+        public double Ubc { get; set; }
+        public double Uca { get; set; }
+        public double Ia { get; set; }
+        public double Ib { get; set; }
+        public double Ic { get; set; }
+        public double Pa { get; set; }
+        public double Pb { get; set; }
+        public double Pc { get; set; }
+        public double Pt { get; set; }
+        public double Qa { get; set; }
+        public double Qb { get; set; }
+        public double Qc { get; set; }
+        public double Qt { get; set; }
+    }
+}

+ 23 - 0
ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/EnergyConsumptionDto.cs

@@ -0,0 +1,23 @@
+using Org.BouncyCastle.Crypto.IO;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Core.Utils;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos
+{
+    public class EnergyConsumptionDto
+    {
+        public string Date { get; set; } = string.Empty;
+        /// <summary>
+        /// 0:早班 1:夜班
+        /// </summary>
+        public int Shift { get; set; }
+        public int Capacity { get; set; }
+        public double ElectricEnergy { get; set; }
+    }
+}

+ 165 - 0
ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/EnergyConsumptionReportDto.cs

@@ -0,0 +1,165 @@
+using NPOI.SS.Formula.Functions;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Utils;
+using ProductionLineMonitor.Web.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos
+{
+    public class LineEnergyConsumptionDto
+    {
+        public LineEnergyConsumptionDto(string id, string name)
+        {
+            LineId = id;
+            LineName = name;
+        }
+        public string LineId { get; set; }
+        public string LineName { get; set; }
+        public double ElectricEnergiy
+        {
+            get
+            {
+                return Math.Round(MachineElectricEnergiy.Sum(), 2);
+            }
+        }
+        public double[] MachineElectricEnergiy { get; set; } = new double[7];
+        public IList<ElectricEnergyMeterDataDto> ElectricEnergyMeterDtos { get; set; } 
+            = new List<ElectricEnergyMeterDataDto>();
+    }
+
+    public class EnergyConsumptionReportDto
+    {
+        public EnergyConsumptionReportDto(string startDate, string endDate, IUnitOfWork unitOfWork)
+        {
+            StartDate = Convert.ToDateTime($"{startDate} 08:00:00");
+            EndDate = Convert.ToDateTime($"{endDate} 08:00:00").AddDays(1);
+
+            var lines = unitOfWork.ProductionLineRepository.GetList()
+                .OrderBy(o => o.Order).ToList();
+            var machines = unitOfWork.MachineRepository.GetList()
+                .OrderBy(o => o.ProductionLineOrder).ToList();
+
+            foreach (var line in lines)
+            {
+                if (machines.Where(x => x.ProductionLineId == line.Id).Count() > 0)
+                {
+                    LineEnergyConsumptionDto dto = new LineEnergyConsumptionDto(line.Id, line.Name);
+                    LineDtos.Add(dto);
+                }
+            }
+
+            foreach (var line in LineDtos)
+            {
+                var ms = machines.Where(x => x.ProductionLineId == line.LineId);
+                double electricEnergiy = 0;
+                foreach (var machine in ms)
+                {
+                    int index = -1;
+                    switch (machine.Type)
+                    {
+                        case "AG + FPL": index = 0; break;
+                        case "FPL": index = 0; break;
+                        case "COG": index = 1; break;
+                        case "FOG": index = 2; break;
+                        case "BT":  index = 3; break;
+                        case "PS":  index = 4; break;
+                        case "TP":  index = 5; break;
+                        case "EC":  index = 6; break;
+                        default: break;
+                    }
+                    if (index != -1)
+                    {
+                        var ees = MesApiService.GetPowerByEqpAndMonth(machine.Topic, startDate, endDate);
+                        if (ees != null)
+                        {
+                            DateTime[] dts = ees
+                                .OrderBy(o => o.DataTime)
+                                .Select(x => x.DataTime)
+                                .Distinct().ToArray();
+                            if (dts.Count() >= 2)
+                            {
+                                int i = dts.Length - 1;
+                                double electricEnergiy0 = ees.Where(x => x.DataTime == dts[i - 1]).Sum(x => x.ElectricEnergy);
+                                double electricEnergiy1 = ees.Where(x => x.DataTime == dts[i]).Sum(x => x.ElectricEnergy);
+                                electricEnergiy = electricEnergiy1 - electricEnergiy0;
+                            }
+                        }
+                        line.MachineElectricEnergiy[index] = Math.Round(electricEnergiy, 2);
+                    }
+                }
+            }
+        }
+
+        public EnergyConsumptionReportDto(DateTime startDate, DateTime endDate, IUnitOfWork unitOfWork)
+        {
+            StartDate = startDate;
+            EndDate = endDate;
+
+            var lines = unitOfWork.ProductionLineRepository.GetList()
+                .OrderBy(o => o.Order).ToList();
+            var machines = unitOfWork.MachineRepository.GetList()
+                .OrderBy(o => o.ProductionLineOrder).ToList();
+
+            foreach (var line in lines)
+            {
+                if (machines.Where(x => x.ProductionLineId == line.Id).Count() > 0)
+                {
+                    LineEnergyConsumptionDto dto = new LineEnergyConsumptionDto(line.Id, line.Name);
+                    LineDtos.Add(dto);
+                }
+            }
+
+            foreach (var line in LineDtos)
+            {
+                var ms = machines.Where(x => x.ProductionLineId == line.LineId);
+                double electricEnergiy = 0;
+                foreach (var machine in ms)
+                {
+                    int index = -1;
+                    switch (machine.Type)
+                    {
+                        case "AG + FPL": index = 0; break;
+                        case "FPL": index = 0; break;
+                        case "COG": index = 1; break;
+                        case "FOG": index = 2; break;
+                        case "BT": index = 3; break;
+                        case "PS": index = 4; break;
+                        case "TP": index = 5; break;
+                        case "EC": index = 6; break;
+                        default: break;
+                    }
+                    if (index != -1)
+                    {
+                        var ees = MesApiService.GetPowerByEqpAndMonth(
+                            machine.Topic, 
+                            startDate.ToString("yyyy-MM-dd HH:mm:ss"), 
+                            endDate.ToString("yyyy-MM-dd HH:mm:ss"));
+                        if (ees != null)
+                        {
+                            DateTime[] dts = ees
+                                .OrderBy(o => o.DataTime)
+                                .Select(x => x.DataTime)
+                                .Distinct().ToArray();
+                            if (dts.Count() >= 2)
+                            {
+                                int i = dts.Length - 1;
+                                double electricEnergiy0 = ees.Where(x => x.DataTime == dts[i - 1]).Sum(x => x.ElectricEnergy);
+                                double electricEnergiy1 = ees.Where(x => x.DataTime == dts[i]).Sum(x => x.ElectricEnergy);
+                                electricEnergiy = electricEnergiy1 - electricEnergiy0;
+                            }
+                        }
+                        line.MachineElectricEnergiy[index] = Math.Round(electricEnergiy, 2);
+                    }
+                }
+            }
+        }
+
+        public DateTime StartDate { get; set; }
+        public DateTime EndDate { get; set; }
+        public List<LineEnergyConsumptionDto> LineDtos { get; set; }
+            = new List<LineEnergyConsumptionDto>();
+    }
+}

+ 13 - 0
ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/HourElectricEnergy.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos
+{
+    public class HourElectricEnergy
+    {
+        public string Time { get; set; } = string.Empty;
+        public int Capacity { get; set; }
+        public double ElectricEnergy { get; set; }
+    }
+}

+ 150 - 0
ProductionLineMonitor.Application/Services/EnergyConsumptionService/Dtos/MachineElectricEnergyDto.cs

@@ -0,0 +1,150 @@
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Web.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos
+{
+    public class MachineElectricEnergyDto : Machine
+    {
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public List<ElectricEnergyMeter> Meters { get; set; } 
+            = new List<ElectricEnergyMeter>();
+    }
+
+    public class MachineElectricEnergyMeterCheckDto : MachineElectricEnergyDto
+    {
+        public enum MachineMeterState
+        {
+            Normal,
+            TimeOut,
+            ValueDoesNotChange,
+            MesApiGetNull,
+            OValue
+        }
+
+        public int MachineState {  get; set; }
+        
+        public MachineMeterState State { get; private set; } = MachineMeterState.Normal;
+        public string Message { get; private set; } = string.Empty;
+        public int TimeOutValue { get; private set; } = 10;
+        public int Index { get; private set; } = 0;
+        public int LastIndex {  get; private set; } = 0;
+        public List<ElectricEnergyMeterDataDto>[] ElectricEnergyMeterDtos { get; private set; }
+            = new List<ElectricEnergyMeterDataDto>[10];
+        public void GetElectricEnergyMeterDtos()
+        {
+            if (Index >= 10)
+                Index = 0;
+
+            var dtos = MesApiService.GetElectricEnergyMeters(Topic).ToList();
+            ElectricEnergyMeterDtos[Index] = dtos;
+
+            Index++;
+        }
+        public void Check()
+        {
+            DateTime time = DateTime.Now;
+
+            if (Meters == null || Meters.Count() == 0)
+            {
+                State = MachineMeterState.MesApiGetNull;
+                Message = $"待导入!";
+                return;
+            }
+
+            if (ElectricEnergyMeterDtos[Index - 1] == null || ElectricEnergyMeterDtos[Index - 1].Count() == 0)
+            {
+                State = MachineMeterState.MesApiGetNull;
+                Message = $"MesApi 未获取到数据!";
+                return;
+            }
+
+            foreach (var electricEnergyMeterDto in ElectricEnergyMeterDtos[Index - 1])
+            {
+                var meter = Meters.FirstOrDefault(x => x.SerialNumber == electricEnergyMeterDto.SerialNumber);
+
+                if (meter == null)
+                    continue;
+
+                if (!meter.Enable)
+                    continue;
+
+                double min = (time - electricEnergyMeterDto.DataTime).TotalMinutes;
+                if (min > TimeOutValue)
+                {
+                    State = MachineMeterState.TimeOut;
+                    Message = $"编号 {electricEnergyMeterDto.SerialNumber} 超过 {(int)min} 分钟未推送!";
+                    return;
+                }
+
+                if (electricEnergyMeterDto.ElectricEnergy == 0)
+                {
+                    State = MachineMeterState.OValue;
+                    Message = $"编号 {electricEnergyMeterDto.SerialNumber} 能耗为 0!";
+                    return;
+                }
+
+                if (Index - 2 < 0)
+                    LastIndex = 9;
+                else
+                    LastIndex = Index - 2;
+
+                var last = ElectricEnergyMeterDtos[LastIndex];
+
+                if (last == null)
+                    continue;
+
+                var lastMeter = last.FirstOrDefault(x => x.SerialNumber == meter.SerialNumber);
+
+                if (lastMeter == null)
+                    continue;
+
+                if (electricEnergyMeterDto.ElectricEnergy < lastMeter.ElectricEnergy)
+                {
+                    State = MachineMeterState.ValueDoesNotChange;
+                    Message = $"编号 {electricEnergyMeterDto.SerialNumber} 能耗在负增长!";
+                    return;
+                }
+
+                if (Index == 10)
+                    LastIndex = 0;
+                else
+                    LastIndex = Index;
+
+                last = ElectricEnergyMeterDtos[LastIndex];
+
+                if (last == null)
+                    continue;
+
+                lastMeter = last.FirstOrDefault(x => x.SerialNumber == meter.SerialNumber);
+
+                if (lastMeter == null)
+                    continue;
+
+                if (electricEnergyMeterDto.ElectricEnergy == lastMeter.ElectricEnergy
+                    && electricEnergyMeterDto.Uab == lastMeter.Uab
+                    && electricEnergyMeterDto.Ubc == lastMeter.Ubc
+                    && electricEnergyMeterDto.Uca == lastMeter.Uca
+                    && electricEnergyMeterDto.Ua == lastMeter.Ua
+                    && electricEnergyMeterDto.Ub == lastMeter.Ub
+                    && electricEnergyMeterDto.Uc == lastMeter.Uc
+                    && electricEnergyMeterDto.Ia == lastMeter.Ia
+                    && electricEnergyMeterDto.Ib == lastMeter.Ib
+                    && electricEnergyMeterDto.Ic == lastMeter.Ic)
+                {
+                    double min1 = (electricEnergyMeterDto.DataTime - lastMeter.DataTime).TotalMinutes;
+                    State = MachineMeterState.ValueDoesNotChange;
+                    Message = $"编号 {electricEnergyMeterDto.SerialNumber} {(int)min1}分钟 值无变化!";
+                    return;
+                }
+
+            }
+
+            State = MachineMeterState.Normal;
+            Message = "正常";
+        }
+    }
+}

+ 489 - 0
ProductionLineMonitor.Application/Services/ExcelService.cs

@@ -0,0 +1,489 @@
+using Core.Dtos;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NPOI.HSSF.Record.CF;
+using OfficeOpenXml;
+using OfficeOpenXml.ConditionalFormatting;
+using OfficeOpenXml.Drawing.Chart;
+using OfficeOpenXml.Style;
+using ProductionLineMonitor.Application.Services.FaultService;
+using ProductionLineMonitor.Application.Services.OEEService;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using static ProductionLineMonitor.Core.Utils.EmailHelper;
+
+namespace ProductionLineMonitor.Application.Services
+{
+    public class ExcelService : IExcelService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        private readonly IOEEService _oeeService;
+        private readonly IFaultService _faultService;
+
+        public ExcelService(
+            IUnitOfWork unitOfWork, 
+            IOEEService oEEService,
+            IFaultService faultService)
+        {
+            _unitOfWork = unitOfWork;
+            _oeeService = oEEService;
+            _faultService = faultService;
+        }
+
+        public bool EquipmentOperationReport(DateTime dateTime)
+        {
+            string date = dateTime.ToString("yyyy-MM-dd");
+            if (dateTime.Hour == 8) {
+                date = dateTime.AddDays(-1).ToString("yyyy-MM-dd");
+            }
+
+            string machineIdString = "";
+            string receiverAppSettingName = "";
+            bool isSend = false;
+            using (StreamReader file = File.OpenText("D:\\ReportForms\\Email.json"))
+            {
+                using JsonTextReader reader = new JsonTextReader(file);
+                JObject jsonObject = (JObject)JToken.ReadFrom(reader);
+                machineIdString = jsonObject["UtilizationRateEmail"]["MachineIDs"].ToString();
+                receiverAppSettingName = jsonObject["UtilizationRateEmail"]["ReceiverAppSettingName"].ToString();
+                isSend = jsonObject["UtilizationRateEmail"]["IsSend"].ToObject<bool>();
+            }
+
+            if (string.IsNullOrEmpty(machineIdString))
+            {
+                return false;
+            }
+
+            DateTime startTime = DateTime.Parse($"{date} 08:00:00");
+            DateTime endTime= startTime.AddDays(1);
+            DateTime dataStartTime = endTime.AddDays(-7);
+
+            // 文件夹存储路径
+            string folderPath = "D:\\ReportForms";
+            if (!Directory.Exists(folderPath))
+                Directory.CreateDirectory(folderPath);
+            string excelName = $"{DateTime.Now:yyyyMMddHHmmss}.xlsx";
+            var path = Path.Combine(folderPath, excelName);
+
+            string[] machineIds = machineIdString.Split(',');
+            foreach (var machineId in machineIds)
+            {
+                var machine = _unitOfWork.MachineRepository.GetById(machineId);
+                var line = _unitOfWork.ProductionLineRepository.FirstOrDefault(x => x.Id == machine.ProductionLineId);
+                var machineOEE = _oeeService.GetOEE(machineId, dataStartTime.ToString("yyyy-MM-dd"), startTime.ToString("yyyy-MM-dd"));
+                var machineHourDatas = _unitOfWork.MachineOutPutPerHourRepository.GetList(
+                    x =>
+                    x.MachineId == machine.Id &&
+                    x.DataTime >= startTime && 
+                    x.DataTime < endTime).OrderBy(o => o.DataTime).ToList();
+                
+
+                FileInfo file = new FileInfo(path);
+
+                //ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
+
+                using ExcelPackage package = new ExcelPackage(file);
+
+                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add($"{line.Name} {machine.Name}");
+                worksheet.View.ZoomScale = 80;
+
+                worksheet.Cells.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+
+                #region tab header
+
+                worksheet.Cells[1, 1].Value = "NO";
+                worksheet.Cells[1, 2].Value = "时间";
+                worksheet.Cells[1, 3].Value = "稼动率(%)";
+                worksheet.Cells[1, 4].Value = "待机率(%)";
+                worksheet.Cells[1, 5].Value = "报警率(%)";
+                worksheet.Cells[1, 6].Value = "换料率(%)";
+                worksheet.Cells[1, 7].Value = "产能(pcs)";
+                worksheet.Cells[1, 8].Value = "稼动(min)";
+                worksheet.Cells[1, 9].Value = "待机(min)";
+                worksheet.Cells[1, 10].Value = "报警(min)";
+                worksheet.Cells[1, 11].Value = "换料(min)";
+                worksheet.Cells[1, 12].Value = "合计(min)";
+
+                worksheet.Cells[1, 15].Value = $"{startTime:yyyy-MM-dd} 早班";
+                worksheet.Cells[1, 15, 1, 24].Merge = true;
+
+                worksheet.Cells[2, 15].Value = "NO";
+                worksheet.Cells[2, 16].Value = "时段";
+                worksheet.Cells[2, 17].Value = "机种";
+                worksheet.Cells[2, 18].Value = "运行时间";
+                worksheet.Cells[2, 19].Value = "报警时间";
+                worksheet.Cells[2, 20].Value = "待料时间";
+                worksheet.Cells[2, 21].Value = "换料时间";
+                worksheet.Cells[2, 22].Value = "产能";
+                worksheet.Cells[2, 23].Value = "TT";
+                worksheet.Cells[2, 24].Value = "报警次数";
+
+                worksheet.Cells[16, 15].Value = "NO";
+                worksheet.Cells[16, 16].Value = "故障码";
+                worksheet.Cells[16, 17].Value = "故障详情";
+                worksheet.Cells[16, 24].Value = "报警次数";
+
+                worksheet.Cells[16, 17, 16, 23].Merge = true;
+                worksheet.Cells[17, 17, 17, 23].Merge = true;
+                worksheet.Cells[18, 17, 18, 23].Merge = true;
+                worksheet.Cells[19, 17, 19, 23].Merge = true;
+                worksheet.Cells[20, 17, 20, 23].Merge = true;
+                worksheet.Cells[21, 17, 21, 23].Merge = true;
+                worksheet.Cells[22, 17, 22, 23].Merge = true;
+                worksheet.Cells[23, 17, 23, 23].Merge = true;
+                worksheet.Cells[24, 17, 24, 23].Merge = true;
+                worksheet.Cells[25, 17, 25, 23].Merge = true;
+                worksheet.Cells[26, 17, 26, 23].Merge = true;
+
+                worksheet.Cells[1, 26].Value = $"{startTime:yyyy-MM-dd} 夜班";
+                worksheet.Cells[1, 26, 1, 35].Merge = true;
+
+                worksheet.Cells[2, 26].Value = "NO";
+                worksheet.Cells[2, 27].Value = "时段";
+                worksheet.Cells[2, 28].Value = "机种";
+                worksheet.Cells[2, 29].Value = "运行时间";
+                worksheet.Cells[2, 30].Value = "报警时间";
+                worksheet.Cells[2, 31].Value = "待料时间";
+                worksheet.Cells[2, 32].Value = "换料时间";
+                worksheet.Cells[2, 33].Value = "产能";
+                worksheet.Cells[2, 34].Value = "TT";
+                worksheet.Cells[2, 35].Value = "报警次数";
+
+                worksheet.Cells[16, 26].Value = "NO";
+                worksheet.Cells[16, 27].Value = "故障码";
+                worksheet.Cells[16, 28].Value = "故障详情";
+                worksheet.Cells[16, 35].Value = "报警次数";
+
+                worksheet.Cells[16, 28, 16, 34].Merge = true;
+                worksheet.Cells[17, 28, 17, 34].Merge = true;
+                worksheet.Cells[18, 28, 18, 34].Merge = true;
+                worksheet.Cells[19, 28, 19, 34].Merge = true;
+                worksheet.Cells[20, 28, 20, 34].Merge = true;
+                worksheet.Cells[21, 28, 21, 34].Merge = true;
+                worksheet.Cells[22, 28, 22, 34].Merge = true;
+                worksheet.Cells[23, 28, 23, 34].Merge = true;
+                worksheet.Cells[24, 28, 24, 34].Merge = true;
+                worksheet.Cells[25, 28, 25, 34].Merge = true;
+                worksheet.Cells[26, 28, 26, 34].Merge = true;
+
+                worksheet.Column(1).Width = 5;
+                worksheet.Column(2).Width = 12;
+                worksheet.Column(3).Width = 12;
+                worksheet.Column(4).Width = 12;
+                worksheet.Column(5).Width = 12;
+                worksheet.Column(6).Width = 12;
+                worksheet.Column(7).Width = 12;
+                worksheet.Column(8).Width = 12;
+                worksheet.Column(9).Width = 12;
+                worksheet.Column(10).Width = 12;
+                worksheet.Column(11).Width = 12;
+                worksheet.Column(12).Width = 12;
+
+                worksheet.Column(13).Width = 2;
+                worksheet.Column(14).Width = 2;
+                
+                worksheet.Column(15).Width = 5;
+                worksheet.Column(16).Width = 12;
+                worksheet.Column(17).Width = 12;
+                worksheet.Column(18).Width = 12;
+                worksheet.Column(19).Width = 12;
+                worksheet.Column(20).Width = 12;
+                worksheet.Column(21).Width = 12;
+                worksheet.Column(22).Width = 12;
+                worksheet.Column(23).Width = 12;
+                worksheet.Column(24).Width = 12;
+                worksheet.Column(22).Width = 12;
+                worksheet.Column(23).Width = 12;
+
+                worksheet.Column(25).Width = 2;
+
+                worksheet.Column(26).Width = 5;
+                worksheet.Column(27).Width = 12;
+                worksheet.Column(28).Width = 12;
+                worksheet.Column(29).Width = 12;
+                worksheet.Column(30).Width = 12;
+                worksheet.Column(31).Width = 12;
+                worksheet.Column(32).Width = 12;
+                worksheet.Column(33).Width = 12;
+                worksheet.Column(34).Width = 12;
+                worksheet.Column(35).Width = 12;
+
+                #endregion
+
+                for (int i = 0; i < machineOEE.Count; i++)
+                {
+                    worksheet.Cells[i + 2, 1].Value = i + 1;
+                    worksheet.Cells[i + 2, 2].Value = machineOEE[i].Date + " " + machineOEE[i].Shift;
+                    worksheet.Cells[i + 2, 3].Value = (int)machineOEE[i].RunTimeRate;
+                    worksheet.Cells[i + 2, 4].Value = (int)machineOEE[i].IdelTimeRate;
+                    worksheet.Cells[i + 2, 5].Value = (int)machineOEE[i].DownTimeRate;
+                    worksheet.Cells[i + 2, 6].Value = (int)machineOEE[i].LoadMATTimeRate;
+                    worksheet.Cells[i + 2, 7].Value = machineOEE[i].Outputs;
+                    worksheet.Cells[i + 2, 8].Value = machineOEE[i].RunTime / 60;
+                    worksheet.Cells[i + 2, 9].Value = machineOEE[i].IdelTime / 60;
+                    worksheet.Cells[i + 2, 10].Value = machineOEE[i].DownTime / 60;
+                    worksheet.Cells[i + 2, 11].Value = machineOEE[i].LoadMATTime / 60;
+                    worksheet.Cells[i + 2, 12].Value
+                        = (machineOEE[i].RunTime + machineOEE[i].IdelTime + machineOEE[i].DownTime + machineOEE[i].LoadMATTime) / 60;
+                }
+
+                if (machineHourDatas != null)
+                {
+                    if (machineHourDatas.Count == 24)
+                    {
+                        {
+                            int autoSum = 0;
+                            int alarmTimeSum = 0;
+                            int idelSum = 0;
+                            int outputSum = 0;
+                            double ttSum = 0;
+                            int alarmSum = 0;
+                            int loadTimeSum = 0;
+                            for (int i = 0; i < 12; i++)
+                            {
+                                int auto = machineHourDatas[i].AutoRunTime.Value / 60;
+                                autoSum += auto;
+                                int alarmTime = machineHourDatas[i].AlarmTime.Value / 60;
+                                alarmTimeSum += alarmTime;
+                                int idel = machineHourDatas[i].IdleTime.Value / 60;
+                                idelSum += idel;
+                                int output = machineHourDatas[i].OutPut.Value;
+                                outputSum += output;
+                                double tt = machineHourDatas[i].TT;
+                                //ttSum += tt;
+                                int alarm = machineHourDatas[i].AlarmSum.Value;
+                                alarmSum += alarm;
+                                int loadTime = machineHourDatas[i].LoadMATTime.Value / 60;
+                                loadTimeSum += loadTime;
+
+                                worksheet.Cells[i + 3, 15].Value = i + 1;
+                                worksheet.Cells[i + 3, 16].Value = machineHourDatas[i].Period;
+                                worksheet.Cells[i + 3, 17].Value = machineHourDatas[i].ModuleType;
+                                worksheet.Cells[i + 3, 18].Value = auto;
+                                worksheet.Cells[i + 3, 19].Value = alarmTime;
+                                worksheet.Cells[i + 3, 20].Value = idel;
+                                worksheet.Cells[i + 3, 21].Value = loadTime;
+                                worksheet.Cells[i + 3, 22].Value = output;
+                                worksheet.Cells[i + 3, 23].Value = Math.Round(tt, 2);
+                                worksheet.Cells[i + 3, 24].Value = alarm;
+                            }
+
+                            worksheet.Cells[15, 15].Value = "合计";
+                            worksheet.Cells[15, 15, 15, 17].Merge = true;
+                            worksheet.Cells[15, 18].Value = autoSum;
+                            worksheet.Cells[15, 19].Value = alarmTimeSum;
+                            worksheet.Cells[15, 20].Value = idelSum;
+                            worksheet.Cells[15, 21].Value = loadTimeSum;
+                            worksheet.Cells[15, 22].Value = outputSum;
+                            if (outputSum != 0)
+                                ttSum = autoSum * 60.0 / outputSum;
+                            else
+                                ttSum = 0;
+                            worksheet.Cells[15, 23].Value = Math.Round(ttSum, 2);
+                            worksheet.Cells[15, 24].Value = alarmSum;
+
+                            var faults = _faultService.GetFaultFrequencyTop10(machineId, startTime, startTime.AddHours(12), "安全门,门禁,提示上料,提示卸料");
+
+                            for (int i = 0; i < faults.Count; i++)
+                            {
+                                worksheet.Cells[17 + i, 15].Value = i + 1;
+                                worksheet.Cells[17 + i, 16].Value = faults[i].FaultCode;
+                                worksheet.Cells[17 + i, 17].Value = faults[i].FaultInfo;
+                                worksheet.Cells[17 + i, 24].Value = faults[i].Count;
+                            }
+                        }
+
+                        {
+                            int autoSum = 0;
+                            int alarmTimeSum = 0;
+                            int idelSum = 0;
+                            int outputSum = 0;
+                            double ttSum = 0;
+                            int alarmSum = 0;
+                            int loadTimeSum = 0;
+                            for (int i = 12; i < 24; i++)
+                            {
+                                int auto = machineHourDatas[i].AutoRunTime.Value / 60;
+                                autoSum += auto;
+                                int alarmTime = machineHourDatas[i].AlarmTime.Value / 60;
+                                alarmTimeSum += alarmTime;
+                                int idel = machineHourDatas[i].IdleTime.Value / 60;
+                                idelSum += idel;
+                                int output = machineHourDatas[i].OutPut.Value;
+                                outputSum += output;
+                                double tt = machineHourDatas[i].TT;
+                                //ttSum += tt;
+                                int alarm = machineHourDatas[i].AlarmSum.Value;
+                                alarmSum += alarm;
+                                int loadTime = machineHourDatas[i].LoadMATTime.Value / 60;
+                                loadTimeSum += loadTime;
+
+                                worksheet.Cells[i - 12 + 3, 26].Value = i + 1;
+                                worksheet.Cells[i - 12 + 3, 27].Value = machineHourDatas[i].Period;
+                                worksheet.Cells[i - 12 + 3, 28].Value = machineHourDatas[i].ModuleType;
+                                worksheet.Cells[i - 12 + 3, 29].Value = auto;
+                                worksheet.Cells[i - 12 + 3, 30].Value = alarmTime;
+                                worksheet.Cells[i - 12 + 3, 31].Value = idel;
+                                worksheet.Cells[i - 12 + 3, 32].Value = loadTime;
+                                worksheet.Cells[i - 12 + 3, 33].Value = output;
+                                worksheet.Cells[i - 12 + 3, 34].Value = Math.Round(tt, 2);
+                                worksheet.Cells[i - 12 + 3, 35].Value = alarm;
+                            }
+
+                            worksheet.Cells[15, 26].Value = "合计";
+                            worksheet.Cells[15, 26, 15, 28].Merge = true;
+                            worksheet.Cells[15, 29].Value = autoSum;
+                            worksheet.Cells[15, 30].Value = alarmTimeSum;
+                            worksheet.Cells[15, 31].Value = idelSum;
+                            worksheet.Cells[15, 32].Value = loadTimeSum;
+                            worksheet.Cells[15, 33].Value = outputSum;
+                            if (outputSum != 0)
+                                ttSum = autoSum * 60.0 / outputSum;
+                            else
+                                ttSum = 0;
+                            worksheet.Cells[15, 34].Value = Math.Round(ttSum, 2);
+                            worksheet.Cells[15, 35].Value = alarmSum;
+
+                            var faults = _faultService.GetFaultFrequencyTop10(machineId, startTime.AddHours(12), startTime.AddHours(24), "安全门,门禁,提示上料,提示卸料");
+
+                            for (int i = 0; i < faults.Count; i++)
+                            {
+                                worksheet.Cells[17 + i, 26].Value = i + 1;
+                                worksheet.Cells[17 + i, 27].Value = faults[i].FaultCode;
+                                worksheet.Cells[17 + i, 28].Value = faults[i].FaultInfo;
+                                worksheet.Cells[17 + i, 35].Value = faults[i].Count;
+                            }
+                        }
+                    }          
+                }
+
+                //var faultFrequencys = _faultService.GetFaultFrequencyTop10ByShift(
+                //    machine.Id, dataStartTime, endTime, "安全门,门禁,提示上料,提示卸料");
+
+                //var tables = new FaultFrequencyByShiftTable(faultFrequencys);
+
+                //ExcelWorksheet worksheet1 = package.Workbook.Worksheets.Add($"{line.Name} {machine.Name} 报警");
+                //worksheet1.View.ZoomScale = 80;
+                //worksheet1.Cells.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+
+                //worksheet1.Column(1).Width = 5;
+                //worksheet1.Column(2).Width = 10;
+                //worksheet1.Column(3).Width = 30;
+
+                //worksheet1.Cells[1, 1].Value = "NO";
+                //worksheet1.Cells[1, 2].Value = "Code";
+                //worksheet1.Cells[1, 3].Value = "Name";
+
+                //for (int i = 0; i < tables.Shifts.Count(); i++)
+                //{
+                //    worksheet1.Column(i + 4).Width = 20;
+                //    worksheet1.Cells[1, i + 4].Value = tables.Shifts[i];
+                //}
+                
+                //for (int i = 0; i < tables.Nos.Count; i++)
+                //{
+                //    worksheet1.Cells[i + 2, 1].Value = tables.Nos[i];
+                //    worksheet1.Cells[i + 2, 2].Value = tables.FaultCodes[i];
+                //    worksheet1.Cells[i + 2, 3].Value = tables.FaultInfos[i];
+                //    for (int j = 0; j < tables.Shifts.Count(); j++)
+                //    {
+                //        worksheet1.Cells[i + 2, j + 4].Value = tables.Counts[j, i];
+                //    }
+                //}
+
+                //var databar = worksheet1.Cells["D2:Q33"].ConditionalFormatting.AddDatabar(Color.Red);
+
+                //// 创建数据条条件格式对象
+                //ExcelConditionalFormattingDataBar dataBarConditionalFormatting = (ExcelConditionalFormattingDataBar)worksheet.Cells["D2:Q33"].ConditionalFormatting;
+                //// 设置渐变填充
+                //dataBarConditionalFormatting.BarFill.Type = ExcelFillStyle.DarkGray;
+
+                ExcelChartSerie chartSerie;
+                ExcelChart chart = worksheet.Drawings.AddChart("chart", eChartType.ColumnStacked);
+
+                chart.YAxis.MinValue = 0;
+                chart.YAxis.MaxValue = 100;
+                chart.YAxis.Format = "0 \"%\"";
+
+                chart.Legend.Position = eLegendPosition.Bottom;
+                chart.Legend.Add();
+                chart.Title.Text = "设备稼动曲线";    //设置图表的名称   
+                chart.SetPosition(540, 20);         //设置图表位置   
+                chart.SetSize(1000, 400);           //设置图表大小   
+                chart.ShowHiddenData = true;
+
+                chartSerie = chart.Series.Add(worksheet.Cells[2, 3, machineOEE.Count + 1, 3], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                chartSerie.HeaderAddress = worksheet.Cells[1, 3];
+                chartSerie = chart.Series.Add(worksheet.Cells[2, 4, machineOEE.Count + 1, 4], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                chartSerie.HeaderAddress = worksheet.Cells[1, 4];
+                chartSerie = chart.Series.Add(worksheet.Cells[2, 5, machineOEE.Count + 1, 5], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                chartSerie.HeaderAddress = worksheet.Cells[1, 5];
+                chartSerie = chart.Series.Add(worksheet.Cells[2, 6, machineOEE.Count + 1, 6], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                chartSerie.HeaderAddress = worksheet.Cells[1, 6];
+
+                //var chartType2 = chart.PlotArea.ChartTypes.Add(eChartType.Line);
+                //chartType2.UseSecondaryAxis = true;
+                //chartType2.YAxis.Format = "0 \"pcs\"";
+                //chartSerie = chartType2.Series.Add(worksheet.Cells[2, 6, machineOEE.Count + 1, 6], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                //chartSerie.HeaderAddress = worksheet.Cells[1, 6];
+
+                foreach (var item in chart.Series)
+                {
+                    var pieSerie = (ExcelBarChartSerie)item;
+                    pieSerie.DataLabel.ShowValue = true;
+                }
+
+                ExcelChart chart1 = worksheet.Drawings.AddChart("chart1", eChartType.ColumnStacked);
+                chart1.Legend.Position = eLegendPosition.Bottom;
+                chart1.Legend.Add();
+                chart1.Title.Text = "设备产能曲线";    //设置图表的名称   
+                chart1.SetPosition(540, 1030);         //设置图表位置   
+                chart1.SetSize(1000, 400);           //设置图表大小   
+                chart1.ShowHiddenData = true;
+                chart1.YAxis.Format = "0 \"pcs\"";
+                ExcelChartSerie chartSerie1;
+                chartSerie1 = chart1.Series.Add(worksheet.Cells[2, 7, machineOEE.Count + 1, 7], worksheet.Cells[2, 2, machineOEE.Count + 1, 2]);
+                chartSerie1.HeaderAddress = worksheet.Cells[1, 7];
+                foreach (var item in chart1.Series)
+                {
+                    var pieSerie = (ExcelBarChartSerie)item;
+                    pieSerie.DataLabel.ShowValue = true;
+                }
+
+                package.Save();
+            }
+
+       
+
+            if (!isSend)
+            {
+                return false;
+            }
+
+            if (string.IsNullOrEmpty(receiverAppSettingName))
+            {
+                return false;
+            }
+
+            SendMail(
+                MailType.Message,
+                receiverAppSettingName,
+                "",
+                "",
+                "EQP.EAPAUTO01@eink.com",
+                "",
+                $"稼动数据 -- {excelName}",
+                "",
+                new string[] { path },
+                Encoding.UTF8);
+
+            return true;
+        }
+    }
+}

+ 10 - 0
ProductionLineMonitor.Application/Services/FaultService/Dtos/FaultDto.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.FaultService.Dtos
+{
+    public class FaultDto
+    {
+    }
+}

+ 19 - 0
ProductionLineMonitor.Application/Services/FaultService/Dtos/LineAccumulatedFaultDto.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.FaultService.Dtos
+{
+    public class LineAccumulatedFaultDto
+    {
+        /// <summary>
+        /// 数据源 0:自动上抛 1:人工录入
+        /// </summary>
+        public int DataSource { get; set; } = 0;
+        public string MachineType { get; set; } = string.Empty;
+        public string FaultCode { get; set; } = string.Empty;
+        public string FaultInfo { get; set; } = string.Empty;
+        public int AccumulativeTotal { get; set; }
+        public double AccumulativeTime { get; set; }
+    }
+}

+ 12 - 0
ProductionLineMonitor.Application/Services/FaultService/Dtos/LineFaultDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.FaultService.Dtos
+{
+    public class LineFaultDto : MachineFaultDto
+    {
+        public string MachineType { get; set; } = string.Empty;
+        public int DataSource { get; set; }
+    }
+}

+ 47 - 0
ProductionLineMonitor.Application/Services/FaultService/Dtos/MachineFaultDto.cs

@@ -0,0 +1,47 @@
+using System;
+
+namespace ProductionLineMonitor.Application.Services.FaultService.Dtos
+{
+    public class MachineFaultDto
+    {
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 故障码
+        /// </summary>
+        public string FaultCode { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 故障详情
+        /// </summary>
+        public string FaultInfo { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 故障未处理完触发的次数
+        /// </summary>
+        public int TriggerNumber { get; set; }
+
+        /// <summary>
+        /// 动作状态(1:手动,2:自动)
+        /// </summary>
+        public int MachineState { get; set; }
+
+        /// <summary>
+        /// 1:处理中 2:处理完成
+        /// </summary>
+        public int State { get; set; }
+
+        /// <summary>
+        /// 持续时间
+        /// </summary>
+        public double Duration { get; set; }
+    }
+}

+ 805 - 0
ProductionLineMonitor.Application/Services/FaultService/FaultService.cs

@@ -0,0 +1,805 @@
+using AutoMapper;
+using Microsoft.Data.Sqlite;
+using Org.BouncyCastle.Crypto.IO;
+using ProductionLineMonitor.Application.Services.Dtos;
+using ProductionLineMonitor.Application.Services.FaultService.Dtos;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.EntityFramework;
+using ProductionLineMonitor.Web.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+
+namespace ProductionLineMonitor.Application.Services.FaultService
+{
+    public class FaultService : IFaultService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        private readonly IMapper _mapper;
+        public FaultService(IUnitOfWork unitOfWork, IMapper mapper)
+        {
+            _unitOfWork = unitOfWork;
+            _mapper = mapper;
+        }
+
+        public void AddMachineFaultComparisons(string faultTopic, IEnumerable<MachineFaultComparison> machineFaultComparisons)
+        {
+            string sql = "delete from MachineFaultComparisons where FaultTopic=@FaultTopic";
+            _unitOfWork.ExecuteSql(sql, new[] { new SqliteParameter("@FaultTopic", faultTopic) });
+            _unitOfWork.MachineFaultComparisonRepository.Create(machineFaultComparisons);
+            _unitOfWork.SaveChanges();
+        }
+
+        public IEnumerable<LineFaultDto> GetLineFault(string lineId, DateTime startTime, DateTime endTime, int durationThreshold)
+        {
+            List<LineFaultDto> dtos = new List<LineFaultDto>();
+
+            if (!(_unitOfWork.GetDbContext() is ProductionLineContext context))
+                return dtos;
+
+            var line = context.ProductionLines?.Find(lineId);
+            if (line == null)
+                return dtos;
+
+            var machines = context.Machines
+                .Where(x => x.ProductionLineId == line.Id && x.IsInclusionLineStatistics == true)
+                .OrderBy(o => o.ProductionLineOrder);
+
+            foreach (var machine in machines)
+            {
+                var query = from mfr in context.Set<MachineFaultRecord>()
+                            join mfc in context.Set<MachineFaultComparison>()
+                                on mfr.FaultCode equals mfc.FaultCode
+                            where
+                            mfr.MachineId == machine.Id &&
+                            mfc.FaultTopic == machine.FaultTopic &&
+                            mfr.StartTime >= startTime &&
+                            mfr.StartTime < endTime
+                            orderby
+                            mfr.StartTime
+                            select new { mfr, mfc };
+
+                MachineFaultRecord? temp = null;
+                foreach (var item in query)
+                {
+                    if (!item.mfc.FaultInfo.Contains("安全门")
+                         && !item.mfc.FaultInfo.Contains("门禁")
+                         && !item.mfc.FaultInfo.Contains("提示上料")
+                         && !item.mfc.FaultInfo.Contains("提示卸料")
+                         && item.mfr.StartTime != null
+                         && item.mfr.EndTime != null)
+                    {
+                        if (temp != null)
+                        {
+                            if (item.mfr.StartTime >= temp.StartTime && item.mfr.EndTime <= temp.EndTime)
+                            {
+                                continue;
+                            }
+                        }
+
+                        LineFaultDto dto = new LineFaultDto()
+                        {
+                            MachineType = machine.Type,
+                            FaultCode = item.mfr.FaultCode.ToString(),
+                            FaultInfo = item.mfc.FaultInfo,
+                            StartTime = item.mfr.StartTime.Value,
+                            EndTime = item.mfr.EndTime.Value,
+                            MachineState = item.mfr.MachineState == null ? 0 : item.mfr.MachineState.Value,
+                            TriggerNumber = item.mfr.TriggerNumber == null ? 0 : item.mfr.TriggerNumber.Value,
+                            State = item.mfr.State == null ? 0 : item.mfr.State.Value,
+                            DataSource = 0
+                        };
+
+                        if (dto.EndTime > endTime)
+                        {
+                            dto.EndTime = endTime;
+                        }
+
+                        dto.Duration = (dto.EndTime - dto.StartTime).Value.TotalMinutes;
+
+                        if (dto.Duration >= durationThreshold)
+                        {
+                            dtos.Add(dto);
+                            temp = item.mfr;
+                        }
+                            
+                    }
+                }
+            }
+
+            List<LineFaultDto> keyInFaults = new List<LineFaultDto>();
+
+            //keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "COG", endTime, "COG"));
+            keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "BC机", endTime, "BC机"));
+
+            foreach (var machine in machines)
+            {
+                switch (machine.Type)
+                {
+                    case "AG + FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL01", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL02", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL03", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL04", endTime, machine.Type));
+                        break;                                   
+                    case "FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL01", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL02", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL03", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL04", endTime, machine.Type));
+                        break;
+                    case "FOG":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FOG", endTime, machine.Type));
+                        break;
+                    case "AOI":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "LOT2-AOI", endTime, machine.Type));
+                        break;
+                    default:
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), machine.Type, endTime, machine.Type));
+                        break;
+                }
+            }
+
+            foreach (var keyInFault in keyInFaults)
+            {
+                if (keyInFault.StartTime != null
+                     && keyInFault.EndTime != null)
+                {
+                    LineFaultDto dto = new LineFaultDto
+                    {
+                        MachineType = keyInFault.MachineType,
+                        FaultCode = keyInFault.FaultCode.ToString(),
+                        FaultInfo = keyInFault.FaultInfo,
+                        StartTime = keyInFault.StartTime,
+                        EndTime = keyInFault.EndTime,
+                        DataSource = 1
+                    };
+                    dto.Duration = (keyInFault.EndTime.Value - keyInFault.StartTime).TotalMinutes;
+                    if (dto.Duration >= durationThreshold)
+                    {
+                        dtos.Add(dto);
+                    }
+                }
+            }
+
+            return dtos;
+        }
+
+        public IEnumerable<LineAccumulatedFaultDto> GetAccumulatedFaultTop10ByLine(string lineId, DateTime startTime, DateTime endTime)
+        {
+            List<LineAccumulatedFaultDto> dtos = new List<LineAccumulatedFaultDto>();
+
+            if (!(_unitOfWork.GetDbContext() is ProductionLineContext context))
+                return dtos;
+
+            var line = context.ProductionLines?.Find(lineId);
+            if (line == null)
+                return dtos;
+
+            var machines = context.Machines
+                .Where(x => x.ProductionLineId == line.Id && x.IsInclusionLineStatistics == true)
+                .OrderBy(o => o.ProductionLineOrder);
+
+            foreach (var machine in machines)
+            {
+                var query = from mfr in context.Set<MachineFaultRecord>()
+                            join mfc in context.Set<MachineFaultComparison>()
+                                on mfr.FaultCode equals mfc.FaultCode
+                            where
+                            mfr.MachineId == machine.Id &&
+                            mfc.FaultTopic == machine.FaultTopic &&
+                            mfr.StartTime >= startTime &&
+                            mfr.StartTime < endTime
+                            orderby
+                            mfr.StartTime
+                            select new { mfr, mfc };
+                foreach (var item in query)
+                {
+                    if (!item.mfc.FaultInfo.Contains("安全门")
+                         && !item.mfc.FaultInfo.Contains("门禁")
+                         && !item.mfc.FaultInfo.Contains("提示上料")
+                         && !item.mfc.FaultInfo.Contains("提示卸料")
+                         && item.mfr.StartTime != null
+                         && item.mfr.EndTime != null)
+                    {
+                        LineAccumulatedFaultDto statisticDto = new LineAccumulatedFaultDto
+                        {
+                            MachineType = machine.Type,
+                            FaultCode = item.mfr.FaultCode.ToString(),
+                            FaultInfo = item.mfc.FaultInfo
+                        };
+
+                        var temp = dtos.Find(
+                            x => x.MachineType == statisticDto.MachineType 
+                            && x.FaultInfo == statisticDto.FaultInfo);
+                        if (temp == null)
+                        {
+                            statisticDto.AccumulativeTotal = 1;
+
+                            if (item.mfr.EndTime.Value > endTime)
+                                statisticDto.AccumulativeTime = (endTime - item.mfr.StartTime.Value).TotalMinutes;
+                            else
+                                statisticDto.AccumulativeTime = (item.mfr.EndTime.Value - item.mfr.StartTime.Value).TotalMinutes;
+
+                            dtos.Add(statisticDto);
+                        }
+                        else
+                        {
+                            temp.AccumulativeTotal += 1;
+                            if (item.mfr.EndTime.Value > endTime)
+                                temp.AccumulativeTime += (endTime - item.mfr.StartTime.Value).TotalMinutes;
+                            else
+                                temp.AccumulativeTime += (item.mfr.EndTime.Value - item.mfr.StartTime.Value).TotalMinutes;
+                        }
+                    }
+                }
+            }
+
+
+            List<LineFaultDto> keyInFaults = new List<LineFaultDto>();
+
+            //keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "COG", endTime, "COG"));
+            keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "BC机", endTime, "BC机"));
+
+            foreach (var machine in machines)
+            {
+                switch (machine.Type)
+                {
+                    case "AG + FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL01", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL02", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL03", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL04", endTime, machine.Type));
+                        break;
+                    case "FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        break;
+                    case "FOG":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FOG", endTime, machine.Type));
+                        break;
+                    case "AOI":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "LOT2-AOI", endTime, machine.Type));
+                        break;
+                    default:
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), machine.Type, endTime, machine.Type));
+                        break;
+                }
+            }
+
+            foreach (var item in keyInFaults)
+            {
+                if (item.StartTime != null
+                     && item.EndTime != null
+                     && item.StartTime >= startTime
+                     && item.StartTime < endTime)
+                {
+                    LineAccumulatedFaultDto statisticDto = new LineAccumulatedFaultDto
+                    {
+                        MachineType = item.MachineType,
+                        FaultCode = item.FaultCode.ToString(),
+                        FaultInfo = item.FaultInfo,
+                        DataSource = 1
+                    };
+
+                    var temp = dtos.Find(
+                        x => x.MachineType == statisticDto.MachineType
+                        && x.FaultInfo == statisticDto.FaultInfo
+                        && x.DataSource == statisticDto.DataSource);
+                    if (temp == null)
+                    {
+                        statisticDto.AccumulativeTotal = 1;
+                        statisticDto.AccumulativeTime = (item.EndTime.Value - item.StartTime).TotalMinutes;
+                        dtos.Add(statisticDto);
+                    }
+                    else
+                    {
+                        temp.AccumulativeTotal += 1;
+                        temp.AccumulativeTime += (item.EndTime.Value - item.StartTime).TotalMinutes;
+                    }
+                }
+            }
+
+            dtos = dtos.Where(x => x.AccumulativeTime >= 1).OrderByDescending(o => o.AccumulativeTime).Take(10).ToList();
+
+            return dtos;
+        }
+
+        private List<LineFaultDto> GetKeyInLineFault(int floor, int line, string date, string type, DateTime endTime, string machineType)
+        {
+            List<LineFaultDto> dtos = new List<LineFaultDto>();
+            var fs = MesApiService.GetKeyInFaults(floor, line, date, type);
+            foreach (var f in fs)
+            {
+                LineFaultDto dto = new LineFaultDto
+                {
+                    MachineType = machineType,
+                    FaultCode = f.FaultCode,
+                    FaultInfo = f.FaultInfo,
+                    StartTime = f.StartTime
+                };
+                if (f.EndTime == null)
+                    dto.EndTime = endTime;
+                else
+                    dto.EndTime = f.EndTime.Value;
+                dto.Duration = (dto.EndTime.Value - dto.StartTime).TotalMinutes;
+                dtos.Add(dto);
+            }
+            return dtos;
+        }
+
+        public IEnumerable<LineFaultDto> FaultSplitToHour(IEnumerable<LineFaultDto> lineFaultDtos)
+        {
+            IList<LineFaultDto> dtos = new List<LineFaultDto>();
+            foreach (var lineFaultDto in lineFaultDtos)
+            {
+                if (lineFaultDto.EndTime == null)
+                    continue;
+
+                IList<DateTime> dateTimes = new List<DateTime>
+                {
+                    lineFaultDto.StartTime
+                };
+
+                DateTime d1 = lineFaultDto.StartTime;
+                int j = 1;
+                while (d1 < lineFaultDto.EndTime.Value)
+                {
+                    d1 = lineFaultDto.StartTime.AddHours(j);
+                    DateTime d2 = Convert.ToDateTime($"{d1:yyyy-MM-dd HH}:00:00");
+                    j++;
+                    if (d2 < lineFaultDto.EndTime.Value)
+                    {
+                        dateTimes.Add(d2);
+                    }
+                }
+
+                dateTimes.Add(lineFaultDto.EndTime.Value);
+
+                for (int i = 0; i < dateTimes.Count - 1; i++)
+                {
+                    LineFaultDto dto = new LineFaultDto
+                    {
+                        MachineType = lineFaultDto.MachineType,
+                        FaultCode = lineFaultDto.FaultCode,
+                        FaultInfo = lineFaultDto.FaultInfo,
+                        DataSource = lineFaultDto.DataSource,
+                        StartTime = dateTimes[i],
+                        EndTime = dateTimes[i + 1]
+                    };
+                    dto.Duration = (dto.EndTime - dto.StartTime).Value.TotalMinutes;
+                    dtos.Add(dto);
+                }
+            }
+            return dtos;
+        }
+
+        public IEnumerable<LineFaultDto> FilterDuplicateFaults(IEnumerable<LineFaultDto> lineFaultDtos)
+        {
+            IList<LineFaultDto> dtos = new List<LineFaultDto>();
+
+            LineFaultDto? temp = null;
+            foreach (var item in lineFaultDtos)
+            {
+                if (temp == null)
+                {
+                    temp = item;
+                }
+                else
+                {
+                    if (item.StartTime >= temp.StartTime && item.EndTime <= temp.EndTime)
+                    {
+                        continue;
+                    }
+                    temp = item;
+                }
+                dtos.Add(item);
+            }
+
+            return dtos;
+        }
+
+        public List<LineAccumulatedFaultDto> GetAccumulatedFaultTopByLine(string lineId, DateTime startTime, DateTime endTime)
+        {
+            List<LineAccumulatedFaultDto> dtos = new List<LineAccumulatedFaultDto>();
+
+            if (!(_unitOfWork.GetDbContext() is ProductionLineContext context))
+                return dtos;
+
+            var line = context.ProductionLines?.Find(lineId);
+            if (line == null)
+                return dtos;
+
+            var machines = context.Machines
+                .Where(x => x.ProductionLineId == line.Id && x.IsInclusionLineStatistics == true)
+                .OrderBy(o => o.ProductionLineOrder);
+
+            foreach (var machine in machines)
+            {
+                var query = from mfr in context.Set<MachineFaultRecord>()
+                            join mfc in context.Set<MachineFaultComparison>()
+                                on mfr.FaultCode equals mfc.FaultCode
+                            where
+                            mfr.MachineId == machine.Id &&
+                            mfc.FaultTopic == machine.FaultTopic &&
+                            mfr.StartTime >= startTime &&
+                            mfr.StartTime < endTime
+                            orderby
+                            mfr.StartTime
+                            select new { mfr, mfc };
+                foreach (var item in query)
+                {
+                    if (!item.mfc.FaultInfo.Contains("安全门")
+                         && !item.mfc.FaultInfo.Contains("门禁")
+                         && !item.mfc.FaultInfo.Contains("提示上料")
+                         && !item.mfc.FaultInfo.Contains("提示卸料")
+                         && item.mfr.StartTime != null
+                         && item.mfr.EndTime != null)
+                    {
+                        LineAccumulatedFaultDto statisticDto = new LineAccumulatedFaultDto
+                        {
+                            MachineType = machine.Type,
+                            FaultCode = item.mfr.FaultCode.ToString(),
+                            FaultInfo = item.mfc.FaultInfo
+                        };
+
+                        var temp = dtos.Find(
+                            x => x.MachineType == statisticDto.MachineType
+                            && x.FaultInfo == statisticDto.FaultInfo);
+                        if (temp == null)
+                        {
+                            statisticDto.AccumulativeTotal = 1;
+
+                            if (item.mfr.EndTime.Value > endTime)
+                                statisticDto.AccumulativeTime = (endTime - item.mfr.StartTime.Value).TotalMinutes;
+                            else
+                                statisticDto.AccumulativeTime = (item.mfr.EndTime.Value - item.mfr.StartTime.Value).TotalMinutes;
+
+                            dtos.Add(statisticDto);
+                        }
+                        else
+                        {
+                            temp.AccumulativeTotal += 1;
+                            if (item.mfr.EndTime.Value > endTime)
+                                temp.AccumulativeTime += (endTime - item.mfr.StartTime.Value).TotalMinutes;
+                            else
+                                temp.AccumulativeTime += (item.mfr.EndTime.Value - item.mfr.StartTime.Value).TotalMinutes;
+                        }
+                    }
+                }
+            }
+
+
+            List<LineFaultDto> keyInFaults = new List<LineFaultDto>();
+
+            //keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "COG", endTime, "COG"));
+            keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "BC机", endTime, "BC机"));
+
+            foreach (var machine in machines)
+            {
+                switch (machine.Type)
+                {
+                    case "AG + FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL01", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL02", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL03", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL04", endTime, machine.Type));
+                        break;
+                    case "FPL":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "TPA", endTime, machine.Type));
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FPL", endTime, machine.Type));
+                        break;
+                    case "FOG":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "FOG", endTime, machine.Type));
+                        break;
+                    case "AOI":
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), "LOT2-AOI", endTime, machine.Type));
+                        break;
+                    default:
+                        keyInFaults.AddRange(GetKeyInLineFault(line.Floor, line.Line, startTime.ToString("yyyy-MM-dd"), machine.Type, endTime, machine.Type));
+                        break;
+                }
+            }
+
+            foreach (var item in keyInFaults)
+            {
+                if (item.StartTime != null
+                     && item.EndTime != null
+                     && item.StartTime >= startTime
+                     && item.StartTime < endTime)
+                {
+                    LineAccumulatedFaultDto statisticDto = new LineAccumulatedFaultDto
+                    {
+                        MachineType = item.MachineType,
+                        FaultCode = item.FaultCode.ToString(),
+                        FaultInfo = item.FaultInfo,
+                        DataSource = 1
+                    };
+
+                    var temp = dtos.Find(
+                        x => x.MachineType == statisticDto.MachineType
+                        && x.FaultInfo == statisticDto.FaultInfo
+                        && x.DataSource == statisticDto.DataSource);
+                    if (temp == null)
+                    {
+                        statisticDto.AccumulativeTotal = 1;
+                        statisticDto.AccumulativeTime = (item.EndTime.Value - item.StartTime).TotalMinutes;
+                        dtos.Add(statisticDto);
+                    }
+                    else
+                    {
+                        temp.AccumulativeTotal += 1;
+                        temp.AccumulativeTime += (item.EndTime.Value - item.StartTime).TotalMinutes;
+                    }
+                }
+            }
+
+            dtos = dtos.Where(x => x.AccumulativeTime >= 1).OrderByDescending(o => o.AccumulativeTime).ToList();
+
+            return dtos;
+        }
+
+        public List<MachineFaultRecordDto> GetMachineFaultsByTop10(string machineId, DateTime day, string keywords)
+        {
+            List<MachineFaultRecordDto> dtos = new List<MachineFaultRecordDto>();
+
+            var machine = _unitOfWork.MachineRepository.GetById(machineId);
+            if (machine != null)
+            {
+                var faults = _unitOfWork.MachineFaultRecordRepository.GetList(
+                    x => x.MachineId == machineId);
+
+                DateTime startTime = day.AddHours(+8);
+                DateTime endTime = day.AddDays(+1).AddHours(+8);
+
+                var lst = _unitOfWork.MachineFaultRecordRepository
+                    .GetList(x =>
+                        x.MachineId == machineId
+                        && x.StartTime >= startTime
+                        && x.StartTime < endTime)
+                    .OrderBy(o => o.StartTime)
+                    .ToList();
+
+                dtos = _mapper.Map<List<MachineFaultRecordDto>>(lst);
+
+                foreach (var faultRecord in dtos)
+                {
+                    faultRecord.MachineName = machine.Name;
+                    MachineFaultComparison machineFaultComparison = _unitOfWork.MachineFaultComparisonRepository
+                        .FirstOrDefault(x => x.FaultTopic == machine.FaultTopic && x.FaultCode == faultRecord.FaultCode);
+                    if (machineFaultComparison != null)
+                    {
+                        faultRecord.FaultLevel = (int)machineFaultComparison.FaultLevel;
+                        faultRecord.FaultInfo = machineFaultComparison.FaultInfo;
+                        faultRecord.FaultCategory = machineFaultComparison.FaultCategory;
+                    }
+                    else
+                    {
+                        faultRecord.FaultLevel = 0;
+                        faultRecord.FaultInfo = "未知故障";
+                        faultRecord.FaultCategory = "未知";
+                    }
+
+                    if (faultRecord.StartTime != null && faultRecord.EndTime != null)
+                        faultRecord.Duration = (faultRecord.EndTime.Value - faultRecord.StartTime.Value).TotalMinutes;
+                    else
+                        faultRecord.Duration = 0;
+                }
+
+                if (keywords != null)
+                {
+                    string[] strs = keywords.Split(',');
+                    dtos = dtos.Where(x =>
+                        strs.All(s => !x.FaultInfo.Contains(s))).ToList();
+                }
+
+                //dtos = dtos.Where(x => 
+                //    !x.FaultInfo.Contains(x.FaultInfo)).ToList();
+
+                dtos = CheckSameTimeFault(dtos);
+
+                dtos = dtos.OrderByDescending(o => o.Duration).Take(10).ToList();
+            }
+
+            return dtos;
+        }
+
+        private List<MachineFaultRecordDto> CheckSameTimeFault(List<MachineFaultRecordDto> lst)
+        {
+            List<MachineFaultRecordDto> faults = new List<MachineFaultRecordDto>();
+            MachineFaultRecordDto lastFault = null;
+            foreach (var l in lst)
+            {
+                if (lastFault != null)
+                {
+                    if (l.EndTime != null && (l.StartTime < lastFault.StartTime || l.EndTime > lastFault.EndTime))
+                    {
+                        faults.Add(l);
+                        lastFault = l;
+                    }
+                }
+                else
+                {
+                    faults.Add(l);
+                    lastFault = l;
+                }
+            }
+            return faults;
+        }
+
+        public List<FaultFrequency> GetFaultFrequencyTop10(string machineId, DateTime startTime, DateTime endTime, string keywords)
+        {
+            List<FaultFrequency> faultFrequencies = new List<FaultFrequency>();
+
+            var machine = _unitOfWork.MachineRepository.FirstOrDefault(x => x.Id == machineId);
+
+            var faults = _unitOfWork.MachineFaultRecordRepository.GetList(
+                x => x.MachineId == machineId
+                && x.StartTime >= startTime
+                && x.StartTime < endTime
+                && x.MachineState == 2).ToList();
+
+            var mfcs = _unitOfWork.MachineFaultComparisonRepository.GetList(
+                    x => x.FaultTopic == machine.FaultTopic).ToList();
+
+            foreach (var fault in faults)
+            {
+                var f = faultFrequencies.Find(x => x.FaultCode == fault.FaultCode);
+                if (f == null)
+                {
+                    FaultFrequency m = new FaultFrequency()
+                    {
+                        FaultCode = fault.FaultCode.Value,
+                        Count = fault.TriggerNumber.Value
+                    };
+                    faultFrequencies.Add(m);
+                }
+                else
+                {
+                    f.Count += fault.TriggerNumber.Value;
+                }
+            }
+
+            foreach (var item in faultFrequencies)
+            {
+                var mfc = mfcs.FirstOrDefault(x => x.FaultCode == item.FaultCode);
+                if (mfc == null)
+                {
+                    item.FaultInfo = item.FaultCode.ToString() + " " + "未知故障!";
+                }
+                else
+                {
+                    item.FaultInfo = item.FaultCode.ToString() + " " + mfc.FaultInfo;
+                }
+            }
+
+            if (keywords != null)
+            {
+                string[] strs = keywords.Split(',');
+                faultFrequencies = faultFrequencies.Where(x =>
+                    strs.All(s => !x.FaultInfo.Contains(s))).ToList();
+            }
+
+            return faultFrequencies.OrderByDescending(o => o.Count).Take(10).ToList();
+        }
+
+        public List<FaultFrequencyByShift> GetFaultFrequencyTop10ByShift(string machineId, DateTime startTime, DateTime endTime, string keywords)
+        {
+            List<FaultFrequencyByShift> faultFrequencies = new List<FaultFrequencyByShift>();
+
+            var machine = _unitOfWork.MachineRepository.FirstOrDefault(x => x.Id == machineId);
+
+            var faults = _unitOfWork.MachineFaultRecordRepository.GetList(
+                x => x.MachineId == machineId
+                && x.StartTime >= startTime
+                && x.StartTime < endTime
+                && x.MachineState == 2).ToList();
+
+            var mfcs = _unitOfWork.MachineFaultComparisonRepository.GetList(
+                    x => x.FaultTopic == machine.FaultTopic).ToList();
+
+            List<FactoryShift> shifts = GetShifts(startTime, endTime);
+
+            foreach (var shift in shifts)
+            {
+                FaultFrequencyByShift faultFrequencyByShift = GetFaultFrequency(
+                faults, mfcs, keywords, shift);
+                faultFrequencies.Add(faultFrequencyByShift);
+            }
+
+            return faultFrequencies;
+        }
+
+        private FaultFrequencyByShift GetFaultFrequency(List<MachineFaultRecord> faults, List<MachineFaultComparison> mfcs, string keywords, FactoryShift shift)
+        {
+            FaultFrequencyByShift faultFrequencyByShift = new FaultFrequencyByShift()
+            {
+                Shift = $"{shift.Date} {shift.Shifts}"
+            };
+            
+            var fs = faults.Where(x => x.StartTime >= shift.StartTime && x.StartTime < shift.EndTime);
+
+            foreach (var f in fs)
+            {
+                var temp = faultFrequencyByShift.FaultFrequencies.Find(x => x.FaultCode == f.FaultCode);
+                if (temp != null)
+                {
+                    if (f.TriggerNumber != null)
+                    {
+                        temp.Count += f.TriggerNumber.Value;
+                    }
+                }
+                else
+                {
+                    FaultFrequency faultFrequency = new FaultFrequency
+                    {
+                        FaultCode = f.FaultCode != null ? f.FaultCode.Value : 0,
+                        Count = f.TriggerNumber != null ? f.TriggerNumber.Value : 1
+                    };
+                    faultFrequencyByShift.FaultFrequencies.Add(faultFrequency);
+                }
+            }
+
+            faultFrequencyByShift.FaultFrequencies = faultFrequencyByShift.FaultFrequencies.OrderBy(x => x.Count).ToList();
+
+            foreach (var item in faultFrequencyByShift.FaultFrequencies)
+            {
+                var mfc = mfcs.FirstOrDefault(x => x.FaultCode == item.FaultCode);
+                if (mfc != null)
+                    item.FaultInfo = mfc.FaultInfo;
+                else
+                    item.FaultInfo = "未知故障";
+            }
+
+            if (!string.IsNullOrEmpty(keywords))
+            {
+                string[] strs = keywords.Split(',');
+                faultFrequencyByShift.FaultFrequencies = faultFrequencyByShift.FaultFrequencies.Where(x =>
+                    strs.All(s => !x.FaultInfo.Contains(s))).ToList();
+            }
+
+            // faultFrequencyByShift.FaultFrequencies = faultFrequencyByShift.FaultFrequencies.OrderByDescending(o => o.Count).Take(10).ToList();
+
+            return faultFrequencyByShift;
+        }
+
+        private List<FactoryShift> GetShifts(DateTime startTime, DateTime endTime)
+        {
+            List<FactoryShift> shifts = new List<FactoryShift>();
+
+            for (DateTime i = startTime; i <= endTime; i = i.AddDays(1))
+            {
+                FactoryShift shift = new FactoryShift
+                {
+                    Date = i.ToString("yyyy-MM-dd"),
+                    Shifts = "白班",
+                    StartTime = i,
+                    EndTime = i.AddHours(12)
+                };
+                shifts.Add(shift);
+
+                FactoryShift shift1 = new FactoryShift
+                {
+                    Date = i.ToString("yyyy-MM-dd"),
+                    Shifts = "夜班",
+                    StartTime = i.AddHours(12),
+                    EndTime = i.AddHours(24)
+                };
+                shifts.Add(shift1);
+            }
+
+            return shifts;
+        }
+    }
+}

+ 23 - 0
ProductionLineMonitor.Application/Services/FaultService/IFaultService.cs

@@ -0,0 +1,23 @@
+using ProductionLineMonitor.Application.Services.FaultService.Dtos;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.FaultService
+{
+    public interface IFaultService
+    {
+        void AddMachineFaultComparisons(string faultTopic, IEnumerable<MachineFaultComparison> machineFaultComparisons);
+        IEnumerable<LineAccumulatedFaultDto> GetAccumulatedFaultTop10ByLine(string lineId, DateTime startTime, DateTime endTime);
+        List<LineAccumulatedFaultDto> GetAccumulatedFaultTopByLine(string lineId, DateTime startTime, DateTime endTime);
+        IEnumerable<LineFaultDto> GetLineFault(string lineId, DateTime startTime, DateTime endTime, int durationThreshold);
+        IEnumerable<LineFaultDto> FaultSplitToHour(IEnumerable<LineFaultDto> lineFaultDtos);
+        IEnumerable<LineFaultDto> FilterDuplicateFaults(IEnumerable<LineFaultDto> lineFaultDtos);
+        List<MachineFaultRecordDto> GetMachineFaultsByTop10(string machineId, DateTime day, string keywords);
+        List<FaultFrequency> GetFaultFrequencyTop10(string machineId, DateTime startTime, DateTime endTime, string keywords);
+        List<FaultFrequencyByShift> GetFaultFrequencyTop10ByShift(string machineId, DateTime startTime, DateTime endTime, string keywords);
+    }
+}

+ 16 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/LineDto.cs

@@ -0,0 +1,16 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class LineDto : ProductionLine
+    {
+        public LineDto(Machine machine)
+        {
+            Machine = machine;
+        }
+        public Machine Machine { get; set; }
+    }
+}

+ 137 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/LineModuleTypeOverview.cs

@@ -0,0 +1,137 @@
+using NPOI.SS.Formula.Functions;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Web.Services;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class LineModuleTypeOverview
+    {
+        public LineModuleTypeOverview(ProductionLine line, DateTime startDate, DateTime endDate, int moduleTypeLen, DateTime rangeEndDate)
+        {
+            _startDate = startDate;
+            _endDate = endDate;
+            _rangeEndDate = rangeEndDate;
+
+            Line = new ProductionLine
+            {
+                Id = line.Id,
+                Floor = line.Floor,
+                Name = line.Name,
+                Order = line.Order,
+                Line = line.Line,
+                MakeClassification = line.MakeClassification,
+                HourDataTopic = line.HourDataTopic
+            };
+
+            Plans = MesApiService.GetProductionPlanByTimelot(
+                Line.Floor, Line.Line,
+                _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+
+            if (!string.IsNullOrEmpty(line.HourDataTopic))
+            {
+                HourOuts = MesApiService.GetOutPutPerHours(
+                    line.HourDataTopic,
+                    _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"));
+            }
+
+            ModuleTypes = Plans
+                .Where(x => x.ModuleType != "" && x.ModuleType.Length >= moduleTypeLen)
+                .Select(x => x.ModuleType[..moduleTypeLen])
+                .Distinct().ToArray();
+
+            //PlanCapacity = new int[ModuleTypes.Length];
+            //Capacity = new int[ModuleTypes.Length];
+            //Difference = new int[ModuleTypes.Length];
+
+            //for (int i = 0; i < ModuleTypes.Length; i++)
+            //{
+            //    PlanCapacity[i] = Plans.Where(x =>
+            //        x.ShiftDate <= _endDate &&
+            //        x.ModuleType != "" &&
+            //        x.ModuleType.Length >= moduleTypeLen &&
+            //        x.ModuleType[..moduleTypeLen] == ModuleTypes[i]).Select(x => x.PlanCapacity).Sum();
+            //    Capacity[i] = HourOuts.Where(x =>
+            //        x.ModuleType.Length >= moduleTypeLen &&
+            //        x.ModuleType[..moduleTypeLen] == ModuleTypes[i]).Select(x => x.OutPut).Sum();
+            //    Difference[i] = Capacity[i] - PlanCapacity[i];
+            //}
+
+            ModuleTypes = Plans
+                .Where(x => x.ModuleType != "" && x.ModuleType.Length >= moduleTypeLen)
+                .Select(x => x.ModuleType)
+                .Distinct().ToArray();
+
+            PlanCapacity = new int[ModuleTypes.Length];
+            Capacity = new int[ModuleTypes.Length];
+            Difference = new int[ModuleTypes.Length];
+
+            for (int i = 0; i < ModuleTypes.Length; i++)
+            {
+                PlanCapacity[i] = Plans.Where(x =>
+                    x.ShiftDate <= _endDate &&
+                    x.ModuleType != "" && 
+                    x.ModuleType.Length >= moduleTypeLen &&
+                    x.ModuleType == ModuleTypes[i]).Select(x => x.PlanCapacity).Sum();
+                Capacity[i] = HourOuts.Where(x =>
+                    x.ModuleType.Length >= moduleTypeLen &&
+                    x.ModuleType.Length >= ModuleTypes[i].Length &&
+                    x.ModuleType[..ModuleTypes[i].Length] == ModuleTypes[i]).Select(x => x.OutPut).Sum();
+                Difference[i] = Capacity[i] - PlanCapacity[i];
+            }
+        }
+
+        private readonly DateTime _startDate;
+        private readonly DateTime _endDate;
+        private readonly DateTime _rangeEndDate;
+
+        public ProductionLine Line { get; private set; }
+
+        public IList<ProductionPlanDto> Plans { get; set; } 
+            = new List<ProductionPlanDto>();
+
+        public IList<MachineDayOutPutPerHour> HourOuts { get; set; } 
+            = new List<MachineDayOutPutPerHour>();
+
+        public string[] ModuleTypes { get; set; }
+        public int[] PlanCapacity { get; set; }
+        public int[] Capacity { get; set; } 
+        public int[] Difference { get; set; }
+
+        /// <summary>
+        /// 根据机种获取计划、产能、差异
+        /// </summary>
+        /// <param name="moduleType"></param>
+        /// <returns>(是否存在,计划,产能,差异)</returns>
+        public (bool, int, int, int) GetModuleTypeData(string moduleType)
+        {
+            if (string.IsNullOrEmpty(moduleType))
+                return (false, 0, 0, 0);
+
+            int index = Array.IndexOf(ModuleTypes, moduleType);
+            if (index == -1)
+                return (false, 0, 0, 0);
+
+            //return (true, PlanCapacity[index], Capacity[index], Difference[index]);
+
+            int planCapacity = 0, capacity = 0, difference = 0;
+
+            for (int i = 0; i < ModuleTypes.Length; i++)
+            {
+                if (ModuleTypes[i].Contains(moduleType))
+                {
+                    planCapacity += PlanCapacity[i];
+                    capacity += Capacity[i];
+                    difference += Difference[i];
+                }
+            }
+
+            return (true, planCapacity, capacity, difference);
+
+        }
+    }
+}

+ 550 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/ModuleTypeOverview.cs

@@ -0,0 +1,550 @@
+using OfficeOpenXml.VBA;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Web.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class ModuleTypeOverview
+    {
+        public ModuleTypeOverview(IList<ProductionLine> lines, int moduleTypeLen, int year, int month, string mark)
+        {
+            _year = year;
+            _month = month;
+
+            _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            _startDate = _rangeEndDate.AddMonths(-1).AddDays(+1);
+
+            DateTime _date = DateTime.Now;
+            if (_date < _rangeEndDate.AddDays(1))
+                _endDate = Convert.ToDateTime($"{_date.AddDays(-1):yyyy-MM-dd} 08:00:00");
+            else
+                _endDate = _rangeEndDate;
+
+            //_year = year;
+            //_month = month;
+
+            //_rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            //_startDate = _rangeEndDate.AddMonths(-1).AddDays(+1);
+
+            //DateTime _date = DateTime.Now;
+            //if (_date <= _rangeEndDate)
+            //    _endDate = Convert.ToDateTime($"{_date.AddDays(-1):yyyy-MM-dd} 08:00:00");
+            //else
+            //    _endDate = _rangeEndDate;
+
+            var MonthModuleType = MesApiService.GetMonthModuleTypeDtosByMark(year, month, mark);
+
+            if (MonthModuleType.Count == 0)
+                return;
+
+            foreach (var item in MonthModuleType)
+            {
+                MonthModuleTypeDtos.Add(new MonthModuleTypeDto()
+                {
+                    Id = item.Id,
+                    ModuleType = item.ModuleType,
+                    Mark = item.Mark,
+                    Year = item.Year,
+                    Month = item.Month,
+                    PlanCapacity = item.PlanCapacity,
+                    LastModuleCapacity = item.LastModuleCapacity,
+                    Remark = item.Remark,
+                });
+            }
+
+            _lines = lines;
+
+            List<Thread> threads = new List<Thread>();
+            foreach (var line in _lines)
+            {
+                Thread thread = new Thread(() =>
+                {
+                    LineModuleTypeOverview lineModuleTypeOverview = new LineModuleTypeOverview(
+                        line, _startDate, _endDate, moduleTypeLen, _rangeEndDate);
+                    LineModuleTypeOverviews.Add(lineModuleTypeOverview);
+                });
+                thread.Start();
+                threads.Add(thread);
+            }
+
+            foreach (var item in threads)
+            {
+                item.Join();
+            }
+
+            if (mark == "2F")
+            {
+                // 添加FL半品
+                //AddLGPAGOCA();
+            }
+
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                foreach (var lineModuleTypeOverview in LineModuleTypeOverviews)
+                {
+                    var rev = lineModuleTypeOverview.GetModuleTypeData(monthModuleTypeDto.ModuleType);
+                    if (rev.Item1 == true)
+                    {
+                        monthModuleTypeDto.Capacity += rev.Item3;
+                        monthModuleTypeDto.Difference += rev.Item4;
+                        monthModuleTypeDto.Lines.Add(lineModuleTypeOverview.Line);
+                    }
+                }
+            }
+
+            // 产能已经超过月计划
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                //if (monthModuleTypeDto.Capacity >= (monthModuleTypeDto.PlanCapacity + monthModuleTypeDto.LastModuleCapacity))
+                //{
+                //    monthModuleTypeDto.Difference = 0;
+                //}
+
+                if (monthModuleTypeDto.Capacity >= (monthModuleTypeDto.PlanCapacity + monthModuleTypeDto.LastModuleCapacity))
+                {
+                    if (monthModuleTypeDto.Capacity - (monthModuleTypeDto.PlanCapacity + monthModuleTypeDto.LastModuleCapacity) < 500)
+                    {
+                        monthModuleTypeDto.Difference = 0;
+                    }
+                }
+
+                monthModuleTypeDto.Lines = monthModuleTypeDto.Lines.OrderBy(o => o.Line).ToList();
+            }
+
+            // 上月做完的MPS
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                if (monthModuleTypeDto.Lines.Count() == 0 &&
+                    monthModuleTypeDto.LastModuleCapacity < 0 &&
+                    Math.Abs(monthModuleTypeDto.LastModuleCapacity) >= monthModuleTypeDto.PlanCapacity)
+                {
+                    monthModuleTypeDto.Capacity = Math.Abs(monthModuleTypeDto.LastModuleCapacity);
+                    monthModuleTypeDto.LastModuleCapacity = 0;
+                }
+            }
+
+            MonthModuleTypeDtos = MonthModuleTypeDtos.OrderByDescending(
+                x => (x.PlanCapacity + x.LastModuleCapacity)).ToList();
+        }
+
+        /// <summary>
+        /// 已弃用构造函数
+        /// </summary>
+        /// <param name="lines"></param>
+        /// <param name="moduleTypeLen"></param>
+        public ModuleTypeOverview(IList<ProductionLine> lines, int moduleTypeLen)
+        {
+            _date = DateTime.Now;
+
+            // 根据当前日期判断年月
+            _year = _date.Year;
+            if (_date.Day >= 21)
+            {
+                _month = _date.Month + 1;
+                _startDate = Convert.ToDateTime($"{_year}-{_month - 1}-21 08:00:00");
+                _endDate = Convert.ToDateTime($"{_year}-{_month - 1}-{_date.Day} 08:00:00");
+                _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            }
+            else
+            {
+                _month = _date.Month;
+                _startDate = Convert.ToDateTime($"{_year}-{_month - 1}-21 08:00:00");
+                _endDate = Convert.ToDateTime($"{_year}-{_month}-{_date.Day} 08:00:00");
+                _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            }
+
+            MonthModuleTypeDtos = MesApiService.GetMonthModuleTypeDtos(_year, _month);
+
+            if (MonthModuleTypeDtos.Count == 0)
+            {
+                return;
+            }
+
+            _lines = lines;
+
+            List<Thread> threads = new List<Thread>();
+            foreach (var line in _lines)
+            {
+                Thread thread = new Thread(() =>
+                {
+                    LineModuleTypeOverview lineModuleTypeOverview = new LineModuleTypeOverview(
+                        line, _startDate, _endDate, moduleTypeLen, _rangeEndDate);
+                    LineModuleTypeOverviews.Add(lineModuleTypeOverview);
+                });
+                thread.Start();
+                threads.Add(thread);
+            }
+
+            foreach (var item in threads)
+            {
+                item.Join();
+            }
+
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                foreach (var lineModuleTypeOverview in LineModuleTypeOverviews)
+                {
+                    var rev = lineModuleTypeOverview.GetModuleTypeData(monthModuleTypeDto.ModuleType);
+                    if (rev.Item1 == true)
+                    {
+                        monthModuleTypeDto.Capacity += rev.Item3;
+                        monthModuleTypeDto.Difference += rev.Item4;
+                        monthModuleTypeDto.Lines.Add(lineModuleTypeOverview.Line);
+                    }
+                }
+            }
+
+            // 产能已经超过月计划
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                monthModuleTypeDto.Difference = monthModuleTypeDto.Capacity - monthModuleTypeDto.PlanCapacity;
+                if (monthModuleTypeDto.Capacity > monthModuleTypeDto.PlanCapacity)
+                {
+                    //monthModuleTypeDto.Difference = monthModuleTypeDto.Capacity - monthModuleTypeDto.PlanCapacity;
+                    monthModuleTypeDto.Difference = 0;
+                }
+            }
+
+            MonthModuleTypeDtos = MonthModuleTypeDtos.OrderByDescending(x => x.PlanCapacity).ToList();
+
+            // 添加FL半品
+            AddLGPAGOCA();
+        }
+
+        private void AddLGPAGOCA()
+        {
+            var plans = MesApiService.GetProductionPlanByTimelotV1(2, 20, 
+                _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"));
+
+            var outs = MesApiService.GetOutPutPerHours(
+                "LGPAGOCAData#5#05-2LGPAGOCA01", 
+                _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"));
+            
+            var kha = MonthModuleTypeDtos.FirstOrDefault(x => x.ModuleType == "KHA-FL半品");
+            if (kha != null)
+            {
+                kha.Capacity = outs.Where(x => x.ModuleType.Contains("KHA")).Sum(x => x.OutPut);
+                if (kha.Capacity > kha.PlanCapacity)
+                    kha.Difference = kha.Capacity - kha.PlanCapacity;
+                else
+                    kha.Difference = kha.PlanCapacity - kha.Capacity;
+
+                kha.Lines.Add(new ProductionLine()
+                {
+                    Floor = 2,
+                    Line = 5
+                });
+            }
+            
+            var khc = MonthModuleTypeDtos.FirstOrDefault(x => x.ModuleType == "KHC-FL半品");
+            if (khc != null)
+            {
+                khc.Capacity = outs.Where(x => x.ModuleType.Contains("KHC")).Sum(x => x.OutPut);
+                if (khc.Capacity > khc.PlanCapacity)
+                    khc.Difference = khc.Capacity - khc.PlanCapacity;
+                else
+                    khc.Difference = khc.PlanCapacity - khc.Capacity;
+            }
+        }
+
+        private readonly DateTime _date;
+        private readonly int _year;
+        private readonly int _month; 
+        private readonly DateTime _startDate;
+        private readonly DateTime _endDate;
+        private readonly DateTime _rangeEndDate;
+        private readonly IList<ProductionLine> _lines 
+            = new List<ProductionLine>();
+
+        public string Mark { get; set; } = string.Empty;
+
+        public string Title
+        {
+            get
+            {
+                return $"{_startDate:MM/dd} ~ {_rangeEndDate:MM/dd}";
+            }
+        }
+
+        public string StatisticsTime
+        {
+            get
+            {
+                return $"{_endDate.AddDays(1):MM/dd - HH}时";
+            }
+        }
+
+        public int Month
+        {
+            get
+            {
+                return _month;
+            }
+        }
+
+        public int LastMonth
+        {
+            get
+            {
+                int m = _month - 1;
+                if (m < 0)
+                    m = 12;
+                return m;
+            }
+        }
+
+        public IList<MonthModuleTypeDto> MonthModuleTypeDtos { get; set; } 
+            = new List<MonthModuleTypeDto>();
+
+        public IList<LineModuleTypeOverview> LineModuleTypeOverviews { get; set; } 
+            = new List<LineModuleTypeOverview>();
+
+    }
+
+    public class ModuleTypeOverviewV1
+    {
+        public ModuleTypeOverviewV1(IList<ProductionLine> lines, int moduleTypeLen, int year, int month, string mark)
+        {
+            _year = year;
+            _month = month;
+
+            var MonthModuleType = MesApiService.GetMonthModuleTypeDtosByMark(year, month, mark);
+            if (MonthModuleType.Count == 0)
+                return;
+
+            _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            _startDate = _rangeEndDate.AddMonths(-1).AddDays(+1);
+
+            DateTime _date = DateTime.Now;
+            if (_date < _rangeEndDate.AddDays(1))
+                _endDate = Convert.ToDateTime($"{_date.AddDays(-1):yyyy-MM-dd} 08:00:00");
+            else
+                _endDate = _rangeEndDate;
+
+            foreach (var item in MonthModuleType)
+            {
+                MonthModuleTypeDtos.Add(new MonthModuleTypeDto()
+                {
+                    ModuleType = item.ModuleType,
+                    Mark = item.Mark,
+                    Year = item.Year,
+                    Month = item.Month,
+                    PlanCapacity = item.PlanCapacity,
+                    LastModuleCapacity = item.LastModuleCapacity,
+                    Remark = item.Remark,
+                });
+            }
+
+            _lines = lines;
+
+            List<Thread> threads = new List<Thread>();
+            foreach (var line in _lines)
+            {
+                Thread thread = new Thread(() =>
+                {
+                    LineModuleTypeOverview lineModuleTypeOverview = new LineModuleTypeOverview(
+                        line, _startDate, _endDate, moduleTypeLen, _rangeEndDate);
+                    LineModuleTypeOverviews.Add(lineModuleTypeOverview);
+                });
+                thread.Start();
+                threads.Add(thread);
+            }
+
+            foreach (var item in threads)
+            {
+                item.Join();
+            }
+
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                foreach (var lineModuleTypeOverview in LineModuleTypeOverviews)
+                {
+                    var rev = lineModuleTypeOverview.GetModuleTypeData(monthModuleTypeDto.ModuleType);
+                    if (rev.Item1 == true)
+                    {
+                        monthModuleTypeDto.Capacity += rev.Item3;
+                        monthModuleTypeDto.Difference += rev.Item4;
+                        monthModuleTypeDto.Lines.Add(lineModuleTypeOverview.Line);
+                    }
+                }
+            }
+
+            //string[] ModuleTypes = MonthModuleTypeDtos
+            //    .Where(x => x.ModuleType != "" && x.ModuleType.Length >= 8)
+            //    .Select(x => x.ModuleType[..8])
+            //    .Distinct().ToArray();
+
+            List<string> ModuleTypes = new List<string>();
+            
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtos)
+            {
+                if (string.IsNullOrEmpty(monthModuleTypeDto.ModuleType))
+                {
+                    continue;
+                }
+                
+                if (monthModuleTypeDto.ModuleType.Length < 8)
+                {
+                    continue;
+                }
+
+                string mtStr = "";
+
+                // 2F 11 Line GTP-ED070KH3、FTP-AC080KH1 等 需要判断前12码
+                if (monthModuleTypeDto.ModuleType[..4] == "GTP-" ||
+                    monthModuleTypeDto.ModuleType[..4] == "FTP-")
+                {
+                    mtStr = monthModuleTypeDto.ModuleType[..12];
+                }
+                else
+                {
+                    mtStr = monthModuleTypeDto.ModuleType[..8];
+                }
+
+                if (mtStr == "")
+                {
+                    continue;
+                }
+
+                if (ModuleTypes.Any(x => x == mtStr))
+                {
+                    continue;
+                }
+
+                ModuleTypes.Add(mtStr);
+            }
+
+            foreach (var moduleType in ModuleTypes)
+            {
+                int len = moduleType.Length;
+                var dtos = MonthModuleTypeDtos.Where(x => 
+                    x.ModuleType != "" 
+                    && x.ModuleType.Length >= len
+                    && x.ModuleType[..len] == moduleType);
+                MonthModuleTypeDtoV1 v1 = new MonthModuleTypeDtoV1();
+                foreach (var d in dtos)
+                {
+                    v1.AddMonthModuleType(d);
+                    v1.AddModels(d.ModuleType);
+                    v1.AddLines(d.Lines);
+                    v1.AddPlanCapacity(d.PlanCapacity);
+                    v1.AddLastModuleCapacity(d.LastModuleCapacity);
+                    v1.AddCapacity(d.Capacity);
+                    v1.AddDifference(d.Difference);
+                    v1.AddRemarks(d.Remark);
+                }
+                MonthModuleTypeDtosV1s.Add(v1);
+            }
+
+            // 当 MTD Dev. 大于 Remainder, MTD Dev. = Remainder
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtosV1s)
+            {
+                int remainder = monthModuleTypeDto.Capacity - 
+                    (monthModuleTypeDto.PlanCapacity + monthModuleTypeDto.LastModuleCapacity);
+                
+                if (remainder < 0)
+                {
+                    if (monthModuleTypeDto.Difference < remainder)
+                    {
+                        monthModuleTypeDto.Difference = remainder;
+                    }
+                }
+            }
+
+            // 产能已经超过月计划
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtosV1s)
+            {
+                int remainder = monthModuleTypeDto.Capacity -
+                    (monthModuleTypeDto.PlanCapacity + monthModuleTypeDto.LastModuleCapacity);
+                
+                if (remainder >= 0 && monthModuleTypeDto.Difference < 500)
+                {
+                    monthModuleTypeDto.Difference = 0;
+                    //monthModuleTypeDto.ChearRemarks();
+                }
+
+                monthModuleTypeDto.Lines = monthModuleTypeDto.Lines.OrderBy(o => o.Line).ToList();
+            }
+
+            // 上月做完的MPS
+            foreach (var monthModuleTypeDto in MonthModuleTypeDtosV1s)
+            {
+                if (monthModuleTypeDto.Lines.Count() == 0 &&
+                    Math.Abs(monthModuleTypeDto.LastModuleCapacity) >= monthModuleTypeDto.PlanCapacity)
+                {
+                    monthModuleTypeDto.Capacity = Math.Abs(monthModuleTypeDto.LastModuleCapacity);
+                    monthModuleTypeDto.LastModuleCapacity = 0;
+                }
+            }
+
+            var m1 = MonthModuleTypeDtosV1s
+                .Where(x => x.Capacity < (x.PlanCapacity + x.LastModuleCapacity))
+                .OrderByDescending(o => (o.PlanCapacity + o.LastModuleCapacity)).ToList();
+            var m2 = MonthModuleTypeDtosV1s
+                .Where(x => x.Capacity >= (x.PlanCapacity + x.LastModuleCapacity))
+                .OrderByDescending(o => (o.PlanCapacity + o.LastModuleCapacity)).ToList();
+            MonthModuleTypeDtosV1s.Clear();
+            MonthModuleTypeDtosV1s.AddRange(m1);
+            MonthModuleTypeDtosV1s.AddRange(m2);
+        }
+
+        private readonly int _year;
+        private readonly int _month;
+        private readonly DateTime _startDate;
+        private readonly DateTime _endDate;
+        private readonly DateTime _rangeEndDate;
+        private readonly IList<ProductionLine> _lines
+            = new List<ProductionLine>();
+
+        public string Mark { get; set; } = string.Empty;
+
+        public string Title
+        {
+            get
+            {
+                return $"{_startDate:MM/dd} ~ {_rangeEndDate:MM/dd}";
+            }
+        }
+
+        public string StatisticsTime
+        {
+            get
+            {
+                return $"{_endDate.AddDays(1):MM/dd - HH}时";
+            }
+        }
+
+        public int Month
+        {
+            get
+            {
+                return _month;
+            }
+        }
+
+        public int LastMonth
+        {
+            get
+            {
+                int m = _month - 1;
+                if (m < 0)
+                    m = 12;
+                return m;
+            }
+        }
+
+        public IList<MonthModuleTypeDto> MonthModuleTypeDtos { get; set; }
+            = new List<MonthModuleTypeDto>();
+
+        public IList<LineModuleTypeOverview> LineModuleTypeOverviews { get; set; }
+            = new List<LineModuleTypeOverview>();
+
+        public List<MonthModuleTypeDtoV1> MonthModuleTypeDtosV1s { get; set; }
+            = new List<MonthModuleTypeDtoV1>();
+    }
+}

+ 18 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthModuleTypeCreateAndUpdateDto.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class MonthModuleTypeCreateAndUpdateDto
+    {
+        public string Id { get; set; } = string.Empty;
+        public int Year { get; set; }
+        public int Month { get; set; }
+        public string ModuleType { get; set; } = string.Empty;
+        public int PlanCapacity { get; set; }
+        public int LastModuleCapacity { get; set; }
+        public string Mark { get; set; } = string.Empty;
+        public string Remark { get; set; } = string.Empty;
+    }
+}

+ 143 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthModuleTypeDto.cs

@@ -0,0 +1,143 @@
+using OfficeOpenXml.VBA;
+using ProductionLineMonitor.Application.Services.HomeService.Models;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.AccessControl;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class MonthModuleTypeDto : MonthModuleType
+    {
+        public int Capacity { get; set; }
+        public int Difference { get; set; }
+        public double Reach
+        {
+            get
+            {
+                if ((PlanCapacity + LastModuleCapacity) <= 0)
+                    return 0;
+                return Capacity * 1.0 / (PlanCapacity + LastModuleCapacity) * 100;
+            }
+        }
+        public List<ProductionLine> Lines { get; set; } 
+            = new List<ProductionLine>();
+
+
+        public string Href
+        {
+            get
+            {
+                int[] lines = Lines.Select(x => x.Line).Distinct().ToArray();
+                return $"/MonthModule/Difference?" +
+                    $"mark={Mark}&year={Year}&month={Month}&module={ModuleType}&" +
+                    $"lines={string.Join(",", lines)}";
+            }
+        }
+    }
+
+    public class MonthModuleTypeDtoV1 : MonthModuleType
+    {
+        public IList<string> Models { get; private set; } = new List<string>();
+        public IList<ProductionLine> Lines { get; set; } 
+            = new List<ProductionLine>();
+        //public int PlanCapacity { get; private set; }
+        //public int LastModuleCapacity { get; private set; }
+        public int Capacity { get; set; }
+        public int Difference { get; set; }
+        public string Remarks { get; private set; } = string.Empty;
+
+        public void ChearRemarks()
+        {
+            Remarks = string.Empty;
+        }
+
+        public void AddMonthModuleType(MonthModuleType monthModuleType)
+        {
+            Id = monthModuleType.Id;
+            Year = monthModuleType.Year;
+            Month = monthModuleType.Month;
+            Mark = monthModuleType.Mark;
+        }
+
+        public double Reach
+        {
+            get
+            {
+                if ((PlanCapacity + LastModuleCapacity) <= 0)
+                    return 0;
+                return Capacity * 1.0 / (PlanCapacity + LastModuleCapacity) * 100;
+            }
+        }
+
+        public void AddModels(string model)
+        {
+            if (string.IsNullOrEmpty(model))
+            {
+                return;
+            }
+
+            if (Models.IndexOf(model) != -1)
+            {
+                return;
+            }
+
+            Models.Add(model);
+        }
+
+        public void AddLines(IList<ProductionLine> lines)
+        {
+            if (lines == null)
+            {
+                return;
+            }
+
+            foreach (var item in lines)
+            {
+                var l = Lines.FirstOrDefault(x => x.Id == item.Id);
+                if (l == null)
+                {
+                    Lines.Add(item);
+                }
+            }
+        }
+
+        public void AddPlanCapacity(int planCapacity)
+        {
+            PlanCapacity += planCapacity;
+        }
+
+        public void AddLastModuleCapacity(int lastModuleCapacity)
+        {
+            LastModuleCapacity += lastModuleCapacity;
+        }
+
+        public void AddCapacity(int capacity)
+        {
+            Capacity += capacity;
+        }
+
+        public void AddDifference(int difference)
+        {
+            Difference += difference;
+        }
+
+        public void AddRemarks(string remarks)
+        {
+            Remarks += remarks;
+        }
+
+        public string Href
+        {
+            get
+            {
+                int[] lines = Lines.Select(x => x.Line).Distinct().ToArray();
+                return $"/v1/MonthModule/Difference?" +
+                    $"mark={Mark}&year={Year}&month={Month}&module={string.Join(",", Models)}&" +
+                    $"lines={string.Join(",", lines)}";
+            }
+        }
+    }
+}

+ 459 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/MonthOverview.cs

@@ -0,0 +1,459 @@
+using NPOI.HSSF.Record.Chart;
+using NPOI.OpenXmlFormats.Dml;
+using NPOI.SS.Formula.Functions;
+using OfficeOpenXml.VBA;
+using Org.BouncyCastle.Crypto.EC;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Core.Utils;
+using ProductionLineMonitor.Web.Services;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using static OfficeOpenXml.ExcelErrorValue;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class MonthOverview
+    {
+        public MonthOverview(
+            string mark, int year, int month, string moduleType, 
+            IList<ProductionLine> lst) 
+        {
+            _year = year;
+            _month = month;
+
+            _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            _startDate = _rangeEndDate.AddMonths(-1).AddDays(+1);
+
+            DateTime _date = DateTime.Now;
+            if (_date <= _rangeEndDate)
+                _endDate = Convert.ToDateTime($"{_date.AddDays(-1):yyyy-MM-dd} 08:00:00");
+            else
+                _endDate = _rangeEndDate;
+
+            Dates = DateTimeHelper.GetDateString(_startDate, _rangeEndDate).ToArray();
+
+            _planCapacities = new int[Dates.Length];
+            _capacities = new int[Dates.Length];
+
+            NewDates = new string[Dates.Length];
+            PlanCapacities = new string[Dates.Length];
+            Capacities = new string[Dates.Length]; 
+            Differences = new string[Dates.Length];
+
+            for (int i = 0; i < lst.Count; i++)
+            {
+                var plans = MesApiService.GetProductionPlanByTimelotV1(lst[i].Floor, lst[i].Line,
+                    _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"))
+                    .Where(x => string.IsNullOrEmpty(moduleType) != true && x.ModuleType.Length >= 10 && x.ModuleType[..10] == moduleType);
+                var outs = MesApiService.GetOutPutPerHours(lst[i].HourDataTopic,
+                    _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"))
+                    .Where(x => string.IsNullOrEmpty(moduleType) != true && x.ModuleType.Length >= 10 && x.ModuleType[..10] == moduleType);
+                for (int j = 0; j < Dates.Length; j++)
+                {
+                    NewDates[j] = $"{Dates[j].Substring(8, 2)}";
+                    
+                    _planCapacities[j] += plans.Where(x => x.ShiftDate == Dates[j]).Sum(x => x.PlanCapacity);
+                    if (_planCapacities[j] == 0)
+                        PlanCapacities[j] = "";
+                    else
+                        PlanCapacities[j] = _planCapacities[j].ToString();
+
+                    _capacities[j] += GetOuts(Dates[j], outs).Sum(x => x.OutPut);
+                    if (_capacities[j] == 0)
+                        Capacities[j] = "";
+                    else
+                        Capacities[j] = _capacities[j].ToString();
+
+                    if (_planCapacities[j] == 0 || _capacities[j] == 0)
+                    {
+                        Differences[j] = "";
+                    }
+                    else
+                    {
+                        int d = _planCapacities[j] - _capacities[j];
+                        Differences[j] = d > 0 ? d.ToString() : "";
+                    }
+                }
+            }
+        }
+
+        public MonthOverview(int year, int month, string moduleType,
+            IList<ProductionLine> lst, IList<string> moduleTypes, int line)
+        {
+            _year = year;
+            _month = month;
+            _rangeEndDate = Convert.ToDateTime($"{_year}-{_month}-20 08:00:00");
+            _startDate = _rangeEndDate.AddMonths(-1).AddDays(+1);
+            
+            DateTime _date = DateTime.Now;
+            if (_date < _rangeEndDate.AddDays(1))
+                _endDate = Convert.ToDateTime($"{_date.AddDays(-1):yyyy-MM-dd} 08:00:00");
+            else
+                _endDate = _rangeEndDate;
+
+            Dates = DateTimeHelper.GetDateString(_startDate, _rangeEndDate).ToArray();
+
+            _planCapacities = new int[Dates.Length];
+            _capacities = new int[Dates.Length];
+            NewDates = new string[Dates.Length];
+            PlanCapacities = new string[Dates.Length];
+            Capacities = new string[Dates.Length];
+            Differences = new string[Dates.Length];
+
+            AddX();
+
+            AddLinePlans(line, lst, moduleType, moduleTypes);
+
+            _ps = new List<ProductionPlanDtoV1>();
+            _os = new List<MachineDayOutPutPerHour>();
+            
+            GetPsOrOsByLine(line, lst);
+            
+            GetPsOrOsByModuleType(moduleType, moduleTypes);
+
+            AddY();
+
+            Difference = _os.Sum(x => x.OutPut) - 
+                _ps.Where(x => Convert.ToDateTime($"{x.ShiftDate} 08:00:00") <= _endDate)
+                .Sum(x => x.PlanCapacity);
+
+            AddKeyInInfos(line, lst);
+
+            AddDifferenceInfosTop5();
+        }
+
+        private void AddLinePlans(int line, IList<ProductionLine> lines, string moduleType, IList<string> moduleTypes)
+        {
+            List<ProductionPlanDtoV1> ps = new List<ProductionPlanDtoV1>();
+
+            if (moduleType != "ALL" && line != 0)
+            {                
+                var l = lines.FirstOrDefault(x => x.Line == line);
+                if (l == null)
+                {
+                    return;
+                }
+
+                ps = MesApiService.GetProductionPlanByTimelotV1(l.Floor, l.Line,
+                        _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+                
+                ps = ps.Where(x => string.IsNullOrEmpty(moduleType) != true
+                        && x.ModuleType.Length >= 10
+                        && x.ModuleType == moduleType).ToList();
+
+                LinePlans.Add(new LinePlan(Dates, ps, l.Line));
+
+                return;
+            }
+
+            if (moduleType != "ALL" && line == 0)
+            {
+                for (int i = 0; i < lines.Count; i++)
+                {
+                    ps = MesApiService.GetProductionPlanByTimelotV1(lines[i].Floor, lines[i].Line,
+                            _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+
+                    ps = ps.Where(x => string.IsNullOrEmpty(moduleType) != true
+                        && x.ModuleType.Length >= 10
+                        && x.ModuleType == moduleType).ToList();
+
+                    LinePlans.Add(new LinePlan(Dates, ps, lines[i].Line));
+                }
+
+                return;
+            }
+
+            if (moduleType == "ALL" && line == 0)
+            {
+                for (int i = 0; i < lines.Count; i++)
+                {
+                    foreach (var item in moduleTypes)
+                    {
+                        ps = MesApiService.GetProductionPlanByTimelotV1(lines[i].Floor, lines[i].Line,
+                            _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+
+                        ps = ps.Where(x => string.IsNullOrEmpty(item) != true
+                            && x.ModuleType.Length >= 10
+                            && x.ModuleType == item).ToList();
+
+                        LinePlans.Add(new LinePlan(Dates, ps, lines[i].Line));
+                    }
+                }
+
+                return;
+            }
+
+            if (moduleType == "ALL" && line != 0)
+            {
+                var l1 = lines.FirstOrDefault(x => x.Line == line);
+                if (l1 == null)
+                {
+                    return;
+                }
+
+                foreach (var item in moduleTypes)
+                {
+                    ps = MesApiService.GetProductionPlanByTimelotV1(l1.Floor, l1.Line,
+                        _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+
+                    ps = ps.Where(x => string.IsNullOrEmpty(item) != true
+                        && x.ModuleType.Length >= 10
+                        && x.ModuleType == item).ToList();
+
+                    LinePlans.Add(new LinePlan(Dates, ps, l1.Line));
+                }
+
+                return;
+            }
+        }
+
+        private void AddKeyInInfos(int line, IList<ProductionLine> lines)
+        {
+            for (int i = 0; i < Differences.Length; i++)
+            {
+                if (Differences[i] != "")
+                {
+                    DateTime start = Convert.ToDateTime($"{Dates[i]} 8:00:00");
+                    if (line == 0)
+                    {
+                        foreach (var item in lines)
+                        {
+                            KeyInInfos.AddRange(MesApiService.GetAlarmByKeyIn(item.Floor, item.Line,
+                                start, start.AddDays(1)));
+                        }
+                    }
+                    else
+                    {
+                        var l = lines.FirstOrDefault(x => x.Line == line);
+                        if (l != null)
+                        {
+                            KeyInInfos.AddRange(MesApiService.GetAlarmByKeyIn(l.Floor, l.Line,
+                                start, start.AddDays(1)));
+                        }
+                    } 
+                }
+            }
+        }
+        private void GetPsOrOsByLine(int line, IList<ProductionLine> lines)
+        {
+            if (line == 0)
+            {
+                for (int i = 0; i < lines.Count(); i++)
+                {
+                    List<ProductionPlanDtoV1> ps = MesApiService.GetProductionPlanByTimelotV1(lines[i].Floor, lines[i].Line,
+                        _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+                    _ps.AddRange(ps);
+                    _os.AddRange(MesApiService.GetOutPutPerHours(lines[i].HourDataTopic,
+                        _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd")));
+                }
+
+                return;
+            }
+
+            var l = lines.FirstOrDefault(x => x.Line == line);
+            if (l == null)
+            {
+                return;
+            }
+
+            List<ProductionPlanDtoV1> ps1 = MesApiService.GetProductionPlanByTimelotV1(l.Floor, l.Line,
+                _startDate.ToString("yyyy-MM-dd"), _rangeEndDate.ToString("yyyy-MM-dd"));
+
+            _ps.AddRange(ps1);
+            _os.AddRange(MesApiService.GetOutPutPerHours(l.HourDataTopic,
+                _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd")));
+        }
+        private void GetPsOrOsByModuleType(string moduleType, IList<string> moduleTypes)
+        {
+            if (moduleType != "ALL")
+            {
+                _ps = _ps.Where(x => string.IsNullOrEmpty(moduleType) != true
+                    && x.ModuleType.Length >= 10
+                    && x.ModuleType[..moduleType.Length] == moduleType).ToList();
+                _os = _os.Where(x => string.IsNullOrEmpty(moduleType) != true
+                    && x.ModuleType.Length >= 10
+                    && x.ModuleType[..moduleType.Length] == moduleType).ToList();
+            }
+            else
+            {
+                List<ProductionPlanDtoV1> p1 = _ps.ToList();
+                List<MachineDayOutPutPerHour> o1 = _os.ToList();
+                _ps.Clear();
+                _os.Clear();
+
+                foreach (var item in moduleTypes)
+                {
+                    _ps.AddRange(p1.Where(x => string.IsNullOrEmpty(item) != true
+                        && x.ModuleType.Length >= 10
+                        && x.ModuleType == item).ToList());
+                    _os.AddRange(o1.Where(x => string.IsNullOrEmpty(item) != true
+                        && x.ModuleType.Length >= 10
+                        && x.ModuleType.Length >= item.Length
+                        && x.ModuleType[..item.Length] == item).ToList());
+                }
+            }
+        }
+        private void AddX()
+        {
+            for (int j = 0; j < Dates.Length; j++)
+            {
+                NewDates[j] = $"{Dates[j].Substring(8, 2)}";
+                DateTime d = Convert.ToDateTime(Dates[j]);
+                if (d.DayOfWeek == DayOfWeek.Sunday)
+                {
+                    NewDates[j] += "\n周日";
+                }
+            }
+        }
+        private void AddY()
+        {
+            for (int j = 0; j < Dates.Length; j++)
+            {
+                int plan = _ps.Where(x => x.ShiftDate == Dates[j]).Sum(x => x.PlanCapacity);
+
+                _planCapacities[j] += plan;
+                if (_planCapacities[j] == 0)
+                    PlanCapacities[j] = "";
+                else
+                    PlanCapacities[j] = _planCapacities[j].ToString();
+
+                _capacities[j] += GetOuts(Dates[j], _os).Sum(x => x.OutPut);
+                if (_capacities[j] == 0)
+                    Capacities[j] = "";
+                else
+                    Capacities[j] = _capacities[j].ToString();
+
+                if (_planCapacities[j] == 0 || _capacities[j] == 0)
+                {
+                    Differences[j] = "";
+                }
+                else
+                {
+                    int d = _planCapacities[j] - _capacities[j];
+                    Differences[j] = d > 0 ? d.ToString() : "";
+                }
+            }
+        }
+        private void AddDifferenceInfosTop5()
+        {
+            List<DifferenceInfo> differenceInfos = new List<DifferenceInfo>();
+
+            foreach (var item in KeyInInfos)
+            {
+                var d = differenceInfos.FirstOrDefault(x => x.Description == item.Description);
+                if (d == null)
+                {
+                    differenceInfos.Add(new DifferenceInfo()
+                    {
+                        Description = item.Description,
+                        Frequency = 1,
+                        DifferenceTime = item.AffectTime,
+                        TypeName = item.KeyInTypeName
+                    });
+                }
+                else
+                {
+                    d.Frequency += 1;
+                    d.DifferenceTime += item.AffectTime;
+                }
+            }
+
+            DifferenceInfos = differenceInfos.OrderByDescending(o => o.DifferenceTime).Take(5).ToList();
+        }
+        private IEnumerable<MachineDayOutPutPerHour> GetOuts(string date, IEnumerable<MachineDayOutPutPerHour> HourOuts)
+        {
+            DateTime sTime = Convert.ToDateTime($"{date} 08:00:00");
+            DateTime eTime = sTime.AddHours(24);
+            return HourOuts.Where(x => x.DataTime >= sTime && x.DataTime < eTime);
+        }
+
+        private readonly DateTime _startDate;
+        private readonly DateTime _endDate;
+        private readonly DateTime _rangeEndDate;
+        private readonly int _year;
+        private readonly int _month;
+        private readonly int[] _planCapacities;
+        private readonly int[] _capacities;
+        private List<ProductionPlanDtoV1> _ps;
+        private List<MachineDayOutPutPerHour> _os;
+
+        public string[] Dates { get; set; }
+        public string[] NewDates { get; set; }
+        public string[] PlanCapacities { get; set; }
+        public string[] Capacities { get; set; }
+        public string[] Differences { get; set; }
+        public int Difference { get; set; }
+        public List<KeyInInfo> KeyInInfos { get; set; } = new List<KeyInInfo>();
+        public List<DifferenceInfo> DifferenceInfos { get; set; } = new List<DifferenceInfo>();
+        public List<LinePlan> LinePlans { get; set; } = new List<LinePlan>();
+    }
+
+    public class DifferenceInfo
+    {
+        public string TypeName { get; set; } = string.Empty;
+        public string Description { get; set; } = string.Empty;
+        public string MachineType { get; set; } = string.Empty;
+        public int Frequency { get; set; }
+        public int DifferenceTime { get; set; }
+    }
+
+    public class LinePlan
+    {
+        public LinePlan(string[] dates, List<ProductionPlanDtoV1> plans, int line)
+        {
+            Line = line;
+            PlanDates = new string[dates.Length];
+            PlanCapacity = 0;
+
+            int[] values = new int[dates.Length + 2];
+
+            for (int i = 1; i <= dates.Length; i++)
+            {
+                int plan = plans.Where(x => x.ShiftDate == dates[i - 1]).Sum(x => x.PlanCapacity);
+                PlanCapacity += plan;
+                values[i] = plan == 0 ? 0 : 1;
+            }
+
+            // 平滑过渡周日无排程
+            for (int i = 0; i < values.Length; i++)
+            {
+                if (values[i] == 1)
+                {
+                    continue;
+                }
+
+                if (i - 1 < 0 || i + 1 >= values.Length)
+                {
+                    continue;
+                }
+
+                if (values[i - 1] == 1 && values[i + 1] == 1)
+                {
+                    values[i] = 1;
+                }
+            }
+
+            for (int i = 1; i < values.Length - 1; i++)
+            {
+                if (values[i] == 1)
+                {
+                    PlanDates[i - 1] = "1";
+                }
+                else
+                {
+                    PlanDates[i - 1] = "";
+                }
+            }
+
+            Start = Array.IndexOf(PlanDates, "1");
+        }
+        public int Line { get; set; }
+        public int PlanCapacity { get; set; }
+        public string[] PlanDates { get; set; }
+        public int Start { get; set; }
+    }
+}

+ 22 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/ProductionPlanDto.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class ProductionPlanDto
+    {
+        public DateTime ShiftDate { get; set; }
+        public string Shift { get; set; } = string.Empty;
+        public string ModuleType { get; set; } = string.Empty;
+        public int PlanCapacity { get; set; }
+    }
+
+    public class ProductionPlanDtoV1
+    {
+        public string ShiftDate { get; set; } = string.Empty;
+        public string Shift { get; set; } = string.Empty;
+        public string ModuleType { get; set; } = string.Empty;
+        public int PlanCapacity { get; set; }
+    }
+}

+ 14 - 0
ProductionLineMonitor.Application/Services/HomeService/Dtos/UpdateRemarkDto.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Dtos
+{
+    public class UpdateRemarkDto
+    {
+        public string Id { get; set; } = string.Empty;
+        public string Remark { get; set; } = string.Empty;
+        public int Year { get; set; }
+        public int Month { get; set; }
+    }
+}

+ 148 - 0
ProductionLineMonitor.Application/Services/HomeService/HomeService.cs

@@ -0,0 +1,148 @@
+using NPOI.SS.Formula.Functions;
+using ProductionLineMonitor.Application.Services.HomeService.Dtos;
+using ProductionLineMonitor.Application.Services.HomeService.Models;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Web.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService
+{
+    public class HomeService : IHomeService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        public HomeService(IUnitOfWork unitOfWork)
+        {
+            _unitOfWork = unitOfWork;
+        }
+
+        public ResultDto CreateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            DateTime date = DateTime.Now;
+            int year, month;
+            year = date.Year;
+            if (date.Day >= 21)
+                month = date.Month + 1;
+            else
+                month = date.Month;
+
+            // 判断机种是否已经添加
+            IList<MonthModuleTypeDto> monthModuleTypes = MesApiService.GetMonthModuleTypeDtos(year, month);
+            if (monthModuleTypes != null)
+            {
+                if (monthModuleTypes.Any(x => x.ModuleType == dto.ModuleType))
+                {
+                    return ResultDto.Fail($"当月{dto.ModuleType}机种已经添加");
+                }
+            }
+
+            dto.Id = Guid.NewGuid().ToString();
+            dto.Year = year;
+            dto.Month = month;
+
+            return MesApiService.CreateMonthModuleType(dto);
+        }
+
+        public ResultDto CreateMonthModuleTypeV1(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            // 判断机种是否已经添加
+            IList<MonthModuleType> monthModuleTypes = MesApiService
+                .GetMonthModuleTypeDtosByMark(dto.Year, dto.Month, dto.Mark);
+            if (monthModuleTypes != null)
+            {
+                if (monthModuleTypes.Any(x => x.ModuleType == dto.ModuleType))
+                {
+                    return ResultDto.Fail($"当月{dto.ModuleType}机种已经添加");
+                }
+            }
+
+            dto.Id = Guid.NewGuid().ToString();
+
+            return MesApiService.CreateMonthModuleType(dto);
+        }
+
+        public ResultDto DeleteMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.Id))
+                return ResultDto.Fail("Id 异常!");
+            return MesApiService.DeleteMonthModuleType(dto);
+        }
+
+        public ResultDto UpdateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            DateTime date = DateTime.Now;
+            int year, month;
+            year = date.Year;
+            if (date.Day >= 21)
+                month = date.Month + 1;
+            else
+                month = date.Month;
+
+            IList<MonthModuleTypeDto> monthModuleTypes = MesApiService.GetMonthModuleTypeDtos(year, month);
+            if (monthModuleTypes == null)
+            {
+                return ResultDto.Fail("修改对象不存在!");
+            }
+
+            dto.Year = year;
+            dto.Month = month;
+
+            return MesApiService.UpdateMonthModuleType(dto);
+        }
+
+        public ResultDto UpdateMonthModuleTypeV1(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            // 判断机种是否已经添加
+            IList<MonthModuleType> monthModuleTypes = MesApiService
+                .GetMonthModuleTypeDtosByMark(dto.Year, dto.Month, dto.Mark);
+            if (monthModuleTypes == null)
+            {
+                return ResultDto.Fail("修改对象不存在!");
+            }
+            return MesApiService.UpdateMonthModuleType(dto);
+        }
+
+        public ModuleTypeOverview GetModuleTypeOverview()
+        {
+            var lines = GetLinesByMark("2F");
+            return new ModuleTypeOverview(lines, 10);
+        }
+
+        public IEnumerable<MonthModuleType> GetMonthModuleTypes(int year, int month)
+        {
+            return MesApiService.GetMonthModuleTypes(year, month).OrderByDescending(o => o.PlanCapacity);
+        }
+
+        public ModuleTypeOverview GetModuleTypeOverview(string mark, int year, int month)
+        {
+            IList<ProductionLine> lines = GetLinesByMark(mark);
+            return new ModuleTypeOverview(lines, 10, year, month, mark);
+        }
+
+        private IList<ProductionLine> GetLinesByMark(string mark)
+        {
+            return mark switch
+            {
+                "2F" => _unitOfWork.ProductionLineRepository.GetList(x => x.Floor == 2 && x.MakeClassification == "EPD").ToList(),
+                "1F" => _unitOfWork.ProductionLineRepository.GetList(x => x.Floor == 1 && x.MakeClassification == "EPD").ToList(),
+                "FPL" => _unitOfWork.ProductionLineRepository.GetList(x => x.Floor == 1 && x.MakeClassification == "FPL").ToList(),
+                _ => new List<ProductionLine>(),
+            };
+        }
+
+        public IEnumerable<MonthModuleType> GetMonthModuleTypesByMark(string mark, int year, int month)
+        {
+            return MesApiService.GetMonthModuleTypeDtosByMark(year, month, mark).OrderByDescending(o => o.PlanCapacity);
+        }
+
+        public ModuleTypeOverviewV1 GetModuleTypeOverviewV1(string mark, int year, int month)
+        {
+            IList<ProductionLine> lines = GetLinesByMark(mark);
+            return new ModuleTypeOverviewV1(lines, 10, year, month, mark);
+        }
+    }
+}

+ 23 - 0
ProductionLineMonitor.Application/Services/HomeService/IHomeService.cs

@@ -0,0 +1,23 @@
+using ProductionLineMonitor.Application.Services.HomeService.Dtos;
+using ProductionLineMonitor.Application.Services.HomeService.Models;
+using ProductionLineMonitor.Core.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService
+{
+    public interface IHomeService
+    {
+        ModuleTypeOverview GetModuleTypeOverview();
+        IEnumerable<MonthModuleType> GetMonthModuleTypesByMark(string mark, int year, int month);
+        IEnumerable<MonthModuleType> GetMonthModuleTypes(int year, int month);
+        ResultDto CreateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto);
+        ResultDto UpdateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto);
+        ResultDto CreateMonthModuleTypeV1(MonthModuleTypeCreateAndUpdateDto dto);
+        ResultDto UpdateMonthModuleTypeV1(MonthModuleTypeCreateAndUpdateDto dto);
+        ResultDto DeleteMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto);
+        ModuleTypeOverview GetModuleTypeOverview(string mark, int year, int month);
+        ModuleTypeOverviewV1 GetModuleTypeOverviewV1(string mark, int year, int month);
+    }
+}

+ 18 - 0
ProductionLineMonitor.Application/Services/HomeService/Models/MonthModuleType.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.HomeService.Models
+{
+    public class MonthModuleType
+    {
+        public string Id { get; set; } = string.Empty;
+        public int Year { get; set; }
+        public int Month { get; set; }
+        public string ModuleType { get; set; } = string.Empty;
+        public int PlanCapacity { get; set; }
+        public int LastModuleCapacity { get; set; }
+        public string Mark { get; set; } = string.Empty;
+        public string Remark { get; set; } = string.Empty;
+    }
+}

+ 11 - 0
ProductionLineMonitor.Application/Services/IExcelService.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services
+{
+    public interface IExcelService
+    {
+        bool EquipmentOperationReport(DateTime dateTime);
+    }
+}

+ 12 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/CapaDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    public class CapaDto
+    {
+        public int Capa {  get; set; }
+        public double TargetTT { get; set; }
+    }
+}

+ 86 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/KeyInInfo.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Net.NetworkInformation;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class KeyInInfo
+    {
+        /// <summary>
+        /// 计划 Id 唯一标识
+        /// </summary>
+        public string Id { get; set; } = string.Empty;
+
+        /// <summary>
+        ///  0:品质异常停线
+        ///  1:宕机
+        ///  2:换线
+        ///  3:实验
+        ///  4:W/F送样
+        ///  5:物料缺料或生管调整影响
+        ///  6:放假
+        ///  7:停电气等停线
+        ///  8:换耗材类
+        ///  9:停机未生产
+        /// 10:缺WF:2023-09-22 IT与制造将 “效率爬升” 修改为 “缺WF”
+        /// </summary>
+        public int KeyInType { get; set; }
+
+        public string KeyInTypeName
+        {
+            get
+            {
+                return KeyInType switch
+                {
+                    0 => "品质异常停线",
+                    1 => "宕机",
+                    2 => "换线",
+                    3 => "实验",
+                    4 => "W/F送样/前导批",
+                    5 => "物料缺料或生管调整影响",
+                    6 => "放假",
+                    7 => "停电气等停线",
+                    8 => "换耗材",
+                    9 => "停机未生产",
+                    10 => "缺WF",
+                    _ => "",
+                };
+            }
+        }
+
+        /// <summary>
+        ///  描述
+        ///  0:品质异常停线
+        ///  1:宕机
+        ///  2:换线                       机种切换-有程序 EL097ER1->TS097SC1
+        ///  3:实验                       EL097ER1 1300pcs 李文健 测试新程序
+        ///  4:W/F送样
+        ///  5:物料缺料或生管调整影响
+        ///  6:放假
+        ///  7:停电气等停线
+        ///  8:换耗材类
+        ///  9:停机未生产
+        /// 10:缺WF
+        /// </summary>
+        public string Description { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public DateTime? StartTime { get; set; }
+
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 影响时间 min
+        /// </summary>
+        public int AffectTime { get; set; }
+
+        /// <summary>
+        /// 影响产能
+        /// </summary>
+        public int AffectCapacity { get; set; }
+    }
+}

+ 14 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/LineKeyInInfo.cs

@@ -0,0 +1,14 @@
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class LineKeyInInfo : ProductionLine
+    {
+        public List<ProductionPlanDto> ProductionPlans { get; set; } = new List<ProductionPlanDto>();
+        public List<KeyInInfo> KeyInInfos { get; set; } = new List<KeyInInfo>();
+    }
+}

+ 313 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/LineMonthData.cs

@@ -0,0 +1,313 @@
+using ProductionLineMonitor.Core.Models;
+using ProductionLineMonitor.Core.Utils;
+using ProductionLineMonitor.Web.Services;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    public class LineMonthData
+    {
+        public LineMonthData(ProductionLine line, DateTime date, int moduleTypeLen, string moduleType)
+        {
+            DateTimeHelper.GetStartEndTime(date, out _startDate, out _endDate);
+
+            Plans = MesApiService.GetProductionPlanByTimelotV1(
+                line.Floor, line.Line,
+                _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"));
+
+            // 兼容 8/20日之前机种为8码数据
+            if (date <= Convert.ToDateTime("2023-08-20 08:00:00"))
+            {
+                foreach (var item in Plans)
+                {
+                    if (item.ModuleType != "" && item.ModuleType.Length > 10)
+                    {
+                        item.ModuleType = item.ModuleType[..10];
+                    }
+                }
+            }
+
+            HourOuts = MesApiService.GetOutPutPerHours(
+                line.HourDataTopic,
+                _startDate.ToString("yyyy-MM-dd"), _endDate.ToString("yyyy-MM-dd"));
+
+            Dates = DateTimeHelper.GetDateString(_startDate, _endDate).ToArray();
+
+            NewDates = new string[Dates.Length];
+            PlanCapacities = new string[Dates.Length];
+            Capacities = new string[Dates.Length];
+            Differences = new string[Dates.Length];
+            AccumulatedDifferences = new string[Dates.Length];
+
+            ModuleTypes = Plans
+                .Where(x => x.ModuleType != "")
+                .Select(x => x.ModuleType)
+                .Distinct().ToArray();
+
+            if (moduleType != "ALL")
+            {
+                Plans = Plans.Where(
+                    x => x.ModuleType != "" && 
+                    x.ModuleType.Length >= moduleType.Length && 
+                    x.ModuleType[..moduleType.Length] == moduleType)
+                    .OrderBy(o => o.ShiftDate)
+                    .ToList();
+
+                HourOuts = HourOuts.Where(
+                    x => x.ModuleType != "" && 
+                    x.ModuleType.Length >= moduleType.Length && 
+                    x.ModuleType[..moduleType.Length] == moduleType)
+                    .ToList();
+
+                var ts = Plans.OrderBy(o => o.ShiftDate)
+                        .ToList();
+
+                var sValues = GetPlanGanttChart(ts);
+
+                foreach (var item in sValues)
+                {
+                    Serie serie = new Serie(Dates.Length)
+                    {
+                        Name = moduleType,
+                        Start = item.Start,
+                        End = item.End,
+                        Values = item.Values,
+                        PlanCapacities = item.Value,
+                        StartDate = item.StartDate,
+                        EndDate = item.EndDate
+                    };
+                    Series.Add(serie);
+                }
+            }
+            else
+            {
+                foreach (var module in ModuleTypes)
+                {
+                    var ts = Plans.Where(x => x.ModuleType == module)
+                        .OrderBy(o => o.ShiftDate)
+                        .ToList();
+
+                    if (ts.Count() == 0)
+                    {
+                        continue;
+                    }
+
+                    var sValues = GetPlanGanttChart(ts);
+
+                    foreach (var item in sValues)
+                    {
+                        Serie serie = new Serie(Dates.Length)
+                        {
+                            Name = module,
+                            Start = item.Start,
+                            End = item.End,
+                            Values = item.Values,
+                            PlanCapacities = item.Value,
+                            StartDate = item.StartDate,
+                            EndDate = item.EndDate
+                        };
+                        Series.Add(serie);
+                    }
+                }
+            }
+
+            Series = Series.OrderBy(o => o.Start).ToList();
+
+            for (int i = 0; i < Dates.Length; i++)
+            {
+                NewDates[i] = $"{Dates[i].Substring(8, 2)}";
+                DateTime day = Convert.ToDateTime(Dates[i]);
+                if (day.DayOfWeek == DayOfWeek.Sunday)
+                {
+                    NewDates[i] += "\n周日";
+                }
+
+                int v1 = Plans.Where(x => x.ShiftDate == Dates[i]).Sum(x => x.PlanCapacity);
+                PlanCapacities[i] = v1 == 0 ? "" : v1.ToString();
+
+                DateTime d = Convert.ToDateTime(Dates[i]);
+                if (d < DateTime.Now.AddDays(-1))
+                {
+                    var outs = GetOuts(Dates[i]);
+                    int v2 = outs.Sum(x => x.OutPut);
+                    if (v1 == 0 && v2 == 0)
+                        Capacities[i] = "";
+                    else
+                        Capacities[i] = v2.ToString();
+
+                    int v3 = v2 - v1;
+                    if (v3 < 0)
+                        Differences[i] = Math.Abs(v3).ToString();
+                    else
+                        Differences[i] = "";
+
+                    if (i == 0)
+                        AccumulatedDifferences[i] = v3.ToString();
+                    else
+                        AccumulatedDifferences[i] = (Convert.ToInt32(AccumulatedDifferences[i - 1]) + v3).ToString();
+
+                    AccumulatedDifference = Convert.ToInt32(AccumulatedDifferences[i]);
+                }
+            }
+        }
+
+        private List<Position> GetPlanGanttChart(List<HomeService.Dtos.ProductionPlanDtoV1> ts)
+        {
+            int[] values = new int[Dates.Length + 2];
+            int[] values1 = new int[Dates.Length + 2];
+            
+            // 生产:1 不生产:0
+            for (int i = 1; i <= Dates.Length; i++)
+            {
+                int v1 = ts.Where(x => x.ShiftDate == Dates[i - 1]).Sum(x => x.PlanCapacity);
+                values[i] = v1 == 0 ? 0 : 1;
+                values1[i] = v1;
+            }
+
+            // 平滑过渡周日无排程
+            for (int i = 0; i < values.Length; i++)
+            {
+                if (values[i] == 1)
+                {
+                    continue;
+                }
+
+                if (i - 1 < 0 || i + 1 >= values.Length)
+                {
+                    continue;
+                }
+
+                if (values[i - 1] == 1 && values[i + 1] == 1)
+                {
+                    values[i] = 1;
+                }
+            }
+
+            // 循环查找 0-1 或 1-0 变化坐标
+            List<Position> xs = new List<Position>();
+
+            for (int i = 1; i <= values.Length - 1; i++)
+            {
+                if (values[i] == 1 && values[i - 1] == 0)
+                {
+                    for (int j = i; j <= values.Length - 1; j++)
+                    {
+                        if (values[j] == 0 && values[j - 1] == 1)
+                        {
+                            Position position = new Position(Dates.Length)
+                            {
+                                Start = i - 1,
+                                End = j - 2,
+                            };
+                            for (int k = position.Start; k <= position.End; k++)
+                            {
+                                position.Values[k] = "1";
+                                position.Value += values1[k + 1];
+                            }
+                            position.StartDate = (string)Dates.GetValue(position.Start);
+                            position.EndDate = (string)Dates.GetValue(position.End);
+                            xs.Add(position);
+                            i = j;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            return xs;
+        }
+
+        public string MonthTitle
+        {
+            get
+            {
+                return $"{_startDate:MM/dd} ~ {_endDate:MM/dd}";
+            }
+        }
+
+        private IEnumerable<MachineDayOutPutPerHour> GetOuts(string date)
+        {
+            DateTime sTime = Convert.ToDateTime($"{date} 08:00:00");
+            DateTime eTime = sTime.AddHours(24);
+            return HourOuts.Where(x => x.DataTime >= sTime && x.DataTime < eTime);
+        }
+
+        private readonly DateTime _startDate;
+        private readonly DateTime _endDate;
+
+        private IList<HomeService.Dtos.ProductionPlanDtoV1> Plans { get; set; }
+            = new List<HomeService.Dtos.ProductionPlanDtoV1>();
+        private IList<MachineDayOutPutPerHour> HourOuts { get; set; }
+            = new List<MachineDayOutPutPerHour>();
+
+        /// <summary>
+        /// 日期
+        /// </summary>
+        public string[] Dates { get; set; }
+        /// <summary>
+        /// 日期图表显示
+        /// </summary>
+        public string[] NewDates { get; set; }
+        /// <summary>
+        /// 计划产能
+        /// </summary>
+        public string[] PlanCapacities { get; set; }
+        /// <summary>
+        /// 差异产能
+        /// </summary>
+        public string[] Differences { get; set; }
+        /// <summary>
+        /// 实际产能
+        /// </summary>
+        public string[] Capacities { get; set; }
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string[] ModuleTypes { get; set; }
+        /// <summary>
+        /// 累计差异趋势
+        /// </summary>
+        public string[] AccumulatedDifferences { get; set; }
+        /// <summary>
+        /// 累计差异
+        /// </summary>
+        public int AccumulatedDifference { get; set; }
+
+        public IList<Serie> Series { get; set; } 
+            = new List<Serie>();  
+    }
+
+    public class Serie
+    {
+        public Serie(int len)
+        {
+            Values = new string[len];
+        }
+
+        public string Name { get; set; } = string.Empty;
+        public string[] Values { get; set; }
+        public List<int> Indexs { get; set; } = new List<int>();
+        public int Start { get; set; }
+        public int End { get; set; }
+        public int PlanCapacities { get; set; }
+        public string StartDate { get; set; } = string.Empty;
+        public string EndDate { get; set; } = string.Empty;
+    }
+
+    public class Position
+    {
+        public Position(int len)
+        {
+            Values = new string[len];
+        }
+        public int Start { get; set; }
+        public int End { get; set; }
+        public string[] Values { get; set; }
+        public int Value {  get; set; }
+        public string StartDate { get; set; } = string.Empty;
+        public string EndDate { get; set; } = string.Empty;
+    }
+}

+ 43 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/LineOverviewDto.cs

@@ -0,0 +1,43 @@
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    public class OverviewProductionPlanDto : ProductionPlan
+    {
+        public OverviewProductionPlanDto()
+        {
+
+        }
+
+        /// <summary>
+        /// 产能
+        /// </summary>
+        public int Capacity { get; set; }
+
+        public double AchievementRate
+        {
+            get
+            {
+                if (PlanCapacity == 0)
+                {
+                    return 0;
+                }
+                return Math.Round((float)Capacity / PlanCapacity * 100, 0);
+            }
+        }
+    }
+
+    public class LineOverviewDto
+    {
+        public string Id { get; set; } = string.Empty;
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public string LineName { get; set; } = string.Empty;
+        public string Name { get; set; } = string.Empty;
+        public IList<OverviewProductionPlanDto> OverviewProductionPlans { get; set; }
+            = new List<OverviewProductionPlanDto>();
+    }
+}

+ 73 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/MachineDayOutPutPerHour.cs

@@ -0,0 +1,73 @@
+using System;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class MachineDayOutPutPerHour
+    {
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; } = string.Empty;
+        /// <summary>
+        /// 数据时间
+        /// Convert.ToDateTime("2023-02-21 08:00:00:00");
+        /// </summary>
+        public DateTime DataTime { get; set; }
+        /// <summary>
+        /// 运行时间
+        /// </summary>
+        public int AutoRunTime { get; set; }
+        /// <summary>
+        /// 报警时间
+        /// </summary>
+        public int AlarmTime { get; set; }
+        /// <summary>   
+        /// 待料时间
+        /// </summary>
+        public int IdleTime { get; set; }
+        /// <summary>
+        /// 上游待料时间
+        /// </summary>
+        public int IdleTimeUpstream { get; set; }
+        /// <summary>
+        /// 下游待料时间
+        /// </summary>
+        public int IdleTimeDownstream { get; set; }
+        /// <summary>
+        /// 自身待料时间
+        /// </summary>
+        public int IdleTimeSelf { get; set; }
+        /// <summary>
+        /// 产能
+        /// </summary>
+        public int OutPut { get; set; }
+        /// <summary>
+        /// 目标TT
+        /// </summary>
+        public int TargetTT { get; set; }
+        /// <summary>
+        /// 实际TT
+        /// </summary>
+        public int ActualTT { get; set; }
+        /// <summary>
+        /// 报警次数
+        /// </summary>
+        public int AlarmSum { get; set; }
+        /// <summary>
+        /// 换料次数
+        /// </summary>
+        public int LoadMATSum { get; set; }
+        /// <summary>
+        /// 换料时间
+        /// </summary>
+        public int LoadMATTime { get; set; }
+    }
+
+    public class NewMachineDayOutPutPerHour : MachineDayOutPutPerHour
+    {
+        /// <summary>
+        /// 负荷时间
+        /// </summary>
+        public int LoadTime { get; set; }
+    }
+}

+ 77 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/MachineFaultViewModel.cs

@@ -0,0 +1,77 @@
+using ProductionLineMonitor.Application.Services.FaultService.Dtos;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class MachineFaultViewModel : MachineViewModel
+    {
+        public MachineFaultViewModel(string topic, string date)
+        {
+            Date = date;
+            AnalysisTopic(topic);
+
+            ProductionPlans = MesApiService.GetProductionPlansV1(Floor, Line, Date);
+            if (ProductionPlans.Count == 0)
+                return;
+            if (ProductionPlans.Count == 1)
+                if (ProductionPlans[0].ModuleType == "未指定")
+                    return;
+
+            KeyInInfos = MesApiService.GetKeyInInfos(Floor, Line, Date);
+            foreach (var key in KeyInInfos)
+            {
+                if (key.KeyInType == 2 || key.KeyInType == 3 || key.KeyInType == 4 ||
+                    key.KeyInType == 6 || key.KeyInType == 7 || key.KeyInType == 9)
+                    if (key.AffectTime >= 1440)
+                        return;
+            }
+
+            FaultRecords.AddRange(MesApiService.GetFaults(Topic, Date));
+            FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, Type));
+
+            switch (Type)
+            {
+                case "AG":
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "TPA"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL01"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL02"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL03"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL04"));
+                    break;
+                case "FPL":
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL01"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL02"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL03"));
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "FPL04"));
+                    break;
+                case "FOG":
+                    FaultRecords.AddRange(MesApiService.GetKeyInFaults(Floor, Line, Date, "COG"));
+                    break;
+                default:
+                    break;
+            }
+            foreach (var key in KeyInInfos)
+            {
+                if (key.KeyInType == 2 || key.KeyInType == 3 || key.KeyInType == 4 ||
+                    key.KeyInType == 6 || key.KeyInType == 7 || key.KeyInType == 9)
+                    FaultRecords.RemoveAll(x => x.StartTime >= key.StartTime && x.StartTime <= key.EndTime);
+            }
+            //FaultRecords = FaultRecords.Where(x => x.Duration >= 3).OrderBy(o => o.StartTime).ToList();
+            FaultRecords = FaultRecords.OrderBy(o => o.StartTime).ToList();
+        }
+
+        /// <summary>
+        /// 故障
+        /// </summary>
+        public List<MachineFaultDto> FaultRecords
+            = new List<MachineFaultDto>();
+    }
+}

+ 491 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/MachineViewModel.cs

@@ -0,0 +1,491 @@
+using NPOI.SS.Formula.Functions;
+using NPOI.XWPF.UserModel;
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 机台
+    /// </summary>
+    public class MachineViewModel
+    {
+        public MachineViewModel()
+        {
+
+        }
+
+        public MachineViewModel(string topic, string date, List<KeyInInfo> keyInInfos, 
+            List<ProductionPlanDtoV1> productionPlans, bool isLine)
+        {
+            CurrentTime = DateTime.Now;
+            Date = date;
+            if (!isLine)
+                AnalysisTopic(topic);
+            else
+                Topic = topic;
+            ProductionPlans = productionPlans;
+            KeyInInfos = keyInInfos;
+            LoadOutPutPerHours = MesApiService.GetOutPutPerHours(Topic, Date);
+
+            if (LoadOutPutPerHours.Count() < 24)
+            {
+                Statistics = new List<Statistic>();
+                return;
+            }
+
+            WashOutPutPerHours();
+            CalculateV1();
+        }
+
+        /// <summary>
+        /// 解析Topic
+        /// </summary>
+        /// <param name="topic"></param>
+        protected void AnalysisTopic(string topic)
+        {
+            Topic = topic;
+            int index = topic.IndexOf('-');
+            Floor = Convert.ToInt16(topic.Substring(index + 1, 1));
+            Line = Convert.ToInt16(topic.Substring(index - 2, 2));
+            index = topic.IndexOf("Data");
+            Type = topic[..index];
+        }
+
+
+        private string _date = string.Empty;
+
+        /// <summary>
+        /// 日期
+        /// </summary>
+        public string Date
+        {
+            get
+            {
+                return _date;
+            }
+            set
+            {
+                _date = value;
+                StartTime = Convert.ToDateTime($"{_date} 08:00:00");
+                EndTime = StartTime.AddDays(1);
+            }
+        }
+
+        public DateTime StartTime { get; set; }
+        
+        public DateTime EndTime { get; set; }
+
+        /// <summary>
+        /// 当前时间
+        /// </summary>
+        public DateTime CurrentTime { get; set; }
+
+        /// <summary>
+        /// topic
+        /// </summary>
+        public string Topic { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 楼层
+        /// </summary>
+        public int Floor { get; set; }
+
+        /// <summary>
+        /// 线别
+        /// </summary>
+        public int Line { get; set; }
+
+        /// <summary>
+        /// 类型
+        /// </summary>
+        public string Type { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 生产计划
+        /// </summary>
+        public List<ProductionPlanDtoV1> ProductionPlans { get; set; }
+            = new List<ProductionPlanDtoV1>();
+
+        /// <summary>
+        /// key in 的异常
+        /// </summary>
+        public List<KeyInInfo> KeyInInfos { get; set; }
+            = new List<KeyInInfo>();
+
+        /// <summary>
+        /// 小时产能
+        /// </summary>
+        public List<MachineDayOutPutPerHour> LoadOutPutPerHours { get; set; }
+            = new List<MachineDayOutPutPerHour>();
+
+        /// <summary>
+        /// 清理后小时产能
+        /// </summary>
+        public List<NewMachineDayOutPutPerHour> NewOutPutPerHours { get; set; }
+            = new List<NewMachineDayOutPutPerHour>();
+
+        /// <summary>
+        /// 统计
+        /// </summary>
+        public List<Statistic> Statistics { get; set; }
+            = new List<Statistic>();
+
+        /// <summary>
+        /// 获取小时产能
+        /// </summary>
+        public void UpdateModuleType()
+        {
+            if (Type != "AG"
+                && Type != "FPL"
+                && Type != "BT"
+                && Type != "PS"
+                && Type != "EC"
+                && Type != "TP")
+                return;
+
+            string topic = $"FOGData#{Line}#{Line}-{Floor}FOG01";
+            if (Line < 10)
+                topic = $"FOGData#{Line}#0{Line}-{Floor}FOG01";
+
+            var outs = MesApiService.GetOutPutPerHours(topic, Date);
+            foreach (var item in outs)
+            {
+                var t = LoadOutPutPerHours.Find(x => x.DataTime == item.DataTime);
+                if (t != null)
+                    t.ModuleType = item.ModuleType;
+            }
+        }
+
+        /// <summary>
+        /// 根据 key in 时间清洗小时产能
+        /// </summary>
+        public void WashOutPutPerHours()
+        {
+            /*
+                    0:  品质异常停线                   
+                    1:  宕机
+                    2:  换线                             
+                    3:  实验
+                    4:  W/F送样
+                    5:  物料缺料或生管调整影响
+                    6:  放假
+                    7:  停电气等停线
+                    8:  换耗材类
+                    9:  停机未生产              
+                    10: 效率爬升        
+             */
+
+            NewOutPutPerHours = new List<NewMachineDayOutPutPerHour>();
+
+            if (LoadOutPutPerHours == null)
+            {
+                return;
+            }
+
+            if (LoadOutPutPerHours.Count() == 0)
+            {
+                return;
+            }
+
+            foreach (var item in LoadOutPutPerHours)
+            {
+                NewMachineDayOutPutPerHour outPutPerHour = new NewMachineDayOutPutPerHour()
+                {
+                    DataTime = item.DataTime,
+                    ModuleType = item.ModuleType,
+                    OutPut = item.OutPut,
+                    AutoRunTime = item.AutoRunTime,
+                    AlarmTime = item.AlarmTime,
+                    IdleTime = item.IdleTime,
+                    IdleTimeDownstream = item.IdleTimeDownstream,
+                    IdleTimeUpstream = item.IdleTimeUpstream,
+                    AlarmSum = item.AlarmSum,
+                    IdleTimeSelf = item.IdleTimeSelf,
+                    TargetTT = item.TargetTT,
+                    ActualTT = item.ActualTT,
+                    LoadMATSum = item.LoadMATSum,
+                    LoadMATTime = item.LoadMATTime,
+                    LoadTime = 0,
+                };
+                if (item.DataTime > DateTime.Now)
+                {
+                    outPutPerHour.ModuleType = "";
+                }
+                NewOutPutPerHours.Add(outPutPerHour);
+            }
+
+            if (NewOutPutPerHours.Count == 0)
+                return;
+
+            foreach (var key in KeyInInfos)
+            {   
+                if (key.KeyInType == 2 || key.KeyInType == 3 || key.KeyInType == 4 ||
+                    key.KeyInType == 6 || key.KeyInType == 7 || key.KeyInType == 9)
+                {
+                    if (key.StartTime == null)
+                        continue;
+
+                    if (key.EndTime == null || key.EndTime.Value.Year < 2000)
+                    {
+                        if (key.StartTime.Value.Hour >= 0 || key.StartTime.Value.Hour <= 7)
+                            key.EndTime = Convert.ToDateTime($"{key.StartTime.Value:yyyy-MM-dd} 08:00:00");
+                        else
+                            key.EndTime = Convert.ToDateTime($"{key.StartTime.Value.AddDays(1):yyyy-MM-dd} 08:00:00");
+
+                        if (key.EndTime > EndTime)
+                            key.EndTime = EndTime;
+                    }
+
+                    var ds = GetLoadTime(key.StartTime.Value, key.EndTime.Value);
+                    for (int i = 0; i < ds.Length; i++)
+                    {
+                        if (ds[i] > 0)
+                        {
+                            if (ds[i] == 60)
+                            {
+                                NewOutPutPerHours[i].ModuleType = "";
+                                NewOutPutPerHours[i].OutPut = 0;
+                                NewOutPutPerHours[i].AutoRunTime = 0;
+                                NewOutPutPerHours[i].AlarmTime = 0;
+                                NewOutPutPerHours[i].IdleTime = 0;
+                                NewOutPutPerHours[i].IdleTimeDownstream = 0;
+                                NewOutPutPerHours[i].IdleTimeUpstream = 0;
+                                NewOutPutPerHours[i].AlarmSum = 0;
+                                NewOutPutPerHours[i].IdleTimeSelf = 0;
+                                NewOutPutPerHours[i].TargetTT = 0;
+                                NewOutPutPerHours[i].ActualTT = 0;
+                                NewOutPutPerHours[i].LoadMATSum = 0;
+                                NewOutPutPerHours[i].LoadMATTime = 0;
+                                NewOutPutPerHours[i].LoadTime = 60;
+                            }
+                            else
+                            {
+                                NewOutPutPerHours[i].LoadTime += (int)ds[i];
+                                if (NewOutPutPerHours[i].LoadTime > 60)
+                                    NewOutPutPerHours[i].LoadTime = 60;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private double[] GetLoadTime(DateTime startTime, DateTime endTime)
+        {
+            // [00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
+            // [08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 00 01 02 03 04 05 06 07]
+            // [00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00]
+
+            double[] loadTimes = new double[24];
+
+            startTime = Convert.ToDateTime($"{startTime:yyyy-MM-dd HH:mm:00}");
+            endTime = Convert.ToDateTime($"{endTime:yyyy-MM-dd HH:mm:00}");
+
+            if ((endTime - startTime).TotalMinutes <= 60 && endTime.Hour == startTime.Hour)
+            {
+                loadTimes[endTime.Hour >= 8 ? endTime.Hour - 8 : endTime.Hour + 16] = Math.Ceiling((endTime - startTime).TotalMinutes);
+                return loadTimes;
+            }
+
+            DateTime d1 = Convert.ToDateTime($"{startTime.AddHours(1):yyyy-MM-dd HH}:00:00");
+            DateTime d2 = Convert.ToDateTime($"{endTime:yyyy-MM-dd HH}:00:00");
+
+            for (DateTime dt = d1; dt < d2; dt = dt.AddHours(+1))
+            {
+                loadTimes[dt.Hour >= 8 ? dt.Hour - 8 : dt.Hour + 16] = 60;
+            }
+
+            loadTimes[startTime.Hour >= 8 ? startTime.Hour - 8 : startTime.Hour + 16] = Math.Ceiling((d1 - startTime).TotalMinutes);
+
+            if (endTime > d2)
+                loadTimes[endTime.Hour >= 8 ? endTime.Hour - 8 : endTime.Hour + 16] = Math.Ceiling((endTime - d2).TotalMinutes);
+
+            return loadTimes;
+        }
+
+        /// <summary>
+        /// 计算
+        /// </summary>
+        public void Calculate()
+        {
+            int len = 10;
+
+            if (NewOutPutPerHours.Count() <= 0)
+            {
+                return;
+            }
+
+            IEnumerable<string> moduleTypes = NewOutPutPerHours
+                    .Where(x => x.ModuleType != null && x.ModuleType != "" && x.ModuleType.Length >= len)
+                    .Select(x => x.ModuleType[..len])
+                    .Distinct();
+
+            if (moduleTypes.Count() == 0)
+            {
+                Statistic statistic = new Statistic();
+                Statistics.Add(statistic);
+                return;
+            }
+
+            foreach (var moduleType in moduleTypes)
+            {
+                string str = moduleType[..len];
+                Statistic statistic = new Statistic()
+                {
+                    ModuleType = str
+                };
+
+                var plan = ProductionPlans.Where(
+                    x =>
+                    x.ModuleType != "" &&
+                    x.ModuleType.Length >= 8 &&
+                    x.ModuleType[..len] == str).ToList();
+
+                if (plan != null && plan.Count() > 0)
+                {
+                    statistic.Capa = plan[0].Capa == -1 ? 0 : plan[0].Capa;
+                    statistic.TT = plan[0].TT == -1 ? 0 : plan[0].TT;
+                    statistic.PlanCapacity = plan.Where(x => x.PlanCapacity > 0).Select(x => x.PlanCapacity).Sum();
+
+                    var newOutPuts = NewOutPutPerHours.Where(
+                        x =>
+                        x.ModuleType != null &&
+                        x.ModuleType != "" &&
+                        x.ModuleType[..len] == str &&
+                        x.DataTime <= CurrentTime).ToList();
+
+                    statistic.RunTime = newOutPuts.Sum(x => x.AutoRunTime);
+                    double loadTime = newOutPuts.Sum(x => x.LoadTime);
+
+                    DateTime endTime = Convert.ToDateTime($"{Date} 08:00:00").AddDays(1);
+                    if (CurrentTime > endTime)
+                        statistic.LoadTime = newOutPuts.Count() * 60 - loadTime;
+                    else
+                        statistic.LoadTime = (newOutPuts.Count() - 1) * 60 - loadTime
+                                + Math.Round((CurrentTime - newOutPuts.Last().DataTime).TotalMinutes);
+
+                    statistic.DownTime = newOutPuts.Sum(x => x.AlarmTime);
+                    statistic.IdelTime = statistic.LoadTime - statistic.DownTime - statistic.RunTime;
+                    statistic.Capacity = newOutPuts.Sum(x => x.OutPut);
+                    statistic.MorningShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 8 && x.DataTime.Hour < 20).Sum(x => x.OutPut);
+                    statistic.NightShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 20 || x.DataTime.Hour < 7).Sum(x => x.OutPut);
+
+                    statistic.Availability = Math.Round(statistic.LoadTime == 0 ? 0 : statistic.RunTime / statistic.LoadTime, 2);
+                    statistic.Performance = Math.Round(statistic.RunTime == 0 ? 0 : statistic.TT / 60 * statistic.Capacity / statistic.RunTime, 2);
+                    statistic.Quality = 1;
+
+                    statistic.OEE = Math.Round(statistic.Availability * statistic.Performance * statistic.Quality, 2);
+                }
+                else
+                {
+                    var newOutPuts = NewOutPutPerHours.Where(
+                        x =>
+                        x.ModuleType != null &&
+                        x.ModuleType != "" &&
+                        x.ModuleType[..len] == str &&
+                        x.DataTime <= CurrentTime).ToList();
+
+                    statistic.RunTime = newOutPuts.Sum(x => x.AutoRunTime);
+                    double loadTime = newOutPuts.Sum(x => x.LoadTime);
+
+                    DateTime endTime = Convert.ToDateTime($"{Date} 08:00:00").AddDays(1);
+                    if (CurrentTime > endTime)
+                        statistic.LoadTime = newOutPuts.Count() * 60 - loadTime;
+                    else
+                        statistic.LoadTime = (newOutPuts.Count() - 1) * 60 - loadTime
+                                + Math.Round((CurrentTime - newOutPuts.Last().DataTime).TotalMinutes);
+
+                    statistic.DownTime = newOutPuts.Sum(x => x.AlarmTime);
+                    statistic.IdelTime = statistic.LoadTime - statistic.DownTime - statistic.RunTime;
+                    statistic.Capacity = newOutPuts.Sum(x => x.OutPut);
+                    statistic.MorningShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 8 && x.DataTime.Hour < 20).Sum(x => x.OutPut);
+                    statistic.NightShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 20 || x.DataTime.Hour < 7).Sum(x => x.OutPut);
+                    statistic.Availability = Math.Round(statistic.LoadTime == 0 ? 0 : statistic.RunTime / statistic.LoadTime, 2);
+                }
+
+                Statistics.Add(statistic);
+            }
+        }
+
+        public void CalculateV1()
+        {
+            IEnumerable<string> moduleTypes = ProductionPlans
+                    .Where(x => x.ModuleType != null && x.ModuleType != "")
+                    .Select(x => x.ModuleType)
+                    .Distinct();
+
+            if (moduleTypes.Count() == 0)
+            {
+                Statistic statistic = new Statistic();
+                Statistics.Add(statistic);
+                return;
+            }
+
+            foreach (var moduleType in moduleTypes)
+            {
+                Statistic statistic = new Statistic()
+                {
+                    ModuleType = moduleType
+                };
+
+                var plan = ProductionPlans.Where(
+                    x =>
+                    x.ModuleType != "" &&
+                    x.ModuleType == moduleType).ToList();
+
+                if (plan == null || plan.Count() <= 0)
+                {
+                    Statistics.Add(statistic);
+                    continue;
+                }
+
+                statistic.Capa = plan[0].Capa == -1 ? 0 : plan[0].Capa;
+                statistic.TT = plan[0].TT == -1 ? 0 : plan[0].TT;
+                statistic.PlanCapacity = plan.Where(x => x.PlanCapacity > 0).Select(x => x.PlanCapacity).Sum();
+
+                var newOutPuts = NewOutPutPerHours.Where(
+                    x =>
+                    x.ModuleType != null &&
+                    x.ModuleType != "" &&
+                    x.ModuleType.Length >= moduleType.Length &&
+                    x.ModuleType[..moduleType.Length] == moduleType &&
+                    x.DataTime <= CurrentTime).ToList();
+
+                if (newOutPuts == null || newOutPuts.Count <= 0)
+                {
+                    Statistics.Add(statistic);
+                    continue;
+                }
+
+                statistic.RunTime = newOutPuts.Sum(x => x.AutoRunTime);
+                double loadTime = newOutPuts.Sum(x => x.LoadTime);
+
+                DateTime endTime = Convert.ToDateTime($"{Date} 08:00:00").AddDays(1);
+                if (CurrentTime > endTime)
+                    statistic.LoadTime = newOutPuts.Count() * 60 - loadTime;
+                else
+                    statistic.LoadTime = (newOutPuts.Count() - 1) * 60 - loadTime
+                            + Math.Round((CurrentTime - newOutPuts.Last().DataTime).TotalMinutes);
+
+                statistic.DownTime = newOutPuts.Sum(x => x.AlarmTime);
+                statistic.IdelTime = statistic.LoadTime - statistic.DownTime - statistic.RunTime;
+                statistic.Capacity = newOutPuts.Sum(x => x.OutPut);
+                statistic.MorningShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 8 && x.DataTime.Hour < 20).Sum(x => x.OutPut);
+                statistic.NightShiftCapacity = newOutPuts.Where(x => x.DataTime.Hour >= 20 || x.DataTime.Hour < 7).Sum(x => x.OutPut);
+
+                statistic.Availability = Math.Round(statistic.LoadTime == 0 ? 0 : statistic.RunTime / statistic.LoadTime, 2);
+                statistic.Performance = Math.Round(statistic.RunTime == 0 ? 0 : statistic.TT / 60 * statistic.Capacity / statistic.RunTime, 2);
+                statistic.Quality = 1;
+
+                statistic.OEE = Math.Round(statistic.Availability * statistic.Performance * statistic.Quality, 2);
+
+                Statistics.Add(statistic);
+            }
+
+        }
+    }
+}

+ 95 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionLineFaultViewModel.cs

@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class LineDayFaultRecord
+    {
+        /// <summary>
+        /// 设备类型
+        /// </summary>
+        public string MechineName { get; set; }
+
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 故障码
+        /// </summary>
+        public string FaultCode { get; set; }
+
+        /// <summary>
+        /// 故障详情
+        /// </summary>
+        public string FaultInfo { get; set; }
+
+        /// <summary>
+        /// 持续时间
+        /// </summary>
+        public double Duration { get; set; }
+
+        /// <summary>
+        /// 数据源 0: 系统自动抓取 1:人工录入
+        /// </summary>
+        public int DataSource { get; set; }
+    }
+    public class ProductionLineFaultViewModel : ProductionLineViewModel
+    {
+        public ProductionLineFaultViewModel(int floor, int line, string date, string[] topics)
+        {
+            Floor = floor;
+            Line = line;
+            Date = date;
+
+            KeyInInfos = MesApiService.GetKeyInInfos(floor, line, date);
+            foreach (var key in KeyInInfos)
+            {
+                if (key.KeyInType == 2 || key.KeyInType == 3 || key.KeyInType == 4 ||
+                    key.KeyInType == 6 || key.KeyInType == 7 || key.KeyInType == 9 ||
+                    key.KeyInType == 0)
+                    LineDayFaultRecords.Add(new LineDayFaultRecord
+                    {
+                        FaultCode = key.KeyInType.ToString(),
+                        FaultInfo = key.Description,
+                        EndTime = key.EndTime,
+                        MechineName = "Line",
+                        StartTime = key.StartTime.Value,
+                        Duration = key.AffectTime
+                    });
+            }
+
+            // string[] topics = Global.App.ProductionLines.Find(x => x.Floor == floor && x.Line == line)?.Topics;
+
+            foreach (var topic in topics)
+            {
+                MachineFaultViewModel model = new MachineFaultViewModel(topic, date);
+                var faults = model.FaultRecords.Where(x => x.Duration >= 3 && !x.FaultInfo.Contains("安全门")).ToList();
+                foreach (var fault in faults)
+                {
+                    LineDayFaultRecords.Add(new LineDayFaultRecord
+                    {
+                        FaultCode = fault.FaultCode,
+                        FaultInfo = fault.FaultInfo,
+                        EndTime = fault.EndTime,
+                        MechineName = model.Type,
+                        StartTime = fault.StartTime,
+                        Duration = fault.Duration
+                    });
+                }
+            }
+
+            LineDayFaultRecords = LineDayFaultRecords.OrderBy(x => x.StartTime).ToList();
+        }
+
+        public List<LineDayFaultRecord> LineDayFaultRecords { get; set; }
+            = new List<LineDayFaultRecord>();
+    }
+}

+ 438 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionLineViewModel.cs

@@ -0,0 +1,438 @@
+using OfficeOpenXml.VBA;
+using ProductionLineMonitor.Application.Services.FaultService.Dtos;
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using System.Timers;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 产线详情
+    /// </summary>
+    public class ProductionLineViewModel
+    {
+        public ProductionLineViewModel() { }
+
+        public ProductionLineViewModel(int floor, int line, string date, string topic, string lineName)
+        {
+            Floor = floor;
+            Line = line;
+            Date = date;
+            LineName = lineName;
+
+            ProductionPlans = MesApiService.GetProductionPlansV1(Floor, Line, Date);
+            KeyInInfos = MesApiService.GetKeyInInfos(Floor, Line, Date);
+            HourKeyInInfos = GetHourKeyInInfos(KeyInInfos);
+
+            try
+            {
+                DateTime dateTime = Convert.ToDateTime($"{date} 08:00:00");
+                DateTime[] m = new DateTime[] { dateTime, dateTime.AddHours(12), dateTime.AddHours(24) };
+                MorningReplaceConsumables = AccumulativeReplaceConsumableTime(KeyInInfos.Where(x => x.StartTime >= m[0] && x.StartTime < m[1]));
+                NightReplaceConsumables = AccumulativeReplaceConsumableTime(KeyInInfos.Where(x => x.StartTime >= m[1] && x.StartTime < m[2]));
+            }
+            catch (Exception)
+            {
+                return;
+            }
+
+            MachineViewModel machineViewModel = new MachineViewModel(
+                        topic, date,
+                        KeyInInfos,
+                        ProductionPlans, true);
+
+            if (machineViewModel.Statistics.Count() == 0)
+            {
+                return;
+            }
+
+            ModuleType = machineViewModel.Statistics.Last().ModuleType;
+            Capa = machineViewModel.Statistics.Last().Capa;
+            Capacity = machineViewModel.Statistics.Last().Capacity;
+            PlanCapacity = machineViewModel.Statistics.Last().PlanCapacity;
+            OEE = machineViewModel.Statistics.Last().OEE;
+            Statistics = machineViewModel.Statistics;
+            LoadOutPutPerHours = machineViewModel.LoadOutPutPerHours;
+            NewOutPutPerHours = machineViewModel.NewOutPutPerHours;
+
+            UpdateDown();
+        }
+
+        private List<ReplaceConsumable> AccumulativeReplaceConsumableTime(IEnumerable<KeyInInfo> keyInInfos)
+        {
+            List<ReplaceConsumable> rev = new List<ReplaceConsumable>();
+            foreach (var keyInInfo in keyInInfos)
+            {
+                if (keyInInfo.KeyInType != 8)
+                {
+                    continue;
+                }
+
+                var temp = rev.Find(x => x.Name == keyInInfo.Description);
+                if (temp != null)
+                {
+                    temp.Count += 1;
+                    temp.AccumulativeTime += keyInInfo.AffectTime;
+                }
+                else
+                {
+                    var replaceConsumable = new ReplaceConsumable()
+                    {
+                        Name = keyInInfo.Description,
+                        Count = 1,
+                        AccumulativeTime = keyInInfo.AffectTime
+                    };
+                    rev.Add(replaceConsumable);
+                }
+            }
+            return rev;
+        }
+
+        private IEnumerable<KeyInInfo> GetHourKeyInInfos(List<KeyInInfo> keyInInfos)
+        {
+            IList<KeyInInfo> dtos = new List<KeyInInfo>();
+            foreach (var key in keyInInfos)
+            {
+                if (key.StartTime == null || key.EndTime == null)
+                    continue;
+
+                IList<DateTime> dateTimes = new List<DateTime>
+                {
+                    key.StartTime.Value
+                };
+
+                DateTime d1 = key.StartTime.Value;
+                int j = 1;
+                while (d1 < key.EndTime.Value)
+                {
+                    d1 = key.StartTime.Value.AddHours(j);
+                    DateTime d2 = Convert.ToDateTime($"{d1:yyyy-MM-dd HH}:00:00");
+                    j++;
+                    if (d2 < key.EndTime.Value)
+                    {
+                        dateTimes.Add(d2);
+                    }
+                }
+                dateTimes.Add(key.EndTime.Value);
+
+                for (int i = 0; i < dateTimes.Count - 1; i++)
+                {
+                    KeyInInfo dto = new KeyInInfo
+                    {
+                        KeyInType = key.KeyInType,
+                        Description = key.Description,
+                        Id = key.Id,  
+                        StartTime = dateTimes[i],
+                        EndTime = dateTimes[i + 1]
+                    };
+                    dto.AffectTime = (int)(dto.EndTime - dto.StartTime).Value.TotalMinutes;
+                    if (dto.AffectTime > 1)
+                    {
+                        dtos.Add(dto);
+                    }
+                }
+            }
+            return dtos;
+        }
+
+        private void UpdateDown()
+        {
+            if (Statistics != null)
+            {
+                if (Statistics.Count == 1)
+                {
+                    Statistics[0].Reload = KeyInInfos.Where(x => x.KeyInType == 8).Sum(x => x.AffectTime);
+                    double d = Statistics[0].IdelTime - Statistics[0].Reload;
+                    if (d < 0)
+                        d = 0;
+                    Statistics[0].DownTime += d;
+                }
+                else
+                {
+                    foreach (var item in Statistics)
+                    {
+                        var outs = NewOutPutPerHours.Where(x => x.ModuleType != "" && x.ModuleType[..8] == item.ModuleType).ToList();
+                        if (outs.Count() > 0)
+                        {
+                            DateTime start = outs[0].DataTime;
+                            DateTime end = outs.Last().DataTime.AddHours(1);
+                            item.Reload = KeyInInfos.Where(
+                                x => x.KeyInType == 8 && x.StartTime >= start && x.StartTime.Value < end).Sum(x => x.AffectTime);
+                            double d = item.IdelTime - item.Reload;
+                            if (d < 0)
+                                d = 0;
+                            item.DownTime += d;
+                        }
+                    }
+                }
+            }
+        }
+
+        private void CheckFault()
+        {
+            DateTime startTime = Convert.ToDateTime($"{Date} 08:00:00");
+            DateTime endTime = startTime.AddDays(1);
+
+            foreach (var item in Machines)
+            {
+                FaultStatistic faultStatistic = new FaultStatistic();
+                faultStatistic.Name = item.Type;
+                switch (item.Type)
+                {
+                    case "AG":
+                        faultStatistic.Name = "AG + FPL";
+                        List<LineDayFaultRecord> tempFaults = new List<LineDayFaultRecord>();
+                        tempFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "FPL", endTime));
+                        tempFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "FPL01", endTime));
+                        tempFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "FPL02", endTime));
+                        tempFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "FPL03", endTime));
+                        tempFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "FPL04", endTime));
+                        double time1 = tempFaults.Sum(x => x.Duration);
+                        double time2 = item.Statistics.Sum(x => x.DownTime);
+                        faultStatistic.DownTime = Math.Round(time1 + time2);
+                        break;
+                    case "FOG":
+                        // 在 FOG 前增加COG
+                        List<LineDayFaultRecord> tempCogFaults = new List<LineDayFaultRecord>();
+                        tempCogFaults.AddRange(getKeyInLineDayFaultRecord(Floor, Line, Date, "COG", endTime));
+                        FaultStatistic cog = new FaultStatistic
+                        {
+                            Name = "COG",
+                            DownTime = Math.Round(tempCogFaults.Sum(x => x.Duration))
+                        };
+                        FaultStatistics.Add(cog);
+
+                        // FOG 故障时间
+                        faultStatistic.DownTime = Math.Round(item.Statistics.Sum(x => x.DownTime));
+                        break;
+                    default:
+                        faultStatistic.DownTime = Math.Round(item.Statistics.Sum(x => x.DownTime));
+                        break;
+                }
+                FaultStatistics.Add(faultStatistic);
+            }
+        }
+        private List<LineDayFaultRecord> getKeyInLineDayFaultRecord(int floor, int line, string date, string type, DateTime endTime)
+        {
+            List<LineDayFaultRecord> lineDayFaultRecords = new List<LineDayFaultRecord>();
+            var fs = MesApiService.GetKeyInFaults(floor, line, date, type);
+            foreach (var f in fs)
+            {
+                LineDayFaultRecord fLineDayFaultRecord = new LineDayFaultRecord
+                {
+                    MechineName = type,
+                    FaultCode = f.FaultCode,
+                    FaultInfo = f.FaultInfo,
+                    StartTime = f.StartTime,
+                    DataSource = 1
+                };
+                if (f.EndTime == null)
+                    fLineDayFaultRecord.EndTime = endTime;
+                else
+                    fLineDayFaultRecord.EndTime = f.EndTime.Value;
+                fLineDayFaultRecord.Duration = (fLineDayFaultRecord.EndTime.Value - fLineDayFaultRecord.StartTime).TotalMinutes;
+                lineDayFaultRecords.Add(fLineDayFaultRecord);
+            }
+            return lineDayFaultRecords;
+        }
+        private void CheckModuleType()
+        {
+            if (ModuleType != null && ModuleType != "")
+            {
+                if (ProductionPlans
+                    .Where(x => x.ModuleType.Length >= 8)
+                    .Where(x => x.ModuleType.Substring(0, 8) == ModuleType).Count() == 0)
+                {
+                    State = 99;
+                    StateMessage = "生产机种与排程不符";
+                }
+            }
+        }
+        public virtual void CheckState()
+        {
+            DateTime now = DateTime.Now;
+            DateTime time = Convert.ToDateTime($"{Date} {now:HH:mm:ss}");
+            if (time.Day != now.Day || time.Year != now.Year || time.Month != now.Month)
+            {
+                time = Convert.ToDateTime($"{Date} 07:59:00").AddDays(1);
+            }
+
+            foreach (var item in KeyInInfos)
+            {
+                if (item.KeyInType == 2 || item.KeyInType == 3 || item.KeyInType == 4 ||
+                    item.KeyInType == 6 || item.KeyInType == 7 || item.KeyInType == 9)
+                {
+                    if (time >= item.StartTime && time <= item.EndTime)
+                    {
+                        State = item.KeyInType;
+                        StateMessage = item.Description;
+                        return;
+                    }
+                }
+            }
+
+            if (ProductionPlans.Count == 0)
+            {
+                State = 11;
+                StateMessage = "";
+                return;
+            }
+
+            if (ProductionPlans.Count == 1)
+            {
+                if (ProductionPlans[0].ModuleType == "未指定")
+                {
+                    State = 11;
+                    StateMessage = "";
+                    return;
+                }
+            }
+
+            State = 99;
+            StateMessage = "";
+        }
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public string LineName { get; set; } = string.Empty;
+        public string Date { get; set; } = string.Empty;
+
+        private int _state = -1;
+        public int State
+        {
+            get { return _state; }
+            set
+            {
+                ///  0:品质异常停线
+                ///  ---------------1:宕机
+                ///  2:换线
+                ///  3:实验
+                ///  4:W/F送样
+                ///  ---------------5:物料缺料或生管调整影响
+                ///  6:放假
+                ///  7:停电气等停线
+                ///  ---------------8:换耗材类
+                ///  9:停机未生产
+                /// ----------------10:效率爬升
+                _state = value;
+                switch (_state)
+                {
+                    case 0: StateName = "品质异常停线"; break;
+                    case 1: break;
+                    case 2: StateName = "换线"; break;
+                    case 3: StateName = "实验"; break;
+                    case 4: StateName = "W/F送样"; break;
+                    case 5: break;
+                    case 6: StateName = "放假"; break;
+                    case 7: StateName = "停电气等停线"; break;
+                    case 8: break;
+                    case 9: StateName = "停机未生产"; break;
+                    case 10: break;
+                    case 11: StateName = "未排产"; break;
+                    default: StateName = "正常生产"; break;
+                }
+            }
+        }
+
+        public string StateName { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 状态描述
+        /// </summary>
+        public string StateMessage { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Capa
+        /// </summary>
+        public int Capa { get; set; }
+
+        /// <summary>
+        /// 计划产能
+        /// </summary>
+        public int PlanCapacity { get; set; }
+
+        /// <summary>
+        /// 产能
+        /// </summary>
+        public int Capacity { get; set; }
+
+        /// <summary>
+        /// OEE
+        /// </summary>
+        public double OEE { get; set; }
+
+        public double TheoryRefueledTime { get; set; }
+
+        /// <summary>
+        /// 生产计划
+        /// </summary>
+        public List<ProductionPlanDtoV1> ProductionPlans { get; set; }
+            = new List<ProductionPlanDtoV1>();
+
+        /// <summary>
+        /// key in 的异常
+        /// </summary>
+        public List<KeyInInfo> KeyInInfos { get; set; }
+            = new List<KeyInInfo>();
+
+        public IEnumerable<KeyInInfo> HourKeyInInfos { get; set; }
+            = new List<KeyInInfo>();
+
+        /// <summary>
+        /// 小时产能
+        /// </summary>
+        public List<MachineDayOutPutPerHour> LoadOutPutPerHours { get; set; }
+            = new List<MachineDayOutPutPerHour>();
+
+        /// <summary>
+        /// 清理后小时产能
+        /// </summary>
+        public List<NewMachineDayOutPutPerHour> NewOutPutPerHours { get; set; }
+            = new List<NewMachineDayOutPutPerHour>();
+
+        /// <summary>
+        /// 统计
+        /// </summary>
+        public List<Statistic> Statistics { get; set; }
+            = new List<Statistic>();
+
+        /// <summary>
+        /// 机台
+        /// </summary>
+        public List<MachineViewModel> Machines { get; set; }
+            = new List<MachineViewModel>();
+
+        /// <summary>
+        /// key in 故障
+        /// </summary>
+        public List<LineDayFaultRecord> KeyInFaults { get; set; }
+            = new List<LineDayFaultRecord>();
+
+        public List<FaultStatistic> FaultStatistics { get; set; }
+            = new List<FaultStatistic>();
+
+        public class FaultStatistic
+        {
+            public string Name { get; set; } = string.Empty;
+            public double DownTime { get; set; }
+        }
+
+        public List<ReplaceConsumable> ReplaceConsumables { get; set; }
+             = new List<ReplaceConsumable>();
+
+        public List<ReplaceConsumable> MorningReplaceConsumables { get; set; }
+             = new List<ReplaceConsumable>();
+
+        public List<ReplaceConsumable> NightReplaceConsumables { get; set; }
+             = new List<ReplaceConsumable>();
+
+    }
+}

+ 28 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionPlan.cs

@@ -0,0 +1,28 @@
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 生产计划
+    /// </summary>
+    public class ProductionPlan
+    {
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; }
+
+        /// <summary>
+        /// 计划产能
+        /// </summary>
+        public int PlanCapacity { get; set; }
+
+        /// <summary>
+        /// 理论产能
+        /// </summary>
+        public int Capa { get; set; }
+
+        /// <summary>
+        /// 理论TT
+        /// </summary>
+        public double TT { get; set; }
+    }
+}

+ 171 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/ProductionPlanDto.cs

@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    public class ProductionPlanDto
+    {
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 计划产能
+        /// </summary>
+        public int PlanCapacity { get; set; }
+
+        /// <summary>
+        /// 理论产能
+        /// </summary>
+        public int Capa { get; set; }
+
+        /// <summary>
+        /// 理论TT
+        /// </summary>
+        public double TT { get; set; }
+    }
+
+    public class ProductionPlanDtoV1 : ProductionPlanDto
+    {
+        public string Shift { get; set; } = string.Empty;
+    }
+
+    public class OverProductionPlanDto
+    {
+        public OverProductionPlanDto(int floor, int line, IList<ProductionPlanDtoV1> lst, string lineId, string topic, string lineName)
+        {
+            LineId = lineId;
+            Floor = floor;
+            Line = line;
+            LineName = lineName;
+            HourDataTopic = topic;
+
+
+            IList<string> ModuleTypes = lst
+                .Where(x => x.ModuleType != "")
+                .Select(x => x.ModuleType)
+                .Distinct().ToList();
+
+            int[] capas = new int[ModuleTypes.Count()];
+            for (int i = 0; i < ModuleTypes.Count(); i++)
+            {
+                var l = lst.FirstOrDefault(x => x.ModuleType == ModuleTypes[i]);
+                if (l != null)
+                {
+                    capas[i] = l.Capa;
+                }
+            }
+
+            Capa = string.Join (" / ", capas);
+
+            //for (int i = 0; i < ModuleTypes.Count(); i++)
+            //{
+            //    if (ModuleTypes[i].Length > 10)
+            //    {
+            //        ModuleTypes[i] = ModuleTypes[i][..10];
+            //    }
+            //}
+            ModuleType = string.Join(" / ", ModuleTypes);
+
+            var ms = lst.Where(x => x.Shift == "1");
+            if (ms != null && ms.Count() > 0)
+            {
+                PlanMorningShiftCapacity = ms.Select(x => x.PlanCapacity).Sum();
+            }
+
+            var ns = lst.Where(x => x.Shift == "2");
+            if (ns != null && ns.Count() > 0)
+            {
+                PlanNightShiftCapacity = ns.Select(x => x.PlanCapacity).Sum();
+            }
+        }
+
+        public void SetModuleType(string moduleType)
+        {
+            // 11 12 不截取
+            if ((Line == 11 || Line == 12) && Floor == 2)
+            {
+                ModuleType = moduleType;
+                return;
+            }
+
+            if (moduleType.Length > 10)
+            {
+                moduleType = moduleType[..10];
+            }
+            ModuleType = moduleType;
+        }
+
+        public string LineId { get; private set; }
+
+        public void SetCapacity(int capacity)
+        {
+            Capacity = capacity;
+        }
+
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public string HourDataTopic { get; set; }
+        public string ModuleType { get; private set; } = string.Empty;
+        public string Capa { get; set; } = string.Empty;
+        public string LineName { get; set; } = string.Empty;
+
+        public bool MorningShiftState
+        {
+            get
+            {
+                return PlanMorningShiftCapacity != 0;
+            }
+        }
+        
+        public int PlanMorningShiftCapacity { get; set; } = 0;
+        
+        public bool NightShiftState
+        {
+            get
+            {
+                return PlanNightShiftCapacity != 0;
+            }
+        }
+
+        public int PlanNightShiftCapacity { get; set; } = 0;
+
+        /// <summary>
+        /// 实时产能
+        /// </summary>
+        public int Capacity { get; private set; } = 0;
+
+        /// <summary>
+        /// 达成率
+        /// </summary>
+        //public string AchievementRate
+        //{
+        //    get
+        //    {
+        //        int p = PlanMorningShiftCapacity + PlanNightShiftCapacity;
+        //        if (p == 0)
+        //        {
+        //            return "-";
+        //        }
+        //        return Math.Round((float)Capacity / p * 100, 2).ToString() + " %";
+        //    }
+        //}
+
+        public double AchievementRate
+        {
+            get
+            {
+                int p = PlanMorningShiftCapacity + PlanNightShiftCapacity;
+                if (p == 0)
+                {
+                    return 0;
+                }
+                return Math.Round((float)Capacity / p * 100, 2);
+            }
+        }
+    }
+}

+ 13 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/QueryCapa.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    public class QueryCapa
+    {
+        public int Floor { get; set; }
+        public int Line { get; set; }   
+        public string Device { get; set; } = string.Empty;
+    }
+}

+ 17 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/QueryMachineKeyInFaultParam.cs

@@ -0,0 +1,17 @@
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 查询机台 key in 故障参数 
+    /// </summary>
+    public class QueryMachineKeyInFaultParam
+    {
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public string Date { get; set; }
+
+        /// <summary>
+        /// 机台类别
+        /// </summary>
+        public string MachineType { get; set; }
+    }
+}

+ 19 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/QueryMachineParam.cs

@@ -0,0 +1,19 @@
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 查询机台数据参数
+    /// </summary>
+    public class QueryMachineParam
+    {
+        /// <summary>
+        /// 机台topic 
+        /// 例:BTData#1#01-2BT01
+        /// </summary>
+        public string Equiptopic { get; set; } = string.Empty;
+        /// <summary>
+        /// 日期 
+        /// 例:2023-2-10
+        /// </summary>
+        public string Date { get; set; } = string.Empty;
+    }
+}

+ 12 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/QueryPlanParam.cs

@@ -0,0 +1,12 @@
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    /// <summary>
+    /// 查询计划参数
+    /// </summary>
+    public class QueryPlanParam
+    {
+        public int Floor { get; set; }
+        public int Line { get; set; }
+        public string Date { get; set; }
+    }
+}

+ 16 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/ReplaceConsumable.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService.Dtos
+{
+    /// <summary>
+    /// 换耗材
+    /// </summary>
+    public class ReplaceConsumable
+    {
+        public string Name { get; set; } = string.Empty;
+        public int Count { get; set; }
+        public double AccumulativeTime { get; set; }
+    }
+}

+ 97 - 0
ProductionLineMonitor.Application/Services/LineService/Dtos/Statistic.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace ProductionLineMonitor.Web.Services.LineService
+{
+    public class Statistic
+    {
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Capa
+        /// </summary>
+        public int Capa { get; set; }
+
+        /// <summary>
+        /// 计划产能
+        /// </summary>
+        public int PlanCapacity { get; set; }
+
+        /// <summary>
+        /// TT
+        /// </summary>
+        public double TT { get; set; }
+
+        /// <summary>
+        /// 产能
+        /// </summary>
+        public int Capacity { get; set; }
+
+        /// <summary>
+        /// 早班产能
+        /// </summary>
+        public int MorningShiftCapacity { get; set; }
+
+        /// <summary>
+        /// 夜班产能
+        /// </summary>
+        public int NightShiftCapacity { get; set; }
+
+        /// <summary>
+        /// 时间稼动率
+        /// </summary>
+        public double Availability { get; set; }
+
+        /// <summary>
+        /// 性能稼动率
+        /// </summary>
+        public double Performance { get; set; }
+
+        /// <summary>
+        /// 良率
+        /// </summary>
+        public double Quality { get; set; }
+
+        /// <summary>
+        /// 设备综合效率
+        /// </summary>
+        public double OEE { get; set; }
+
+        /// <summary>
+        /// 运行时间
+        /// </summary>
+        public double RunTime { get; set; }
+
+        /// <summary>
+        /// 待料时间
+        /// </summary>
+        public double IdelTime { get; set; }
+
+        /// <summary>
+        /// 宕机时间
+        /// </summary>
+        public double DownTime { get; set; }
+
+        /// <summary>
+        /// 负荷时间
+        /// </summary>
+        public double LoadTime { get; set; }
+
+        public double Reload { get; set; }
+
+        public double AchievementRate
+        {
+            get
+            {
+                if (PlanCapacity == 0)
+                {
+                    return 0;
+                }
+                return Math.Round((float)Capacity / PlanCapacity * 100, 0);
+            }
+        }
+    }
+}

+ 15 - 0
ProductionLineMonitor.Application/Services/LineService/ILineService.cs

@@ -0,0 +1,15 @@
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.LineService
+{
+    public interface ILineService
+    {
+        IList<LineOverviewDto> GetLineOverview(string date);
+        IList<OverProductionPlanDto> GetLineOverviewV1(string date);
+        LineMonthData GetLineMonthData(string lineId, string date, string moduleType);
+    }
+}

+ 135 - 0
ProductionLineMonitor.Application/Services/LineService/LineService.cs

@@ -0,0 +1,135 @@
+using ProductionLineMonitor.Application.Services.HomeService.Dtos;
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Web.Services;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ProductionLineMonitor.Application.Services.LineService
+{
+    public class LineService : ILineService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        public LineService(IUnitOfWork unitOfWork)
+        {
+            _unitOfWork = unitOfWork;
+        }
+
+        public LineMonthData GetLineMonthData(string lineId, string date, string moduleType)
+        {
+            var line = _unitOfWork.ProductionLineRepository.FirstOrDefault(x => x.Id == lineId);
+            return new LineMonthData(line, Convert.ToDateTime($"{date} 08:00:00"), 10, moduleType);
+        }
+
+        public IList<LineOverviewDto> GetLineOverview(string date)
+        {
+            IList<LineOverviewDto> lineOverviews = new List<LineOverviewDto>();
+
+            var lines = _unitOfWork.ProductionLineRepository.GetList(
+                x => x.FactoryNo == "03");
+            foreach (var line in lines)
+            {
+                LineOverviewDto lineOverview = new LineOverviewDto
+                {
+                    Id = line.Id,
+                    Floor = line.Floor,
+                    Line = line.Line,
+                    Name = line.Name,
+                    LineName = line.LineName
+                };
+                lineOverviews.Add(lineOverview);
+            }
+
+            List<Thread> threads = new List<Thread>();
+            foreach (var lineOverview in lineOverviews)
+            {
+                Thread thread = new Thread(() =>
+                {
+                    ProductionLineViewModel viewModel = new ProductionLineViewModel(
+                        lineOverview.Floor, lineOverview.Line, date, "FOG", lineOverview.LineName);
+                    foreach (var productionPlan in viewModel.ProductionPlans)
+                    {
+                        OverviewProductionPlanDto plan = new OverviewProductionPlanDto
+                        {
+                            ModuleType = productionPlan.ModuleType,
+                            Capa = productionPlan.Capa,
+                            PlanCapacity = productionPlan.PlanCapacity
+                        };
+                        lineOverview.OverviewProductionPlans.Add(plan);
+                    }
+                });
+                thread.Start();
+                threads.Add(thread);
+            }
+
+            foreach (var item in threads)
+            {
+                item.Join();
+            }
+
+            return lineOverviews;
+        }
+
+        public IList<OverProductionPlanDto> GetLineOverviewV1(string date)
+        {
+            IList<OverProductionPlanDto> lineOverviews = new List<OverProductionPlanDto>();
+
+            var lines = _unitOfWork.ProductionLineRepository.GetList(
+                x => x.FactoryNo == "03").OrderBy(o => o.Order);
+            foreach (var line in lines)
+            {
+                var lst = MesApiService.GetProductionPlansV1(line.Floor, line.Line, date);
+                OverProductionPlanDto lineOverview = new OverProductionPlanDto(
+                    line.Floor, line.Line, lst, line.Id, line.HourDataTopic, line.LineName);
+                lineOverviews.Add(lineOverview);
+            }
+
+            var rs = _unitOfWork.RecipeRepository.GetList().ToList();
+
+            List<Thread> threads = new List<Thread>();
+            foreach (var lineOverview in lineOverviews)
+            {
+                Thread thread = new Thread(() =>
+                {
+                    ProductionLineViewModel viewModel = new ProductionLineViewModel(lineOverview.Floor, 
+                        lineOverview.Line, date, lineOverview.HourDataTopic, lineOverview.LineName);
+                    int c = 0;
+                    if (viewModel.LoadOutPutPerHours.Count() > 0)
+                    {
+                        c = viewModel.LoadOutPutPerHours.Select(x => x.OutPut).Sum();
+                    }
+
+                    #region 临时处理机种获取不到问题
+
+                    if (lineOverview.ModuleType == null || lineOverview.ModuleType == "")
+                    {
+                        if (viewModel.LoadOutPutPerHours.Count() > 0)
+                        {
+                            string m = viewModel.LoadOutPutPerHours[0].ModuleType;
+                            lineOverview.SetModuleType(m);
+                            lineOverview.Capa = MesApiService.GetCapaTT(viewModel.Floor, viewModel.Line, m).Capa.ToString();
+                        }
+                    }
+
+                    #endregion
+
+                    lineOverview.SetCapacity(c);
+                });
+                thread.Start();
+                threads.Add(thread);
+            }
+
+            foreach (var item in threads)
+            {
+                item.Join();
+            }
+
+            return lineOverviews;
+        }
+    }
+}

+ 584 - 0
ProductionLineMonitor.Application/Services/MesApiService/MesApiService.cs

@@ -0,0 +1,584 @@
+using NPOI.SS.Formula.Functions;
+using NPOI.XWPF.UserModel;
+using ProductionLineMonitor.Application.Services;
+using ProductionLineMonitor.Application.Services.EnergyConsumptionService.Dtos;
+using ProductionLineMonitor.Application.Services.FaultService.Dtos;
+using ProductionLineMonitor.Application.Services.HomeService.Dtos;
+using ProductionLineMonitor.Application.Services.HomeService.Models;
+using ProductionLineMonitor.Application.Services.LineService.Dtos;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.Utils;
+using ProductionLineMonitor.Web.Services.LineService;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
+using CodeEnum = ProductionLineMonitor.Application.Services.CodeEnum;
+
+namespace ProductionLineMonitor.Web.Services
+{
+    public class MesApiService
+    {
+        public static CapaDto GetCapaTT(int floor, int line, string moduleType)
+        {
+            CapaDto dto = new CapaDto();
+
+            QueryCapa query = new QueryCapa()
+            {
+                Device = moduleType,
+                Floor = floor,
+                Line = line,
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetCapaByDevice",
+                query.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<CapaDto>>();
+                if (root != null)
+                    if (root.Data != null)
+                    {
+                        dto = root.Data;
+                    }
+            }
+
+            return dto;
+        }
+
+        /// <summary>
+        /// 获取生产计划
+        /// </summary>
+        public static List<Application.Services.LineService.Dtos.ProductionPlanDto> GetProductionPlans(int floor, int line, string date)
+        {
+            List<Application.Services.LineService.Dtos.ProductionPlanDto> productionPlans = new List<Application.Services.LineService.Dtos.ProductionPlanDto>();
+
+            QueryPlanParam query = new QueryPlanParam()
+            {
+                Floor = floor,
+                Line = line,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetScheduledProduction",
+                query.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<Application.Services.LineService.Dtos.ProductionPlanDto>>>();
+                if (root != null)
+                    if (root.Data != null)
+                    {
+                        productionPlans = root.Data.ToList();
+                        foreach (var item in productionPlans)
+                        {
+                            item.PlanCapacity = item.PlanCapacity == -1 ? 0 : item.PlanCapacity;
+                            item.Capa = item.Capa == -1 ? 0 : item.Capa;
+                            item.TT = item.TT == -1 ? 0 : item.TT;
+                        }
+                    }
+            }
+
+            return productionPlans;
+        }
+
+        public static List<Application.Services.LineService.Dtos.ProductionPlanDtoV1> GetProductionPlansV1(int floor, int line, string date)
+        {
+            List<Application.Services.LineService.Dtos.ProductionPlanDtoV1> productionPlans = new List<Application.Services.LineService.Dtos.ProductionPlanDtoV1>();
+
+            QueryPlanParam query = new QueryPlanParam()
+            {
+                Floor = floor,
+                Line = line,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetScheduledProductionNew",
+                query.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<Application.Services.LineService.Dtos.ProductionPlanDtoV1>>>();
+                if (root != null)
+                    if (root.Data != null)
+                    {
+                        productionPlans = root.Data.ToList();
+                        foreach (var item in productionPlans)
+                        {
+                            item.PlanCapacity = item.PlanCapacity == -1 ? 0 : item.PlanCapacity;
+                            item.Capa = item.Capa == -1 ? 0 : item.Capa;
+                            item.TT = item.TT == -1 ? 0 : item.TT;
+                        }
+                    }
+            }
+
+            return productionPlans;
+        }
+        
+        /// <summary>
+        /// 获取 key in 数据
+        /// </summary>
+        public static List<KeyInInfo> GetKeyInInfos(int floor, int line, string date)
+        {
+            List<KeyInInfo> keyInInfos = new List<KeyInInfo>();
+
+            QueryPlanParam query = new QueryPlanParam()
+            {
+                Floor = floor,
+                Line = line,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetExceptionsExceptBreakdown",
+                query.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<KeyInInfo>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        keyInInfos = root.Data.ToList();
+                    }
+            }
+
+            return keyInInfos.OrderBy(o => o.StartTime).ToList();
+        }
+
+        /// <summary>
+        /// 读取key in 故障
+        /// </summary>
+        public static List<MachineFaultDto> GetKeyInFaults(int floor, int line, string date, string type)
+        {
+            List<MachineFaultDto> faultRecords = new List<MachineFaultDto>();
+
+            QueryMachineKeyInFaultParam query = new QueryMachineKeyInFaultParam()
+            {
+                Floor = floor,
+                Line = line,
+                MachineType = type,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetKeyInDayException",
+                query.ToJson(), 20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MachineFaultDto>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                {
+                    if (root.Data != null)
+                    {
+                        faultRecords = root.Data.OrderBy(o => o.StartTime).ToList();
+                        foreach (var item in faultRecords)
+                        {
+                            if (item.EndTime != null)
+                            {
+                                item.Duration = Math.Round((item.EndTime - item.StartTime).Value.TotalMinutes, 2);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return faultRecords.OrderBy(o => o.StartTime).ToList();
+        }
+
+        /// <summary>
+        /// 获取小时产能
+        /// </summary>
+        public static List<MachineDayOutPutPerHour> GetOutPutPerHours(string topic, string date)
+        {
+            List<MachineDayOutPutPerHour> outPutPerHours = new List<MachineDayOutPutPerHour>();
+
+            QueryMachineParam query = new QueryMachineParam()
+            {
+                Equiptopic = topic,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetOutput",
+                query.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MachineDayOutPutPerHour>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                {
+                    if (root.Data != null)
+                    {
+                        outPutPerHours = root.Data.OrderBy(o => o.DataTime).ToList();
+                        foreach (var item in outPutPerHours)
+                        {
+                            item.AutoRunTime /= 60;
+                            item.AlarmTime /= 60;
+                            item.IdleTime /= 60;
+                            item.IdleTimeDownstream /= 60;
+                            item.IdleTimeUpstream /= 60;
+                            item.IdleTimeSelf /= 60;
+                            item.LoadMATTime /= 60;
+                        }
+                    }
+                }
+            }
+
+            return outPutPerHours;
+        }
+
+        /// <summary>
+        /// 读取故障
+        /// </summary>
+        public static List<MachineFaultDto> GetFaults(string topic, string date)
+        {
+            List<MachineFaultDto> faultRecords = new List<MachineFaultDto>();
+
+            QueryMachineParam query = new QueryMachineParam()
+            {
+                Equiptopic = topic,
+                Date = date
+            };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetAlarm",
+                query.ToJson(), 20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MachineFaultDto>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                {
+                    if (root.Data != null)
+                    {
+                        faultRecords = root.Data.OrderBy(o => o.StartTime).ToList();
+                        foreach (var item in faultRecords)
+                        {
+                            if (item.EndTime != null)
+                            {
+                                item.Duration = Math.Round((item.EndTime - item.StartTime).Value.TotalMinutes, 2);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return faultRecords;
+        }
+
+        public static List<Application.Services.HomeService.Dtos.ProductionPlanDto> GetProductionPlanByTimelot(
+            int floor, int line, string startDate, string endDate)
+        {
+            var dto = new { Floor = floor, Line = line, StartDate = startDate, EndDate = endDate };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetScheduledProductionBytimeslot",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<Application.Services.HomeService.Dtos.ProductionPlanDto>>>();
+                if (root.Code == CodeEnum.Success)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<Application.Services.HomeService.Dtos.ProductionPlanDto>();
+        }
+
+        public static List<Application.Services.HomeService.Dtos.ProductionPlanDtoV1> GetProductionPlanByTimelotV1(
+            int floor, int line, string startDate, string endDate)
+        {
+            var dto = new { Floor = floor, Line = line, StartDate = startDate, EndDate = endDate };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetScheduledProductionBytimeslot",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<Application.Services.HomeService.Dtos.ProductionPlanDtoV1>>>();
+                if (root.Code == CodeEnum.Success)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<Application.Services.HomeService.Dtos.ProductionPlanDtoV1>();
+        }
+
+        public static List<MachineDayOutPutPerHour> GetOutPutPerHours(string topic, string startDate, string endDate)
+        {
+            var dto = new { Topic = topic, StartDate = startDate, EndDate = endDate };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetOutputByTimeSlot",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MachineDayOutPutPerHour>>>();
+                if (root.Code == CodeEnum.Success)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<MachineDayOutPutPerHour>();
+        }
+
+        public static List<MonthModuleTypeDto> GetMonthModuleTypeDtos(int year, int month)
+        {
+            var dto = new { Year = year, Month = month };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/GetMonthDevice",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MonthModuleTypeDto>>>();
+                if (root.Code == 0)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<MonthModuleTypeDto>();
+        }
+
+        public static List<MonthModuleType> GetMonthModuleTypeDtosByMark(int year, int month, string mark)
+        {
+            #region 测试
+
+            //List<MonthModuleType> monthModuleTypes = new List<MonthModuleType>();
+            //var a = new MonthModuleType()
+            //{
+            //    Id = Guid.NewGuid().ToString(),
+            //    LastModuleCapacity = 0,
+            //    Year = 2023,
+            //    Month = 7,
+            //    Mark = "2F",
+            //    PlanCapacity = 1000,
+            //    ModuleType = "TC097SC12",
+            //    Remark = "asdsadasd"
+            //};
+            //monthModuleTypes.Add(a);
+            //return monthModuleTypes;
+
+            #endregion
+
+
+            var dto = new { Year = year, Month = month, Mark = mark };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/GetMonthDeviceByMark",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MonthModuleType>>>();
+                if (root.Code == 0)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<MonthModuleType>();
+        }
+
+        public static List<MonthModuleType> GetMonthModuleTypes(int year, int month)
+        {
+            var dto = new { Year = year, Month = month };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/GetMonthDevice",
+                dto.ToJson(), 20);
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<MonthModuleType>>>();
+                if (root.Code == 0)
+                {
+                    return root.Data;
+                }
+            }
+            return new List<MonthModuleType>();
+        }
+
+        public static ResultDto CreateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/Create",
+                dto.ToJson(), 20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<ResultDto>();
+                return root;
+            }
+            return ResultDto.Fail("接口调用失败!");
+        }
+
+        public static ResultDto UpdateMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/Update",
+                dto.ToJson(), 20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<ResultDto>();
+                return root;
+            }
+            return ResultDto.Fail("接口调用失败!");
+        }
+
+        public static ResultDto DeleteMonthModuleType(MonthModuleTypeCreateAndUpdateDto dto)
+        {
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/MonthDevice/Delete",
+                dto.ToJson(), 20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<ResultDto>();
+                return root;
+            }
+            return ResultDto.Fail("接口调用失败!");
+        }
+
+        public static List<KeyInInfo> GetKeyInInfoTop5(int floor, int line, DateTime startDate, DateTime endDate)
+        {
+            List<KeyInInfo> keyInInfos = new List<KeyInInfo>();
+
+            var dto = new { Floor = floor, Line = line, StartDate = startDate, EndDate = endDate };
+
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/GetExceptionsExceptBreakdown",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<KeyInInfo>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        keyInInfos = root.Data.ToList();
+                    }
+            }
+
+            return keyInInfos.OrderBy(o => o.StartTime).ToList();
+        }
+
+        public static IEnumerable<ElectricEnergyMeterDataDto> GetElectricEnergyMeters(string topic)
+        {
+            IEnumerable<ElectricEnergyMeterDataDto> dtos = new List<ElectricEnergyMeterDataDto>();
+            var dto = new { Topic = topic };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/EnergyInfo/GetPowerByEqp",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<ElectricEnergyMeterDataDto>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        dtos = root.Data.ToList();
+                    }
+            }
+
+            return dtos;
+        }
+
+        public static IEnumerable<KeyInInfo> GetAlarmByKeyIn(int floor, int line, DateTime startTime, DateTime endTime)
+        {
+            IEnumerable<KeyInInfo> dtos = new List<KeyInInfo>();
+
+            var dto = new { Floor = floor, Line = line, StartTime = startTime, EndTime = endTime };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/ProductAndAlarm/GetAlarmByKeyIn",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<KeyInInfo>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        dtos = root.Data.ToList();
+                    }
+            }
+
+            return dtos;
+        }
+
+        public static IEnumerable<EnergyConsumptionDto> GetDayEnergyConsumptions(string topic, string startDate, string endDate)
+        {
+            IEnumerable<EnergyConsumptionDto> dtos = new List<EnergyConsumptionDto>();
+
+            var dto = new { Topic = topic, StartDate = startDate, EndDate = endDate };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/EnergyInfo/GetPowerByEqpAndDay",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<EnergyConsumptionDto>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        dtos = root.Data.ToList();
+                    }
+            }
+
+            return dtos;
+        }
+
+
+        public static IEnumerable<HourElectricEnergy> GetHourElectricEnergys(string topic, string date, int shift)
+        {
+            IEnumerable<HourElectricEnergy> dtos = new List<HourElectricEnergy>();
+
+            var dto = new { Topic = topic, Date = date, Shift = shift };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/EnergyInfo/GetPowerByEqpAndHour",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<HourElectricEnergy>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        dtos = root.Data.ToList();
+                    }
+            }
+
+            return dtos;
+        }
+
+        public static IEnumerable<ElectricEnergyMeterDataDto> GetPowerByEqpAndMonth(string topic, string startDate, string endDate)
+        {
+            IEnumerable<ElectricEnergyMeterDataDto> dtos = new List<ElectricEnergyMeterDataDto>();
+
+            var dto = new { Topic = topic, StartDate = startDate, EndDate = endDate };
+            var rev = HttpHelper.Post(
+                "http://eyzms.toc.eink.com:8088/MesApi/EnergyInfo/GetPowerByEqpAndMonth",
+                dto.ToJson(),
+                20);
+
+            if (rev.Item1 == true)
+            {
+                var root = rev.Item2.ToObject<Result<List<ElectricEnergyMeterDataDto>>>();
+                if (root != null && root.Code == CodeEnum.Success)
+                    if (root.Data != null)
+                    {
+                        dtos = root.Data.ToList();
+                    }
+            }
+
+            return dtos;
+        }
+    }
+}

+ 103 - 0
ProductionLineMonitor.Application/Services/OEEService/Dtos/MachineOEEInfo.cs

@@ -0,0 +1,103 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.OEEService.Dtos
+{
+    public class MachineOEEInfo
+    {
+        public List<MachineOutPutPerHour> OutPutPerHours { get; set; }
+            = new List<MachineOutPutPerHour>();
+        public DateTime StartTime { get; set; }
+        public DateTime EndTime { get; set; }
+        public string Date
+        {
+            get
+            {
+                return StartTime.ToString("MM/dd");
+            }
+        }
+        public string Shift
+        {
+            get
+            {
+                if (StartTime.Hour == 8)
+                {
+                    return "白班";
+                }
+                if (StartTime.Hour == 20)
+                {
+                    return "夜班";
+                }
+                return "";
+            }
+        }
+        public int Outputs { get; private set; }
+        public int RunTime { get; private set; }
+        public int IdelTime { get; private set; }
+        public int DownTime { get; private set; }
+        public int TotalTime { get; private set; }
+        public int LoadMATTime { get; private set; }
+        public double IdelTimeRate
+        {
+            get
+            {
+                if (TotalTime == 0)
+                {
+                    return 0;
+                }
+                return Math.Round(IdelTime * 1.0 / TotalTime * 100, 2);
+            }
+        }
+        public double RunTimeRate
+        {
+            get
+            {
+                if (TotalTime == 0)
+                {
+                    return 0;
+                }
+                return Math.Round(RunTime * 1.0 / TotalTime * 100, 2);
+            }
+        }
+        public double DownTimeRate
+        {
+            get
+            {
+                if (TotalTime == 0)
+                {
+                    return 0;
+                }
+                return Math.Round(DownTime * 1.0 / TotalTime * 100, 2);
+            }
+        }
+        public double LoadMATTimeRate
+        {
+            get
+            {
+                if (TotalTime == 0)
+                {
+                    return 0;
+                }
+                return Math.Round(LoadMATTime * 1.0 / TotalTime * 100, 2);
+            }
+        }
+        public void CalculateOEE()
+        {
+            Outputs = OutPutPerHours.Sum(s => s.OutPut.Value);
+            RunTime = OutPutPerHours.Sum(s => s.AutoRunTime.Value);
+            IdelTime = OutPutPerHours.Sum(s => s.IdleTime.Value);
+            DownTime = OutPutPerHours.Sum(s => s.AlarmTime.Value);
+            LoadMATTime = OutPutPerHours.Sum(s => s.LoadMATTime.Value);
+            TotalTime = OutPutPerHours.Count() * 60 * 60;
+        }
+    }
+
+    public class MachineOEE
+    {
+        public List<MachineOEEInfo> Infos { get; set; } 
+            = new List<MachineOEEInfo>();
+    }
+}

+ 12 - 0
ProductionLineMonitor.Application/Services/OEEService/IOEEService.cs

@@ -0,0 +1,12 @@
+using ProductionLineMonitor.Application.Services.OEEService.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.OEEService
+{
+    public interface IOEEService
+    {
+        List<MachineOEEInfo> GetOEE(string machineId, string startDate, string endDate);
+    }
+}

+ 56 - 0
ProductionLineMonitor.Application/Services/OEEService/OEEService.cs

@@ -0,0 +1,56 @@
+using AutoMapper;
+using Core.Dtos;
+using NPOI.Util;
+using ProductionLineMonitor.Application.Services.OEEService.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Application.Services.OEEService
+{
+    public class OEEService : IOEEService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        public OEEService(IUnitOfWork unitOfWork)
+        {
+            _unitOfWork = unitOfWork;
+        }
+
+        public List<MachineOEEInfo> GetOEE(string machineId, string startDate, string endDate)
+        {
+            List<MachineOEEInfo> OEES = new List<MachineOEEInfo>();
+            
+            DateTime startTime = DateTime.Parse($"{startDate} 08:00:00");  
+            DateTime endTime = DateTime.Parse($"{endDate} 08:00:00").AddDays(1);
+            
+            List<MachineOutPutPerHour> outputs = _unitOfWork.MachineOutPutPerHourRepository
+                .GetList(
+                    x =>
+                    x.MachineId == machineId &&
+                    x.DataTime >= startTime &&
+                    x.DataTime < endTime)
+                .OrderBy(
+                    o => 
+                    o.DataTime)
+                .ToList();
+
+            for (var i = startTime; i < endTime; i = i.AddHours(12))
+            {
+                DateTime tempTime = i.AddHours(12);
+                MachineOEEInfo machineOEE = new MachineOEEInfo
+                {
+                    StartTime = i,
+                    EndTime = tempTime,
+                    OutPutPerHours = outputs.FindAll(x => x.DataTime >= i && x.DataTime < tempTime)
+                };
+                machineOEE.CalculateOEE();
+                OEES.Add(machineOEE);
+            }
+
+            return OEES;
+        }
+    }
+}

+ 18 - 0
ProductionLineMonitor.Application/Services/RecipeService/IRecipeService.cs

@@ -0,0 +1,18 @@
+using ProductionLineMonitor.Core.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Services
+{
+    public interface IRecipeService
+    {
+        PageDto<IEnumerable<RecipeDto>> GetPageList(int pageIndex, int pageSize, string? keyword = null);
+        ResultDto<IEnumerable<RecipeDto>> GetList();
+        ResultDto<RecipeDto> GetById(string id);
+        ResultDto<RecipeDto> Create(RecipeCreateOrUpdateDto input);
+        ResultDto Update(string id, RecipeCreateOrUpdateDto input);
+        ResultDto Delete(string id);
+    }
+}

+ 121 - 0
ProductionLineMonitor.Application/Services/RecipeService/RecipeService.cs

@@ -0,0 +1,121 @@
+using AutoMapper;
+using ProductionLineMonitor.Core.Dtos;
+using ProductionLineMonitor.Core.IRepositories;
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Services
+{
+    public class RecipeService : IRecipeService
+    {
+        private readonly IUnitOfWork _unitOfWork;
+        private readonly IMapper _mapper;
+        public RecipeService(IUnitOfWork unitOfWork, IMapper mapper)
+        {
+            _unitOfWork = unitOfWork;
+            _mapper = mapper;
+        }
+
+        public ResultDto<RecipeDto> Create(RecipeCreateOrUpdateDto input)
+        {
+            if (_unitOfWork.RecipeRepository.Any(
+                x => x.ModuleType == input.ModuleType &&
+                x.ProductionLineId == input.ProductionLineId))
+            {
+                return ResultDto<RecipeDto>.Fail("该机种对应线别已存在,请检查!");
+            }
+            var recipe = _mapper.Map<Recipe>(input);
+            recipe.CreateTime = DateTime.Now;
+            _unitOfWork.RecipeRepository.Create(recipe);
+            _unitOfWork.SaveChanges();
+            var recipeDto = _mapper.Map<RecipeDto>(recipe);
+            return ResultDto<RecipeDto>.Success(recipeDto);
+        }
+
+        public ResultDto Delete(string id)
+        {
+            var recipe = _unitOfWork.RecipeRepository.GetById(id);
+            if (recipe == null)
+            {
+                return ResultDto.Fail("资源不存在!");
+            }
+            _unitOfWork.RecipeRepository.Delete(recipe);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+
+        public ResultDto<RecipeDto> GetById(string id)
+        {
+            var recipe = _unitOfWork.RecipeRepository.GetById(id);
+            if (recipe == null)
+            {
+                return ResultDto<RecipeDto>.Fail("资源不存在!");
+            }
+            var recipeDto = _mapper.Map<RecipeDto>(recipe);
+            var line = _unitOfWork.ProductionLineRepository.GetById(recipe.ProductionLineId);
+            if (line != null)
+            {
+                recipeDto.ProductionLineName = line.Name;
+            }
+            return ResultDto<RecipeDto>.Success(recipeDto);
+        }
+
+        public ResultDto<IEnumerable<RecipeDto>> GetList()
+        {
+            List<RecipeDto> list = new List<RecipeDto>();
+            var recipes = _unitOfWork.RecipeRepository.GetList();
+            if (recipes != null)
+            {
+                foreach (var recipe in recipes)
+                {
+                    var recipeDto = _mapper.Map<RecipeDto>(recipe);
+                    var line = _unitOfWork.ProductionLineRepository.GetById(recipeDto.ProductionLineId);
+                    if (line != null)
+                    {
+                        recipeDto.ProductionLineName = line.Name;
+                    }
+                    list.Add(recipeDto);
+                }
+            }
+            return ResultDto<IEnumerable<RecipeDto>>.Success(list);
+        }
+
+        public PageDto<IEnumerable<RecipeDto>> GetPageList(int pageIndex, int pageSize, string? keyword = null)
+        {
+            List<RecipeDto> recipeDtos = new List<RecipeDto>();
+            var recipes = _unitOfWork.RecipeRepository.GetPageList(out int total, pageIndex, pageSize, 
+                o => o.OrderByDescending(o => o.CreateTime), x => x.ModuleType.Contains(keyword));
+            if (recipes != null)
+            {
+                foreach (var recipe in recipes)
+                {
+                    var recipeDto = _mapper.Map<RecipeDto>(recipe);
+                    var line = _unitOfWork.ProductionLineRepository.GetById(recipeDto.ProductionLineId);
+                    if (line != null)
+                    {
+                        recipeDto.ProductionLineName = line.Name;
+                    }
+                    recipeDtos.Add(recipeDto);
+                }
+            }
+            return new PageDto<IEnumerable<RecipeDto>>(total, recipeDtos);
+        }
+
+        public ResultDto Update(string id, RecipeCreateOrUpdateDto input)
+        {
+            var recipe = _unitOfWork.RecipeRepository.GetById(id);
+            if (recipe == null)
+            {
+                return ResultDto.Fail("资源不存在!");
+            }
+            _mapper.Map(input, recipe);
+            recipe.UpdateTime = DateTime.Now;
+            _unitOfWork.RecipeRepository.Update(recipe);
+            _unitOfWork.SaveChanges();
+            return ResultDto.Success();
+        }
+    }
+}

+ 76 - 0
ProductionLineMonitor.Application/Services/Result.cs

@@ -0,0 +1,76 @@
+namespace ProductionLineMonitor.Application.Services
+{
+    public enum CodeEnum
+    {
+        Success,
+        Fail,
+        Error
+    }
+
+    public class Result
+    {
+        public Result() { }
+
+        public Result(CodeEnum code, string message)
+        {
+            Code = code;
+            Message = message;
+        }
+
+        public CodeEnum Code { get; set; }
+        public string Message { get; set; }
+
+        public static Result Success()
+        {
+            return new Result(CodeEnum.Success, "Ok!");
+        }
+
+        public static Result Fail(string message)
+        {
+            return new Result(CodeEnum.Fail, message);
+        }
+
+        public static Result Error(string error)
+        {
+            return new Result(CodeEnum.Error, error);
+        }
+    }
+
+    public class Result<T> : Result
+    {
+        public Result() : base() { }
+
+        public Result(CodeEnum code, string message)
+            : base(code, message)
+        {
+        }
+
+        public Result(CodeEnum code, string message, T data)
+            : base(code, message)
+        {
+            Data = data;
+        }
+
+        public T Data { get; set; }
+
+        public static new Result<T> Success()
+        {
+            return new Result<T>(CodeEnum.Success, "Ok!");
+        }
+
+        public static Result<T> Success(T data)
+        {
+            return new Result<T>(CodeEnum.Success, "Ok!", data);
+        }
+
+        public static new Result<T> Fail(string message)
+        {
+            return new Result<T>(CodeEnum.Fail, message);
+        }
+
+        public static new Result<T> Error(string message)
+        {
+            return new Result<T>(CodeEnum.Error, message);
+        }
+    }
+}

+ 12 - 0
ProductionLineMonitor.Core/Dtos/ChangeShiftDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class ChangeShiftDto
+    {
+        public string Topic { get; set; }
+        public bool IsChangeShift { get; set; }
+    }
+}

+ 20 - 0
ProductionLineMonitor.Core/Dtos/CimDto.cs

@@ -0,0 +1,20 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class CimDto
+    {
+        public string Topic { get; set; }
+        public DateTime Time { get; set; }
+        public string IpAddress { get; set; }
+        public string User { get; set; }
+        public bool PLCConnect { get; set; }
+        public bool EAPConnect { get; set; }
+        public string Version { get; set; }
+        public string EAPIP { get; set; }
+        public int EAPPort { get; set; }
+    }
+}

+ 12 - 0
ProductionLineMonitor.Core/Dtos/CimListDto.cs

@@ -0,0 +1,12 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class CimListDto : Cim
+    {
+        public int LineNo { get; set; }
+    }
+}

+ 21 - 0
ProductionLineMonitor.Core/Dtos/ECharts.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class ECharts
+    {
+        public string ChartName { get; set; }
+        public List<object> X { get; set; }
+        public List<string> Legend { get; set; } = new List<string>();
+        public List<Serie> Series { get; set; } = new List<Serie>();
+    }
+
+    public class Serie
+    {
+        public string Id { get; set; }
+        public string Name { get; set; }
+        public List<object[]> Data { get; set; } = new List<object[]>();
+    }
+}

+ 18 - 0
ProductionLineMonitor.Core/Dtos/EChartsDto.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class EChartsDto
+    {
+        public List<object> X { get; set; }
+        public List<SerieDto> Series { get; set; } = new List<SerieDto>();
+    }
+
+    public class SerieDto
+    {
+        public string Name { get; set; }
+        public object Data { get; set; }
+    }
+}

+ 1304 - 0
ProductionLineMonitor.Core/Dtos/EQPData.cs

@@ -0,0 +1,1304 @@
+using System;
+using System.Collections.Generic;
+
+namespace Core.Dtos
+{
+    [Serializable]
+    public class Alarm
+    {
+        public int AlarmCode { get; set; }
+        public string AlarmInfo { get; set; }
+        public int AlarmSum { get; set; }
+        public string Date { get; set; }
+        public string Time { get; set; }
+    }
+    [Serializable]
+    public class ALarmHour
+    {
+        public int AlarmCode { get; set; }
+        public int AlarmSum { get; set; }
+    }
+    [Serializable]
+    public class BTRate
+    {
+        public string Shift { get; set; }
+        public BtRatesOfAOI[] BTRatesOfAOI { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+    [Serializable]
+    public class ECRate
+    {
+        public string Shift { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+    [Serializable]
+    public class AGRate
+    {
+        public string Shift { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+    [Serializable]
+    public class RTVRate
+    {
+        public string Shift { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+    [Serializable]
+    public class BtRatesOfAOI
+    {
+        public int BTAOI检测NG数 { get; set; }
+        public int BTAOI检查成功率 { get; set; }
+        public int BT贴附对位NG数 { get; set; }
+        public int BT贴附对位成功率 { get; set; }
+        public int 生产总数 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfAG
+    {
+        public int 点胶总数 { get; set; }
+        public int 点胶NG数 { get; set; }
+        public int 点胶成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfFPL
+    {
+        public int FPL生产总数 { get; set; }
+        public int FPL撕膜总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPL拍照NG数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPL真空异常数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPL贴附精度NG数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPL撕膜失败NG数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPL撕膜成功率 { get; set; }
+    }
+    [Serializable]
+    public class FPLRate
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAG> RatesOfAG { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfFPL> RatesOfFPL { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public PowerConsumptns PowerConsumptn { get; set; }
+        /// <summary>
+        /// 早班
+        /// </summary>
+        public string Shift { get; set; }
+        /// <summary>
+        /// 扫码2
+        /// </summary>
+        public RatesOfScanBC RatesOfScanBC { get; set; }
+        /// <summary>
+        /// 扫码1
+        /// </summary>
+        public RatesOfScanLaserMark RatesOfScanLaserMark { get; set; }
+    }
+    [Serializable]
+    public class RatesOfScanLB
+    {
+        public int 二次成功率 { get; set; }
+        public int 扫码NG总数 { get; set; }
+        public int 扫码二次NG数 { get; set; }
+        public int 扫码一次NG数 { get; set; }
+        public int 扫码总数 { get; set; }
+        public int 扫码二次投入数 { get; set; }
+        public int 一次成功率 { get; set; }
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfFL
+    {
+        public int FL撕膜NG总数 { get; set; }
+        public int FL撕膜二次NG数 { get; set; }
+        public int FL撕膜一次NG数 { get; set; }
+        public int FL撕膜总数 { get; set; }
+        public int FL二次撕膜数 { get; set; }
+        public int 二次成功率 { get; set; }
+        public int 一次成功率 { get; set; }
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class FLRate
+    {
+        /// <summary>
+        /// 班别
+        /// </summary>
+        public string Shift { get; set; }
+
+        /// <summary>
+        /// FL撕膜
+        /// </summary>
+        public List<RatesOfFL> RatesOfFL { get; set; }
+
+        /// <summary>
+        /// EPD撕膜
+        /// </summary>
+        public List<RatesOfEPD> RatesOfEPD { get; set; }
+
+        /// <summary>
+        /// FL对位
+        /// </summary>
+        public List<RatesOfAOIBeforeLam> RatesOfFLAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// EPD对位
+        /// </summary>
+        public List<RatesOfAOIBeforeLam> RatesOfEPDAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// 精度检测
+        /// </summary>
+        public List<RatesOfAOIAfterLam> RatesOfAOIAfterLam { get; set; }
+
+        /// <summary>
+        /// 刻号扫码
+        /// </summary>
+        public RatesOfScanLaserMark RatesOfScanLaserMark { get; set; }
+
+        /// <summary>
+        ///  FL条码扫码
+        /// </summary>
+        public RatesOfScanLB RatesOfScanLB { get; set; }
+
+        /// <summary>
+        /// 厂内BC扫码
+        /// </summary>
+        public RatesOfScanBC RatesOfScanBC { get; set; }
+
+        /// <summary>
+        /// 耗电量
+        /// </summary>
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+    [Serializable]
+    public class FOGRate
+    {
+        public FOGRatesOfFPCs FOGRatesOfFPC { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+        public string Shift { get; set; }
+    }
+    [Serializable]
+    public class FOGRatesOfFPCs
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPC拍照抛料总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public float FPC抛料率 { get; set; }
+
+        /// <summary>
+        /// FPC总投入数
+        /// </summary>
+        public int FPC抛料总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int FPC真空抛料总数 { get; set; }
+        /// <summary>
+        /// FPC总投入数
+        /// </summary>
+        public int FPC总投入数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 校正平台FPC拍照抛料数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 校正平台FPC真空抛料数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 预压头FPC拍照抛料数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 预压头FPC真空抛料数 { get; set; }
+    }
+    [Serializable]
+    public class OutPutPerHour
+    {
+        public string ModuleType { get; set; }
+        public int ActualTT { get; set; }
+        public List<ALarmHour> Alarms { get; set; }
+        public int AlarmSum { get; set; }
+        public int AlarmTime { get; set; }
+        public int AutoRunTime { get; set; }
+        public int IdleTime { get; set; }
+        public int IdleTimeDownstream { get; set; }
+        public int IdleTimeSelf { get; set; }
+        public int IdleTimeUpstream { get; set; }
+        public int LoadMATSum { get; set; }
+        public int LoadMATTime { get; set; }
+        public int OutPut { get; set; }
+        public string Period { get; set; }
+        public int TargetTT { get; set; }
+    }
+    [Serializable]
+    public class PowerConsumptns
+    {
+        /// <summary>
+        /// 耗电量
+        /// </summary>
+        public float 耗电量 { get; set; }
+        /// <summary>
+        /// 耗电量(kwh)/产能(kpcs)
+        /// </summary>
+        public float 耗电量产能比 { get; set; }
+
+        /// <summary>
+        /// 当班总产能 
+        /// </summary>
+        public int 总产能 { get; set; }
+    }
+    [Serializable]
+    public class PSRateInfo
+    {
+        public List<PSRateInfoOfEPD> PSRatesOfEPD { get; set; }
+        public List<PSRateInfoOfPS> PSRatesOfPS { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+        public RatesOfScanLaserMark RatesOfScanLaserMark { get; set; }
+        public RatesOfScanBC RatesOfScanBC { get; set; }
+        public string Shift { get; set; }
+    }
+    [Serializable]
+    public class PSRateInfoOfEPD
+    {
+        public int EPD二次撕膜数 { get; set; }
+        public int EPD撕膜NG数 { get; set; }
+        public int EPD撕膜成功率 { get; set; }
+        public int EPD撕膜数 { get; set; }
+        public int EPD一次撕膜数 { get; set; }
+    }
+    [Serializable]
+    public class PSRateInfoOfPS
+    {
+        public int PS本体撕落抛料数 { get; set; }
+        public int PS放反抛料数 { get; set; }
+        public int PS拍照NG抛料数 { get; set; }
+        public int PS生产总数 { get; set; }
+        public int PS撕膜NG抛料数 { get; set; }
+        public int PS撕膜成功率 { get; set; }
+        public int PS撕膜总数 { get; set; }
+        public int PS贴附精度NG数 { get; set; }
+        public int PS真空异常抛料数 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfAOIAfterLam
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        //public int AOI对位一次NG数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI生产总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+       // public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfAOIAllAfterLam
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI生产总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位二次NG数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位NG总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI二次投入数 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfAOIBeforeLam
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI对位一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI生产总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int AOI二次投入数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfEPD
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int EPD撕膜NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int EPD撕膜二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int EPD撕膜一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int EPD撕膜总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int EPD二次撕膜数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfScanBC
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次投入数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfScanLaserMark
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次投入数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfScanTP
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 扫码二次投入数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+    [Serializable]
+    public class RatesOfScan2D
+    {
+        public int 扫码NG总数 { get; set; }
+        public int 扫码总数 { get; set; }
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfTP
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜NG总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfTPOCATP
+    {
+        public int TP撕膜NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP撕膜总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TP二次撕膜数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfFPC
+    {
+
+        public int FPC撕膜总数 { get; set; }
+
+        public int FPC撕膜NG总数 { get; set; }
+
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfOCA
+    {
+        public int OCA撕膜NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA二次撕膜数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class RatesOfOCAback
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜NG总数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜二次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜一次NG数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA撕膜总数 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OCA二次撕膜数 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 二次成功率 { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 一次成功率 { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public int 总成功率 { get; set; }
+    }
+
+    [Serializable]
+    public class TPRate
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public PowerConsumptns PowerConsumptn { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAOIAfterLam> RatesOfAOIAfterLam { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAOIBeforeLam> RatesOfAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfEPD> RatesOfEPD { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public RatesOfScanBC RatesOfScanBC { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public RatesOfScanLaserMark RatesOfScanLaserMark { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public RatesOfScanTP RatesOfScanTP { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfTP> RatesOfTP { get; set; }
+
+        /// <summary>
+        /// 早班
+        /// </summary>
+        public string Shift { get; set; }
+    }
+    [Serializable]
+    public class FPCRate
+    {
+        /// <summary>
+        /// 能耗
+        /// </summary>
+        public PowerConsumptns PowerConsumptn { get; set; }
+
+        /// <summary>
+        /// 精度检
+        /// </summary>
+        public RatesOfAOIAllAfterLam[] RatesOfAOIAfterLam { get; set; }
+
+        /// <summary>
+        /// 对位
+        /// </summary>
+        public RatesOfAOIBeforeLam[] RatesOfFPCAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// FPC撕膜
+        /// </summary>
+        public RatesOfFPC[] RatesOfFPC { get; set; }
+
+        /// <summary>
+        /// 扫码
+        /// </summary>
+        public RatesOfScan2D RatesOfScan2D { get; set; }
+
+        /// <summary>
+        /// 班别
+        /// </summary>
+        public string Shift { get; set; }
+    }
+
+    [Serializable]
+    public class TPOCARate
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public PowerConsumptns PowerConsumptn { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAOIAfterLam> RatesOfAOIAfterLam { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAOIBeforeLam> RatesOfTPAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfAOIBeforeLam> RatesOfOCAAOIBeforeLam { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfTPOCATP> RatesOfTP { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public RatesOfScan2D RatesOfScanTPofTPOCA { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public List<RatesOfOCA> RatesOfOCA { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public RatesOfOCAback RatesOfOCAback { get; set; }
+
+        /// <summary>
+        /// 早班
+        /// </summary>
+        public string Shift { get; set; }
+    }
+
+    [Serializable]
+    public class EQPData
+    {
+        public string Topic { get; set; }
+        public bool IsNewDay { get; set; }
+        public DateTime Date { get; set; }
+        public List<OutPutPerHour> OutPuts { get; set; }
+        public AGRate[] AGRates { get; set; }
+        public List<PSRateInfo> PSRates { get; set; }
+        public FOGRate[] FOGRates { get; set; }
+        public TPRate[] TPRates { get; set; }
+        public FPCRate[] FPCRates { get; set; }
+        public FPLRate[] FPLRates { get; set; }
+        public FLRate[] FLRates { get; set; }
+        public ECRate[] ECRates { get; set; }
+        public RTVRate[] RTVRates { get; set; }
+        public BTRate[] BTRates { get; set; }
+        public TPOCARate[] TPOCARates { get; set; }
+        public List<Alarm> Alarms { get; set; }
+
+        public void DataInit()
+        {
+            foreach (var item in Alarms)
+            {
+                item.Date = string.Empty;
+                item.Time = string.Empty;
+                item.AlarmSum = 0;
+            }
+            foreach (var item in OutPuts)
+            {
+                item.OutPut = 0;
+                item.AutoRunTime = 0;
+                item.AlarmSum = 0;
+                item.IdleTime = 0;
+                item.ActualTT = 0;
+                item.TargetTT = 0;
+                item.AlarmTime = 0;
+                item.LoadMATSum = 0;
+                item.LoadMATTime = 0;
+                item.Alarms = new List<ALarmHour>();
+            }
+            if (null != PSRates)
+            {
+                foreach (var item in PSRates)
+                {
+                    foreach (var epditem in item.PSRatesOfEPD)
+                    {
+                        epditem.EPD撕膜数 = 0;
+                        epditem.EPD一次撕膜数 = 0;
+                        epditem.EPD二次撕膜数 = 0;
+                        epditem.EPD撕膜NG数 = 0;
+                        epditem.EPD撕膜成功率 = 0;
+                    }
+                    foreach (var psitem in item.PSRatesOfPS)
+                    {
+                        psitem.PS生产总数 = 0;
+                        psitem.PS撕膜总数 = 0;
+                        psitem.PS拍照NG抛料数 = 0;
+                        psitem.PS真空异常抛料数 = 0;
+                        psitem.PS放反抛料数 = 0;
+                        psitem.PS本体撕落抛料数 = 0;
+                        psitem.PS撕膜NG抛料数 = 0;
+                        psitem.PS撕膜成功率 = 0;
+                        psitem.PS贴附精度NG数 = 0;
+                    }
+                }
+
+            }
+            if (null != BTRates)
+            {
+                foreach (var item in BTRates)
+                {
+                    foreach (var aoiitem in item.BTRatesOfAOI)
+                    {
+                        aoiitem.生产总数 = 0;
+                        aoiitem.BT贴附对位NG数 = 0;
+                        aoiitem.BTAOI检测NG数 = 0;
+                        aoiitem.BT贴附对位成功率 = 0;
+                        aoiitem.BTAOI检查成功率 = 0;
+                    }
+                }
+            }
+            if (null != FOGRates)
+            {
+                foreach (var item in FOGRates)
+                {
+                    item.FOGRatesOfFPC.校正平台FPC拍照抛料数 = 0;
+                    item.FOGRatesOfFPC.校正平台FPC真空抛料数 = 0;
+                    item.FOGRatesOfFPC.预压头FPC拍照抛料数 = 0;
+                    item.FOGRatesOfFPC.预压头FPC真空抛料数 = 0;
+                    item.FOGRatesOfFPC.FPC总投入数 = 0;
+                    item.FOGRatesOfFPC.FPC拍照抛料总数 = 0;
+                    item.FOGRatesOfFPC.FPC真空抛料总数 = 0;
+                    item.FOGRatesOfFPC.FPC总投入数 = 0;
+                    item.FOGRatesOfFPC.FPC抛料总数 = 0;
+                    item.FOGRatesOfFPC.FPC抛料率 = 0.0f;
+
+                    item.PowerConsumptn.耗电量 = 0.0f;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0.0f;
+                }
+            }
+            if (null != TPRates)
+            {
+                foreach (var item in TPRates)
+                {
+                    foreach (var epditem in item.RatesOfEPD)
+                    {
+                        epditem.EPD撕膜总数 = 0;
+                        epditem.EPD二次撕膜数 = 0;
+                        epditem.EPD撕膜一次NG数 = 0;
+                        epditem.EPD撕膜二次NG数 = 0;
+                        epditem.EPD撕膜NG总数 = 0;
+                        epditem.一次成功率 = 0;
+                        epditem.二次成功率 = 0;
+                        epditem.总成功率 = 0;
+                    }
+                    foreach (var tpitem in item.RatesOfTP)
+                    {
+                        tpitem.TP撕膜总数 = 0;
+                        tpitem.TP撕膜一次NG数 = 0;
+                        tpitem.TP撕膜NG总数 = 0;
+                        tpitem.一次成功率 = 0;
+                        tpitem.总成功率 = 0;
+                    }
+                    foreach (var aoibeforelamitem in item.RatesOfAOIBeforeLam)
+                    {
+                        aoibeforelamitem.AOI生产总数 = 0;
+                        aoibeforelamitem.AOI二次投入数 = 0;
+                        aoibeforelamitem.AOI对位一次NG数 = 0;
+                        aoibeforelamitem.AOI对位二次NG数 = 0;
+                        aoibeforelamitem.AOI对位NG总数 = 0;
+                        aoibeforelamitem.一次成功率 = 0;
+                        aoibeforelamitem.二次成功率 = 0;
+                        aoibeforelamitem.总成功率 = 0;
+                    }
+                    foreach (var aoiafterlamitem in item.RatesOfAOIAfterLam)
+                    {
+                        aoiafterlamitem.AOI生产总数 = 0;
+                        aoiafterlamitem.AOI对位NG总数 = 0;
+                        aoiafterlamitem.总成功率 = 0;
+                    }
+                    item.RatesOfScanLaserMark.扫码总数 = 0;
+                    item.RatesOfScanLaserMark.扫码二次投入数 = 0;
+                    item.RatesOfScanLaserMark.扫码一次NG数 = 0;
+                    item.RatesOfScanLaserMark.扫码二次NG数 = 0;
+                    item.RatesOfScanLaserMark.扫码NG总数 = 0;
+                    item.RatesOfScanLaserMark.一次成功率 = 0;
+                    item.RatesOfScanLaserMark.二次成功率 = 0;
+                    item.RatesOfScanLaserMark.总成功率 = 0;
+
+                    item.RatesOfScanTP.扫码总数 = 0;
+                    item.RatesOfScanTP.扫码二次投入数 = 0;
+                    item.RatesOfScanTP.扫码一次NG数 = 0;
+                    item.RatesOfScanTP.扫码二次NG数 = 0;
+                    item.RatesOfScanTP.扫码NG总数 = 0;
+                    item.RatesOfScanTP.一次成功率 = 0;
+                    item.RatesOfScanTP.二次成功率 = 0;
+                    item.RatesOfScanTP.总成功率 = 0;
+
+                    item.RatesOfScanBC.扫码总数 = 0;
+                    item.RatesOfScanBC.扫码二次投入数 = 0;
+                    item.RatesOfScanBC.扫码一次NG数 = 0;
+                    item.RatesOfScanBC.扫码二次NG数 = 0;
+                    item.RatesOfScanBC.扫码NG总数 = 0;
+                    item.RatesOfScanBC.一次成功率 = 0;
+                    item.RatesOfScanBC.二次成功率 = 0;
+                    item.RatesOfScanBC.总成功率 = 0;
+
+                    item.PowerConsumptn.耗电量 = 0;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0;
+                }
+            }
+            if (null != FPLRates)
+            {
+                foreach (var item in FPLRates)
+                {
+                    foreach (var agitem in item.RatesOfAG)
+                    {
+                        agitem.点胶总数 = 0;
+                        agitem.点胶NG数 = 0;
+                        agitem.点胶成功率 = 0;
+                    }
+                    foreach (var fplitem in item.RatesOfFPL)
+                    {
+                        fplitem.FPL生产总数 = 0;
+                        fplitem.FPL撕膜总数 = 0;
+                        fplitem.FPL拍照NG数 = 0;
+                        fplitem.FPL撕膜失败NG数 = 0;
+                        fplitem.FPL真空异常数 = 0;
+                        fplitem.FPL贴附精度NG数 = 0;
+                        fplitem.FPL撕膜成功率 = 0;
+                    }
+                    item.PowerConsumptn.耗电量 = 0.0f;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0.0f;
+                }
+            }
+            if (null != FLRates)
+            {
+                foreach (var item in FLRates)
+                {
+                    foreach (var epditem in item.RatesOfEPD)
+                    {
+                        epditem.EPD撕膜总数 = 0;
+                        epditem.EPD二次撕膜数 = 0;
+                        epditem.EPD撕膜一次NG数 = 0;
+                        epditem.EPD撕膜二次NG数 = 0;
+                        epditem.EPD撕膜NG总数 = 0;
+                        epditem.一次成功率 = 0;
+                        epditem.二次成功率 = 0;
+                        epditem.总成功率 = 0;
+                    }
+                    foreach (var flitem in item.RatesOfFL)
+                    {
+                        flitem.FL撕膜总数 = 0;
+                        flitem.FL二次撕膜数 = 0;
+                        flitem.FL撕膜一次NG数 = 0;
+                        flitem.FL撕膜二次NG数 = 0;
+                        flitem.FL撕膜NG总数 = 0;
+                        flitem.一次成功率 = 0;
+                        flitem.二次成功率 = 0;
+                        flitem.总成功率 = 0;
+                    }
+                    foreach (var AOIBeforeLamitem in item.RatesOfFLAOIBeforeLam)
+                    {
+                        AOIBeforeLamitem.AOI生产总数 = 0;
+                        AOIBeforeLamitem.AOI二次投入数 = 0;
+                        AOIBeforeLamitem.AOI对位一次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位二次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位NG总数 = 0;
+                        AOIBeforeLamitem.一次成功率 = 0;
+                        AOIBeforeLamitem.二次成功率 = 0;
+                        AOIBeforeLamitem.总成功率 = 0;
+                    }
+                    foreach (var AOIBeforeLamitem in item.RatesOfEPDAOIBeforeLam)
+                    {
+                        AOIBeforeLamitem.AOI生产总数 = 0;
+                        AOIBeforeLamitem.AOI二次投入数 = 0;
+                        AOIBeforeLamitem.AOI对位一次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位二次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位NG总数 = 0;
+                        AOIBeforeLamitem.一次成功率 = 0;
+                        AOIBeforeLamitem.二次成功率 = 0;
+                        AOIBeforeLamitem.总成功率 = 0;
+                    }
+
+                    foreach (var AOIAfterLamitem in item.RatesOfAOIAfterLam)
+                    {
+
+                        AOIAfterLamitem.AOI生产总数 = 0;
+                        AOIAfterLamitem.AOI对位NG总数 = 0;
+                        AOIAfterLamitem.总成功率 = 0;
+                    }
+                    item.RatesOfScanLaserMark.扫码总数 = 0;
+                    item.RatesOfScanLaserMark.扫码二次投入数 = 0;
+                    item.RatesOfScanLaserMark.扫码一次NG数 = 0;
+                    item.RatesOfScanLaserMark.扫码二次NG数 = 0;
+                    item.RatesOfScanLaserMark.扫码NG总数 = 0;
+                    item.RatesOfScanLaserMark.一次成功率 = 0;
+                    item.RatesOfScanLaserMark.二次成功率 = 0;
+                    item.RatesOfScanLaserMark.总成功率 = 0;
+
+                    item.RatesOfScanLB.扫码总数 = 0;
+                    item.RatesOfScanLB.扫码二次投入数 = 0;
+                    item.RatesOfScanLB.扫码一次NG数 = 0;
+                    item.RatesOfScanLB.扫码二次NG数 = 0;
+                    item.RatesOfScanLB.扫码NG总数 = 0;
+                    item.RatesOfScanLB.一次成功率 = 0;
+                    item.RatesOfScanLB.二次成功率 = 0;
+                    item.RatesOfScanLB.总成功率 = 0;
+
+                    item.RatesOfScanBC.扫码总数 = 0;
+                    item.RatesOfScanBC.扫码二次投入数 = 0;
+                    item.RatesOfScanBC.扫码一次NG数 = 0;
+                    item.RatesOfScanBC.扫码二次NG数 = 0;
+                    item.RatesOfScanBC.扫码NG总数 = 0;
+                    item.RatesOfScanBC.一次成功率 = 0;
+                    item.RatesOfScanBC.二次成功率 = 0;
+                    item.RatesOfScanBC.总成功率 = 0;
+
+                    item.PowerConsumptn.耗电量 = 0;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0;
+                }
+            }
+            if (null != ECRates)
+            {
+                foreach (var item in ECRates)
+                {
+                    item.PowerConsumptn.耗电量 = 0.0f;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0.0f;
+                }
+            }
+            if (null != RTVRates)
+            {
+                foreach (var item in RTVRates)
+                {
+                    item.PowerConsumptn.耗电量 = 0.0f;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0.0f;
+                }
+            }
+            if (null != TPOCARates)
+            {
+                foreach (var item in TPOCARates)
+                {
+                    foreach (var tpitem in item.RatesOfTP)
+                    {
+                        tpitem.TP撕膜总数 = 0;
+                        tpitem.TP二次撕膜数 = 0;
+                        tpitem.TP撕膜一次NG数 = 0;
+                        tpitem.TP撕膜二次NG数 = 0;
+                        tpitem.TP撕膜NG总数 = 0;
+                        tpitem.一次成功率 = 0;
+                        tpitem.二次成功率 = 0;
+                        tpitem.总成功率 = 0;
+                    }
+                    foreach (var ocaitem in item.RatesOfOCA)
+                    {
+                        ocaitem.OCA撕膜总数 = 0;
+                        ocaitem.OCA二次撕膜数 = 0;
+                        ocaitem.OCA撕膜一次NG数 = 0;
+                        ocaitem.OCA撕膜二次NG数 = 0;
+                        ocaitem.OCA撕膜NG总数 = 0;
+                        ocaitem.一次成功率 = 0;
+                        ocaitem.二次成功率 = 0;
+                        ocaitem.总成功率 = 0;
+                    }
+                    foreach (var AOIBeforeLamitem in item.RatesOfTPAOIBeforeLam)
+                    {
+                        AOIBeforeLamitem.AOI生产总数 = 0;
+                        AOIBeforeLamitem.AOI二次投入数 = 0;
+                        AOIBeforeLamitem.AOI对位一次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位二次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位NG总数 = 0;
+                        AOIBeforeLamitem.一次成功率 = 0;
+                        AOIBeforeLamitem.二次成功率 = 0;
+                        AOIBeforeLamitem.总成功率 = 0;
+                    }
+                    foreach (var AOIBeforeLamitem in item.RatesOfOCAAOIBeforeLam)
+                    {
+                        AOIBeforeLamitem.AOI生产总数 = 0;
+                        AOIBeforeLamitem.AOI二次投入数 = 0;
+                        AOIBeforeLamitem.AOI对位一次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位二次NG数 = 0;
+                        AOIBeforeLamitem.AOI对位NG总数 = 0;
+                        AOIBeforeLamitem.一次成功率 = 0;
+                        AOIBeforeLamitem.二次成功率 = 0;
+                        AOIBeforeLamitem.总成功率 = 0;
+                    }
+
+                    foreach (var AOIAfterLamitem in item.RatesOfAOIAfterLam)
+                    {
+
+                        AOIAfterLamitem.AOI生产总数 = 0;
+                        AOIAfterLamitem.AOI对位NG总数 = 0;
+                        AOIAfterLamitem.总成功率 = 0;
+                    }
+
+                    item.RatesOfOCAback.OCA撕膜总数 = 0;
+                    item.RatesOfOCAback.OCA二次撕膜数 = 0;
+                    item.RatesOfOCAback.OCA撕膜一次NG数 = 0;
+                    item.RatesOfOCAback.OCA撕膜二次NG数 = 0;
+                    item.RatesOfOCAback.OCA撕膜NG总数 = 0;
+                    item.RatesOfOCAback.一次成功率 = 0;
+                    item.RatesOfOCAback.二次成功率 = 0;
+                    item.RatesOfOCAback.总成功率 = 0;
+
+
+                    item.RatesOfScanTPofTPOCA.扫码总数 = 0;
+                    item.RatesOfScanTPofTPOCA.扫码NG总数 = 0;
+                    item.RatesOfScanTPofTPOCA.总成功率 = 0;
+
+
+
+                    item.PowerConsumptn.耗电量 = 0;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0;
+                }
+            }
+            if (null != FPCRates)
+            {
+                foreach (var item in FPCRates)
+                {
+                    foreach (var fpcitem in item.RatesOfFPC)
+                    {
+                        fpcitem.FPC撕膜NG总数 = 0;
+                        fpcitem.FPC撕膜总数 = 0;
+                        fpcitem.总成功率 = 0;
+                    }
+                    foreach (var fpcocaitem in item.RatesOfFPCAOIBeforeLam)
+                    {
+                        fpcocaitem.AOI生产总数 = 0;
+                        fpcocaitem.AOI对位一次NG数 = 0;
+                        fpcocaitem.AOI二次投入数 = 0;
+                        fpcocaitem.AOI对位二次NG数 = 0;
+                        fpcocaitem.AOI对位NG总数 = 0;
+                        fpcocaitem.一次成功率 = 0;
+                        fpcocaitem.二次成功率 = 0;
+                        fpcocaitem.总成功率 = 0;
+                    }
+                    foreach (var AOIAfterLamitem in item.RatesOfAOIAfterLam)
+                    {
+                        AOIAfterLamitem.AOI生产总数 = 0;
+                        AOIAfterLamitem.AOI二次投入数 = 0;
+                        AOIAfterLamitem.AOI对位一次NG数 = 0;
+                        AOIAfterLamitem.AOI对位二次NG数 = 0;
+                        AOIAfterLamitem.AOI对位NG总数 = 0;
+                        AOIAfterLamitem.一次成功率 = 0;
+                        AOIAfterLamitem.二次成功率 = 0;
+                        AOIAfterLamitem.总成功率 = 0;
+                    }
+
+                    item.RatesOfScan2D.扫码总数 = 0;
+                    item.RatesOfScan2D.扫码NG总数 = 0;
+                    item.RatesOfScan2D.总成功率 = 0;
+
+
+                    item.PowerConsumptn.耗电量 = 0;
+                    item.PowerConsumptn.总产能 = 0;
+                    item.PowerConsumptn.耗电量产能比 = 0;
+                }
+            }
+
+        }
+    }
+}

+ 144 - 0
ProductionLineMonitor.Core/Dtos/EQPDataDto.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Core.Dtos
+{
+    public class EQPDataDto
+    {
+        public string Topic { get; set; }
+        public DateTime Date { get; set; }
+        public List<OutPutPerHourDto> OutPuts { get; set; } = new List<OutPutPerHourDto>();
+
+        public List<PSRateInfoDto> PSRates { get; set; } = new List<PSRateInfoDto>();
+        public List<ECRateDto> ECRates { get; set; } = new List<ECRateDto>();
+        public List<BTRateInfoDto> BTRates { get; set; } = new List<BTRateInfoDto>();
+        public List<FOGRateDto> FOGRates { get; set; } = new List<FOGRateDto>();
+        public List<FPLRateDto> FPLRates { get; set; } = new List<FPLRateDto>();
+        public List<RTVRateDto> RTVRates { get; set; } = new List<RTVRateDto>();
+        public List<AGRateDto> AGRates { get; set; } = new List<AGRateDto>();
+        public List<TPRateDto> TPRates { get; set; } = new List<TPRateDto>();
+        public List<FPCRateDto> FPCRates { get; set; } = new List<FPCRateDto>();
+        public List<FLRateDto> FLRates { get; set; } = new List<FLRateDto>();
+        public List<OTPRateDto> OTPRates { get; set; } = new List<OTPRateDto>();
+
+    }
+
+    public class OTPRateDto
+    {
+        public string Date { get; set; }
+        public string Shift { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+
+    public class TPRateDto : TPRate
+    {
+        public string Date { get; set; }
+    }
+
+    public class FPCRateDto: FPCRate
+    {
+        public string Date { get; set; }
+    }
+
+    public class FLRateDto : FLRate
+    {
+        public string Date { get; set; }
+    }
+
+    public class AGRateDto : AGRate
+    {
+        public string Date { get; set; }
+    }
+
+    public class RTVRateDto : RTVRate
+    {
+        public string Date { get; set; }
+    }
+
+    public class OutPutPerHourDto : OutPutPerHour
+    {
+        public string Date { get; set; }
+        public DateTime? DateTime
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(Date) || string.IsNullOrEmpty(Period))
+                {
+                    return null;
+                }
+                string time = Period[..Period.IndexOf('~')];
+                return Convert.ToDateTime($"{Date} {time}:00");
+            }
+        }
+
+        public string ModuleTypeString
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(ModuleType))
+                {
+                    return "";
+                }
+
+                if (ModuleType.Length < 8)
+                {
+                    return ModuleType;  
+                }
+
+                return ModuleType[..8];
+            }
+        }
+    }
+
+    #region PS
+
+    public class PSRateInfoDto
+    {
+        public string Date { get; set; }
+        public string Shift { get; set; }    
+        public List<PSRateInfoOfEPD> PSRatesOfEPD { get; set; } = new List<PSRateInfoOfEPD>();
+        public List<PSRateInfoOfPS> PSRatesOfPS { get; set; } = new List<PSRateInfoOfPS>();
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+
+    #endregion
+
+    #region EC
+
+    public class ECRateDto
+    {
+        public string Date { get; set; }
+        public string Shift { get; set; }
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+
+    #endregion
+
+    #region BT
+
+    public class BTRateInfoDto
+    {
+        public string Date { get; set; }
+        public string Shift { get; set; }
+        public List<BtRatesOfAOI> BTRatesOfAOI { get; set; } = new List<BtRatesOfAOI>();
+        public PowerConsumptns PowerConsumptn { get; set; }
+    }
+
+    #endregion
+
+    #region FOG
+
+    public class FOGRateDto : FOGRate
+    {
+        public string Date { get; set; }
+    }
+
+    #endregion
+
+
+    public class FPLRateDto : FPLRate
+    {
+        public string Date { get; set; }
+    }
+}

+ 39 - 0
ProductionLineMonitor.Core/Dtos/EQPDataFaultRecordDto.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class EQPDataFaultRecordDto
+    {
+        public string Id { get; set; }
+        /// <summary>
+        /// 用来区分不同机台
+        /// </summary>
+        public string Topic { get; set; }
+        /// <summary>
+        /// 发生故障时机台状态
+        /// </summary>
+        public int MachineState { get; set; }
+        /// <summary>
+        /// 故障码
+        /// </summary>
+        public int FaultCode { get; set; }
+        /// <summary>
+        /// 故障未处理完触发的次数
+        /// </summary>
+        public int TriggerNumber { get; set; }
+        /// <summary>
+        /// 1:处理中 2:处理完成
+        /// </summary>
+        public int State { get; set; }
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public DateTime? StartTime { get; set; }
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+    }
+}

+ 12 - 0
ProductionLineMonitor.Core/Dtos/EapDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class EapDto
+    {
+        public string Ip { get; set; }
+        public int Port { get; set; }
+    }
+}

+ 13 - 0
ProductionLineMonitor.Core/Dtos/HomeDto.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class HomeDto
+    {
+        public int Capacity { get; set; }
+        public float EnergyConsumption { get; set; }
+        public int Cim { get; set; }
+    }
+}

+ 27 - 0
ProductionLineMonitor.Core/Dtos/MachineDto.cs

@@ -0,0 +1,27 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineDto : Machine
+    {
+        /// <summary>
+        /// 小时产能
+        /// </summary>
+        public List<MachineOutPutPerHourDto> OutPutPerHours { get; set; } = new List<MachineOutPutPerHourDto>();
+        /// <summary>
+        /// 故障记录
+        /// </summary>
+        public List<MachineFaultRecordDto> FaultRecords { get; set; } = new List<MachineFaultRecordDto>();
+        /// <summary>
+        /// 统计
+        /// </summary>
+        public List<MachineStatisticsDto> Statistics { get; set; } = new List<MachineStatisticsDto>();
+        /// <summary>
+        /// 故障TOP5
+        /// </summary>
+        public List<MachineFaultRecordDto> Top5FaultRecords { get; set; } = new List<MachineFaultRecordDto>();
+    }
+}

+ 18 - 0
ProductionLineMonitor.Core/Dtos/MachineEnergyConsumptionDto.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineEnergyConsumptionDto
+    {
+        public int Capacity1 { get; set; }
+        public double EnergyConsumption1 { get; set; }
+
+        public int Capacity2 { get; set; }
+        public double EnergyConsumption2 { get; set; }
+        
+        
+
+    }
+}

+ 20 - 0
ProductionLineMonitor.Core/Dtos/MachineFaultComparisonDto.cs

@@ -0,0 +1,20 @@
+using ProductionLineMonitor.Core.Enums;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class FaultComparisonDto
+    {
+        public string AlarmCode { get; set; } = string.Empty;
+        public AlarmLevelEnum AlarmLevel { get; set; }
+        public string AlarmInfo { get; set; } = string.Empty;
+    }
+
+    public class MachineFaultComparisonDto
+    {
+        public string DeviceNo { get; set; } = string.Empty;
+        public List<FaultComparisonDto> FaultComparisonDtos { get; set; }
+    }
+}

+ 111 - 0
ProductionLineMonitor.Core/Dtos/MachineFaultRecordDto.cs

@@ -0,0 +1,111 @@
+using ProductionLineMonitor.Core.Models;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineFaultRecordDto : MachineFaultRecord
+    {
+        /// <summary>
+        /// 故障描述
+        /// </summary>
+        public string FaultInfo { get; set; }
+        /// <summary>
+        /// 等级 1:提示 2:警告 3:Down机
+        /// </summary>
+        public int? FaultLevel { get; set; }
+        /// <summary>
+        /// 故障类别
+        /// </summary>
+        public string FaultCategory { get; set; }
+        /// <summary>
+        /// 持续时间
+        /// </summary>
+        public double? Duration { get; set; }
+        /// <summary>
+        /// 机台名称
+        /// </summary>
+        public string MachineName { get; set; }
+    }
+
+    public class MachineFaultRecordDtoV1 : MachineFaultRecord
+    {
+        public string FaultInfo { get; set; }
+        public int? FaultLevel { get; set; }
+        public string FaultCategory { get; set; }
+        public int Count
+        {
+            get
+            {
+                return ShiftCount + NightShiftCount;
+            }
+        }
+        public int ShiftCount { get; set; }
+        public int NightShiftCount { get; set; }
+    }
+
+    public class MachineFaultRecordDtoV2
+    {
+        public List<string> Dates { get; set; } = new List<string>();
+        public List<RecordDto> Records { get; set; } = new List<RecordDto>();
+    }
+
+    public class RecordDto
+    {
+        public string FaultInfo { get; set; }
+        public List<object[]> Value { get; set; } = new List<object[]>();
+    }
+
+    public class FaultFrequency
+    {
+        public int FaultCode { get; set; }
+        public string FaultInfo { get; set; }
+        public int Count { get; set; }
+    }
+
+    public class FaultFrequencyByShift
+    {
+        public string Shift { get; set; }
+        public List<FaultFrequency> FaultFrequencies { get; set; } 
+            = new List<FaultFrequency>();
+    }
+
+    public class FaultFrequencyByShiftTable
+    {
+        public FaultFrequencyByShiftTable(List<FaultFrequencyByShift> faultFrequencyByShifts)
+        {
+            Shifts = new string[faultFrequencyByShifts.Count - 2];
+
+            for (int i = 0; i < faultFrequencyByShifts.Count - 2; i++)
+            {
+                Shifts[i] = faultFrequencyByShifts[i].Shift;
+                for (int j = 0; j < faultFrequencyByShifts[i].FaultFrequencies.Count; j++)
+                {
+                    if (!FaultCodes.Contains(faultFrequencyByShifts[i].FaultFrequencies[j].FaultCode))
+                    {
+                        FaultCodes.Add(faultFrequencyByShifts[i].FaultFrequencies[j].FaultCode);
+                        FaultInfos.Add(faultFrequencyByShifts[i].FaultFrequencies[j].FaultInfo);
+                        Nos.Add(FaultCodes.Count);
+                    }
+                }
+            }
+
+            Counts = new int[Shifts.Count(), Nos.Count()];
+
+            for (int i = 0; i < faultFrequencyByShifts.Count - 2; i++)
+            {
+                Shifts[i] = faultFrequencyByShifts[i].Shift;
+                for (int j = 0; j < faultFrequencyByShifts[i].FaultFrequencies.Count; j++)
+                {
+                    int row = FaultCodes.IndexOf(faultFrequencyByShifts[i].FaultFrequencies[j].FaultCode);
+                    Counts[i, row] = faultFrequencyByShifts[i].FaultFrequencies[j].Count;
+                }
+            }
+        }
+        public string[] Shifts { get; set; }
+        public List<int> Nos { get; set; } = new List<int>();
+        public List<int> FaultCodes { get; set; } = new List<int>();
+        public List<string> FaultInfos { get; set; } = new List<string>();
+        public int[,] Counts { get; set; }
+    }
+}

+ 11 - 0
ProductionLineMonitor.Core/Dtos/MachineOutPutPerHourAlarmDto.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineOutPutPerHourAlarmDto : MachineOutPutPerHourDto
+    {
+        public string MachineName { get; set; }
+    }
+}

+ 12 - 0
ProductionLineMonitor.Core/Dtos/MachineOutPutPerHourDto.cs

@@ -0,0 +1,12 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineOutPutPerHourDto : MachineOutPutPerHour
+    {
+        public string Period { get; set; }
+    }
+}

+ 34 - 0
ProductionLineMonitor.Core/Dtos/MachineStatisticsDto.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MachineStatisticsDto
+    {
+        /// <summary>
+        /// 机种
+        /// </summary>
+        public string ModuleType { get; set; }
+        /// <summary>
+        /// 产能
+        /// </summary>
+        public int? Capacity { get; set; }
+        /// <summary>
+        /// 时间稼动率
+        /// </summary>
+        public double? Availability { get; set; }
+        /// <summary>
+        /// 性能稼动率
+        /// </summary>
+        public double? Performance { get; set; }
+        /// <summary>
+        /// 良率
+        /// </summary>
+        public double? Quality { get; set; }
+        /// <summary>
+        /// 设备综合效率
+        /// </summary>
+        public double? OEE { get; set; }
+    }
+}

+ 17 - 0
ProductionLineMonitor.Core/Dtos/MenuDto.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MenuDto
+    {
+        public string Id { get; set; }
+        public string Icon { get; set; }
+        public string Url { get; set; }
+        public string Name { get; set; }       
+
+        public List<MenuDto> Items { get; set;} 
+            = new List<MenuDto>();
+    }
+}

+ 12 - 0
ProductionLineMonitor.Core/Dtos/MqttClientStateDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class MqttClientStateDto
+    {
+        public string ClientId { get; set; }
+        public bool IsConnected { get; set; }
+    }
+}

+ 18 - 0
ProductionLineMonitor.Core/Dtos/PageDto.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class PageDto<T>
+    {
+        public PageDto(int total, T rows)
+        {
+            Total = total;
+            Rows = rows;
+        }
+
+        public int Total { get; set; }
+        public T Rows { get; set; }
+    }
+}

+ 23 - 0
ProductionLineMonitor.Core/Dtos/ProductionLineDto.cs

@@ -0,0 +1,23 @@
+using ProductionLineMonitor.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ProductionLineMonitor.Core.Dtos
+{
+    public class ProductionLineDto : ProductionLine
+    {
+        /// <summary>
+        /// 机台列表
+        /// </summary>
+        public List<MachineDto> Machines { get; set; } = new List<MachineDto>();
+        /// <summary>
+        /// 统计
+        /// </summary>
+        public List<MachineStatisticsDto> Statistics { get; set; } = new List<MachineStatisticsDto>();
+        /// <summary>
+        /// 故障Top5
+        /// </summary>
+        public List<MachineFaultRecordDto> Top5FaultRecords { get; set; } = new List<MachineFaultRecordDto>();
+    }
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов