diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7afbb02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,342 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +appsettings.demo.json \ No newline at end of file diff --git a/Khd.Core.Api.sln b/Khd.Core.Api.sln new file mode 100644 index 0000000..2c1d84d --- /dev/null +++ b/Khd.Core.Api.sln @@ -0,0 +1,85 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Api", "src\Khd.Core.Api\Khd.Core.Api.csproj", "{51C310CD-5D5C-49AF-A823-05743DFA84F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.EntityFramework", "src\Khd.Core.EntityFramework\Khd.Core.EntityFramework.csproj", "{44DF5518-A298-4B86-9169-6119ADF39B2D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Domain", "src\Khd.Core.Domain\Khd.Core.Domain.csproj", "{BAF46618-412D-4515-B4F5-D5F7BFE2ED7C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Library", "src\Khd.Core.Library\Khd.Core.Library.csproj", "{CA8E36B3-560A-49AB-A51B-491879312E48}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Application", "src\Khd.Core.Application\Khd.Core.Application.csproj", "{B1981F15-21AD-4340-8818-36BB1AAA7E3D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Plc", "src\Khd.Core.Plc\Khd.Core.Plc.csproj", "{68C935C1-3FC2-42B2-A2CB-F1D4D3A8C507}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Wcs", "src\Khd.Core.Wcs\Khd.Core.Wcs.csproj", "{D924C4A2-85D6-46D1-86E0-CEBF66F897FD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Thrift", "src\Khd.Core.Thrift\Khd.Core.Thrift.csproj", "{205EBEBA-7AD4-4D1A-9282-052A74ED9C42}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Thrift.Client", "src\Khd.Core.Thrift.Client\Khd.Core.Thrift.Client.csproj", "{2213CBB9-A843-4D73-AE3A-A26923E21855}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Khd.Core.Thrift.Server", "src\Khd.Core.Thrift.Server\Khd.Core.Thrift.Server.csproj", "{23371C02-98D7-4B01-80D6-542F6494D920}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Khd.Core.Wpf", "src\Khd.Core.Wpf\Khd.Core.Wpf.csproj", "{E28EBEAC-E51B-4905-97AD-0CE756E3E1EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {51C310CD-5D5C-49AF-A823-05743DFA84F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51C310CD-5D5C-49AF-A823-05743DFA84F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51C310CD-5D5C-49AF-A823-05743DFA84F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51C310CD-5D5C-49AF-A823-05743DFA84F9}.Release|Any CPU.Build.0 = Release|Any CPU + {44DF5518-A298-4B86-9169-6119ADF39B2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44DF5518-A298-4B86-9169-6119ADF39B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44DF5518-A298-4B86-9169-6119ADF39B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44DF5518-A298-4B86-9169-6119ADF39B2D}.Release|Any CPU.Build.0 = Release|Any CPU + {BAF46618-412D-4515-B4F5-D5F7BFE2ED7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAF46618-412D-4515-B4F5-D5F7BFE2ED7C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAF46618-412D-4515-B4F5-D5F7BFE2ED7C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAF46618-412D-4515-B4F5-D5F7BFE2ED7C}.Release|Any CPU.Build.0 = Release|Any CPU + {CA8E36B3-560A-49AB-A51B-491879312E48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA8E36B3-560A-49AB-A51B-491879312E48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA8E36B3-560A-49AB-A51B-491879312E48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA8E36B3-560A-49AB-A51B-491879312E48}.Release|Any CPU.Build.0 = Release|Any CPU + {B1981F15-21AD-4340-8818-36BB1AAA7E3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1981F15-21AD-4340-8818-36BB1AAA7E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1981F15-21AD-4340-8818-36BB1AAA7E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1981F15-21AD-4340-8818-36BB1AAA7E3D}.Release|Any CPU.Build.0 = Release|Any CPU + {68C935C1-3FC2-42B2-A2CB-F1D4D3A8C507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68C935C1-3FC2-42B2-A2CB-F1D4D3A8C507}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68C935C1-3FC2-42B2-A2CB-F1D4D3A8C507}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68C935C1-3FC2-42B2-A2CB-F1D4D3A8C507}.Release|Any CPU.Build.0 = Release|Any CPU + {D924C4A2-85D6-46D1-86E0-CEBF66F897FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D924C4A2-85D6-46D1-86E0-CEBF66F897FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D924C4A2-85D6-46D1-86E0-CEBF66F897FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D924C4A2-85D6-46D1-86E0-CEBF66F897FD}.Release|Any CPU.Build.0 = Release|Any CPU + {205EBEBA-7AD4-4D1A-9282-052A74ED9C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {205EBEBA-7AD4-4D1A-9282-052A74ED9C42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {205EBEBA-7AD4-4D1A-9282-052A74ED9C42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {205EBEBA-7AD4-4D1A-9282-052A74ED9C42}.Release|Any CPU.Build.0 = Release|Any CPU + {2213CBB9-A843-4D73-AE3A-A26923E21855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2213CBB9-A843-4D73-AE3A-A26923E21855}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2213CBB9-A843-4D73-AE3A-A26923E21855}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2213CBB9-A843-4D73-AE3A-A26923E21855}.Release|Any CPU.Build.0 = Release|Any CPU + {23371C02-98D7-4B01-80D6-542F6494D920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23371C02-98D7-4B01-80D6-542F6494D920}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23371C02-98D7-4B01-80D6-542F6494D920}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23371C02-98D7-4B01-80D6-542F6494D920}.Release|Any CPU.Build.0 = Release|Any CPU + {E28EBEAC-E51B-4905-97AD-0CE756E3E1EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E28EBEAC-E51B-4905-97AD-0CE756E3E1EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E28EBEAC-E51B-4905-97AD-0CE756E3E1EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E28EBEAC-E51B-4905-97AD-0CE756E3E1EF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1938C827-EDD1-4789-AFB4-F08EC6DAA544} + EndGlobalSection +EndGlobal diff --git a/dll/HslCommunication.dll b/dll/HslCommunication.dll new file mode 100644 index 0000000..74af12f Binary files /dev/null and b/dll/HslCommunication.dll differ diff --git a/dll/S7.Net.dll b/dll/S7.Net.dll new file mode 100644 index 0000000..5be36c1 Binary files /dev/null and b/dll/S7.Net.dll differ diff --git a/dll/SevenZipSharp.dll b/dll/SevenZipSharp.dll new file mode 100644 index 0000000..1616630 Binary files /dev/null and b/dll/SevenZipSharp.dll differ diff --git a/src/Khd.Core.Api/.config/dotnet-tools.json b/src/Khd.Core.Api/.config/dotnet-tools.json new file mode 100644 index 0000000..337014b --- /dev/null +++ b/src/Khd.Core.Api/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.10", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Api/Controllers/ReceiveProdPlanController.cs b/src/Khd.Core.Api/Controllers/ReceiveProdPlanController.cs new file mode 100644 index 0000000..22b895c --- /dev/null +++ b/src/Khd.Core.Api/Controllers/ReceiveProdPlanController.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Khd.Core.Application.Interface; +using Khd.Core.Domain.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Masuit.Tools.Logging; +using System; +using NuGet.Protocol; +using Khd.Core.Application; +using Khd.Core.Domain.Dto.webapi; +using Microsoft.Build.Utilities; +using Masuit.Tools; + +namespace Khd.Core.Api.Controllers +{ + [Route("wcs/[controller]")] + [ApiController] + public class ReceiveProdPlanController : ControllerBase + { + private readonly IMesProdPlanApplication _application; + + + public ReceiveProdPlanController(IMesProdPlanApplication application) + { + _application = application; + } + + [HttpPost] + public ReponseBase SaveProdPlan(RequestInfo model) + { + LogManager.Info($"收到MES接口信息:{model.ToJsonString()}"); + return _application.SaveProdPlan(model); + } + + } +} \ No newline at end of file diff --git a/src/Khd.Core.Api/Khd.Core.Api.csproj b/src/Khd.Core.Api/Khd.Core.Api.csproj new file mode 100644 index 0000000..4ddcc19 --- /dev/null +++ b/src/Khd.Core.Api/Khd.Core.Api.csproj @@ -0,0 +1,46 @@ + + + + net6.0 + 051ea637-792d-470a-ad9f-3d79eb0f6635 + + + + bin\Debug\Khd.Core.Api.xml + bin\Debug + NU1605 + 1701;1702;1591 + + + + bin\Release + bin\Release\Khd.Core.Api.xml + NU1605 + 1701;1702;1591 + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + ..\..\dll\S7.Net.dll + + + + \ No newline at end of file diff --git a/src/Khd.Core.Api/Program.cs b/src/Khd.Core.Api/Program.cs new file mode 100644 index 0000000..dea4ad6 --- /dev/null +++ b/src/Khd.Core.Api/Program.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using System; +using System.IO; +using System.Linq; +using System.Timers; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; + +namespace Khd.Core.Api +{ + public class Program + { + public static void Main(string[] args) + { + #region ʱɾ־ + // öʱ24Сʱ86400000룩 + System.Timers.Timer timer = new System.Timers.Timer(86400000); + + timer.Elapsed += TimerElapsed; + timer.AutoReset = true; + timer.Start(); + + // ִɾ + DeleteOldLogFiles(); + #endregion + CreateHostBuilder(args).Build().Run(); + } + + private static void TimerElapsed(object sender, ElapsedEventArgs e) + { + DeleteOldLogFiles(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseUrls("http://0.0.0.0:5001"); + webBuilder.UseStartup(); + }); + } + private static void DeleteOldLogFiles() + { + string logDirectory = AppDomain.CurrentDomain.BaseDirectory + "logs"; // 滻Ϊʵʵ־ļ· + DateTime cutoffDate = DateTime.Now.AddDays(-3); // 3ǰ + + try + { + DirectoryInfo dirInfo = new DirectoryInfo(logDirectory); + + // ȡ־ļɸѡҪɾļ + FileInfo[] logFiles = dirInfo.GetFiles("*.log") + .Where(file => file.LastWriteTime < cutoffDate) + .ToArray(); + + foreach (var file in logFiles) + { + // ɾļ + file.Delete(); + Console.WriteLine($"ɾļ{file.Name}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"ɾ־ļʱִ{ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Api/Properties/launchSettings.json b/src/Khd.Core.Api/Properties/launchSettings.json new file mode 100644 index 0000000..955e9ef --- /dev/null +++ b/src/Khd.Core.Api/Properties/launchSettings.json @@ -0,0 +1,22 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:63413", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "demo": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "demo" + }, + "applicationUrl": "http://localhost:5001" + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Api/Startup.cs b/src/Khd.Core.Api/Startup.cs new file mode 100644 index 0000000..a49089c --- /dev/null +++ b/src/Khd.Core.Api/Startup.cs @@ -0,0 +1,89 @@ +using System; +using System.Text; +using Khd.Core.Application; +using Khd.Core.Domain.Configs; +using Khd.Core.EntityFramework; +using Khd.Core.Library; +using Khd.Core.Library.Swagger; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.IdentityModel.Tokens; + + +namespace Khd.Core.Api +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + private IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + //services.AddDbContextPool( + // options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); }, 20); + + services.AddDbContext(options => + options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), new MySqlServerVersion(new Version(8, 0, 31)))); + + services.AddOptions(); + services.Configure(Configuration.GetSection("JwtSecurityOption")); + + services.AddAuthentication(option => + { + option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(option => + { + option.RequireHttpsMetadata = false; + option.SaveToken = true; + option.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + IssuerSigningKey = new SymmetricSecurityKey( + Encoding.Unicode.GetBytes(Configuration.GetSection("JwtSecurityOption:SigningKey").Value)), + ValidIssuer = Configuration.GetSection("JwtSecurityOption:Issuer").Value, + ValidAudience = Configuration.GetSection("JwtSecurityOption:Audience").Value + }; + }); + + services.AddCors(options => options.AddPolicy("CorsPolicy", builder => + { + builder + .AllowAnyOrigin() + .AllowAnyHeader() + .WithMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"); + })); + + services.AddApplication(); + services.AddLibrary(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); + + app.UseRouting(); + app.UseCors("CorsPolicy"); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + + app.UseLibrarySwagger(); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Api/appsettings.json b/src/Khd.Core.Api/appsettings.json new file mode 100644 index 0000000..243821c --- /dev/null +++ b/src/Khd.Core.Api/appsettings.json @@ -0,0 +1,25 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + }, + "AppSettings": { + "SiteCode": "999" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "server=106.12.13.113;port=3336;database=khd_suspension_chain;uid=khd;pwd=khd@123;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True" + // "DefaultConnection": "Data Source=localhost;Initial Catalog=lanju_op_xiaolan_ds;Persist Security Info=True;User ID=sa;Password=root;MultipleActiveResultSets=True;TrustServerCertificate=True", + //"DefaultConnection": "Data Source=121.36.58.109;Initial Catalog=lanju_op_xiaolan_ds;Persist Security Info=True;User ID=sa;Password=haiwei@123;MultipleActiveResultSets=True;TrustServerCertificate=True" + }, + "JwtSecurityOption": { + "SigningKey": "3c41f60c-1d12-11ec-890d-00163e1b8678", + "Issuer": "Khd.filog.cn", + "Audience": "Khd.filog.cn" + }, + "FileUploadingPath": "C:\\web\\images", + "FileServerHost": "/images/" +} diff --git a/src/Khd.Core.Application/ApplicationExtensions.cs b/src/Khd.Core.Application/ApplicationExtensions.cs new file mode 100644 index 0000000..b3ae082 --- /dev/null +++ b/src/Khd.Core.Application/ApplicationExtensions.cs @@ -0,0 +1,14 @@ +using Khd.Core.Application.Interface; +using Khd.Core.Domain.Models; +using Microsoft.Extensions.DependencyInjection; + +namespace Khd.Core.Application +{ + public static class ApplicationExtensions + { + public static void AddApplication(this IServiceCollection services) + { + services.AddTransient(); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Application/Interface/IBaseApplication.cs b/src/Khd.Core.Application/Interface/IBaseApplication.cs new file mode 100644 index 0000000..c8aafcf --- /dev/null +++ b/src/Khd.Core.Application/Interface/IBaseApplication.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Khd.Core.Application.Interface +{ + public interface IBaseApplication + { + + } + public interface IProWetMaterialPlanDetail { } + +} \ No newline at end of file diff --git a/src/Khd.Core.Application/Interface/IMesProdPlanApplication.cs b/src/Khd.Core.Application/Interface/IMesProdPlanApplication.cs new file mode 100644 index 0000000..f8b7041 --- /dev/null +++ b/src/Khd.Core.Application/Interface/IMesProdPlanApplication.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Khd.Core.Domain.Dto.webapi; +using Khd.Core.Domain.Models; + +namespace Khd.Core.Application.Interface +{ + public interface IMesProdPlanApplication : IBaseApplication + { + MesProdPlan Get(int id); + MesProdPlan Add(MesProdPlan model); + MesProdPlan Update(MesProdPlan model); + ReponseBase SaveProdPlan(RequestInfo model); + } +} \ No newline at end of file diff --git a/src/Khd.Core.Application/Khd.Core.Application.csproj b/src/Khd.Core.Application/Khd.Core.Application.csproj new file mode 100644 index 0000000..c9369a2 --- /dev/null +++ b/src/Khd.Core.Application/Khd.Core.Application.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + + + + + + + + + + + + + + + + + ..\..\dll\S7.Net.dll + + + + \ No newline at end of file diff --git a/src/Khd.Core.Application/MesProdPlanApplication.cs b/src/Khd.Core.Application/MesProdPlanApplication.cs new file mode 100644 index 0000000..488e9f2 --- /dev/null +++ b/src/Khd.Core.Application/MesProdPlanApplication.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Khd.Core.Application.Interface; +using Khd.Core.Domain.Dto.webapi; +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Masuit.Tools.Logging; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.Extensions.DependencyInjection; +using Z.EntityFramework.Plus; + +namespace Khd.Core.Application +{ + public class MesProdPlanApplication : IMesProdPlanApplication + { + private readonly DefaultDbContext _dbContext; + + public MesProdPlanApplication(IServiceProvider serviceProvider) + { + _dbContext = serviceProvider.GetService(); + } + + public MesProdPlan Get(int id) + { + var entity = _dbContext.MesProdPlan + .Where(c => 1== 1) + .FirstOrDefault(); + return entity; + } + public MesProdPlan Add(MesProdPlan model) + { + model.CREATE_TIME = DateTime.Now.ToString(); + var entity = _dbContext.Add(model); + _dbContext.SaveChanges(); + return entity.Entity; + + } + public ReponseBase SaveProdPlan(RequestInfo model) + { + ReponseBase reponseBase = new ReponseBase(); + reponseBase.CODE = "S"; + try + { + foreach (var item in model.DATA) + { + item.ID = Guid.NewGuid(); + item.CREATE_TIME = DateTime.Now.ToString(); + item.FLAG = "0"; + var entity = _dbContext.Add(item); + } + _dbContext.SaveChanges(); + reponseBase.MESSAGE = "接收成功!"; ; + } + catch (Exception ex) + { + reponseBase.CODE = "E"; + reponseBase.MESSAGE = ex.Message; + } + return reponseBase; + } + public MesProdPlan Update(MesProdPlan model) + { + var list = _dbContext.MesProdPlan.Where(t => t.ID == model.ID).Update(a => model); + return model; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Domain/Configs/JwtSecurityOption.cs b/src/Khd.Core.Domain/Configs/JwtSecurityOption.cs new file mode 100644 index 0000000..70d90bf --- /dev/null +++ b/src/Khd.Core.Domain/Configs/JwtSecurityOption.cs @@ -0,0 +1,12 @@ +namespace Khd.Core.Domain.Configs +{ + public class JwtSecurityOption + { + // * SigningKey length >= 16 * + public string SigningKey { get; set; } + + public string Issuer { get; set; } + + public string Audience { get; set; } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Domain/Dto/wcs/NodeSetting.cs b/src/Khd.Core.Domain/Dto/wcs/NodeSetting.cs new file mode 100644 index 0000000..496f899 --- /dev/null +++ b/src/Khd.Core.Domain/Dto/wcs/NodeSetting.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Domain.Dto.wcs +{ + public class NodeSetting + { + public Guid id { get; set; } + + /// + /// 站台编码 + /// + + public string siteNo { get; set; } + + /// + /// 站台名称 + /// + + public string siteName { get; set; } + + /// + /// 站台类型 + /// + + public Guid? siteTasktype { get; set; } + + /// + /// ip地址 + /// + + public string siteIpaddress { get; set; } + + /// + /// 链接服务器端口号 + /// + + public int? siteServerport { get; set; } + + /// + /// thrift端口号 + /// + + public int? thriftPort { get; set; } + + + public int? isDelete { get; set; } + + /// + /// plc点位编码 + /// + + public string plcpointNo { get; set; } + + /// + /// plc点位编名称 + /// + + public string plcpointName { get; set; } + + /// + /// 点位长度 + /// + + public int? plcpointLength { get; set; } + + + public string plcpointAddress { get; set; } + + /// + /// 设备id + /// + + public Guid? plcpointEquipmentId { get; set; } + + /// + /// 设备编码 + /// + + public string plcpointEquipmentNo { get; set; } + + /// + /// 设备名称 + /// + + public string plcpointEquipmentName { get; set; } + + /// + /// plc点位数据类型 + /// + + public int? plcpointType { get; set; } + + /// + /// 站点id + /// + + public Guid? sitenodeId { get; set; } + } +} diff --git a/src/Khd.Core.Domain/Dto/wcs/PlcSetting.cs b/src/Khd.Core.Domain/Dto/wcs/PlcSetting.cs new file mode 100644 index 0000000..fb5e138 --- /dev/null +++ b/src/Khd.Core.Domain/Dto/wcs/PlcSetting.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Domain.Dto.wcs +{ + public class PlcSetting + { + public string Id { get; set; } + /// + /// plc编码 + /// + public string PlcNo { get; set; } + /// + /// 地址位 + /// + public string PlcAddress { get; set; } + /// + /// 地址位长度 + /// + public string PlcValueLength { get; set; } + /// + /// 数据类型 + /// + public int PlcDataType { get; set; } + } +} diff --git a/src/Khd.Core.Domain/Dto/webapi/ReponseBase.cs b/src/Khd.Core.Domain/Dto/webapi/ReponseBase.cs new file mode 100644 index 0000000..51674a2 --- /dev/null +++ b/src/Khd.Core.Domain/Dto/webapi/ReponseBase.cs @@ -0,0 +1,15 @@ +using Khd.Core.Domain.Models; +using System.Collections.Generic; + +namespace Khd.Core.Domain.Dto.webapi +{ + public class RequestInfo + { + public List DATA { get; set; } + } + public class ReponseBase + { + public string CODE { get; set; } + public string MESSAGE { get; set; } + } +} diff --git a/src/Khd.Core.Domain/Khd.Core.Domain.csproj b/src/Khd.Core.Domain/Khd.Core.Domain.csproj new file mode 100644 index 0000000..1456594 --- /dev/null +++ b/src/Khd.Core.Domain/Khd.Core.Domain.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + + + + Khd.Core.Api\bin\Debug\ + Khd.Core.Api\bin\Debug\Khd.Core.Domain.xml + 1701;1702;1591 + + + + Khd.Core.Api\bin\Release\ + Khd.Core.Api\bin\Release\Khd.Core.Domain.xml + 1701;1702;1591 + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Domain/Models/BaseAlertinfo.cs b/src/Khd.Core.Domain/Models/BaseAlertinfo.cs new file mode 100644 index 0000000..0d556b6 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAlertinfo.cs @@ -0,0 +1,89 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_alertinfo")] + public class BaseAlertinfo + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 报警编码 + /// + [Column("alert_no")] + public string alertNo { get; set; } + + /// + /// 报警内容 + /// + [Column("alert_desc")] + public string alertDesc { get; set; } + + /// + /// 最小值 + /// + [Column("alert_minval")] + public int? alertMinval { get; set; } + + /// + /// 最大值 + /// + [Column("alert_maxval")] + public int? alertMaxval { get; set; } + + /// + /// 等于值 + /// + [Column("alert_equipval")] + public int? alertEquipval { get; set; } + + [Column("isdelete")] + public int? isdelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseAmima.cs b/src/Khd.Core.Domain/Models/BaseAmima.cs new file mode 100644 index 0000000..718a57c --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAmima.cs @@ -0,0 +1,37 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_amima")] + public class BaseAmima + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("name")] + public string name { get; set; } + + [Column("password")] + public string password { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + [Column("direction")] + public int direction { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseArea.cs b/src/Khd.Core.Domain/Models/BaseArea.cs new file mode 100644 index 0000000..7ad2b34 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseArea.cs @@ -0,0 +1,110 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_area")] + public class BaseArea + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 区域类型(1库区,0路线) + /// + [Column("area_type_id")] + public int? areaTypeId { get; set; } + + /// + /// 最大存储数 + /// + [Column("max_size")] + public int? maxSize { get; set; } + + /// + /// 库位序号 + /// + [Column("area_order")] + public int? areaOrder { get; set; } + + /// + /// 库区1,2 + /// + [Column("ware_house")] + public int? wareHouse { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("definefield4")] + public string definefield4 { get; set; } + + [Column("definefield5")] + public string definefield5 { get; set; } + + [Column("definefield6")] + public string definefield6 { get; set; } + + [Column("definefield7")] + public string definefield7 { get; set; } + + [Column("definefield8")] + public string definefield8 { get; set; } + + [Column("definefield9")] + public string definefield9 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseAsaveerro.cs b/src/Khd.Core.Domain/Models/BaseAsaveerro.cs new file mode 100644 index 0000000..9f06121 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAsaveerro.cs @@ -0,0 +1,65 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_asaveerro")] + public class BaseAsaveerro + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录读取plc + /// + [Column("status")] + public string status { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseAsaveerroBak.cs b/src/Khd.Core.Domain/Models/BaseAsaveerroBak.cs new file mode 100644 index 0000000..4185739 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAsaveerroBak.cs @@ -0,0 +1,65 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_asaveerro_bak")] + public class BaseAsaveerroBak + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录读取plc + /// + [Column("status")] + public string status { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseAsaveplcnumber.cs b/src/Khd.Core.Domain/Models/BaseAsaveplcnumber.cs new file mode 100644 index 0000000..dc39b69 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAsaveplcnumber.cs @@ -0,0 +1,71 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_asaveplcnumber")] + public class BaseAsaveplcnumber + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录读取plc + /// + [Column("plc_number")] + public string plcNumber { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录 + /// + [Column("kuqu_number")] + public string kuquNumber { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseAsaveplcnumberBak.cs b/src/Khd.Core.Domain/Models/BaseAsaveplcnumberBak.cs new file mode 100644 index 0000000..94103a3 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseAsaveplcnumberBak.cs @@ -0,0 +1,71 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_asaveplcnumber_bak")] + public class BaseAsaveplcnumberBak + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录读取plc + /// + [Column("plc_number")] + public string plcNumber { get; set; } + + /// + /// 当库区车辆数与plc读取的车辆数不一致时,记录 + /// + [Column("kuqu_number")] + public string kuquNumber { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseBomcomponent.cs b/src/Khd.Core.Domain/Models/BaseBomcomponent.cs new file mode 100644 index 0000000..214c89e --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseBomcomponent.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_bomcomponent")] + public class BaseBomcomponent + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// bom + /// + [Column("bom_id")] + public Guid? bomId { get; set; } + + /// + /// 子级物料编码 + /// + [Column("bomcompenent_no")] + public string bomcompenentNo { get; set; } + + /// + /// 子级物料名称 + /// + [Column("bomcompenent_nm")] + public string bomcompenentNm { get; set; } + + /// + /// 数量 + /// + [Column("material_amount")] + public int? materialAmount { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCar.cs b/src/Khd.Core.Domain/Models/BaseCar.cs new file mode 100644 index 0000000..7ea3aa8 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCar.cs @@ -0,0 +1,62 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_car")] + public class BaseCar + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + [Column("car_no")] + public int? carNo { get; set; } + + [Column("car_name")] + public string carName { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCararea.cs b/src/Khd.Core.Domain/Models/BaseCararea.cs new file mode 100644 index 0000000..63bfa39 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCararea.cs @@ -0,0 +1,122 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_cararea")] + public class BaseCararea + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + /// + /// 库区线id + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 库区线编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 库区线名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 小车状态0入库中1入库2出库3倒库4下件带料返库5空车入库 + /// + [Column("car_status")] + public int? carStatus { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarareaRejects.cs b/src/Khd.Core.Domain/Models/BaseCarareaRejects.cs new file mode 100644 index 0000000..4c0a313 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarareaRejects.cs @@ -0,0 +1,122 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_cararea_rejects")] + public class BaseCarareaRejects + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 库区线id + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 库区线编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 库区线名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 小车状态0入库中1入库2出库3倒库4下件带料返库5空车入库 + /// + [Column("car_status")] + public int? carStatus { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarareaoverdue.cs b/src/Khd.Core.Domain/Models/BaseCarareaoverdue.cs new file mode 100644 index 0000000..bebc724 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarareaoverdue.cs @@ -0,0 +1,122 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carareaoverdue")] + public class BaseCarareaoverdue + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 库区线id + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 库区线编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 库区线名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 小车状态0入库中1入库2出库3倒库4下件带料返库5空车入库 + /// + [Column("car_status")] + public int? carStatus { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCargocage.cs b/src/Khd.Core.Domain/Models/BaseCargocage.cs new file mode 100644 index 0000000..690ad0c --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCargocage.cs @@ -0,0 +1,98 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_cargocage")] + public class BaseCargocage + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 货笼编码 + /// + [Column("cargocage_no")] + public string cargocageNo { get; set; } + + /// + /// 货笼名称 + /// + [Column("cargocage_name")] + public string cargocageName { get; set; } + + /// + /// RFID编码 + /// + [Column("cargocage_rfid_no")] + public string cargocageRfidNo { get; set; } + + /// + /// 负载成品码 + /// + [Column("cargocage_materialno")] + public string cargocageMaterialno { get; set; } + + /// + /// 颜色 + /// + [Column("cargocage_color")] + public string cargocageColor { get; set; } + + /// + /// 当前状态 + /// + [Column("cargocage_status")] + public int? cargocageStatus { get; set; } + + /// + /// 操作时间 + /// + [Column("cargocage_optdate")] + public DateTime? cargocageOptdate { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarmateriel.cs b/src/Khd.Core.Domain/Models/BaseCarmateriel.cs new file mode 100644 index 0000000..99145f5 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarmateriel.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carmateriel")] + public class BaseCarmateriel + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 半成品条码 + /// + [Column("barcode")] + public string barcode { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarmateriellog.cs b/src/Khd.Core.Domain/Models/BaseCarmateriellog.cs new file mode 100644 index 0000000..cfc7aeb --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarmateriellog.cs @@ -0,0 +1,74 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carmateriellog")] + public class BaseCarmateriellog + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("car_id")] + public Guid? carId { get; set; } + + [Column("car_no")] + public int? carNo { get; set; } + + [Column("car_name")] + public string carName { get; set; } + + [Column("materiel_id")] + public Guid? materielId { get; set; } + + [Column("materiel_no")] + public string materielNo { get; set; } + + [Column("materiel_name")] + public string materielName { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarmaterielup.cs b/src/Khd.Core.Domain/Models/BaseCarmaterielup.cs new file mode 100644 index 0000000..fbde055 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarmaterielup.cs @@ -0,0 +1,104 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carmaterielup")] + public class BaseCarmaterielup + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编号 + /// + [Column("car_no")] + public int? carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 上件点 + /// + [Column("up_line")] + public int? upLine { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarmaterielupdown.cs b/src/Khd.Core.Domain/Models/BaseCarmaterielupdown.cs new file mode 100644 index 0000000..d27ebf1 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarmaterielupdown.cs @@ -0,0 +1,110 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carmaterielupdown")] + public class BaseCarmaterielupdown + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编号 + /// + [Column("car_no")] + public int? carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 线体 + /// + [Column("line")] + public int? line { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarorder.cs b/src/Khd.Core.Domain/Models/BaseCarorder.cs new file mode 100644 index 0000000..b42b595 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarorder.cs @@ -0,0 +1,32 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carorder")] + public class BaseCarorder + { + + [Key] + [Column("car_no")] + public int carNo { get; set; } + + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("order_id")] + public string orderId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarorderlist.cs b/src/Khd.Core.Domain/Models/BaseCarorderlist.cs new file mode 100644 index 0000000..97f531e --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarorderlist.cs @@ -0,0 +1,62 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carorderlist")] + public class BaseCarorderlist + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("order_code")] + public string orderCode { get; set; } + + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("materiel_no")] + public string materielNo { get; set; } + + [Column("prod_code")] + public string prodCode { get; set; } + + [Column("line_name")] + public string lineName { get; set; } + + [Column("est")] + public string est { get; set; } + + [Column("production_sequence")] + public string productionSequence { get; set; } + + [Column("Quantity")] + public string quantity { get; set; } + + [Column("UpQuantity")] + public string upquantity { get; set; } + + [Column("OutWareHouseQuantity")] + public string outwarehousequantity { get; set; } + + [Column("DownQuantity")] + public string downquantity { get; set; } + + [Column("IsOver")] + public string isover { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarordernum.cs b/src/Khd.Core.Domain/Models/BaseCarordernum.cs new file mode 100644 index 0000000..91357d9 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarordernum.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carordernum")] + public class BaseCarordernum + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("order_code")] + public string orderCode { get; set; } + + [Column("type")] + public string type { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("order_num")] + public int? orderNum { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarordernumnew.cs b/src/Khd.Core.Domain/Models/BaseCarordernumnew.cs new file mode 100644 index 0000000..f501f6a --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarordernumnew.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carordernumnew")] + public class BaseCarordernumnew + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("order_code")] + public string orderCode { get; set; } + + [Column("type")] + public string type { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("order_num")] + public int? orderNum { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarpreline.cs b/src/Khd.Core.Domain/Models/BaseCarpreline.cs new file mode 100644 index 0000000..2fe25f0 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarpreline.cs @@ -0,0 +1,122 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carpreline")] + public class BaseCarpreline + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public int? carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + [Column("preline_id")] + public Guid? prelineId { get; set; } + + /// + /// 预设线编码 + /// + [Column("preline_no")] + public string prelineNo { get; set; } + + /// + /// 预设线名称 + /// + [Column("preline_nm")] + public string prelineNm { get; set; } + + /// + /// 站台分流线id + /// + [Column("sitenode_id")] + public Guid? sitenodeId { get; set; } + + /// + /// 顺序号 + /// + [Column("order_index")] + public int? orderIndex { get; set; } + + /// + /// 站台运输时间 + /// + [Column("duration_time")] + public int? durationTime { get; set; } + + /// + /// 站台转换类型(预留拓展) + /// + [Column("change_type")] + public int? changeType { get; set; } + + /// + /// plc写入值 + /// + [Column("plc_value")] + public string plcValue { get; set; } + + /// + /// 是否入库1入库0非入库 + /// + [Column("IsInStock")] + public int? isinstock { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarproduct.cs b/src/Khd.Core.Domain/Models/BaseCarproduct.cs new file mode 100644 index 0000000..f78313b --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarproduct.cs @@ -0,0 +1,47 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carproduct")] + public class BaseCarproduct + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("car_id")] + public Guid? carId { get; set; } + + [Column("car_no")] + public int? carNo { get; set; } + + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("order_code")] + public string orderCode { get; set; } + + [Column("materiel_id")] + public Guid? materielId { get; set; } + + [Column("materiel_no")] + public string materielNo { get; set; } + + [Column("line_code")] + public string lineCode { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarrier.cs b/src/Khd.Core.Domain/Models/BaseCarrier.cs new file mode 100644 index 0000000..beee3c2 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarrier.cs @@ -0,0 +1,86 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carrier")] + public class BaseCarrier + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 货笼编码 + /// + [Column("carrier_no")] + public string carrierNo { get; set; } + + /// + /// 货笼名称 + /// + [Column("carrier_name")] + public string carrierName { get; set; } + + /// + /// RFID编码 + /// + [Column("carrier_rfid_no")] + public string carrierRfidNo { get; set; } + + /// + /// 颜色 + /// + [Column("carrier_color")] + public string carrierColor { get; set; } + + /// + /// 当前状态 + /// + [Column("carrier_status")] + public int? carrierStatus { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseCarthrough.cs b/src/Khd.Core.Domain/Models/BaseCarthrough.cs new file mode 100644 index 0000000..5ca6685 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseCarthrough.cs @@ -0,0 +1,74 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_carthrough")] + public class BaseCarthrough + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车号 + /// + [Column("car_no")] + public int? carNo { get; set; } + + /// + /// 上件点 + /// + [Column("UpLine")] + public int? upline { get; set; } + + /// + /// 下件点 + /// + [Column("DownLine")] + public int? downline { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseDowncarorderback.cs b/src/Khd.Core.Domain/Models/BaseDowncarorderback.cs new file mode 100644 index 0000000..3a9db44 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseDowncarorderback.cs @@ -0,0 +1,110 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_downcarorderback")] + public class BaseDowncarorderback + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 订单号 + /// + [Column("order_id")] + public string orderId { get; set; } + + /// + /// 线体 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseDownline.cs b/src/Khd.Core.Domain/Models/BaseDownline.cs new file mode 100644 index 0000000..80be0b9 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseDownline.cs @@ -0,0 +1,122 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_downline")] + public class BaseDownline + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 库区线id + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 库区线编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 库区线名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public string carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + /// + /// 物料id + /// + [Column("materiel_id")] + public Guid? materielId { get; set; } + + /// + /// 物料编码 + /// + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 物料名称 + /// + [Column("materiel_name")] + public string materielName { get; set; } + + /// + /// 物料扫描数量 + /// + [Column("materiel_num")] + public int? materielNum { get; set; } + + /// + /// 1下件2倒库 + /// + [Column("run_type")] + public int? runType { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseEquipment.cs b/src/Khd.Core.Domain/Models/BaseEquipment.cs new file mode 100644 index 0000000..1c5a59e --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseEquipment.cs @@ -0,0 +1,86 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_equipment")] + public class BaseEquipment + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 设备编码 + /// + [Column("equipment_no")] + public string equipmentNo { get; set; } + + /// + /// 设备名称 + /// + [Column("equipment_name")] + public string equipmentName { get; set; } + + /// + /// 设备ip + /// + [Column("equipment_ip")] + public string equipmentIp { get; set; } + + /// + /// 设备品牌 + /// + [Column("equipment_spac")] + public string equipmentSpac { get; set; } + + /// + /// 设备类型 + /// + [Column("equipment_type")] + public int? equipmentType { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseFactory.cs b/src/Khd.Core.Domain/Models/BaseFactory.cs new file mode 100644 index 0000000..9054f25 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseFactory.cs @@ -0,0 +1,68 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_factory")] + public class BaseFactory + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 工厂编码 + /// + [Column("factory_no")] + public string factoryNo { get; set; } + + /// + /// 工厂名称 + /// + [Column("factory_name")] + public string factoryName { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseFaultlog.cs b/src/Khd.Core.Domain/Models/BaseFaultlog.cs new file mode 100644 index 0000000..17232c8 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseFaultlog.cs @@ -0,0 +1,86 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_faultlog")] + public class BaseFaultlog + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("line")] + public int? line { get; set; } + + [Column("ddate")] + public DateTime? ddate { get; set; } + + [Column("hourtime")] + public int? hourtime { get; set; } + + [Column("mintime")] + public int? mintime { get; set; } + + [Column("mincreatetime")] + public DateTime? mincreatetime { get; set; } + + [Column("maxcreatetime")] + public DateTime? maxcreatetime { get; set; } + + [Column("faulthourtime")] + public int? faulthourtime { get; set; } + + [Column("faultmintime")] + public int? faultmintime { get; set; } + + [Column("rhourtime")] + public int? rhourtime { get; set; } + + [Column("rfaultmintime")] + public int? rfaultmintime { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseFaulttime.cs b/src/Khd.Core.Domain/Models/BaseFaulttime.cs new file mode 100644 index 0000000..af40a78 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseFaulttime.cs @@ -0,0 +1,50 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_faulttime")] + public class BaseFaulttime + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 线体 + /// + [Column("line")] + public int? line { get; set; } + + /// + /// 日期 + /// + [Column("ddate")] + public DateTime? ddate { get; set; } + + /// + /// 开始时间 + /// + [Column("begintime")] + public DateTime? begintime { get; set; } + + /// + /// 结束时间 + /// + [Column("endtime")] + public DateTime? endtime { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseFlag.cs b/src/Khd.Core.Domain/Models/BaseFlag.cs new file mode 100644 index 0000000..4fd5f85 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseFlag.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_flag")] + public class BaseFlag + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 编码 + /// + [Column("flag_no")] + public string flagNo { get; set; } + + /// + /// 名称 + /// + [Column("flag_name")] + public string flagName { get; set; } + + /// + /// 值 + /// + [Column("n_flag")] + public int? nFlag { get; set; } + + /// + /// 描述 + /// + [Column("flag_memo")] + public string flagMemo { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseFollowmessage.cs b/src/Khd.Core.Domain/Models/BaseFollowmessage.cs new file mode 100644 index 0000000..88f4d68 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseFollowmessage.cs @@ -0,0 +1,116 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_followmessage")] + public class BaseFollowmessage + { + + [Key] + [Column("id")] + public string id { get; set; } + + /// + /// 父订单id + /// + [Column("sid")] + public string sid { get; set; } + + /// + /// 订单号 + /// + [Column("vin_code")] + public string vinCode { get; set; } + + /// + /// 任务上件站点 + /// + [Column("up_site")] + public string upSite { get; set; } + + /// + /// 任务下件站点 + /// + [Column("down_site")] + public string downSite { get; set; } + + /// + /// 挂具号 + /// + [Column("car_no")] + public int carNo { get; set; } + + /// + /// 产品名称 + /// + [Column("material_name")] + public string materialName { get; set; } + + /// + /// 生产线别 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线别名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + + /// + /// 下线数量 + /// + [Column("ActualQuantity")] + public int? actualquantity { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 创建人 + /// + [Column("create_by")] + public string createBy { get; set; } + + /// + /// 下线记录创建时间 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseLinearea.cs b/src/Khd.Core.Domain/Models/BaseLinearea.cs new file mode 100644 index 0000000..54af7fc --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseLinearea.cs @@ -0,0 +1,89 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_linearea")] + public class BaseLinearea + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 产线id + /// + [Column("line_id")] + public Guid? lineId { get; set; } + + /// + /// 产线编码 + /// + [Column("line_no")] + public string lineNo { get; set; } + + /// + /// 产线名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseLineinfo.cs b/src/Khd.Core.Domain/Models/BaseLineinfo.cs new file mode 100644 index 0000000..9981a99 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseLineinfo.cs @@ -0,0 +1,86 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_lineinfo")] + public class BaseLineinfo + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 产线编码 + /// + [Column("line_no")] + public string lineNo { get; set; } + + /// + /// 产线名称 + /// + [Column("linen_ame")] + public string linenAme { get; set; } + + /// + /// 工厂名称 + /// + [Column("line_factory_name")] + public string lineFactoryName { get; set; } + + /// + /// 工厂编码 + /// + [Column("line_factory_no")] + public string lineFactoryNo { get; set; } + + /// + /// 工厂id + /// + [Column("line_factory_id")] + public Guid? lineFactoryId { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseLoginlog.cs b/src/Khd.Core.Domain/Models/BaseLoginlog.cs new file mode 100644 index 0000000..4c3f890 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseLoginlog.cs @@ -0,0 +1,74 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_loginlog")] + public class BaseLoginlog + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 站点 + /// + [Column("sitenode")] + public int? sitenode { get; set; } + + /// + /// 用户名 + /// + [Column("loginname")] + public string loginname { get; set; } + + /// + /// 姓名 + /// + [Column("fullname")] + public string fullname { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseMaterialinfo.cs b/src/Khd.Core.Domain/Models/BaseMaterialinfo.cs new file mode 100644 index 0000000..f1b0c20 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseMaterialinfo.cs @@ -0,0 +1,92 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_materialinfo")] + public class BaseMaterialinfo + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + /// + /// 物料编码 + /// + [Column("material_no")] + public string materialNo { get; set; } + + /// + /// 物料名称 + /// + [Column("material_name")] + public string materialName { get; set; } + + /// + /// 1号上件点K46写入PLC长度,单位:mm + /// + [Column("k46up_length")] + public string k46upLength { get; set; } + + /// + /// 2号上件点K48写入PLC长度,单位:mm + /// + [Column("k48up_length")] + public string k48upLength { get; set; } + + /// + /// 下件写入PLC长度,单位:mm + /// + [Column("down_length")] + public string downLength { get; set; } + + /// + /// 物料类型 + /// + [Column("material_type")] + public string materialType { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseMaterialinfobom.cs b/src/Khd.Core.Domain/Models/BaseMaterialinfobom.cs new file mode 100644 index 0000000..b057759 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseMaterialinfobom.cs @@ -0,0 +1,95 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_materialinfobom")] + public class BaseMaterialinfobom + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 成品id + /// + [Column("materiel_finish_id")] + public Guid? materielFinishId { get; set; } + + /// + /// 成品编码 + /// + [Column("materiel_finish_no")] + public string materielFinishNo { get; set; } + + /// + /// 成品名称 + /// + [Column("materiel_finish_name")] + public string materielFinishName { get; set; } + + /// + /// 箱体id + /// + [Column("materiel_box_id")] + public Guid? materielBoxId { get; set; } + + /// + /// 箱体编码 + /// + [Column("materiel_box_no")] + public string materielBoxNo { get; set; } + + /// + /// 箱体名称 + /// + [Column("materiel_box_name")] + public string materielBoxName { get; set; } + + [Column("version")] + public string version { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseOrderinfo.cs b/src/Khd.Core.Domain/Models/BaseOrderinfo.cs new file mode 100644 index 0000000..28977bb --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseOrderinfo.cs @@ -0,0 +1,134 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_orderinfo")] + public class BaseOrderinfo + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 订单编码 + /// + [Column("order_no")] + public string orderNo { get; set; } + + /// + /// 物料id + /// + [Column("material_id")] + public Guid? materialId { get; set; } + + /// + /// 物料编码 + /// + [Column("material_no")] + public string materialNo { get; set; } + + /// + /// 线体id + /// + [Column("material_line_id")] + public Guid? materialLineId { get; set; } + + /// + /// 线体名称 + /// + [Column("material_line_name")] + public string materialLineName { get; set; } + + /// + /// 线体编码 + /// + [Column("material_line_no")] + public string materialLineNo { get; set; } + + /// + /// 时序顺序 + /// + [Column("product_sortno")] + public int? productSortno { get; set; } + + /// + /// 生产数量 + /// + [Column("product_amount")] + public int? productAmount { get; set; } + + /// + /// bom版本 + /// + [Column("bom_version")] + public string bomVersion { get; set; } + + /// + /// 订单日期 + /// + [Column("plan_starttime")] + public DateTime? planStarttime { get; set; } + + /// + /// 0未完成1已完成 + /// + [Column("plan_state")] + public int? planState { get; set; } + + /// + /// 上件数量 + /// + [Column("up_amount")] + public int? upAmount { get; set; } + + /// + /// 下件数量 + /// + [Column("down_amount")] + public int? downAmount { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseOrderlog.cs b/src/Khd.Core.Domain/Models/BaseOrderlog.cs new file mode 100644 index 0000000..62be8ff --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseOrderlog.cs @@ -0,0 +1,35 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_orderlog")] + public class BaseOrderlog + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("updatememo")] + public string updatememo { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("type")] + public string type { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasePlcpoint.cs b/src/Khd.Core.Domain/Models/BasePlcpoint.cs new file mode 100644 index 0000000..e215739 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasePlcpoint.cs @@ -0,0 +1,107 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_plcpoint")] + public class BasePlcpoint + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + /// + /// plc点位编码 + /// + [Column("plcpoint_no")] + public string plcpointNo { get; set; } + + /// + /// plc点位编名称 + /// + [Column("plcpoint_name")] + public string plcpointName { get; set; } + + /// + /// 点位长度 + /// + [Column("plcpoint_length")] + public int? plcpointLength { get; set; } + + [Column("plcpoint_address")] + public string plcpointAddress { get; set; } + + /// + /// 设备id + /// + [Column("plcpoint_equipment_id")] + public Guid? plcpointEquipmentId { get; set; } + + /// + /// 设备编码 + /// + [Column("plcpoint_equipment_no")] + public string plcpointEquipmentNo { get; set; } + + /// + /// 设备名称 + /// + [Column("plcpoint_equipment_name")] + public string plcpointEquipmentName { get; set; } + + /// + /// plc点位数据类型 + /// + [Column("plcpoint_type")] + public int? plcpointType { get; set; } + + /// + /// 站点id + /// + [Column("sitenode_id")] + public Guid? sitenodeId { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasePlcpointsite.cs b/src/Khd.Core.Domain/Models/BasePlcpointsite.cs new file mode 100644 index 0000000..5e6be65 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasePlcpointsite.cs @@ -0,0 +1,95 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_plcpointsite")] + public class BasePlcpointsite + { + + [Key] + [Column("id")] + public int id { get; set; } + + [Column("site_id")] + public Guid? siteId { get; set; } + + /// + /// 站点编码 + /// + [Column("site_no")] + public string siteNo { get; set; } + + /// + /// 站点名称 + /// + [Column("site_name")] + public string siteName { get; set; } + + /// + /// plcid + /// + [Column("plcpoint_id")] + public Guid? plcpointId { get; set; } + + /// + /// plc点编码 + /// + [Column("plcpoint_no")] + public string plcpointNo { get; set; } + + /// + /// plc点名称 + /// + [Column("plcpoint_name")] + public string plcpointName { get; set; } + + /// + /// plc点逻辑类型 + /// + [Column("plctype_id")] + public Guid? plctypeId { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasePlctype.cs b/src/Khd.Core.Domain/Models/BasePlctype.cs new file mode 100644 index 0000000..3207d02 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasePlctype.cs @@ -0,0 +1,74 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_plctype")] + public class BasePlctype + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// plc类型编码 + /// + [Column("plctype_no")] + public string plctypeNo { get; set; } + + /// + /// plc类型编名称 + /// + [Column("plctype_name")] + public string plctypeName { get; set; } + + /// + /// 指令内容 + /// + [Column("plctype_command")] + public string plctypeCommand { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasePrelinedetail.cs b/src/Khd.Core.Domain/Models/BasePrelinedetail.cs new file mode 100644 index 0000000..7652acc --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasePrelinedetail.cs @@ -0,0 +1,110 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_prelinedetail")] + public class BasePrelinedetail + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("preline_id")] + public Guid? prelineId { get; set; } + + /// + /// 预设线编码 + /// + [Column("preline_no")] + public string prelineNo { get; set; } + + /// + /// 预设线名称 + /// + [Column("preline_nm")] + public string prelineNm { get; set; } + + /// + /// 站台分流线id + /// + [Column("sitenode_id")] + public Guid? sitenodeId { get; set; } + + /// + /// 顺序号 + /// + [Column("order_index")] + public int? orderIndex { get; set; } + + /// + /// 站台运输时间 + /// + [Column("duration_time")] + public int? durationTime { get; set; } + + /// + /// 站台转换类型(预留拓展) + /// + [Column("change_type")] + public int? changeType { get; set; } + + /// + /// plc写入值 + /// + [Column("plc_value")] + public string plcValue { get; set; } + + /// + /// 1入库0非入库 + /// + [Column("IsInStock")] + public int? isinstock { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasePrelineinfo.cs b/src/Khd.Core.Domain/Models/BasePrelineinfo.cs new file mode 100644 index 0000000..8dbd490 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasePrelineinfo.cs @@ -0,0 +1,113 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_prelineinfo")] + public class BasePrelineinfo + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 预设线编码 + /// + [Column("preline_no")] + public string prelineNo { get; set; } + + /// + /// 预设线名称 + /// + [Column("preline_nm")] + public string prelineNm { get; set; } + + /// + /// 开始站台 + /// + [Column("start_site")] + public Guid? startSite { get; set; } + + /// + /// 结束站台 + /// + [Column("end_site")] + public Guid? endSite { get; set; } + + /// + /// 预设送达时间(秒) + /// + [Column("plan_duration")] + public int? planDuration { get; set; } + + /// + /// 输送类型 1入库2下件3倒库4拉回 + /// + [Column("run_type")] + public int? runType { get; set; } + + /// + /// 库区 + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 库区1,2 + /// + [Column("ware_house")] + public int? wareHouse { get; set; } + + /// + /// 501写1,502写2,503写3 + /// + [Column("down_line")] + public int? downLine { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseProductionOrderSplit.cs b/src/Khd.Core.Domain/Models/BaseProductionOrderSplit.cs new file mode 100644 index 0000000..28dded3 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseProductionOrderSplit.cs @@ -0,0 +1,203 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_production_order_split")] + public class BaseProductionOrderSplit + { + + [Key] + [Column("id")] + public string id { get; set; } + + [Column("site_code")] + public string siteCode { get; set; } + + /// + /// 线体编码 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线体名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + /// + /// 产品编码 + /// + [Column("prod_code")] + public string prodCode { get; set; } + + /// + /// 产品名称 + /// + [Column("prod_desc")] + public string prodDesc { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + + /// + /// 可用计划数 + /// + [Column("available_quantity")] + public int? availableQuantity { get; set; } + + /// + /// 上线数量 + /// + [Column("online_quantity")] + public int? onlineQuantity { get; set; } + + /// + /// 下线数量 + /// + [Column("ActualQuantity")] + public int? actualquantity { get; set; } + + [Column("UpQuantity")] + public int? upquantity { get; set; } + + /// + /// 出库数量 + /// + [Column("OutWareHouseQuantity")] + public int? outwarehousequantity { get; set; } + + [Column("DownQuantity")] + public int? downquantity { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 排产时间 + /// + [Column("scheduling_time")] + public DateTime? schedulingTime { get; set; } + + /// + /// 拆单状态:0正在拆单,1正常 + /// + [Column("split_status")] + public string splitStatus { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 创建时间 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + /// + /// 最后更新时间 + /// + [Column("last_update_date")] + public DateTime? lastUpdateDate { get; set; } + + /// + /// 创建人 + /// + [Column("create_by")] + public string createBy { get; set; } + + /// + /// 最后更新人 + /// + [Column("last_update_by")] + public string lastUpdateBy { get; set; } + + /// + /// 父订单id + /// + [Column("parent_order_id")] + public string parentOrderId { get; set; } + + /// + /// 0非源订单,1源订单 + /// + [Column("is_source")] + public string isSource { get; set; } + + /// + /// 0未同步,1已同步 + /// + [Column("is_sync")] + public string isSync { get; set; } + + /// + /// 0为未完成,1为已完成 + /// + [Column("is_completed")] + public string isCompleted { get; set; } + + /// + /// 企业编码 + /// + [Column("Enterprise_Code")] + public string enterpriseCode { get; set; } + + /// + /// 是否启用 + /// + [Column("Active")] + public string active { get; set; } + + /// + /// 工厂Id + /// + [Column("Site_Id")] + public string siteId { get; set; } + + /// + /// 企业Id + /// + [Column("Enterprise_Id")] + public string enterpriseId { get; set; } + + /// + /// 条码区间 + /// + [Column("min_max_barcode")] + public string minMaxBarcode { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseProductionOrderSplitBak.cs b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitBak.cs new file mode 100644 index 0000000..9a3d9b1 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitBak.cs @@ -0,0 +1,83 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_production_order_split_bak")] + public class BaseProductionOrderSplitBak + { + + [Key] + [Column("id")] + public string id { get; set; } + + [Column("site_code")] + public string siteCode { get; set; } + + /// + /// 线体编码 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线体名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + /// + /// 产品编码 + /// + [Column("prod_code")] + public string prodCode { get; set; } + + /// + /// 产品名称 + /// + [Column("prod_desc")] + public string prodDesc { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseProductionOrderSplitLine.cs b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitLine.cs new file mode 100644 index 0000000..ca8547b --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitLine.cs @@ -0,0 +1,209 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_production_order_split_line")] + public class BaseProductionOrderSplitLine + { + + [Key] + [Column("id")] + public string id { get; set; } + + [Column("site_code")] + public string siteCode { get; set; } + + /// + /// 线体编码 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线体名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + /// + /// 产品编码 + /// + [Column("prod_code")] + public string prodCode { get; set; } + + /// + /// 产品名称 + /// + [Column("prod_desc")] + public string prodDesc { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + + /// + /// 可用计划数 + /// + [Column("available_quantity")] + public int? availableQuantity { get; set; } + + /// + /// 上线数量 + /// + [Column("online_quantity")] + public int? onlineQuantity { get; set; } + + /// + /// 下线数量 + /// + [Column("ActualQuantity")] + public int? actualquantity { get; set; } + + [Column("UpQuantity")] + public int? upquantity { get; set; } + + /// + /// 出库数量 + /// + [Column("OutWareHouseQuantity")] + public int? outwarehousequantity { get; set; } + + [Column("DownQuantity")] + public int? downquantity { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 排产时间 + /// + [Column("scheduling_time")] + public DateTime? schedulingTime { get; set; } + + /// + /// 拆单状态:0正在拆单,1正常 + /// + [Column("split_status")] + public string splitStatus { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 创建时间 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + /// + /// 最后更新时间 + /// + [Column("last_update_date")] + public DateTime? lastUpdateDate { get; set; } + + /// + /// 创建人 + /// + [Column("create_by")] + public string createBy { get; set; } + + /// + /// 最后更新人 + /// + [Column("last_update_by")] + public string lastUpdateBy { get; set; } + + /// + /// 父订单id + /// + [Column("parent_order_id")] + public string parentOrderId { get; set; } + + /// + /// 0非源订单,1源订单 + /// + [Column("is_source")] + public string isSource { get; set; } + + /// + /// 0未同步,1已同步 + /// + [Column("is_sync")] + public string isSync { get; set; } + + /// + /// 0为未完成,1为已完成 + /// + [Column("is_completed")] + public string isCompleted { get; set; } + + /// + /// 企业编码 + /// + [Column("Enterprise_Code")] + public string enterpriseCode { get; set; } + + /// + /// 是否启用 + /// + [Column("Active")] + public string active { get; set; } + + /// + /// 工厂Id + /// + [Column("Site_Id")] + public string siteId { get; set; } + + /// + /// 企业Id + /// + [Column("Enterprise_Id")] + public string enterpriseId { get; set; } + + /// + /// 条码区间 + /// + [Column("min_max_barcode")] + public string minMaxBarcode { get; set; } + + /// + /// 订单审核状态(0:待审核,1:确认,2:拒绝,3:待作废) + /// + [Column("order_status")] + public int? orderStatus { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseProductionOrderSplitReduct.cs b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitReduct.cs new file mode 100644 index 0000000..571e7f4 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitReduct.cs @@ -0,0 +1,203 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_production_order_split_reduct")] + public class BaseProductionOrderSplitReduct + { + + [Key] + [Column("id")] + public string id { get; set; } + + [Column("site_code")] + public string siteCode { get; set; } + + /// + /// 线体编码 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线体名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + /// + /// 产品编码 + /// + [Column("prod_code")] + public string prodCode { get; set; } + + /// + /// 产品名称 + /// + [Column("prod_desc")] + public string prodDesc { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + + /// + /// 可用计划数 + /// + [Column("available_quantity")] + public int? availableQuantity { get; set; } + + /// + /// 上线数量 + /// + [Column("online_quantity")] + public int? onlineQuantity { get; set; } + + /// + /// 下线数量 + /// + [Column("ActualQuantity")] + public int? actualquantity { get; set; } + + [Column("UpQuantity")] + public int? upquantity { get; set; } + + /// + /// 出库数量 + /// + [Column("OutWareHouseQuantity")] + public int? outwarehousequantity { get; set; } + + [Column("DownQuantity")] + public int? downquantity { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 排产时间 + /// + [Column("scheduling_time")] + public DateTime? schedulingTime { get; set; } + + /// + /// 拆单状态:0正在拆单,1正常 + /// + [Column("split_status")] + public string splitStatus { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 创建时间 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + /// + /// 最后更新时间 + /// + [Column("last_update_date")] + public DateTime? lastUpdateDate { get; set; } + + /// + /// 创建人 + /// + [Column("create_by")] + public string createBy { get; set; } + + /// + /// 最后更新人 + /// + [Column("last_update_by")] + public string lastUpdateBy { get; set; } + + /// + /// 父订单id + /// + [Column("parent_order_id")] + public string parentOrderId { get; set; } + + /// + /// 0非源订单,1源订单 + /// + [Column("is_source")] + public string isSource { get; set; } + + /// + /// 0未同步,1已同步 + /// + [Column("is_sync")] + public string isSync { get; set; } + + /// + /// 0为未完成,1为已完成 + /// + [Column("is_completed")] + public string isCompleted { get; set; } + + /// + /// 企业编码 + /// + [Column("Enterprise_Code")] + public string enterpriseCode { get; set; } + + /// + /// 是否启用 + /// + [Column("Active")] + public string active { get; set; } + + /// + /// 工厂Id + /// + [Column("Site_Id")] + public string siteId { get; set; } + + /// + /// 企业Id + /// + [Column("Enterprise_Id")] + public string enterpriseId { get; set; } + + /// + /// 条码区间 + /// + [Column("min_max_barcode")] + public string minMaxBarcode { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseProductionOrderSplitTextinsert.cs b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitTextinsert.cs new file mode 100644 index 0000000..2a83b22 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseProductionOrderSplitTextinsert.cs @@ -0,0 +1,203 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_production_order_split_textinsert")] + public class BaseProductionOrderSplitTextinsert + { + + [Key] + [Column("id")] + public string id { get; set; } + + [Column("site_code")] + public string siteCode { get; set; } + + /// + /// 线体编码 + /// + [Column("line_code")] + public string lineCode { get; set; } + + /// + /// 线体名称 + /// + [Column("line_name")] + public string lineName { get; set; } + + /// + /// 订单号 + /// + [Column("order_code")] + public string orderCode { get; set; } + + /// + /// 产品编码 + /// + [Column("prod_code")] + public string prodCode { get; set; } + + /// + /// 产品名称 + /// + [Column("prod_desc")] + public string prodDesc { get; set; } + + /// + /// 订单是否完成0未完成1完成 + /// + [Column("IsOver")] + public int? isover { get; set; } + + /// + /// 计划开始时间 + /// + [Column("est")] + public DateTime? est { get; set; } + + /// + /// 计划数量 + /// + [Column("Quantity")] + public int? quantity { get; set; } + + /// + /// 可用计划数 + /// + [Column("available_quantity")] + public int? availableQuantity { get; set; } + + /// + /// 上线数量 + /// + [Column("online_quantity")] + public int? onlineQuantity { get; set; } + + /// + /// 下线数量 + /// + [Column("ActualQuantity")] + public int? actualquantity { get; set; } + + [Column("UpQuantity")] + public int? upquantity { get; set; } + + /// + /// 出库数量 + /// + [Column("OutWareHouseQuantity")] + public int? outwarehousequantity { get; set; } + + [Column("DownQuantity")] + public int? downquantity { get; set; } + + /// + /// 排产时间 + /// + [Column("scheduling_time")] + public DateTime? schedulingTime { get; set; } + + /// + /// 拆单状态:0正在拆单,1正常 + /// + [Column("split_status")] + public string splitStatus { get; set; } + + /// + /// 订单生产顺序 + /// + [Column("production_sequence")] + public int? productionSequence { get; set; } + + /// + /// 创建时间 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + /// + /// 最后更新时间 + /// + [Column("last_update_date")] + public DateTime? lastUpdateDate { get; set; } + + /// + /// 创建人 + /// + [Column("create_by")] + public string createBy { get; set; } + + /// + /// 最后更新人 + /// + [Column("last_update_by")] + public string lastUpdateBy { get; set; } + + /// + /// 父订单id + /// + [Column("parent_order_id")] + public string parentOrderId { get; set; } + + /// + /// 0非源订单,1源订单 + /// + [Column("is_source")] + public string isSource { get; set; } + + /// + /// 0未同步,1已同步 + /// + [Column("is_sync")] + public string isSync { get; set; } + + /// + /// 0为未完成,1为已完成 + /// + [Column("is_completed")] + public string isCompleted { get; set; } + + /// + /// 企业编码 + /// + [Column("Enterprise_Code")] + public string enterpriseCode { get; set; } + + /// + /// 是否启用 + /// + [Column("Active")] + public string active { get; set; } + + /// + /// 工厂Id + /// + [Column("Site_Id")] + public string siteId { get; set; } + + /// + /// 企业Id + /// + [Column("Enterprise_Id")] + public string enterpriseId { get; set; } + + /// + /// 条码区间 + /// + [Column("min_max_barcode")] + public string minMaxBarcode { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseRejects.cs b/src/Khd.Core.Domain/Models/BaseRejects.cs new file mode 100644 index 0000000..ea8017a --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseRejects.cs @@ -0,0 +1,62 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_rejects")] + public class BaseRejects + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("car_no")] + public int? carNo { get; set; } + + [Column("site_node")] + public int? siteNode { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseSitearea.cs b/src/Khd.Core.Domain/Models/BaseSitearea.cs new file mode 100644 index 0000000..dabdca9 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseSitearea.cs @@ -0,0 +1,98 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_sitearea")] + public class BaseSitearea + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 站台id + /// + [Column("site_id")] + public Guid? siteId { get; set; } + + /// + /// 站台编码 + /// + [Column("site_no")] + public string siteNo { get; set; } + + /// + /// 站台名称 + /// + [Column("site_name")] + public string siteName { get; set; } + + /// + /// 区域id + /// + [Column("area_id")] + public Guid? areaId { get; set; } + + /// + /// 区域编码 + /// + [Column("area_no")] + public string areaNo { get; set; } + + /// + /// 区域名称 + /// + [Column("area_name")] + public string areaName { get; set; } + + /// + /// 路径去向值 + /// + [Column("site_value")] + public string siteValue { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseSitenode.cs b/src/Khd.Core.Domain/Models/BaseSitenode.cs new file mode 100644 index 0000000..b8387a9 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseSitenode.cs @@ -0,0 +1,92 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_sitenode")] + public class BaseSitenode + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + /// + /// 站台编码 + /// + [Column("site_no")] + public string siteNo { get; set; } + + /// + /// 站台名称 + /// + [Column("site_name")] + public string siteName { get; set; } + + /// + /// 站台类型 + /// + [Column("site_tasktype")] + public Guid? siteTasktype { get; set; } + + /// + /// ip地址 + /// + [Column("site_ipaddress")] + public string siteIpaddress { get; set; } + + /// + /// 链接服务器端口号 + /// + [Column("site_serverport")] + public int? siteServerport { get; set; } + + /// + /// thrift端口号 + /// + [Column("thrift_port")] + public int? thriftPort { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseSitenum.cs b/src/Khd.Core.Domain/Models/BaseSitenum.cs new file mode 100644 index 0000000..5f77dca --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseSitenum.cs @@ -0,0 +1,41 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_sitenum")] + public class BaseSitenum + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("sitenode_no")] + public int? sitenodeNo { get; set; } + + [Column("order_code")] + public string orderCode { get; set; } + + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("car_no")] + public int? carNo { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseStandbytime.cs b/src/Khd.Core.Domain/Models/BaseStandbytime.cs new file mode 100644 index 0000000..5b67c68 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseStandbytime.cs @@ -0,0 +1,50 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_standbytime")] + public class BaseStandbytime + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 线体 + /// + [Column("line")] + public int? line { get; set; } + + /// + /// 日期 + /// + [Column("ddate")] + public DateTime? ddate { get; set; } + + /// + /// 开始时间 + /// + [Column("begintime")] + public DateTime? begintime { get; set; } + + /// + /// 结束时间 + /// + [Column("endtime")] + public DateTime? endtime { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseStorage.cs b/src/Khd.Core.Domain/Models/BaseStorage.cs new file mode 100644 index 0000000..0fa2c5d --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseStorage.cs @@ -0,0 +1,68 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_storage")] + public class BaseStorage + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 区域编码 + /// + [Column("storage_no")] + public string storageNo { get; set; } + + /// + /// 区域名称 + /// + [Column("storage_name")] + public string storageName { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseSystemconfig.cs b/src/Khd.Core.Domain/Models/BaseSystemconfig.cs new file mode 100644 index 0000000..ba473da --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseSystemconfig.cs @@ -0,0 +1,77 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_systemconfig")] + public class BaseSystemconfig + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 系统设置编码 + /// + [Column("system_filedno")] + public string systemFiledno { get; set; } + + /// + /// 系统配置值 + /// + [Column("system_filedval")] + public string systemFiledval { get; set; } + + /// + /// 字段描述 + /// + [Column("system_fileddesc")] + public string systemFileddesc { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseTasktstatus.cs b/src/Khd.Core.Domain/Models/BaseTasktstatus.cs new file mode 100644 index 0000000..fc96054 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseTasktstatus.cs @@ -0,0 +1,71 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_tasktstatus")] + public class BaseTasktstatus + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 任务状态编号 + /// + [Column("taskstatus_no")] + public string taskstatusNo { get; set; } + + /// + /// 任务状态名称 + /// + [Column("taskstatus_nm")] + public string taskstatusNm { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseTasktype.cs b/src/Khd.Core.Domain/Models/BaseTasktype.cs new file mode 100644 index 0000000..4f9e4e7 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseTasktype.cs @@ -0,0 +1,71 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_tasktype")] + public class BaseTasktype + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 任务类型编号 + /// + [Column("tasktype_no")] + public string tasktypeNo { get; set; } + + /// + /// 任务类型名称 + /// + [Column("tasktype_nm")] + public string tasktypeNm { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseUpdownnum.cs b/src/Khd.Core.Domain/Models/BaseUpdownnum.cs new file mode 100644 index 0000000..6721309 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseUpdownnum.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_updownnum")] + public class BaseUpdownnum + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 创建日期 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + [Column("site_no")] + public int? siteNo { get; set; } + + [Column("materiel_no")] + public string materielNo { get; set; } + + /// + /// 上件数量 + /// + [Column("upnum")] + public int? upnum { get; set; } + + /// + /// 下件数量 + /// + [Column("downnum")] + public int? downnum { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseUpdownnumnew.cs b/src/Khd.Core.Domain/Models/BaseUpdownnumnew.cs new file mode 100644 index 0000000..07fd354 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseUpdownnumnew.cs @@ -0,0 +1,80 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_updownnumnew")] + public class BaseUpdownnumnew + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + /// + /// 创建日期 + /// + [Column("create_date")] + public DateTime? createDate { get; set; } + + [Column("site_no")] + public int? siteNo { get; set; } + + [Column("material_no")] + public string materialNo { get; set; } + + /// + /// 上件数量 + /// + [Column("upnum")] + public int? upnum { get; set; } + + /// + /// 下件数量 + /// + [Column("downnum")] + public int? downnum { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BaseWaitdownline.cs b/src/Khd.Core.Domain/Models/BaseWaitdownline.cs new file mode 100644 index 0000000..4336e45 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BaseWaitdownline.cs @@ -0,0 +1,83 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("base_waitdownline")] + public class BaseWaitdownline + { + + [Key] + [Column("id")] + public Guid? id { get; set; } + + /// + /// 小车id + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 小车编码 + /// + [Column("car_no")] + public int? carNo { get; set; } + + /// + /// 小车名称 + /// + [Column("car_name")] + public string carName { get; set; } + + [Column("downline")] + public int? downline { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("materiel_num")] + public int? materielNum { get; set; } + + [Column("materiel_no")] + public string materielNo { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasedataPowerclasslog.cs b/src/Khd.Core.Domain/Models/BasedataPowerclasslog.cs new file mode 100644 index 0000000..e37333b --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasedataPowerclasslog.cs @@ -0,0 +1,89 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("basedata_powerclasslog")] + public class BasedataPowerclasslog + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 当前耗电 + /// + [Column("frontElect")] + public string frontelect { get; set; } + + /// + /// 当前总耗电 + /// + [Column("totalElect")] + public string totalelect { get; set; } + + /// + /// 能耗 + /// + [Column("power")] + public string power { get; set; } + + [Column("createTime")] + public DateTime? createtime { get; set; } + + [Column("shiftName")] + public string shiftname { get; set; } + + [Column("lineId")] + public int? lineid { get; set; } + + /// + /// 1 副厂房电表 ,2 主厂房电表 + /// + [Column("powerType")] + public int? powertype { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BasedataUplinescancarlist.cs b/src/Khd.Core.Domain/Models/BasedataUplinescancarlist.cs new file mode 100644 index 0000000..e0978b7 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BasedataUplinescancarlist.cs @@ -0,0 +1,95 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("basedata_uplinescancarlist")] + public class BasedataUplinescancarlist + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 任务订单号 + /// + [Column("MaterialNo")] + public string materialno { get; set; } + + /// + /// 任务号 + /// + [Column("carcode")] + public string carcode { get; set; } + + [Column("site_id")] + public Guid? siteId { get; set; } + + /// + /// 站台编号 + /// + [Column("SiteNo")] + public string siteno { get; set; } + + /// + /// 任务号 + /// + [Column("task_no")] + public Guid? taskNo { get; set; } + + /// + /// 完成状态 + /// + [Column("Number")] + public int? number { get; set; } + + /// + /// 单据状态 + /// + [Column("isover")] + public int? isover { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BusinessCarrierCargocage.cs b/src/Khd.Core.Domain/Models/BusinessCarrierCargocage.cs new file mode 100644 index 0000000..3d33674 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BusinessCarrierCargocage.cs @@ -0,0 +1,92 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("business_carrier_cargocage")] + public class BusinessCarrierCargocage + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 小车id + /// + [Column("carrier_id")] + public Guid? carrierId { get; set; } + + /// + /// 小车编号 + /// + [Column("carrier_no")] + public string carrierNo { get; set; } + + /// + /// 托盘id + /// + [Column("cargocage_id")] + public Guid? cargocageId { get; set; } + + /// + /// 站台分流区 + /// + [Column("sitearea_id")] + public int? siteareaId { get; set; } + + /// + /// 指令id + /// + [Column("task_id")] + public int? taskId { get; set; } + + /// + /// 刷新站点时间 + /// + [Column("operate_time")] + public DateTime? operateTime { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BusinessCommand.cs b/src/Khd.Core.Domain/Models/BusinessCommand.cs new file mode 100644 index 0000000..018206c --- /dev/null +++ b/src/Khd.Core.Domain/Models/BusinessCommand.cs @@ -0,0 +1,89 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("business_command")] + public class BusinessCommand + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 指令编码 + /// + [Column("command_no")] + public string commandNo { get; set; } + + /// + /// 指令类型 + /// + [Column("command_type")] + public int? commandType { get; set; } + + /// + /// 开始站台 + /// + [Column("start_site_id")] + public Guid? startSiteId { get; set; } + + /// + /// 下个站台 + /// + [Column("next_site_id")] + public Guid? nextSiteId { get; set; } + + /// + /// 操作时间 + /// + [Column("optdate")] + public DateTime? optdate { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + + [Column("line_id")] + public Guid? lineId { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BusinessOperationlog.cs b/src/Khd.Core.Domain/Models/BusinessOperationlog.cs new file mode 100644 index 0000000..0dd95bf --- /dev/null +++ b/src/Khd.Core.Domain/Models/BusinessOperationlog.cs @@ -0,0 +1,68 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("business_operationlog")] + public class BusinessOperationlog + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 操作日志标题 + /// + [Column("opterate_title")] + public string opterateTitle { get; set; } + + /// + /// 操作日志内容 + /// + [Column("opterate_desc")] + public string opterateDesc { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BusinessSitehistory.cs b/src/Khd.Core.Domain/Models/BusinessSitehistory.cs new file mode 100644 index 0000000..1be90c4 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BusinessSitehistory.cs @@ -0,0 +1,92 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("business_sitehistory")] + public class BusinessSitehistory + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 站台 + /// + [Column("site_id")] + public Guid? siteId { get; set; } + + /// + /// 载具 + /// + [Column("car_id")] + public Guid? carId { get; set; } + + /// + /// 任务 + /// + [Column("task_id")] + public Guid? taskId { get; set; } + + /// + /// 路径值 + /// + [Column("sitearea_val")] + public string siteareaVal { get; set; } + + /// + /// 指令 + /// + [Column("command_id")] + public Guid? commandId { get; set; } + + /// + /// 操作时间 + /// + [Column("operate_time")] + public DateTime? operateTime { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/BusinessTask.cs b/src/Khd.Core.Domain/Models/BusinessTask.cs new file mode 100644 index 0000000..1f2d2a1 --- /dev/null +++ b/src/Khd.Core.Domain/Models/BusinessTask.cs @@ -0,0 +1,116 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("business_task")] + public class BusinessTask + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 任务订单号 + /// + [Column("order_no")] + public string orderNo { get; set; } + + /// + /// 任务号 + /// + [Column("task_no")] + public Guid? taskNo { get; set; } + + /// + /// 预设线路 + /// + [Column("pre_line_id")] + public Guid? preLineId { get; set; } + + /// + /// 预设线名称 + /// + [Column("pre_line_name")] + public string preLineName { get; set; } + + /// + /// 完成状态 + /// + [Column("complete_state")] + public int? completeState { get; set; } + + /// + /// 单据状态 + /// + [Column("task_state")] + public Guid? taskState { get; set; } + + /// + /// 任务类型 + /// + [Column("task_type")] + public Guid? taskType { get; set; } + + /// + /// 任务出发站台 + /// + [Column("site_id")] + public Guid? siteId { get; set; } + + /// + /// 目标站台分流区 + /// + [Column("sitearea_id")] + public int? siteareaId { get; set; } + + /// + /// 操作时间 + /// + [Column("optdate")] + public DateTime? optdate { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/DataCargocageMaterialdetail.cs b/src/Khd.Core.Domain/Models/DataCargocageMaterialdetail.cs new file mode 100644 index 0000000..7e3ad77 --- /dev/null +++ b/src/Khd.Core.Domain/Models/DataCargocageMaterialdetail.cs @@ -0,0 +1,77 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("data_cargocage_materialdetail")] + public class DataCargocageMaterialdetail + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 托盘id + /// + [Column("cargocage_id")] + public Guid? cargocageId { get; set; } + + /// + /// 物料编码 + /// + [Column("material_no")] + public string materialNo { get; set; } + + [Column("material_barcode")] + public string materialBarcode { get; set; } + + /// + /// 数量 + /// + [Column("material_amount")] + public int? materialAmount { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/DataCargocageProduct.cs b/src/Khd.Core.Domain/Models/DataCargocageProduct.cs new file mode 100644 index 0000000..dd3d075 --- /dev/null +++ b/src/Khd.Core.Domain/Models/DataCargocageProduct.cs @@ -0,0 +1,86 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("data_cargocage_product")] + public class DataCargocageProduct + { + + [Key] + [Column("id")] + public int id { get; set; } + + /// + /// 托盘id + /// + [Column("cargocage_id")] + public Guid? cargocageId { get; set; } + + /// + /// 物料描述 + /// + [Column("material_desc")] + public string materialDesc { get; set; } + + /// + /// 物料编码 + /// + [Column("material_no")] + public string materialNo { get; set; } + + /// + /// boomid + /// + [Column("boom_id")] + public Guid? boomId { get; set; } + + /// + /// 数量 + /// + [Column("material_amount")] + public int? materialAmount { get; set; } + + [Column("is_delete")] + public int? isDelete { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("create_id")] + public Guid? createId { get; set; } + + [Column("create_by")] + public string createBy { get; set; } + + [Column("modify_time")] + public DateTime? modifyTime { get; set; } + + [Column("modify_id")] + public Guid? modifyId { get; set; } + + [Column("modify_by")] + public string modifyBy { get; set; } + + [Column("definefield3")] + public string definefield3 { get; set; } + + [Column("definefield1")] + public string definefield1 { get; set; } + + [Column("definefield2")] + public string definefield2 { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/Discretealarms.cs b/src/Khd.Core.Domain/Models/Discretealarms.cs new file mode 100644 index 0000000..4457ff4 --- /dev/null +++ b/src/Khd.Core.Domain/Models/Discretealarms.cs @@ -0,0 +1,47 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("discretealarms")] + public class Discretealarms + { + + [Key] + [Column("ID")] + public Guid id { get; set; } + + [Column("Alarmtext")] + public string alarmtext { get; set; } + + [Column("FieldInfo")] + public string fieldinfo { get; set; } + + [Column("Triggertag")] + public string triggertag { get; set; } + + [Column("Triggerbit")] + public string triggerbit { get; set; } + + [Column("plc_value")] + public string plcValue { get; set; } + + /// + /// 创建时间 + /// + [Column("create_time")] + public DateTime? createTime { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/MesProdPlan.cs b/src/Khd.Core.Domain/Models/MesProdPlan.cs new file mode 100644 index 0000000..92cdaea --- /dev/null +++ b/src/Khd.Core.Domain/Models/MesProdPlan.cs @@ -0,0 +1,62 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("mes_prod_plan")] + public class MesProdPlan + { + + [Key] + [Column("ID")] + public Guid ID { get; set; } + + /// + /// VIN_CODE + /// + [Column("VIN_CODE")] + public string VIN_CODE { get; set; } + + /// + /// 物料描述 + /// + [Column("MATERIAL_DESC")] + public string MATERIAL_DESC { get; set; } + + /// + /// 线别 + /// + [Column("LINE")] + public string LINE { get; set; } + + /// + /// 数量 + /// + [Column("ORDER_QTY")] + public string ORDER_QTY { get; set; } + + /// + /// 时间 + /// + [Column("CREATE_TIME")] + public string? CREATE_TIME { get; set; } + + /// + /// 处理标志 0 接收 1 已处理 + /// + [Column("FLAG")] + public string FLAG { get; set; } + } +} + diff --git a/src/Khd.Core.Domain/Models/NewMaterial.cs b/src/Khd.Core.Domain/Models/NewMaterial.cs new file mode 100644 index 0000000..36e7203 --- /dev/null +++ b/src/Khd.Core.Domain/Models/NewMaterial.cs @@ -0,0 +1,38 @@ + +//----------------------------------------------------------------------- +// +// * Copyright (C) 2021 KEHAIDASOFT All Rights Reserved +// * version : 4.0.30319.42000 +// * author : khd by t4-2 +// +//----------------------------------------------------------------------- + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Khd.Core.Domain.Models +{ + + [Table("new_material")] + public class NewMaterial + { + + [Key] + [Column("id")] + public Guid id { get; set; } + + [Column("barcode")] + public string barcode { get; set; } + + [Column("is_new")] + public int? isNew { get; set; } + + [Column("create_time")] + public DateTime? createTime { get; set; } + + [Column("site_node")] + public string siteNode { get; set; } + } +} + diff --git a/src/Khd.Core.EntityFramework/DbContextExtensions.cs b/src/Khd.Core.EntityFramework/DbContextExtensions.cs new file mode 100644 index 0000000..578a453 --- /dev/null +++ b/src/Khd.Core.EntityFramework/DbContextExtensions.cs @@ -0,0 +1,79 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.EntityFramework +{ + public static class DbContextExtensions + { + //public static List ExecuteSqlQuery(this DbContext dbContext, string sqlQuery) where T : class + //{ + // using var command = dbContext.Database.GetDbConnection().CreateCommand(); + // command.CommandText = sqlQuery; + // command.CommandType = CommandType.Text; + + // dbContext.Database.OpenConnection(); + + // using var reader = command.ExecuteReader(); + // var result = new List(); + + // while (reader.Read()) + // { + // var entity = Activator.CreateInstance(); + // foreach (var prop in typeof(T).GetProperties()) + // { + // var value = reader[prop.Name]; + // if (value != DBNull.Value) + // { + // prop.SetValue(entity, value); + // } + // } + // result.Add(entity); + // } + + // return result; + //} + + public static List ExecuteSqlQuery(this DbContext dbContext, string sqlQuery) where T : class, new() + { + using var command = dbContext.Database.GetDbConnection().CreateCommand(); + command.CommandText = sqlQuery; + command.CommandType = CommandType.Text; + + dbContext.Database.OpenConnection(); + + using var reader = command.ExecuteReader(); + var result = new List(); + + var columnNames = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).ToList(); + + while (reader.Read()) + { + var entity = new T(); + var properties = typeof(T).GetProperties(); + + foreach (var prop in properties) + { + var propName = prop.Name; + if (!columnNames.Contains(propName)) + continue; + + var value = reader[propName]; + if (value != DBNull.Value) + { + prop.SetValue(entity, value); + } + } + + result.Add(entity); + } + + return result; + } + } + +} diff --git a/src/Khd.Core.EntityFramework/DefaultDbContext.cs b/src/Khd.Core.EntityFramework/DefaultDbContext.cs new file mode 100644 index 0000000..fb67476 --- /dev/null +++ b/src/Khd.Core.EntityFramework/DefaultDbContext.cs @@ -0,0 +1,227 @@ +using Khd.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; + +namespace Khd.Core.EntityFramework +{ + public class DefaultDbContext : DbContext + { + public DefaultDbContext(DbContextOptions options) : base(options) + { + } + public DbSet BaseAlertinfo { get; set; } + + + public DbSet BaseAmima { get; set; } + + + public DbSet BaseArea { get; set; } + + + public DbSet BaseAsaveerro { get; set; } + + + public DbSet BaseAsaveerroBak { get; set; } + + + public DbSet BaseAsaveplcnumber { get; set; } + + + public DbSet BaseAsaveplcnumberBak { get; set; } + + + public DbSet BaseBomcomponent { get; set; } + + + public DbSet BaseCar { get; set; } + + + public DbSet BaseCararea { get; set; } + + + public DbSet BaseCarareaRejects { get; set; } + + + public DbSet BaseCarareaoverdue { get; set; } + + + public DbSet BaseCargocage { get; set; } + + + public DbSet BaseCarmateriel { get; set; } + + + public DbSet BaseCarmateriellog { get; set; } + + + public DbSet BaseCarmaterielup { get; set; } + + + public DbSet BaseCarmaterielupdown { get; set; } + + + public DbSet BaseCarorder { get; set; } + + + public DbSet BaseCarorderlist { get; set; } + + + public DbSet BaseCarordernum { get; set; } + + + public DbSet BaseCarordernumnew { get; set; } + + + public DbSet BaseCarpreline { get; set; } + + + public DbSet BaseCarproduct { get; set; } + + + public DbSet BaseCarrier { get; set; } + + + public DbSet BaseCarthrough { get; set; } + + + public DbSet BaseDowncarorderback { get; set; } + + + public DbSet BaseDownline { get; set; } + + + public DbSet BaseEquipment { get; set; } + + + public DbSet BaseFactory { get; set; } + + + public DbSet BaseFaultlog { get; set; } + + + public DbSet BaseFaulttime { get; set; } + + + public DbSet BaseFlag { get; set; } + + + public DbSet BaseLinearea { get; set; } + + + public DbSet BaseLineinfo { get; set; } + + + public DbSet BaseLoginlog { get; set; } + + + public DbSet BaseMaterialinfo { get; set; } + + + public DbSet BaseMaterialinfobom { get; set; } + + + public DbSet BaseOrderinfo { get; set; } + + + public DbSet BaseOrderlog { get; set; } + + + public DbSet BasePlcpoint { get; set; } + + + public DbSet BasePlcpointsite { get; set; } + + + public DbSet BasePlctype { get; set; } + + + public DbSet BasePrelinedetail { get; set; } + + + public DbSet BasePrelineinfo { get; set; } + + + public DbSet BaseProductionOrderSplit { get; set; } + public DbSet BaseProductionOrderSplitBak { get; set; } + + public DbSet BaseProductionOrderSplitTextinsert { get; set; } + + + public DbSet BaseProductionOrderSplitLine { get; set; } + + + public DbSet BaseProductionOrderSplitReduct { get; set; } + + + public DbSet BaseRejects { get; set; } + + + public DbSet BaseSitearea { get; set; } + + + public DbSet BaseSitenode { get; set; } + + + public DbSet BaseSitenum { get; set; } + + + public DbSet BaseStandbytime { get; set; } + + + public DbSet BaseStorage { get; set; } + + + public DbSet BaseSystemconfig { get; set; } + + + public DbSet BaseTasktstatus { get; set; } + + + public DbSet BaseTasktype { get; set; } + + + public DbSet BaseUpdownnum { get; set; } + + + public DbSet BaseUpdownnumnew { get; set; } + + + public DbSet BaseWaitdownline { get; set; } + + + public DbSet BasedataPowerclasslog { get; set; } + + + public DbSet BasedataUplinescancarlist { get; set; } + + + public DbSet BusinessCarrierCargocage { get; set; } + + + public DbSet BusinessCommand { get; set; } + + + public DbSet BusinessOperationlog { get; set; } + + + public DbSet BusinessSitehistory { get; set; } + + + public DbSet BusinessTask { get; set; } + + + public DbSet DataCargocageMaterialdetail { get; set; } + + + public DbSet DataCargocageProduct { get; set; } + + + public DbSet Discretealarms { get; set; } + + + public DbSet NewMaterial { get; set; } + + public DbSet BaseFollowmessage { get; set; } + + public DbSet MesProdPlan { get; set; } + } +} \ No newline at end of file diff --git a/src/Khd.Core.EntityFramework/Khd.Core.EntityFramework.csproj b/src/Khd.Core.EntityFramework/Khd.Core.EntityFramework.csproj new file mode 100644 index 0000000..46dbf9f --- /dev/null +++ b/src/Khd.Core.EntityFramework/Khd.Core.EntityFramework.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/src/Khd.Core.Library/Attributes/NotWarpApiResultAttribute.cs b/src/Khd.Core.Library/Attributes/NotWarpApiResultAttribute.cs new file mode 100644 index 0000000..ff8d095 --- /dev/null +++ b/src/Khd.Core.Library/Attributes/NotWarpApiResultAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Khd.Core.Library.Attributes +{ + [AttributeUsage(AttributeTargets.Method)] + public class NotWarpApiResultAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Dto/ApiResult.cs b/src/Khd.Core.Library/Dto/ApiResult.cs new file mode 100644 index 0000000..f18c5ed --- /dev/null +++ b/src/Khd.Core.Library/Dto/ApiResult.cs @@ -0,0 +1,72 @@ +namespace Khd.Core.Library.Dto +{ + public sealed class ApiResult : ApiResult + { + public ApiResult(int errorCode, string message, TResult result) + : base(errorCode, message) + { + Result = result; + } + + public TResult Result { get; set; } + + public void SetSuccess(TResult data) + { + IsSuccess = true; + Result = data; + } + + public ApiResult SetApiResult(TResult data) + { + Result = data; + return this; + } + } + + public class ApiResult + { + public ApiResult(int errorCode, string message) + { + ErrorCode = errorCode; + Message = message; + } + + public bool IsSuccess { get; set; } + + public int ErrorCode { get; set; } + + public string Message { get; set; } + + public string OperationId { get; set; } + + public void SetSuccess() + { + IsSuccess = true; + } + + public void SetFailed(int errorCode) + { + IsSuccess = false; + ErrorCode = errorCode; + } + + public void SetFailed(string msg) + { + IsSuccess = false; + ErrorCode = -1; + Message = msg; + } + + public void SetFailed(int errorCode, string msg) + { + IsSuccess = false; + ErrorCode = errorCode; + Message = msg; + } + + public override string ToString() + { + return $"{IsSuccess},ErrorCode:{ErrorCode},Message:{Message}"; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/EntityFrameworkCore/BaseDbContext.cs b/src/Khd.Core.Library/EntityFrameworkCore/BaseDbContext.cs new file mode 100644 index 0000000..11a19ad --- /dev/null +++ b/src/Khd.Core.Library/EntityFrameworkCore/BaseDbContext.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Khd.Core.Library.EntityFrameworkCore +{ + public class BaseDbContext : DbContext + { + public override int SaveChanges() + { + return base.SaveChanges(); + } + + public override int SaveChanges(bool acceptAllChangesOnSuccess) + { + return base.SaveChanges(acceptAllChangesOnSuccess); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return base.SaveChangesAsync(cancellationToken); + } + + public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()) + { + return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } + + private void ApplyConcepts() + { + + } + } +} diff --git a/src/Khd.Core.Library/EntityFrameworkCore/Entity.cs b/src/Khd.Core.Library/EntityFrameworkCore/Entity.cs new file mode 100644 index 0000000..b3f2b1c --- /dev/null +++ b/src/Khd.Core.Library/EntityFrameworkCore/Entity.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; + +namespace Khd.Core.Library.EntityFrameworkCore +{ + public class Entity : ISoftDelete + { + [Key] + public int Id { get; set; } + + [JsonIgnore] + public bool IsDeleted { get; set; } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/EntityFrameworkCore/ISoftDelete.cs b/src/Khd.Core.Library/EntityFrameworkCore/ISoftDelete.cs new file mode 100644 index 0000000..dd210d3 --- /dev/null +++ b/src/Khd.Core.Library/EntityFrameworkCore/ISoftDelete.cs @@ -0,0 +1,7 @@ +namespace Khd.Core.Library.EntityFrameworkCore +{ + public interface ISoftDelete + { + public bool IsDeleted { get; set; } + } +} diff --git a/src/Khd.Core.Library/Exception/ResponseExceptionBase.cs b/src/Khd.Core.Library/Exception/ResponseExceptionBase.cs new file mode 100644 index 0000000..01cca23 --- /dev/null +++ b/src/Khd.Core.Library/Exception/ResponseExceptionBase.cs @@ -0,0 +1,7 @@ +namespace Khd.Core.Library.Exception +{ + public abstract class ResponseExceptionBase : System.Exception + { + public int ErrorCode { get; protected set; } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Exception/StringResponseException.cs b/src/Khd.Core.Library/Exception/StringResponseException.cs new file mode 100644 index 0000000..e172ae3 --- /dev/null +++ b/src/Khd.Core.Library/Exception/StringResponseException.cs @@ -0,0 +1,18 @@ +namespace Khd.Core.Library.Exception +{ + public class StringResponseException : ResponseExceptionBase + { + public StringResponseException(int errorCode, string errorMessage) + { + ErrorCode = errorCode; + Message = errorMessage; + } + + public StringResponseException(string errorMessage) + : this(0, errorMessage) + { + } + + public override string Message { get; } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Extensions/StringExtensions.cs b/src/Khd.Core.Library/Extensions/StringExtensions.cs new file mode 100644 index 0000000..edf2cb7 --- /dev/null +++ b/src/Khd.Core.Library/Extensions/StringExtensions.cs @@ -0,0 +1,19 @@ +using System.Security.Cryptography; +using System.Text; + +namespace Khd.Core.Library.Extensions +{ + public static class StringExtensions + { + public static string GetMd5(this string value) + { + using var md5Hash = MD5.Create(); + var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(value)); + var sBuilder = new StringBuilder(); + foreach (var b in data) sBuilder.Append(b.ToString("x2")); + + var hash = sBuilder.ToString(); + return hash; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Filter/ExceptionFilter.cs b/src/Khd.Core.Library/Filter/ExceptionFilter.cs new file mode 100644 index 0000000..0a394e8 --- /dev/null +++ b/src/Khd.Core.Library/Filter/ExceptionFilter.cs @@ -0,0 +1,72 @@ +using System; +using System.Linq; +using System.Net; +using System.Text.Encodings.Web; +using System.Text.Json; +using Khd.Core.Library.Dto; +using Khd.Core.Library.Exception; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; + +namespace Khd.Core.Library.Filter +{ + public class ExceptionFilter : IExceptionFilter + { + private static readonly JsonSerializerOptions JsonSerializerSettings = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + private readonly ILogger _logger; + + /// + /// 异常处理过滤器 + /// + public ExceptionFilter(ILogger logger) + { + _logger = logger; + } + + public void OnException(ExceptionContext context) + { + System.Exception ex; + if (context.Exception is AggregateException) + ex = ((AggregateException) context.Exception).InnerExceptions.First(); + else + ex = context.Exception; + + if (ex is ResponseExceptionBase rEx) ProcessBusinessException(rEx, context); + } + + /// + /// 业务异常的处理 + /// + /// + /// + private void ProcessBusinessException(ResponseExceptionBase ex, ExceptionContext context) + { + const HttpStatusCode statusCode = (HttpStatusCode) 288; + + string exMessage; + int errorCode; + + if (ex is StringResponseException) + { + errorCode = ex.ErrorCode == 0 ? int.MaxValue : ex.ErrorCode; + exMessage = ex.Message; + } + else + { + errorCode = ex.ErrorCode; + //var message = _enumDescriptionService.GetDescriptionValue(ex.ErrorCode); + exMessage = string.Empty; + } + + context.HttpContext.Response.StatusCode = (int) statusCode; + _logger.LogInformation(errorCode, "发生业务异常 {0} {1}", errorCode.ToString(), exMessage); + context.Result = new JsonResult(new ApiResult(errorCode, exMessage), JsonSerializerSettings); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Filter/ResultFilter.cs b/src/Khd.Core.Library/Filter/ResultFilter.cs new file mode 100644 index 0000000..f4deeea --- /dev/null +++ b/src/Khd.Core.Library/Filter/ResultFilter.cs @@ -0,0 +1,49 @@ +using System.Diagnostics; +using Khd.Core.Library.Attributes; +using Khd.Core.Library.Dto; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Khd.Core.Library.Filter +{ + public class ResultFilter : IResultFilter + { + public void OnResultExecuting(ResultExecutingContext context) + { + var notWarpApiResultAttribute = + (context.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo?.IsDefined( + typeof(NotWarpApiResultAttribute), false) ?? false; + if (notWarpApiResultAttribute) + return; + + var or = context.Result as ObjectResult; + if (or == null || or.Value is ApiResult) + { + if (context.Result is EmptyResult) + { + var emptyResult = new ApiResult(0, string.Empty); + if (Activity.Current != null) + emptyResult.OperationId = Activity.Current.Id; + emptyResult.SetSuccess(); + context.Result = new OkObjectResult(emptyResult); + return; + } + + return; + } + + var response = new ApiResult(or.StatusCode ?? 0, string.Empty, or.Value); + response.SetSuccess(); + if (Activity.Current != null) + response.OperationId = Activity.Current.Id; + or.DeclaredType = typeof(ApiResult<>); + or.Value = response; + } + + public void OnResultExecuted(ResultExecutedContext context) + { + //Nothing + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Khd.Core.Library.csproj b/src/Khd.Core.Library/Khd.Core.Library.csproj new file mode 100644 index 0000000..46987b6 --- /dev/null +++ b/src/Khd.Core.Library/Khd.Core.Library.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Library/LibraryExtensions.cs b/src/Khd.Core.Library/LibraryExtensions.cs new file mode 100644 index 0000000..f17c7c6 --- /dev/null +++ b/src/Khd.Core.Library/LibraryExtensions.cs @@ -0,0 +1,24 @@ +using Khd.Core.Library.Filter; +using Khd.Core.Library.Swagger; +using Microsoft.Extensions.DependencyInjection; + +namespace Khd.Core.Library +{ + public static class LibraryExtensions + { + public static void AddLibrary(this IServiceCollection services) + { + services.AddControllers(options => + { + //可以定义出参格式 + //options.Filters.Add(); + options.Filters.Add(); + }); + services.AddMvc().AddJsonOptions(option => + { + option.JsonSerializerOptions.IgnoreNullValues = true; + }); + services.AddLibrarySwagger(); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Swagger/AdditionOperationFilter.cs b/src/Khd.Core.Library/Swagger/AdditionOperationFilter.cs new file mode 100644 index 0000000..0730ecf --- /dev/null +++ b/src/Khd.Core.Library/Swagger/AdditionOperationFilter.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Khd.Core.Library.Dto; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Khd.Core.Library.Swagger +{ + public class AdditionOperationFilter : IOperationFilter + { + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (context == null) return; + + var actualReturnType = context.MethodInfo.ReturnType.Name == "Task`1" + ? context.MethodInfo.ReturnType.GenericTypeArguments.FirstOrDefault() + : context.MethodInfo.ReturnType; + + if (actualReturnType == null || actualReturnType.Name == "ApiResult`1" || + actualReturnType.Name == "ApiResult") return; + + var wrapApiResultReturnType = actualReturnType == typeof(void) || actualReturnType == typeof(Task) + ? typeof(ApiResult) + : typeof(ApiResult<>).MakeGenericType(actualReturnType); + + operation?.Responses?.Remove("200"); + operation?.Responses?.Add("200", + new OpenApiResponse + { + Description = "Success", + Content = new Dictionary + { + { + "application/json", new OpenApiMediaType + { + Schema = context.SchemaGenerator.GenerateSchema(wrapApiResultReturnType, + context.SchemaRepository) + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Swagger/ApplicationBuilderSwaggerExtensions.cs b/src/Khd.Core.Library/Swagger/ApplicationBuilderSwaggerExtensions.cs new file mode 100644 index 0000000..944f18a --- /dev/null +++ b/src/Khd.Core.Library/Swagger/ApplicationBuilderSwaggerExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Builder; + +namespace Khd.Core.Library.Swagger +{ + public static class ApplicationBuilderSwaggerExtensions + { + public static IApplicationBuilder UseLibrarySwagger(this IApplicationBuilder app) + { + app.UseSwagger(); + app.UseSwaggerUI(); + return app; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Swagger/SecurityRequirementsOperationFilter.cs b/src/Khd.Core.Library/Swagger/SecurityRequirementsOperationFilter.cs new file mode 100644 index 0000000..e91d6ac --- /dev/null +++ b/src/Khd.Core.Library/Swagger/SecurityRequirementsOperationFilter.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Khd.Core.Library.Swagger +{ + public class SecurityRequirementsOperationFilter : IOperationFilter + { + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var actionAttrs = context.MethodInfo.GetCustomAttributes(true); + var controllerAttrs = context.MethodInfo.DeclaringType.GetCustomAttributes(true); + + if (actionAttrs.OfType().Any() || + controllerAttrs.OfType().Any()) return; + + var methodAuthorizeAttrs = actionAttrs.OfType(); + var controllerAuthorizeAttrs = controllerAttrs.OfType(); + + if (!methodAuthorizeAttrs.Any() && !controllerAuthorizeAttrs.Any()) return; + + operation.Responses.Add("401", new OpenApiResponse {Description = "Unauthorized"}); + + operation.Security.Add(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + {Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "Bearer"}}, + new List() + } + }); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Library/Swagger/ServiceCollectionSwaggerExtensions.cs b/src/Khd.Core.Library/Swagger/ServiceCollectionSwaggerExtensions.cs new file mode 100644 index 0000000..809763b --- /dev/null +++ b/src/Khd.Core.Library/Swagger/ServiceCollectionSwaggerExtensions.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Khd.Core.Library.Swagger +{ + public static class ServiceCollectionSwaggerExtensions + { + public static IServiceCollection AddLibrarySwagger(this IServiceCollection services) + { + // 配置SwaggerGen + services.Configure(options => + { + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT模式授权,请输入 Bearer [Token] 进行身份验证", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey + }); + + options.OperationFilter(); + options.OperationFilter(); + + var assemblyName = Assembly.GetEntryAssembly()?.GetName().Name; + if (assemblyName == null) + return; + + options.SwaggerDoc(assemblyName, new OpenApiInfo + { + Version = "v1", + Title = assemblyName, + Contact = new OpenApiContact() + { + Email = "kehaida@163.com", + Name = "khd", + Url = new Uri("https://github.com") + } + }); + options.CustomSchemaIds(t => t.FullName); + + // foreach (var xmlFile in Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "Khd.*.xml")) + foreach (var xmlFile in Directory.EnumerateFiles(AppContext.BaseDirectory, "Khd.*.xml")) + options.IncludeXmlComments(xmlFile, true); + }); + + // 配置 SwaggerOptions + services.Configure(options => + { + options.RouteTemplate = "swagger/{documentName}/{Version}"; + }); + + // 配置 SwaggerUIOptions + services.Configure(options => + { + options.EnableDeepLinking(); + options.DocExpansion(DocExpansion.None); + options.DisplayRequestDuration(); + + var assemblyName = Assembly.GetEntryAssembly()?.GetName().Name; + + if (assemblyName != null) options.SwaggerEndpoint($"{assemblyName}/v1", "V1"); + }); + + services.AddSwaggerGen(); + + return services; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/Khd.Core.Plc.csproj b/src/Khd.Core.Plc/Khd.Core.Plc.csproj new file mode 100644 index 0000000..33ed615 --- /dev/null +++ b/src/Khd.Core.Plc/Khd.Core.Plc.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/Khd.Core.Plc/S7/COTP.cs b/src/Khd.Core.Plc/S7/COTP.cs new file mode 100644 index 0000000..64f17f4 --- /dev/null +++ b/src/Khd.Core.Plc/S7/COTP.cs @@ -0,0 +1,113 @@ +namespace Khd.Core.Plc.S7 +{ + + /// + /// COTP Protocol functions and types + /// + internal class COTP + { + public enum PduType : byte + { + Data = 0xf0, + ConnectionConfirmed = 0xd0 + } + /// + /// Describes a COTP TPDU (Transport protocol data unit) + /// + public class TPDU + { + public TPKT TPkt { get; } + public byte HeaderLength; + public PduType PDUType; + public int TPDUNumber; + public byte[] Data; + public bool LastDataUnit; + + public TPDU(TPKT tPKT) + { + TPkt = tPKT; + + HeaderLength = tPKT.Data[0]; // Header length excluding this length byte + if (HeaderLength >= 2) + { + PDUType = (PduType)tPKT.Data[1]; + if (PDUType == PduType.Data) //DT Data + { + var flags = tPKT.Data[2]; + TPDUNumber = flags & 0x7F; + LastDataUnit = (flags & 0x80) > 0; + Data = new byte[tPKT.Data.Length - HeaderLength - 1]; // substract header length byte + header length. + Array.Copy(tPKT.Data, HeaderLength + 1, Data, 0, Data.Length); + return; + } + //TODO: Handle other PDUTypes + } + Data = new byte[0]; + } + + /// + /// Reads COTP TPDU (Transport protocol data unit) from the network stream + /// See: https://tools.ietf.org/html/rfc905 + /// + /// The socket to read from + /// COTP DPDU instance + public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) + { + var tpkt = await TPKT.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + if (tpkt.Length == 0) + { + throw new TPDUInvalidException("No protocol data received"); + } + return new TPDU(tpkt); + } + + public override string ToString() + { + return string.Format("Length: {0} PDUType: {1} TPDUNumber: {2} Last: {3} Segment Data: {4}", + HeaderLength, + PDUType, + TPDUNumber, + LastDataUnit, + BitConverter.ToString(Data) + ); + } + + } + + /// + /// Describes a COTP TSDU (Transport service data unit). One TSDU consist of 1 ore more TPDUs + /// + public class TSDU + { + /// + /// Reads the full COTP TSDU (Transport service data unit) + /// See: https://tools.ietf.org/html/rfc905 + /// + /// The stream to read from + /// Data in TSDU + public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) + { + var segment = await TPDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + + if (segment.LastDataUnit) + { + return segment.Data; + } + + // More segments are expected, prepare a buffer to store all data + var buffer = new byte[segment.Data.Length]; + Array.Copy(segment.Data, buffer, segment.Data.Length); + + while (!segment.LastDataUnit) + { + segment = await TPDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + var previousLength = buffer.Length; + Array.Resize(ref buffer, buffer.Length + segment.Data.Length); + Array.Copy(segment.Data, 0, buffer, previousLength, segment.Data.Length); + } + + return buffer; + } + } + } +} diff --git a/src/Khd.Core.Plc/S7/Conversion.cs b/src/Khd.Core.Plc/S7/Conversion.cs new file mode 100644 index 0000000..f8a7ffb --- /dev/null +++ b/src/Khd.Core.Plc/S7/Conversion.cs @@ -0,0 +1,225 @@ +using System.Globalization; + +namespace Khd.Core.Plc.S7 +{ + /// + /// Conversion methods to convert from Siemens numeric format to C# and back + /// + public static class Conversion + { + /// + /// Converts a binary string to Int32 value + /// + /// + /// + public static int BinStringToInt32(this string txt) + { + int ret = 0; + + for (int i = 0; i < txt.Length; i++) + { + ret = (ret << 1) | ((txt[i] == '1') ? 1 : 0); + } + return ret; + } + + /// + /// Converts a binary string to a byte. Can return null. + /// + /// + /// + public static byte? BinStringToByte(this string txt) + { + if (txt.Length == 8) return (byte)BinStringToInt32(txt); + return null; + } + + /// + /// Converts the value to a binary string + /// + /// + /// + public static string ValToBinString(this object value) + { + int cnt = 0; + int cnt2 = 0; + int x = 0; + string txt = ""; + long longValue = 0; + + try + { + if (value.GetType().Name.IndexOf("[]") < 0) + { + // ist nur ein Wert + switch (value.GetType().Name) + { + case "Byte": + x = 7; + longValue = (long)((byte)value); + break; + case "Int16": + x = 15; + longValue = (long)((Int16)value); + break; + case "Int32": + x = 31; + longValue = (long)((Int32)value); + break; + case "Int64": + x = 63; + longValue = (long)((Int64)value); + break; + default: + throw new Exception(); + } + + for (cnt = x; cnt >= 0; cnt += -1) + { + if (((Int64)longValue & (Int64)Math.Pow(2, cnt)) > 0) + txt += "1"; + else + txt += "0"; + } + } + else + { + // ist ein Array + switch (value.GetType().Name) + { + case "Byte[]": + x = 7; + byte[] ByteArr = (byte[])value; + for (cnt2 = 0; cnt2 <= ByteArr.Length - 1; cnt2++) + { + for (cnt = x; cnt >= 0; cnt += -1) + if ((ByteArr[cnt2] & (byte)Math.Pow(2, cnt)) > 0) txt += "1"; else txt += "0"; + } + break; + case "Int16[]": + x = 15; + Int16[] Int16Arr = (Int16[])value; + for (cnt2 = 0; cnt2 <= Int16Arr.Length - 1; cnt2++) + { + for (cnt = x; cnt >= 0; cnt += -1) + if ((Int16Arr[cnt2] & (byte)Math.Pow(2, cnt)) > 0) txt += "1"; else txt += "0"; + } + break; + case "Int32[]": + x = 31; + Int32[] Int32Arr = (Int32[])value; + for (cnt2 = 0; cnt2 <= Int32Arr.Length - 1; cnt2++) + { + for (cnt = x; cnt >= 0; cnt += -1) + if ((Int32Arr[cnt2] & (byte)Math.Pow(2, cnt)) > 0) txt += "1"; else txt += "0"; + } + break; + case "Int64[]": + x = 63; + byte[] Int64Arr = (byte[])value; + for (cnt2 = 0; cnt2 <= Int64Arr.Length - 1; cnt2++) + { + for (cnt = x; cnt >= 0; cnt += -1) + if ((Int64Arr[cnt2] & (byte)Math.Pow(2, cnt)) > 0) txt += "1"; else txt += "0"; + } + break; + default: + throw new Exception(); + } + } + return txt; + } + catch + { + return ""; + } + } + + /// + /// Helper to get a bit value given a byte and the bit index. + /// Example: DB1.DBX0.5 -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5); + /// + /// + /// + /// + public static bool SelectBit(this byte data, int bitPosition) + { + int mask = 1 << bitPosition; + int result = data & mask; + + return (result != 0); + } + + /// + /// Converts from ushort value to short value; it's used to retrieve negative values from words + /// + /// + /// + public static short ConvertToShort(this ushort input) + { + short output; + output = short.Parse(input.ToString("X"), NumberStyles.HexNumber); + return output; + } + + /// + /// Converts from short value to ushort value; it's used to pass negative values to DWs + /// + /// + /// + public static ushort ConvertToUshort(this short input) + { + ushort output; + output = ushort.Parse(input.ToString("X"), NumberStyles.HexNumber); + return output; + } + + /// + /// Converts from UInt32 value to Int32 value; it's used to retrieve negative values from DBDs + /// + /// + /// + public static Int32 ConvertToInt(this uint input) + { + int output; + output = int.Parse(input.ToString("X"), NumberStyles.HexNumber); + return output; + } + + /// + /// Converts from Int32 value to UInt32 value; it's used to pass negative values to DBDs + /// + /// + /// + public static UInt32 ConvertToUInt(this int input) + { + uint output; + output = uint.Parse(input.ToString("X"), NumberStyles.HexNumber); + return output; + } + + /// + /// Converts from float to DWord (DBD) + /// + /// + /// + public static UInt32 ConvertToUInt(this float input) + { + uint output; + output = Khd.Core.Plc.S7.Types.DWord.FromByteArray(Khd.Core.Plc.S7.Types.Real.ToByteArray(input)); + return output; + } + + /// + /// Converts from DWord (DBD) to float + /// + /// + /// + public static float ConvertToFloat(this uint input) + { + float output; + output = Khd.Core.Plc.S7.Types.Real.FromByteArray(Khd.Core.Plc.S7.Types.DWord.ToByteArray(input)); + return output; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Enums.cs b/src/Khd.Core.Plc/S7/Enums.cs new file mode 100644 index 0000000..01663ab --- /dev/null +++ b/src/Khd.Core.Plc/S7/Enums.cs @@ -0,0 +1,211 @@ +namespace Khd.Core.Plc.S7 +{ + /// + /// Types of S7 cpu supported by the library + /// + public enum CpuType + { + /// + /// S7 200 cpu type + /// + S7200 = 0, + + /// + /// Siemens Logo 0BA8 + /// + Logo0BA8 = 1, + + /// + /// S7 200 Smart + /// + S7200Smart = 2, + + /// + /// S7 300 cpu type + /// + S7300 = 10, + + /// + /// S7 400 cpu type + /// + S7400 = 20, + + /// + /// S7 1200 cpu type + /// + S71200 = 30, + + /// + /// S7 1500 cpu type + /// + S71500 = 40, + } + + /// + /// Types of error code that can be set after a function is called + /// + public enum ErrorCode + { + /// + /// The function has been executed correctly + /// + NoError = 0, + + /// + /// Wrong type of CPU error + /// + WrongCPU_Type = 1, + + /// + /// Connection error + /// + ConnectionError = 2, + + /// + /// Ip address not available + /// + IPAddressNotAvailable, + + /// + /// Wrong format of the variable + /// + WrongVarFormat = 10, + + /// + /// Wrong number of received bytes + /// + WrongNumberReceivedBytes = 11, + + /// + /// Error on send data + /// + SendData = 20, + + /// + /// Error on read data + /// + ReadData = 30, + + /// + /// Error on write data + /// + WriteData = 50 + } + + /// + /// Types of memory area that can be read + /// + public enum DataType + { + /// + /// Input area memory + /// + Input = 129, + + /// + /// Output area memory + /// + Output = 130, + + /// + /// Merkers area memory (M0, M0.0, ...) + /// + Memory = 131, + + /// + /// DB area memory (DB1, DB2, ...) + /// + DataBlock = 132, + + /// + /// Timer area memory(T1, T2, ...) + /// + Timer = 29, + + /// + /// Counter area memory (C1, C2, ...) + /// + Counter = 28 + } + + /// + /// Types + /// + public enum VarType + { + /// + /// S7 Bit variable type (bool) + /// + Bit, + + /// + /// S7 Byte variable type (8 bits) + /// + Byte, + + /// + /// S7 Word variable type (16 bits, 2 bytes) + /// + Word, + + /// + /// S7 DWord variable type (32 bits, 4 bytes) + /// + DWord, + + /// + /// S7 Int variable type (16 bits, 2 bytes) + /// + Int, + + /// + /// DInt variable type (32 bits, 4 bytes) + /// + DInt, + + /// + /// Real variable type (32 bits, 4 bytes) + /// + Real, + + /// + /// LReal variable type (64 bits, 8 bytes) + /// + LReal, + + /// + /// Char Array / C-String variable type (variable) + /// + String, + + /// + /// S7 String variable type (variable) + /// + S7String, + + /// + /// S7 WString variable type (variable) + /// + S7WString, + + /// + /// Timer variable type + /// + Timer, + + /// + /// Counter variable type + /// + Counter, + + /// + /// DateTIme variable type + /// + DateTime, + + /// + /// DateTimeLong variable type + /// + DateTimeLong + } +} diff --git a/src/Khd.Core.Plc/S7/Help/MemoryStreamExtension.cs b/src/Khd.Core.Plc/S7/Help/MemoryStreamExtension.cs new file mode 100644 index 0000000..60eceeb --- /dev/null +++ b/src/Khd.Core.Plc/S7/Help/MemoryStreamExtension.cs @@ -0,0 +1,18 @@ + +namespace Khd.Core.Plc.S7.Helper +{ + internal static class MemoryStreamExtension + { + /// + /// Helper function to write to whole content of the given byte array to a memory stream. + /// + /// Writes all bytes in value from 0 to value.Length to the memory stream. + /// + /// + /// + public static void WriteByteArray(this System.IO.MemoryStream stream, byte[] value) + { + stream.Write(value, 0, value.Length); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Internal/TaskQueue.cs b/src/Khd.Core.Plc/S7/Internal/TaskQueue.cs new file mode 100644 index 0000000..2394fdf --- /dev/null +++ b/src/Khd.Core.Plc/S7/Internal/TaskQueue.cs @@ -0,0 +1,24 @@ +namespace Khd.Core.Plc.S7.Internal +{ + internal class TaskQueue + { + private static readonly object Sentinel = new object(); + + private Task prev = Task.FromResult(Sentinel); + + public async Task Enqueue(Func> action) + { + var tcs = new TaskCompletionSource(); + await Interlocked.Exchange(ref prev, tcs.Task).ConfigureAwait(false); + + try + { + return await action.Invoke().ConfigureAwait(false); + } + finally + { + tcs.SetResult(Sentinel); + } + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/InvalidDataException.cs b/src/Khd.Core.Plc/S7/InvalidDataException.cs new file mode 100644 index 0000000..5d03e7e --- /dev/null +++ b/src/Khd.Core.Plc/S7/InvalidDataException.cs @@ -0,0 +1,41 @@ +namespace Khd.Core.Plc.S7 +{ +#if NET_FULL + [Serializable] +#endif + public class InvalidDataException : Exception + { + public byte[] ReceivedData { get; } + public int ErrorIndex { get; } + public byte ExpectedValue { get; } + + public InvalidDataException(string message, byte[] receivedData, int errorIndex, byte expectedValue) + : base(FormatMessage(message, receivedData, errorIndex, expectedValue)) + { + ReceivedData = receivedData; + ErrorIndex = errorIndex; + ExpectedValue = expectedValue; + } + +#if NET_FULL + protected InvalidDataException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) + { + ReceivedData = (byte[]) info.GetValue(nameof(ReceivedData), typeof(byte[])); + ErrorIndex = info.GetInt32(nameof(ErrorIndex)); + ExpectedValue = info.GetByte(nameof(ExpectedValue)); + } +#endif + + private static string FormatMessage(string message, byte[] receivedData, int errorIndex, byte expectedValue) + { + if (errorIndex >= receivedData.Length) + throw new ArgumentOutOfRangeException(nameof(errorIndex), + $"{nameof(errorIndex)} {errorIndex} is outside the bounds of {nameof(receivedData)} with length {receivedData.Length}."); + + return $"{message} Invalid data received. Expected '{expectedValue}' at index {errorIndex}, " + + $"but received {receivedData[errorIndex]}. See the {nameof(ReceivedData)} property " + + "for the full message received."; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/PLC.cs b/src/Khd.Core.Plc/S7/PLC.cs new file mode 100644 index 0000000..15c8909 --- /dev/null +++ b/src/Khd.Core.Plc/S7/PLC.cs @@ -0,0 +1,326 @@ +using Khd.Core.Plc.S7.Internal; +using Khd.Core.Plc.S7.Protocol; +using Khd.Core.Plc.S7.Types; +using System.Net.Sockets; + + +namespace Khd.Core.Plc.S7 +{ + /// + /// Creates an instance of S7.Net driver + /// + public partial class Plc : IDisposable + { + /// + /// The default port for the S7 protocol. + /// + public const int DefaultPort = 102; + + /// + /// The default timeout (in milliseconds) used for and . + /// + public const int DefaultTimeout = 10_000; + + private readonly TaskQueue queue = new TaskQueue(); + + //TCP connection to device + private TcpClient? tcpClient; + private NetworkStream? _stream; + + private int readTimeout = DefaultTimeout; // default no timeout + private int writeTimeout = DefaultTimeout; // default no timeout + + /// + /// IP address of the PLC + /// + public string IP { get; } + + /// + /// PORT Number of the PLC, default is 102 + /// + public int Port { get; } + + /// + /// The TSAP addresses used during the connection request. + /// + public TsapPair TsapPair { get; set; } + + /// + /// CPU type of the PLC + /// + public CpuType CPU { get; } + + /// + /// Rack of the PLC + /// + public Int16 Rack { get; } + + /// + /// Slot of the CPU of the PLC + /// + public Int16 Slot { get; } + + /// + /// Max PDU size this cpu supports + /// + public int MaxPDUSize { get; private set; } + + /// Gets or sets the amount of time that a read operation blocks waiting for data from PLC. + /// A that specifies the amount of time, in milliseconds, that will elapse before a read operation fails. The default value, , specifies that the read operation does not time out. + public int ReadTimeout + { + get => readTimeout; + set + { + readTimeout = value; + if (tcpClient != null) tcpClient.ReceiveTimeout = readTimeout; + } + } + + /// Gets or sets the amount of time that a write operation blocks waiting for data to PLC. + /// A that specifies the amount of time, in milliseconds, that will elapse before a write operation fails. The default value, , specifies that the write operation does not time out. + public int WriteTimeout + { + get => writeTimeout; + set + { + writeTimeout = value; + if (tcpClient != null) tcpClient.SendTimeout = writeTimeout; + } + } + + /// + /// Gets a value indicating whether a connection to the PLC has been established. + /// + /// + /// The property gets the connection state of the Client socket as + /// of the last I/O operation. When it returns false, the Client socket was either + /// never connected, or is no longer connected. + /// + /// + /// Because the property only reflects the state of the connection + /// as of the most recent operation, you should attempt to send or receive a message to + /// determine the current state. After the message send fails, this property no longer + /// returns true. Note that this behavior is by design. You cannot reliably test the + /// state of the connection because, in the time between the test and a send/receive, the + /// connection could have been lost. Your code should assume the socket is connected, and + /// gracefully handle failed transmissions. + /// + /// + public bool IsConnected => tcpClient?.Connected ?? false; + + /// + /// Creates a PLC object with all the parameters needed for connections. + /// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0. + /// You need slot > 0 if you are connecting to external ethernet card (CP). + /// For S7-300 and S7-400 the default is rack = 0 and slot = 2. + /// + /// CpuType of the PLC (select from the enum) + /// Ip address of the PLC + /// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal + /// slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500. + /// If you use an external ethernet card, this must be set accordingly. + public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot) + : this(cpu, ip, DefaultPort, rack, slot) + { + } + + /// + /// Creates a PLC object with all the parameters needed for connections. + /// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0. + /// You need slot > 0 if you are connecting to external ethernet card (CP). + /// For S7-300 and S7-400 the default is rack = 0 and slot = 2. + /// + /// CpuType of the PLC (select from the enum) + /// Ip address of the PLC + /// Port number used for the connection, default 102. + /// rack of the PLC, usually it's 0, but check in the hardware configuration of Step7 or TIA portal + /// slot of the CPU of the PLC, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500. + /// If you use an external ethernet card, this must be set accordingly. + public Plc(CpuType cpu, string ip, int port, Int16 rack, Int16 slot) + : this(ip, port, TsapPair.GetDefaultTsapPair(cpu, rack, slot)) + { + if (!Enum.IsDefined(typeof(CpuType), cpu)) + throw new ArgumentException( + $"The value of argument '{nameof(cpu)}' ({cpu}) is invalid for Enum type '{typeof(CpuType).Name}'.", + nameof(cpu)); + + CPU = cpu; + Rack = rack; + Slot = slot; + } + + /// + /// Creates a PLC object with all the parameters needed for connections. + /// For S7-1200 and S7-1500, the default is rack = 0 and slot = 0. + /// You need slot > 0 if you are connecting to external ethernet card (CP). + /// For S7-300 and S7-400 the default is rack = 0 and slot = 2. + /// + /// Ip address of the PLC + /// The TSAP addresses used for the connection request. + public Plc(string ip, TsapPair tsapPair) : this(ip, DefaultPort, tsapPair) + { + } + + /// + /// Creates a PLC object with all the parameters needed for connections. Use this constructor + /// if you want to manually override the TSAP addresses used during the connection request. + /// + /// Ip address of the PLC + /// Port number used for the connection, default 102. + /// The TSAP addresses used for the connection request. + public Plc(string ip, int port, TsapPair tsapPair) + { + if (string.IsNullOrEmpty(ip)) + throw new ArgumentException("IP address must valid.", nameof(ip)); + + IP = ip; + Port = port; + MaxPDUSize = 240; + TsapPair = tsapPair; + } + + /// + /// Close connection to PLC + /// + public void Close() + { + if (tcpClient != null) + { + if (tcpClient.Connected) tcpClient.Close(); + tcpClient = null; // Can not reuse TcpClient once connection gets closed. + } + } + + private void AssertPduSizeForRead(ICollection dataItems) + { + // send request limit: 19 bytes of header data, 12 bytes of parameter data for each dataItem + var requiredRequestSize = 19 + dataItems.Count * 12; + if (requiredRequestSize > MaxPDUSize) throw new Exception($"Too many vars requested for read. Request size ({requiredRequestSize}) is larger than protocol limit ({MaxPDUSize})."); + + // response limit: 14 bytes of header data, 4 bytes of result data for each dataItem and the actual data + var requiredResponseSize = GetDataLength(dataItems) + dataItems.Count * 4 + 14; + if (requiredResponseSize > MaxPDUSize) throw new Exception($"Too much data requested for read. Response size ({requiredResponseSize}) is larger than protocol limit ({MaxPDUSize})."); + } + + private void AssertPduSizeForWrite(ICollection dataItems) + { + // 12 bytes of header data, 18 bytes of parameter data for each dataItem + if (dataItems.Count * 18 + 12 > MaxPDUSize) throw new Exception("Too many vars supplied for write"); + + // 12 bytes of header data, 16 bytes of data for each dataItem and the actual data + if (GetDataLength(dataItems) + dataItems.Count * 16 + 12 > MaxPDUSize) + throw new Exception("Too much data supplied for write"); + } + + private void ConfigureConnection() + { + if (tcpClient == null) + { + return; + } + + tcpClient.ReceiveTimeout = ReadTimeout; + tcpClient.SendTimeout = WriteTimeout; + } + + private int GetDataLength(IEnumerable dataItems) + { + // Odd length variables are 0-padded + return dataItems.Select(di => VarTypeToByteLength(di.VarType, di.Count)) + .Sum(len => (len & 1) == 1 ? len + 1 : len); + } + + private static void AssertReadResponse(byte[] s7Data, int dataLength) + { + var expectedLength = dataLength + 18; + + PlcException NotEnoughBytes() => + new PlcException(ErrorCode.WrongNumberReceivedBytes, + $"Received {s7Data.Length} bytes: '{BitConverter.ToString(s7Data)}', expected {expectedLength} bytes.") + ; + + if (s7Data == null) + throw new PlcException(ErrorCode.WrongNumberReceivedBytes, "No s7Data received."); + + if (s7Data.Length < 15) throw NotEnoughBytes(); + + ValidateResponseCode((ReadWriteErrorCode)s7Data[14]); + + if (s7Data.Length < expectedLength) throw NotEnoughBytes(); + } + + internal static void ValidateResponseCode(ReadWriteErrorCode statusCode) + { + switch (statusCode) + { + case ReadWriteErrorCode.ObjectDoesNotExist: + throw new Exception("Received error from PLC: Object does not exist."); + case ReadWriteErrorCode.DataTypeInconsistent: + throw new Exception("Received error from PLC: Data type inconsistent."); + case ReadWriteErrorCode.DataTypeNotSupported: + throw new Exception("Received error from PLC: Data type not supported."); + case ReadWriteErrorCode.AccessingObjectNotAllowed: + throw new Exception("Received error from PLC: Accessing object not allowed."); + case ReadWriteErrorCode.AddressOutOfRange: + throw new Exception("Received error from PLC: Address out of range."); + case ReadWriteErrorCode.HardwareFault: + throw new Exception("Received error from PLC: Hardware fault."); + case ReadWriteErrorCode.Success: + break; + default: + throw new Exception($"Invalid response from PLC: statusCode={(byte)statusCode}."); + } + } + + private Stream GetStreamIfAvailable() + { + if (_stream == null) + { + throw new PlcException(ErrorCode.ConnectionError, "Plc is not connected"); + } + + return _stream; + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + /// + /// Dispose Plc Object + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + Close(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~Plc() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + void IDisposable.Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + + } +} diff --git a/src/Khd.Core.Plc/S7/PLCAddress.cs b/src/Khd.Core.Plc/S7/PLCAddress.cs new file mode 100644 index 0000000..7c2bad1 --- /dev/null +++ b/src/Khd.Core.Plc/S7/PLCAddress.cs @@ -0,0 +1,207 @@ +namespace Khd.Core.Plc.S7 +{ + internal class PLCAddress + { + private DataType dataType; + private int dbNumber; + private int startByte; + private int bitNumber; + private VarType varType; + + public DataType DataType + { + get => dataType; + set => dataType = value; + } + + public int DbNumber + { + get => dbNumber; + set => dbNumber = value; + } + + public int StartByte + { + get => startByte; + set => startByte = value; + } + + public int BitNumber + { + get => bitNumber; + set => bitNumber = value; + } + + public VarType VarType + { + get => varType; + set => varType = value; + } + + public PLCAddress(string address) + { + Parse(address, out dataType, out dbNumber, out varType, out startByte, out bitNumber); + } + + public static void Parse(string input, out DataType dataType, out int dbNumber, out VarType varType, out int address, out int bitNumber) + { + bitNumber = -1; + dbNumber = 0; + + switch (input.Substring(0, 2)) + { + case "DB": + string[] strings = input.Split(new char[] { '.' }); + if (strings.Length < 2) + throw new InvalidAddressException("To few periods for DB address"); + + dataType = DataType.DataBlock; + dbNumber = int.Parse(strings[0].Substring(2)); + address = int.Parse(strings[1].Substring(3)); + + string dbType = strings[1].Substring(0, 3); + switch (dbType) + { + case "DBB": + varType = VarType.Byte; + return; + case "DBW": + varType = VarType.Word; + return; + case "DBD": + varType = VarType.DWord; + return; + case "DBX": + bitNumber = int.Parse(strings[2]); + if (bitNumber > 7) + throw new InvalidAddressException("Bit can only be 0-7"); + varType = VarType.Bit; + return; + default: + throw new InvalidAddressException(); + } + case "IB": + case "EB": + // Input byte + dataType = DataType.Input; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Byte; + return; + case "IW": + case "EW": + // Input word + dataType = DataType.Input; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Word; + return; + case "ID": + case "ED": + // Input double-word + dataType = DataType.Input; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.DWord; + return; + case "QB": + case "AB": + case "OB": + // Output byte + dataType = DataType.Output; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Byte; + return; + case "QW": + case "AW": + case "OW": + // Output word + dataType = DataType.Output; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Word; + return; + case "QD": + case "AD": + case "OD": + // Output double-word + dataType = DataType.Output; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.DWord; + return; + case "MB": + // Memory byte + dataType = DataType.Memory; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Byte; + return; + case "MW": + // Memory word + dataType = DataType.Memory; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.Word; + return; + case "MD": + // Memory double-word + dataType = DataType.Memory; + dbNumber = 0; + address = int.Parse(input.Substring(2)); + varType = VarType.DWord; + return; + default: + switch (input.Substring(0, 1)) + { + case "E": + case "I": + // Input + dataType = DataType.Input; + varType = VarType.Bit; + break; + case "Q": + case "A": + case "O": + // Output + dataType = DataType.Output; + varType = VarType.Bit; + break; + case "M": + // Memory + dataType = DataType.Memory; + varType = VarType.Bit; + break; + case "T": + // Timer + dataType = DataType.Timer; + dbNumber = 0; + address = int.Parse(input.Substring(1)); + varType = VarType.Timer; + return; + case "Z": + case "C": + // Counter + dataType = DataType.Counter; + dbNumber = 0; + address = int.Parse(input.Substring(1)); + varType = VarType.Counter; + return; + default: + throw new InvalidAddressException(string.Format("{0} is not a valid address", input.Substring(0, 1))); + } + + string txt2 = input.Substring(1); + if (txt2.IndexOf(".") == -1) + throw new InvalidAddressException("To few periods for DB address"); + + address = int.Parse(txt2.Substring(0, txt2.IndexOf("."))); + bitNumber = int.Parse(txt2.Substring(txt2.IndexOf(".") + 1)); + if (bitNumber > 7) + throw new InvalidAddressException("Bit can only be 0-7"); + return; + } + } + } +} diff --git a/src/Khd.Core.Plc/S7/PLCExceptions.cs b/src/Khd.Core.Plc/S7/PLCExceptions.cs new file mode 100644 index 0000000..eb21fa8 --- /dev/null +++ b/src/Khd.Core.Plc/S7/PLCExceptions.cs @@ -0,0 +1,112 @@ +#if NET_FULL +using System.Runtime.Serialization; +#endif + + +namespace Khd.Core.Plc.S7 +{ + internal class WrongNumberOfBytesException : Exception + { + public WrongNumberOfBytesException() : base() + { + } + + public WrongNumberOfBytesException(string message) : base(message) + { + } + + public WrongNumberOfBytesException(string message, Exception innerException) : base(message, innerException) + { + } + +#if NET_FULL + protected WrongNumberOfBytesException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + internal class InvalidAddressException : Exception + { + public InvalidAddressException() : base() + { + } + + public InvalidAddressException(string message) : base(message) + { + } + + public InvalidAddressException(string message, Exception innerException) : base(message, innerException) + { + } + +#if NET_FULL + protected InvalidAddressException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + internal class InvalidVariableTypeException : Exception + { + public InvalidVariableTypeException() : base() + { + } + + public InvalidVariableTypeException(string message) : base(message) + { + } + + public InvalidVariableTypeException(string message, Exception innerException) : base(message, innerException) + { + } + +#if NET_FULL + protected InvalidVariableTypeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + internal class TPKTInvalidException : Exception + { + public TPKTInvalidException() : base() + { + } + + public TPKTInvalidException(string message) : base(message) + { + } + + public TPKTInvalidException(string message, Exception innerException) : base(message, innerException) + { + } + +#if NET_FULL + protected TPKTInvalidException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } + + internal class TPDUInvalidException : Exception + { + public TPDUInvalidException() : base() + { + } + + public TPDUInvalidException(string message) : base(message) + { + } + + public TPDUInvalidException(string message, Exception innerException) : base(message, innerException) + { + } + +#if NET_FULL + protected TPDUInvalidException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } +#endif + } +} diff --git a/src/Khd.Core.Plc/S7/PLCHelpers.cs b/src/Khd.Core.Plc/S7/PLCHelpers.cs new file mode 100644 index 0000000..906161b --- /dev/null +++ b/src/Khd.Core.Plc/S7/PLCHelpers.cs @@ -0,0 +1,264 @@ +using Khd.Core.Plc.S7.Helper; +using Khd.Core.Plc.S7.Protocol.S7; +using Khd.Core.Plc.S7.Types; +using DateTime = Khd.Core.Plc.S7.Types.DateTime; +using Timer = Khd.Core.Plc.S7.Types.Timer; + +namespace Khd.Core.Plc.S7 +{ + public partial class Plc + { + /// + /// Creates the header to read bytes from the PLC + /// + /// + /// + private static void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1) + { + //header size = 19 bytes + stream.WriteByteArray(new byte[] { 0x03, 0x00 }); + //complete package size + stream.WriteByteArray(Types.Int.ToByteArray((short)(19 + (12 * amount)))); + stream.WriteByteArray(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 }); + //data part size + stream.WriteByteArray(Types.Word.ToByteArray((ushort)(2 + (amount * 12)))); + stream.WriteByteArray(new byte[] { 0x00, 0x00, 0x04 }); + //amount of requests + stream.WriteByte((byte)amount); + } + + /// + /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType), + /// the address of the memory, the address of the byte and the bytes count. + /// + /// MemoryType (DB, Timer, Counter, etc.) + /// Address of the memory to be read + /// Start address of the byte + /// Number of bytes to be read + /// + private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, DataType dataType, int db, int startByteAdr, int count = 1) + { + //single data req = 12 + stream.WriteByteArray(new byte[] { 0x12, 0x0a, 0x10 }); + switch (dataType) + { + case DataType.Timer: + case DataType.Counter: + stream.WriteByte((byte)dataType); + break; + default: + stream.WriteByte(0x02); + break; + } + + stream.WriteByteArray(Word.ToByteArray((ushort)(count))); + stream.WriteByteArray(Word.ToByteArray((ushort)(db))); + stream.WriteByte((byte)dataType); + var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 + stream.WriteByte((byte)overflow); + switch (dataType) + { + case DataType.Timer: + case DataType.Counter: + stream.WriteByteArray(Types.Word.ToByteArray((ushort)(startByteAdr))); + break; + default: + stream.WriteByteArray(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); + break; + } + } + + /// + /// Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format. + /// + /// + /// + /// + /// + /// + private object? ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0) + { + if (bytes == null || bytes.Length == 0) + return null; + + switch (varType) + { + case VarType.Byte: + if (varCount == 1) + return bytes[0]; + else + return bytes; + case VarType.Word: + if (varCount == 1) + return Word.FromByteArray(bytes); + else + return Word.ToArray(bytes); + case VarType.Int: + if (varCount == 1) + return Int.FromByteArray(bytes); + else + return Int.ToArray(bytes); + case VarType.DWord: + if (varCount == 1) + return DWord.FromByteArray(bytes); + else + return DWord.ToArray(bytes); + case VarType.DInt: + if (varCount == 1) + return DInt.FromByteArray(bytes); + else + return DInt.ToArray(bytes); + case VarType.Real: + if (varCount == 1) + return Types.Real.FromByteArray(bytes); + else + return Types.Real.ToArray(bytes); + case VarType.LReal: + if (varCount == 1) + return Types.LReal.FromByteArray(bytes); + else + return Types.LReal.ToArray(bytes); + + case VarType.String: + return Types.String.FromByteArray(bytes); + case VarType.S7String: + return S7String.FromByteArray(bytes); + case VarType.S7WString: + return S7WString.FromByteArray(bytes); + + case VarType.Timer: + if (varCount == 1) + return Timer.FromByteArray(bytes); + else + return Timer.ToArray(bytes); + case VarType.Counter: + if (varCount == 1) + return Counter.FromByteArray(bytes); + else + return Counter.ToArray(bytes); + case VarType.Bit: + if (varCount == 1) + { + if (bitAdr > 7) + return null; + else + return Bit.FromByte(bytes[0], bitAdr); + } + else + { + return Bit.ToBitArray(bytes, varCount); + } + case VarType.DateTime: + if (varCount == 1) + { + return DateTime.FromByteArray(bytes); + } + else + { + return DateTime.ToArray(bytes); + } + case VarType.DateTimeLong: + if (varCount == 1) + { + return DateTimeLong.FromByteArray(bytes); + } + else + { + return DateTimeLong.ToArray(bytes); + } + default: + return null; + } + } + + /// + /// Given a S7 (Bool, Word, DWord, etc.), it returns how many bytes to read. + /// + /// + /// + /// Byte lenght of variable + internal static int VarTypeToByteLength(VarType varType, int varCount = 1) + { + switch (varType) + { + case VarType.Bit: + return (varCount + 7) / 8; + case VarType.Byte: + return (varCount < 1) ? 1 : varCount; + case VarType.String: + return varCount; + case VarType.S7String: + return ((varCount + 2) & 1) == 1 ? (varCount + 3) : (varCount + 2); + case VarType.S7WString: + return (varCount * 2) + 4; + case VarType.Word: + case VarType.Timer: + case VarType.Int: + case VarType.Counter: + return varCount * 2; + case VarType.DWord: + case VarType.DInt: + case VarType.Real: + return varCount * 4; + case VarType.LReal: + case VarType.DateTime: + return varCount * 8; + case VarType.DateTimeLong: + return varCount * 12; + default: + return 0; + } + } + + private byte[] GetS7ConnectionSetup() + { + return new byte[] { 3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3, + 3, 192 // Use 960 PDU size + }; + } + + private void ParseDataIntoDataItems(byte[] s7data, List dataItems) + { + int offset = 14; + foreach (var dataItem in dataItems) + { + // check for Return Code = Success + if (s7data[offset] != 0xff) + throw new PlcException(ErrorCode.WrongNumberReceivedBytes); + + // to Data bytes + offset += 4; + + int byteCnt = VarTypeToByteLength(dataItem.VarType, dataItem.Count); + dataItem.Value = ParseBytes( + dataItem.VarType, + s7data.Skip(offset).Take(byteCnt).ToArray(), + dataItem.Count, + dataItem.BitAdr + ); + + // next Item + offset += byteCnt; + + // Always align to even offset + if (offset % 2 != 0) + offset++; + } + } + + private static byte[] BuildReadRequestPackage(IList dataItems) + { + int packageSize = 19 + (dataItems.Count * 12); + var package = new System.IO.MemoryStream(packageSize); + + BuildHeaderPackage(package, dataItems.Count); + + foreach (var dataItem in dataItems) + { + BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAddress, dataItem.ByteLength); + } + + return package.ToArray(); + } + } +} diff --git a/src/Khd.Core.Plc/S7/PlcAsynchronous.cs b/src/Khd.Core.Plc/S7/PlcAsynchronous.cs new file mode 100644 index 0000000..53812df --- /dev/null +++ b/src/Khd.Core.Plc/S7/PlcAsynchronous.cs @@ -0,0 +1,560 @@ +using Khd.Core.Plc.S7.Protocol; +using Khd.Core.Plc.S7.Protocol.S7; +using Khd.Core.Plc.S7.Types; +using System.Net.Sockets; + +namespace Khd.Core.Plc.S7 +{ + /// + /// Creates an instance of S7.Net driver + /// + public partial class Plc + { + /// + /// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup. + /// + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that the cancellation will not affect opening the socket in any way and only affects data transfers for configuring the connection after the socket connection is successfully established. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous open operation. + public async Task OpenAsync(CancellationToken cancellationToken = default) + { + var stream = await ConnectAsync().ConfigureAwait(false); + try + { + await queue.Enqueue(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + await EstablishConnection(stream, cancellationToken).ConfigureAwait(false); + _stream = stream; + + return default(object); + }).ConfigureAwait(false); + } + catch (Exception) + { + stream.Dispose(); + throw; + } + } + + private async Task ConnectAsync() + { + tcpClient = new TcpClient(); + ConfigureConnection(); + await tcpClient.ConnectAsync(IP, Port).ConfigureAwait(false); + return tcpClient.GetStream(); + } + + private async Task EstablishConnection(Stream stream, CancellationToken cancellationToken) + { + await RequestConnection(stream, cancellationToken).ConfigureAwait(false); + await SetupConnection(stream, cancellationToken).ConfigureAwait(false); + } + + private async Task RequestConnection(Stream stream, CancellationToken cancellationToken) + { + var requestData = ConnectionRequest.GetCOTPConnectionRequest(TsapPair); + var response = await NoLockRequestTpduAsync(stream, requestData, cancellationToken).ConfigureAwait(false); + + if (response.PDUType != COTP.PduType.ConnectionConfirmed) + { + throw new InvalidDataException("Connection request was denied", response.TPkt.Data, 1, 0x0d); + } + } + + private async Task SetupConnection(Stream stream, CancellationToken cancellationToken) + { + var setupData = GetS7ConnectionSetup(); + + var s7data = await NoLockRequestTsduAsync(stream, setupData, 0, setupData.Length, cancellationToken) + .ConfigureAwait(false); + + if (s7data.Length < 2) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); + + //Check for S7 Ack Data + if (s7data[1] != 0x03) + throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03); + + if (s7data.Length < 20) + throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup"); + + // TODO: check if this should not rather be UInt16. + MaxPDUSize = s7data[18] * 256 + s7data[19]; + } + + + /// + /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Byte count, if you want to read 120 bytes, set this to 120. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// Returns the bytes in an array + public async Task ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count, CancellationToken cancellationToken = default) + { + var resultBytes = new byte[count]; + int index = 0; + while (count > 0) + { + //This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0. + var maxToRead = Math.Min(count, MaxPDUSize - 18); + await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead, cancellationToken).ConfigureAwait(false); + count -= maxToRead; + index += maxToRead; + } + return resultBytes; + } + + /// + /// Read and decode a certain number of bytes of the "VarType" provided. + /// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc). + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Type of the variable/s that you are reading + /// Address of bit. If you want to read DB1.DBX200.6, set 6 to this parameter. + /// + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + public async Task ReadAsync(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0, CancellationToken cancellationToken = default) + { + int cntBytes = VarTypeToByteLength(varType, varCount); + byte[] bytes = await ReadBytesAsync(dataType, db, startByteAdr, cntBytes, cancellationToken).ConfigureAwait(false); + return ParseBytes(varType, bytes, varCount, bitAdr); + } + + /// + /// Reads a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// Returns an object that contains the value. This object must be cast accordingly. + public async Task ReadAsync(string variable, CancellationToken cancellationToken = default) + { + var adr = new PLCAddress(variable); + return await ReadAsync(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber, cancellationToken).ConfigureAwait(false); + } + + /// + /// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct. + /// + /// Type of the struct to be readed (es.: TypeOf(MyStruct)). + /// Address of the DB. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// Returns a struct that must be cast. + public async Task ReadStructAsync(Type structType, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) + { + int numBytes = Types.Struct.GetStructSize(structType); + // now read the package + var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken).ConfigureAwait(false); + + // and decode it + return Types.Struct.FromBytes(structType, resultBytes); + } + + /// + /// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read. + /// + /// The struct type + /// Address of the DB. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// Returns a nulable struct. If nothing was read null will be returned. + public async Task ReadStructAsync(int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : struct + { + return await ReadStructAsync(typeof(T), db, startByteAdr, cancellationToken).ConfigureAwait(false) as T?; + } + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. + /// + /// Instance of the class that will store the values + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// The number of read bytes + public async Task> ReadClassAsync(object sourceClass, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) + { + int numBytes = (int)Class.GetClassSize(sourceClass); + if (numBytes <= 0) + { + throw new Exception("The size of the class is less than 1 byte and therefore cannot be read"); + } + + // now read the package + var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken).ConfigureAwait(false); + // and decode it + Class.FromBytes(sourceClass, resultBytes); + + return new Tuple(resultBytes.Length, sourceClass); + } + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic + /// type, the class needs a default constructor. + /// + /// The class that will be instantiated. Requires a default constructor + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// An instance of the class with the values read from the PLC. If no data has been read, null will be returned + public async Task ReadClassAsync(int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : class + { + return await ReadClassAsync(() => Activator.CreateInstance(), db, startByteAdr, cancellationToken).ConfigureAwait(false); + } + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. + /// + /// The class that will be instantiated + /// Function to instantiate the class + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// An instance of the class with the values read from the PLC. If no data has been read, null will be returned + public async Task ReadClassAsync(Func classFactory, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : class + { + var instance = classFactory(); + var res = await ReadClassAsync(instance, db, startByteAdr, cancellationToken).ConfigureAwait(false); + int readBytes = res.Item1; + if (readBytes <= 0) + { + return null; + } + + return (T)res.Item2; + } + + /// + /// Reads multiple vars in a single request. + /// You have to create and pass a list of DataItems and you obtain in response the same list with the values. + /// Values are stored in the property "Value" of the dataItem and are already converted. + /// If you don't want the conversion, just create a dataItem of bytes. + /// The number of DataItems as well as the total size of the requested data can not exceed a certain limit (protocol restriction). + /// + /// List of dataitems that contains the list of variables that must be read. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + public async Task> ReadMultipleVarsAsync(List dataItems, CancellationToken cancellationToken = default) + { + //Snap7 seems to choke on PDU sizes above 256 even if snap7 + //replies with bigger PDU size in connection setup. + AssertPduSizeForRead(dataItems); + + try + { + var dataToSend = BuildReadRequestPackage(dataItems.Select(d => DataItem.GetDataItemAddress(d)).ToList()); + var s7data = await RequestTsduAsync(dataToSend, cancellationToken); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + + ParseDataIntoDataItems(s7data, dataItems); + } + catch (SocketException socketException) + { + throw new PlcException(ErrorCode.ReadData, socketException); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.ReadData, exc); + } + return dataItems; + } + + + /// + /// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. + /// If the write was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// Bytes to write. If more than 200, multiple requests will be made. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value, CancellationToken cancellationToken = default) + { + int localIndex = 0; + int count = value.Length; + while (count > 0) + { + var maxToWrite = (int)Math.Min(count, MaxPDUSize - 35); + await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite, cancellationToken).ConfigureAwait(false); + count -= maxToWrite; + localIndex += maxToWrite; + } + } + + /// + /// Write a single bit from a DB with the specified index. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// The address of the bit. (0-7) + /// Bytes to write. If more than 200, multiple requests will be made. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool value, CancellationToken cancellationToken = default) + { + if (bitAdr < 0 || bitAdr > 7) + throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr)); + + await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value, cancellationToken).ConfigureAwait(false); + } + + /// + /// Write a single bit from a DB with the specified index. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// The address of the bit. (0-7) + /// Bytes to write. If more than 200, multiple requests will be made. + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, int value, CancellationToken cancellationToken = default) + { + if (value < 0 || value > 1) + throw new ArgumentException("Value must be 0 or 1", nameof(value)); + + await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1, cancellationToken).ConfigureAwait(false); + } + + /// + /// Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type. + /// You must specify the memory area type, memory are address, byte start address and bytes count. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. + /// The address of the bit. (0-7) + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteAsync(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1, CancellationToken cancellationToken = default) + { + if (bitAdr != -1) + { + //Must be writing a bit value as bitAdr is specified + if (value is bool boolean) + { + await WriteBitAsync(dataType, db, startByteAdr, bitAdr, boolean, cancellationToken).ConfigureAwait(false); + } + else if (value is int intValue) + { + if (intValue < 0 || intValue > 7) + throw new ArgumentOutOfRangeException( + string.Format( + "Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", + bitAdr), nameof(bitAdr)); + + await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1, cancellationToken).ConfigureAwait(false); + } + else throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value)); + } + else await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value), cancellationToken).ConfigureAwait(false); + } + + /// + /// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// If the write was not successful, check or . + /// + /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// Value to be written to the PLC + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteAsync(string variable, object value, CancellationToken cancellationToken = default) + { + var adr = new PLCAddress(variable); + await WriteAsync(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber, cancellationToken).ConfigureAwait(false); + } + + /// + /// Writes a C# struct to a DB in the PLC + /// + /// The struct to be written + /// Db address + /// Start bytes on the PLC + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteStructAsync(object structValue, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) + { + var bytes = Struct.ToBytes(structValue).ToList(); + await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray(), cancellationToken).ConfigureAwait(false); + } + + /// + /// Writes a C# class to a DB in the PLC + /// + /// The class to be written + /// Db address + /// Start bytes on the PLC + /// The token to monitor for cancellation requests. The default value is None. + /// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases. + /// A task that represents the asynchronous write operation. + public async Task WriteClassAsync(object classValue, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) + { + byte[] bytes = new byte[(int)Class.GetClassSize(classValue)]; + Types.Class.ToBytes(classValue, bytes); + await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes, cancellationToken).ConfigureAwait(false); + } + + private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var dataToSend = BuildReadRequestPackage(new[] { new DataItemAddress(dataType, db, startByteAdr, count) }); + + var s7data = await RequestTsduAsync(dataToSend, cancellationToken); + AssertReadResponse(s7data, count); + + Array.Copy(s7data, 18, buffer, offset, count); + } + + /// + /// Write DataItem(s) to the PLC. Throws an exception if the response is invalid + /// or when the PLC reports errors for item(s) written. + /// + /// The DataItem(s) to write to the PLC. + /// Task that completes when response from PLC is parsed. + public async Task WriteAsync(params DataItem[] dataItems) + { + AssertPduSizeForWrite(dataItems); + + var message = new ByteArray(); + var length = S7WriteMultiple.CreateRequest(message, dataItems); + + var response = await RequestTsduAsync(message.Array, 0, length).ConfigureAwait(false); + + S7WriteMultiple.ParseResponse(response, response.Length, dataItems); + } + + /// + /// Writes up to 200 bytes to the PLC. You must specify the memory area type, memory are address, byte start address and bytes count. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. + /// A task that represents the asynchronous write operation. + private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count, CancellationToken cancellationToken) + { + try + { + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); + var s7data = await RequestTsduAsync(dataToSend, cancellationToken).ConfigureAwait(false); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.WriteData, exc); + } + } + + private async Task WriteBitWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue, CancellationToken cancellationToken) + { + try + { + var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr); + var s7data = await RequestTsduAsync(dataToSend, cancellationToken).ConfigureAwait(false); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.WriteData, exc); + } + } + + private Task RequestTsduAsync(byte[] requestData, CancellationToken cancellationToken = default) => + RequestTsduAsync(requestData, 0, requestData.Length, cancellationToken); + + private Task RequestTsduAsync(byte[] requestData, int offset, int length, CancellationToken cancellationToken = default) + { + var stream = GetStreamIfAvailable(); + + return queue.Enqueue(() => + NoLockRequestTsduAsync(stream, requestData, offset, length, cancellationToken)); + } + + private async Task NoLockRequestTpduAsync(Stream stream, byte[] requestData, + CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + using var closeOnCancellation = cancellationToken.Register(Close); + await stream.WriteAsync(requestData, 0, requestData.Length, cancellationToken).ConfigureAwait(false); + return await COTP.TPDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + } + catch (Exception exc) + { + if (exc is TPDUInvalidException || exc is TPKTInvalidException) + { + Close(); + } + + throw; + } + } + + private async Task NoLockRequestTsduAsync(Stream stream, byte[] requestData, int offset, int length, + CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + using var closeOnCancellation = cancellationToken.Register(Close); + await stream.WriteAsync(requestData, offset, length, cancellationToken).ConfigureAwait(false); + return await COTP.TSDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + } + catch (Exception exc) + { + if (exc is TPDUInvalidException || exc is TPKTInvalidException) + { + Close(); + } + + throw; + } + } + } +} diff --git a/src/Khd.Core.Plc/S7/PlcException.cs b/src/Khd.Core.Plc/S7/PlcException.cs new file mode 100644 index 0000000..0fa473c --- /dev/null +++ b/src/Khd.Core.Plc/S7/PlcException.cs @@ -0,0 +1,37 @@ +namespace Khd.Core.Plc.S7 +{ +#if NET_FULL + [Serializable] +#endif + public class PlcException : Exception + { + public ErrorCode ErrorCode { get; } + + public PlcException(ErrorCode errorCode) : this(errorCode, $"PLC communication failed with error '{errorCode}'.") + { + } + + public PlcException(ErrorCode errorCode, Exception innerException) : this(errorCode, innerException.Message, + innerException) + { + } + + public PlcException(ErrorCode errorCode, string message) : base(message) + { + ErrorCode = errorCode; + } + + public PlcException(ErrorCode errorCode, string message, Exception inner) : base(message, inner) + { + ErrorCode = errorCode; + } + +#if NET_FULL + protected PlcException(System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) + { + ErrorCode = (ErrorCode) info.GetInt32(nameof(ErrorCode)); + } +#endif + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/PlcSynchronous.cs b/src/Khd.Core.Plc/S7/PlcSynchronous.cs new file mode 100644 index 0000000..8c35dc2 --- /dev/null +++ b/src/Khd.Core.Plc/S7/PlcSynchronous.cs @@ -0,0 +1,472 @@ +using Khd.Core.Plc.S7.Helper; +using Khd.Core.Plc.S7.Protocol; +using Khd.Core.Plc.S7.Types; + +//Implement synchronous methods here +namespace Khd.Core.Plc.S7 +{ + public partial class Plc + { + /// + /// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup. + /// + public void Open() + { + try + { + OpenAsync().GetAwaiter().GetResult(); + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.ConnectionError, + $"Couldn't establish the connection to {IP}.\nMessage: {exc.Message}", exc); + } + } + + + /// + /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Byte count, if you want to read 120 bytes, set this to 120. + /// Returns the bytes in an array + public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count) + { + var result = new byte[count]; + int index = 0; + while (count > 0) + { + //This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0. + var maxToRead = Math.Min(count, MaxPDUSize - 18); + ReadBytesWithSingleRequest(dataType, db, startByteAdr + index, result, index, maxToRead); + count -= maxToRead; + index += maxToRead; + } + return result; + } + + /// + /// Read and decode a certain number of bytes of the "VarType" provided. + /// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc). + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Type of the variable/s that you are reading + /// Address of bit. If you want to read DB1.DBX200.6, set 6 to this parameter. + /// + public object? Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0) + { + int cntBytes = VarTypeToByteLength(varType, varCount); + byte[] bytes = ReadBytes(dataType, db, startByteAdr, cntBytes); + + return ParseBytes(varType, bytes, varCount, bitAdr); + } + + /// + /// Reads a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// Returns an object that contains the value. This object must be cast accordingly. If no data has been read, null will be returned + public object? Read(string variable) + { + var adr = new PLCAddress(variable); + return Read(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber); + } + + /// + /// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct. + /// + /// Type of the struct to be readed (es.: TypeOf(MyStruct)). + /// Address of the DB. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Returns a struct that must be cast. If no data has been read, null will be returned + public object? ReadStruct(Type structType, int db, int startByteAdr = 0) + { + int numBytes = Struct.GetStructSize(structType); + // now read the package + var resultBytes = ReadBytes(DataType.DataBlock, db, startByteAdr, numBytes); + + // and decode it + return Struct.FromBytes(structType, resultBytes); + } + + /// + /// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read. + /// + /// The struct type + /// Address of the DB. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Returns a nullable struct. If nothing was read null will be returned. + public T? ReadStruct(int db, int startByteAdr = 0) where T : struct + { + return ReadStruct(typeof(T), db, startByteAdr) as T?; + } + + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. + /// + /// Instance of the class that will store the values + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// The number of read bytes + public int ReadClass(object sourceClass, int db, int startByteAdr = 0) + { + int numBytes = (int)Class.GetClassSize(sourceClass); + if (numBytes <= 0) + { + throw new Exception("The size of the class is less than 1 byte and therefore cannot be read"); + } + + // now read the package + var resultBytes = ReadBytes(DataType.DataBlock, db, startByteAdr, numBytes); + // and decode it + Class.FromBytes(sourceClass, resultBytes); + return resultBytes.Length; + } + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic + /// type, the class needs a default constructor. + /// + /// The class that will be instantiated. Requires a default constructor + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// An instance of the class with the values read from the PLC. If no data has been read, null will be returned + public T? ReadClass(int db, int startByteAdr = 0) where T : class + { + return ReadClass(() => Activator.CreateInstance(), db, startByteAdr); + } + + /// + /// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC. + /// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. + /// + /// The class that will be instantiated + /// Function to instantiate the class + /// Index of the DB; es.: 1 is for DB1 + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// An instance of the class with the values read from the PLC. If no data has been read, null will be returned + public T? ReadClass(Func classFactory, int db, int startByteAdr = 0) where T : class + { + var instance = classFactory(); + int readBytes = ReadClass(instance, db, startByteAdr); + if (readBytes <= 0) + { + return null; + } + return instance; + } + + /// + /// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. + /// If the write was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// Bytes to write. If more than 200, multiple requests will be made. + public void WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value) + { + int localIndex = 0; + int count = value.Length; + while (count > 0) + { + //TODO: Figure out how to use MaxPDUSize here + //Snap7 seems to choke on PDU sizes above 256 even if snap7 + //replies with bigger PDU size in connection setup. + var maxToWrite = Math.Min(count, MaxPDUSize - 28);//TODO tested only when the MaxPDUSize is 480 + WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite); + count -= maxToWrite; + localIndex += maxToWrite; + } + } + + /// + /// Write a single bit from a DB with the specified index. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// The address of the bit. (0-7) + /// Bytes to write. If more than 200, multiple requests will be made. + public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value) + { + if (bitAdr < 0 || bitAdr > 7) + throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr)); + + WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value); + } + + /// + /// Write a single bit to a DB with the specified index. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to write DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to write DB1.DBW200, this is 200. + /// The address of the bit. (0-7) + /// Value to write (0 or 1). + public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, int value) + { + if (value < 0 || value > 1) + throw new ArgumentException("Value must be 0 or 1", nameof(value)); + + WriteBit(dataType, db, startByteAdr, bitAdr, value == 1); + } + + /// + /// Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type. + /// You must specify the memory area type, memory are address, byte start address and bytes count. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. + /// The address of the bit. (0-7) + public void Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1) + { + if (bitAdr != -1) + { + //Must be writing a bit value as bitAdr is specified + if (value is bool boolean) + { + WriteBit(dataType, db, startByteAdr, bitAdr, boolean); + } + else if (value is int intValue) + { + if (intValue < 0 || intValue > 7) + throw new ArgumentOutOfRangeException( + string.Format( + "Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", + bitAdr), nameof(bitAdr)); + + WriteBit(dataType, db, startByteAdr, bitAdr, intValue == 1); + } + else + throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value)); + } + else WriteBytes(dataType, db, startByteAdr, Serialization.SerializeValue(value)); + } + + /// + /// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// If the write was not successful, check or . + /// + /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// Value to be written to the PLC + public void Write(string variable, object value) + { + var adr = new PLCAddress(variable); + Write(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber); + } + + /// + /// Writes a C# struct to a DB in the PLC + /// + /// The struct to be written + /// Db address + /// Start bytes on the PLC + public void WriteStruct(object structValue, int db, int startByteAdr = 0) + { + WriteStructAsync(structValue, db, startByteAdr).GetAwaiter().GetResult(); + } + + /// + /// Writes a C# class to a DB in the PLC + /// + /// The class to be written + /// Db address + /// Start bytes on the PLC + public void WriteClass(object classValue, int db, int startByteAdr = 0) + { + WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult(); + } + + private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count) + { + try + { + // first create the header + int packageSize = 19 + 12; // 19 header + 12 for 1 request + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package); + // package.Add(0x02); // datenart + BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count); + + var dataToSend = package.ToArray(); + var s7data = RequestTsdu(dataToSend); + AssertReadResponse(s7data, count); + + Array.Copy(s7data, 18, buffer, offset, count); + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.ReadData, exc); + } + } + + /// + /// Write DataItem(s) to the PLC. Throws an exception if the response is invalid + /// or when the PLC reports errors for item(s) written. + /// + /// The DataItem(s) to write to the PLC. + public void Write(params DataItem[] dataItems) + { + AssertPduSizeForWrite(dataItems); + + + var message = new ByteArray(); + var length = S7WriteMultiple.CreateRequest(message, dataItems); + var response = RequestTsdu(message.Array, 0, length); + + S7WriteMultiple.ParseResponse(response, response.Length, dataItems); + } + + private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) + { + try + { + var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count); + var s7data = RequestTsdu(dataToSend); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.WriteData, exc); + } + } + + private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count) + { + int varCount = count; + // first create the header + int packageSize = 35 + varCount; + var package = new MemoryStream(new byte[packageSize]); + + package.WriteByte(3); + package.WriteByte(0); + //complete package size + package.WriteByteArray(Int.ToByteArray((short)packageSize)); + package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); + package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1))); + package.WriteByteArray(new byte[] { 0, 0x0e }); + package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4))); + package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 }); + package.WriteByteArray(Word.ToByteArray((ushort)varCount)); + package.WriteByteArray(Word.ToByteArray((ushort)(db))); + package.WriteByte((byte)dataType); + var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 + package.WriteByte((byte)overflow); + package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8))); + package.WriteByteArray(new byte[] { 0, 4 }); + package.WriteByteArray(Word.ToByteArray((ushort)(varCount * 8))); + + // now join the header and the data + package.Write(value, dataOffset, count); + + return package.ToArray(); + } + + private byte[] BuildWriteBitPackage(DataType dataType, int db, int startByteAdr, bool bitValue, int bitAdr) + { + var value = new[] { bitValue ? (byte)1 : (byte)0 }; + int varCount = 1; + // first create the header + int packageSize = 35 + varCount; + var package = new MemoryStream(new byte[packageSize]); + + package.WriteByte(3); + package.WriteByte(0); + //complete package size + package.WriteByteArray(Int.ToByteArray((short)packageSize)); + package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 }); + package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1))); + package.WriteByteArray(new byte[] { 0, 0x0e }); + package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4))); + package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit + package.WriteByteArray(Word.ToByteArray((ushort)varCount)); + package.WriteByteArray(Word.ToByteArray((ushort)(db))); + package.WriteByte((byte)dataType); + var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 + package.WriteByte((byte)overflow); + package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr))); + package.WriteByteArray(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit + package.WriteByteArray(Word.ToByteArray((ushort)(varCount))); + + // now join the header and the data + package.WriteByteArray(value); + + return package.ToArray(); + } + + + private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue) + { + try + { + var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr); + var s7data = RequestTsdu(dataToSend); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.WriteData, exc); + } + } + + /// + /// Reads multiple vars in a single request. + /// You have to create and pass a list of DataItems and you obtain in response the same list with the values. + /// Values are stored in the property "Value" of the dataItem and are already converted. + /// If you don't want the conversion, just create a dataItem of bytes. + /// The number of DataItems as well as the total size of the requested data can not exceed a certain limit (protocol restriction). + /// + /// List of dataitems that contains the list of variables that must be read. + public void ReadMultipleVars(List dataItems) + { + AssertPduSizeForRead(dataItems); + + try + { + // first create the header + int packageSize = 19 + (dataItems.Count * 12); + var package = new System.IO.MemoryStream(packageSize); + BuildHeaderPackage(package, dataItems.Count); + // package.Add(0x02); // datenart + foreach (var dataItem in dataItems) + { + BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)); + } + + var dataToSend = package.ToArray(); + var s7data = RequestTsdu(dataToSend); + + ValidateResponseCode((ReadWriteErrorCode)s7data[14]); + + ParseDataIntoDataItems(s7data, dataItems); + } + catch (Exception exc) + { + throw new PlcException(ErrorCode.ReadData, exc); + } + } + + private byte[] RequestTsdu(byte[] requestData) => RequestTsdu(requestData, 0, requestData.Length); + + private byte[] RequestTsdu(byte[] requestData, int offset, int length) + { + return RequestTsduAsync(requestData, offset, length).GetAwaiter().GetResult(); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/ConnectionRequest.cs b/src/Khd.Core.Plc/S7/Protocol/ConnectionRequest.cs new file mode 100644 index 0000000..44b7eb3 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/ConnectionRequest.cs @@ -0,0 +1,28 @@ +namespace Khd.Core.Plc.S7.Protocol +{ + internal static class ConnectionRequest + { + public static byte[] GetCOTPConnectionRequest(TsapPair tsapPair) + { + byte[] bSend1 = { + 3, 0, 0, 22, //TPKT + 17, //COTP Header Length + 224, //Connect Request + 0, 0, //Destination Reference + 0, 46, //Source Reference + 0, //Flags + 193, //Parameter Code (src-tasp) + 2, //Parameter Length + tsapPair.Local.FirstByte, tsapPair.Local.SecondByte, //Source TASP + 194, //Parameter Code (dst-tasp) + 2, //Parameter Length + tsapPair.Remote.FirstByte, tsapPair.Remote.SecondByte, //Destination TASP + 192, //Parameter Code (tpdu-size) + 1, //Parameter Length + 10 //TPDU Size (2^10 = 1024) + }; + + return bSend1; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/ReadWriteErrorCode.cs b/src/Khd.Core.Plc/S7/Protocol/ReadWriteErrorCode.cs new file mode 100644 index 0000000..9423780 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/ReadWriteErrorCode.cs @@ -0,0 +1,15 @@ + +namespace Khd.Core.Plc.S7.Protocol +{ + internal enum ReadWriteErrorCode : byte + { + Reserved = 0x00, + HardwareFault = 0x01, + AccessingObjectNotAllowed = 0x03, + AddressOutOfRange = 0x05, + DataTypeNotSupported = 0x06, + DataTypeInconsistent = 0x07, + ObjectDoesNotExist = 0x0a, + Success = 0xff + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/S7/DataItemAddress.cs b/src/Khd.Core.Plc/S7/Protocol/S7/DataItemAddress.cs new file mode 100644 index 0000000..f068c32 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/S7/DataItemAddress.cs @@ -0,0 +1,37 @@ +namespace Khd.Core.Plc.S7.Protocol.S7 +{ + /// + /// Represents an area of memory in the PLC + /// + internal class DataItemAddress + { + public DataItemAddress(DataType dataType, int db, int startByteAddress, int byteLength) + { + DataType = dataType; + DB = db; + StartByteAddress = startByteAddress; + ByteLength = byteLength; + } + + + /// + /// Memory area to read + /// + public DataType DataType { get; } + + /// + /// Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45) + /// + public int DB { get; } + + /// + /// Address of the first byte to read + /// + public int StartByteAddress { get; } + + /// + /// Length of data to read + /// + public int ByteLength { get; } + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/S7WriteMultiple.cs b/src/Khd.Core.Plc/S7/Protocol/S7WriteMultiple.cs new file mode 100644 index 0000000..ff92dc9 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/S7WriteMultiple.cs @@ -0,0 +1,162 @@ +using Khd.Core.Plc.S7.Types; + +namespace Khd.Core.Plc.S7.Protocol +{ + internal static class S7WriteMultiple + { + public static int CreateRequest(ByteArray message, DataItem[] dataItems) + { + message.Add(Header.Template); + + message[Header.Offsets.ParameterCount] = (byte)dataItems.Length; + var paramSize = dataItems.Length * Parameter.Template.Length; + + Serialization.SetWordAt(message, Header.Offsets.ParameterSize, + (ushort)(2 + paramSize)); + + var paramOffset = Header.Template.Length; + var data = new ByteArray(); + + var itemCount = 0; + + foreach (var item in dataItems) + { + itemCount++; + message.Add(Parameter.Template); + var value = Serialization.SerializeDataItem(item); + var wordLen = item.Value is bool ? 1 : 2; + + message[paramOffset + Parameter.Offsets.WordLength] = (byte)wordLen; + Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.Amount, (ushort)value.Length); + Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.DbNumber, (ushort)item.DB); + message[paramOffset + Parameter.Offsets.Area] = (byte)item.DataType; + + data.Add(0x00); + if (item.Value is bool b) + { + if (item.BitAdr > 7) + throw new ArgumentException( + $"Cannot read bit with invalid {nameof(item.BitAdr)} '{item.BitAdr}'.", nameof(dataItems)); + + Serialization.SetAddressAt(message, paramOffset + Parameter.Offsets.Address, item.StartByteAdr, + item.BitAdr); + + data.Add(0x03); + data.AddWord(1); + + data.Add(b ? (byte)1 : (byte)0); + if (itemCount != dataItems.Length) + { + data.Add(0); + } + } + else + { + Serialization.SetAddressAt(message, paramOffset + Parameter.Offsets.Address, item.StartByteAdr, 0); + + var len = value.Length; + data.Add(0x04); + data.AddWord((ushort)(len << 3)); + data.Add(value); + + if ((len & 0b1) == 1 && itemCount != dataItems.Length) + { + data.Add(0); + } + } + + paramOffset += Parameter.Template.Length; + } + + message.Add(data.Array); + + Serialization.SetWordAt(message, Header.Offsets.MessageLength, (ushort)message.Length); + Serialization.SetWordAt(message, Header.Offsets.DataLength, (ushort)(message.Length - paramOffset)); + + return message.Length; + } + + public static void ParseResponse(byte[] message, int length, DataItem[] dataItems) + { + if (length < 12) throw new Exception("Not enough data received to parse write response."); + + var messageError = Serialization.GetWordAt(message, 10); + if (messageError != 0) + throw new Exception($"Write failed with error {messageError}."); + + if (length < 14 + dataItems.Length) + throw new Exception("Not enough data received to parse individual item responses."); + + IList itemResults = new ArraySegment(message, 14, dataItems.Length); + + List? errors = null; + + for (int i = 0; i < dataItems.Length; i++) + { + try + { + Plc.ValidateResponseCode((ReadWriteErrorCode)itemResults[i]); + } + catch (Exception e) + { + if (errors == null) errors = new List(); + errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}.")); + } + + } + + if (errors != null) + throw new AggregateException( + $"Write failed for {errors.Count} items. See the innerExceptions for details.", errors); + } + + private static class Header + { + public static byte[] Template { get; } = + { + 0x03, 0x00, 0x00, 0x00, // TPKT + 0x02, 0xf0, 0x80, // ISO DT + 0x32, // S7 protocol ID + 0x01, // JobRequest + 0x00, 0x00, // Reserved + 0x05, 0x00, // PDU reference + 0x00, 0x0e, // Parameters length + 0x00, 0x00, // Data length + 0x05, // Function: Write var + 0x00, // Number of items to write + }; + + public static class Offsets + { + public const int MessageLength = 2; + public const int ParameterSize = 13; + public const int DataLength = 15; + public const int ParameterCount = 18; + } + } + + private static class Parameter + { + public static byte[] Template { get; } = + { + 0x12, // Spec + 0x0a, // Length of remaining bytes + 0x10, // Addressing mode + 0x02, // Transport size + 0x00, 0x00, // Number of elements + 0x00, 0x00, // DB number + 0x84, // Area type + 0x00, 0x00, 0x00 // Area offset + }; + + public static class Offsets + { + public const int WordLength = 3; + public const int Amount = 4; + public const int DbNumber = 6; + public const int Area = 8; + public const int Address = 9; + } + } + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/Serialization.cs b/src/Khd.Core.Plc/S7/Protocol/Serialization.cs new file mode 100644 index 0000000..f0786d5 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/Serialization.cs @@ -0,0 +1,96 @@ +using Khd.Core.Plc.S7.Types; + +namespace Khd.Core.Plc.S7.Protocol +{ + internal static class Serialization + { + public static ushort GetWordAt(IList buf, int index) + { + return (ushort)((buf[index] << 8) + buf[index]); + } + + public static byte[] SerializeDataItem(DataItem dataItem) + { + if (dataItem.Value == null) + { + throw new Exception($"DataItem.Value is null, cannot serialize. StartAddr={dataItem.StartByteAdr} VarType={dataItem.VarType}"); + } + + if (dataItem.Value is string s) + return dataItem.VarType switch + { + VarType.S7String => S7String.ToByteArray(s, dataItem.Count), + VarType.S7WString => S7WString.ToByteArray(s, dataItem.Count), + _ => Types.String.ToByteArray(s, dataItem.Count) + }; + + return SerializeValue(dataItem.Value); + } + + public static byte[] SerializeValue(object value) + { + switch (value.GetType().Name) + { + case "Boolean": + return new[] { (byte)((bool)value ? 1 : 0) }; + case "Byte": + return Types.Byte.ToByteArray((byte)value); + case "Int16": + return Types.Int.ToByteArray((Int16)value); + case "UInt16": + return Types.Word.ToByteArray((UInt16)value); + case "Int32": + return Types.DInt.ToByteArray((Int32)value); + case "UInt32": + return Types.DWord.ToByteArray((UInt32)value); + case "Single": + return Types.Real.ToByteArray((float)value); + case "Double": + return Types.LReal.ToByteArray((double)value); + case "DateTime": + return Types.DateTime.ToByteArray((System.DateTime)value); + case "Byte[]": + return (byte[])value; + case "Int16[]": + return Types.Int.ToByteArray((Int16[])value); + case "UInt16[]": + return Types.Word.ToByteArray((UInt16[])value); + case "Int32[]": + return Types.DInt.ToByteArray((Int32[])value); + case "UInt32[]": + return Types.DWord.ToByteArray((UInt32[])value); + case "Single[]": + return Types.Real.ToByteArray((float[])value); + case "Double[]": + return Types.LReal.ToByteArray((double[])value); + case "String": + // Hack: This is backwards compatible with the old code, but functionally it's broken + // if the consumer does not pay attention to string length. + var stringVal = (string)value; + return Types.String.ToByteArray(stringVal, stringVal.Length); + case "DateTime[]": + return Types.DateTime.ToByteArray((System.DateTime[])value); + case "DateTimeLong[]": + return Types.DateTimeLong.ToByteArray((System.DateTime[])value); + default: + throw new InvalidVariableTypeException(); + } + } + + public static void SetAddressAt(ByteArray buffer, int index, int startByte, byte bitNumber) + { + var start = startByte * 8 + bitNumber; + buffer[index + 2] = (byte)start; + start >>= 8; + buffer[index + 1] = (byte)start; + start >>= 8; + buffer[index] = (byte)start; + } + + public static void SetWordAt(ByteArray buffer, int index, ushort value) + { + buffer[index] = (byte)(value >> 8); + buffer[index + 1] = (byte)value; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Protocol/Tsap.cs b/src/Khd.Core.Plc/S7/Protocol/Tsap.cs new file mode 100644 index 0000000..35293b6 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/Tsap.cs @@ -0,0 +1,31 @@ +namespace Khd.Core.Plc.S7.Protocol +{ + /// + /// Provides a representation of the Transport Service Access Point, or TSAP in short. TSAP's are used + /// to specify a client and server address. For most PLC types a default TSAP is available that allows + /// connection from any IP and can be calculated using the rack and slot numbers. + /// + public struct Tsap + { + /// + /// First byte of the TSAP. + /// + public byte FirstByte { get; set; } + + /// + /// Second byte of the TSAP. + /// + public byte SecondByte { get; set; } + + /// + /// Initializes a new instance of the class using the specified values. + /// + /// The first byte of the TSAP. + /// The second byte of the TSAP. + public Tsap(byte firstByte, byte secondByte) + { + FirstByte = firstByte; + SecondByte = secondByte; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/Protocol/TsapPair.cs b/src/Khd.Core.Plc/S7/Protocol/TsapPair.cs new file mode 100644 index 0000000..dcf8365 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Protocol/TsapPair.cs @@ -0,0 +1,94 @@ +namespace Khd.Core.Plc.S7.Protocol +{ + /// + /// Implements a pair of TSAP addresses used to connect to a PLC. + /// + public class TsapPair + { + /// + /// The local . + /// + public Tsap Local { get; set; } + + /// + /// The remote + /// + public Tsap Remote { get; set; } + + /// + /// Initializes a new instance of the class using the specified local and + /// remote TSAP. + /// + /// The local TSAP. + /// The remote TSAP. + public TsapPair(Tsap local, Tsap remote) + { + Local = local; + Remote = remote; + } + + /// + /// Builds a that can be used to connect to a PLC using the default connection + /// addresses. + /// + /// + /// The remote TSAP is constructed using new Tsap(0x03, (byte) ((rack << 5) | slot)). + /// + /// The CPU type of the PLC. + /// The rack of the PLC's network card. + /// The slot of the PLC's network card. + /// A TSAP pair that matches the given parameters. + /// The is invalid. + /// + /// -or- + /// + /// The parameter is less than 0. + /// + /// -or- + /// + /// The parameter is greater than 15. + /// + /// -or- + /// + /// The parameter is less than 0. + /// + /// -or- + /// + /// The parameter is greater than 15. + public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot) + { + if (rack < 0) throw InvalidRackOrSlot(rack, nameof(rack), "minimum", 0); + if (rack > 0x0F) throw InvalidRackOrSlot(rack, nameof(rack), "maximum", 0x0F); + + if (slot < 0) throw InvalidRackOrSlot(slot, nameof(slot), "minimum", 0); + if (slot > 0x0F) throw InvalidRackOrSlot(slot, nameof(slot), "maximum", 0x0F); + + switch (cpuType) + { + case CpuType.S7200: + return new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x01)); + case CpuType.Logo0BA8: + // The actual values are probably on a per-project basis + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)); + case CpuType.S7200Smart: + case CpuType.S71200: + case CpuType.S71500: + case CpuType.S7300: + case CpuType.S7400: + // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other + // PLC types. + return new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte)((rack << 5) | slot))); + default: + throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified"); + } + } + + private static ArgumentOutOfRangeException InvalidRackOrSlot(int value, string name, string extrema, + int extremaValue) + { + return new ArgumentOutOfRangeException(name, + $"Invalid {name} value specified (decimal: {value}, hexadecimal: {value:X}), {extrema} value " + + $"is {extremaValue} (decimal) or {extremaValue:X} (hexadecimal)."); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/SiPLC.cs b/src/Khd.Core.Plc/S7/SiPLC.cs new file mode 100644 index 0000000..9d126c3 --- /dev/null +++ b/src/Khd.Core.Plc/S7/SiPLC.cs @@ -0,0 +1,63 @@ +using HslCommunication.Profinet.Siemens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RainTest.Helper +{ + public class SiPLC + { + private SiemensS7Net PLC = new SiemensS7Net(SiemensPLCS.S200Smart); + private Random dom = new Random(); + public string IP { get; set; } + public bool Open() + { + PLC.IpAddress = IP; + return PLC.ConnectServer().IsSuccess; + } + public float ReadFloat(string address) + { + //var test = PLC.ReadFloat("M0.7"); + return PLC.ReadFloat(address).Content; + } + /// + /// 用这个方法试试 + /// + /// + /// + public bool ReadBool(string address) + { + return PLC.ReadBool(address).Content; + } + public bool ReadWord(string address) + { + return PLC.ReadBool(address).Content; + } + public HslCommunication.OperateResult WriteFloat(string address,float value) + { + return PLC.Write(address,value); + } + public HslCommunication.OperateResult WriteFloat1(string address, float value) + { + float test = value / 1000; + return PLC.Write(address, test); + } + public HslCommunication.OperateResult WriteFloat(string address, bool value) + { + return PLC.Write(address, value); + } + public bool Close() + { + try + { + return PLC.ConnectClose().IsSuccess; + } + catch(Exception ex) + { + } + return true; + } + } +} diff --git a/src/Khd.Core.Plc/S7/StreamExtensions.cs b/src/Khd.Core.Plc/S7/StreamExtensions.cs new file mode 100644 index 0000000..6175e8e --- /dev/null +++ b/src/Khd.Core.Plc/S7/StreamExtensions.cs @@ -0,0 +1,52 @@ +namespace Khd.Core.Plc.S7 +{ + /// + /// Extensions for Streams + /// + public static class StreamExtensions + { + /// + /// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read + /// + /// the Stream to read from + /// the buffer to read into + /// the offset in the buffer to read into + /// the amount of bytes to read into the buffer + /// returns the amount of read bytes + public static int ReadExact(this Stream stream, byte[] buffer, int offset, int count) + { + int read = 0; + int received; + do + { + received = stream.Read(buffer, offset + read, count - read); + read += received; + } + while (read < count && received > 0); + + return read; + } + + /// + /// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read + /// + /// the Stream to read from + /// the buffer to read into + /// the offset in the buffer to read into + /// the amount of bytes to read into the buffer + /// returns the amount of read bytes + public static async Task ReadExactAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + int read = 0; + int received; + do + { + received = await stream.ReadAsync(buffer, offset + read, count - read, cancellationToken).ConfigureAwait(false); + read += received; + } + while (read < count && received > 0); + + return read; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/TPKT.cs b/src/Khd.Core.Plc/S7/TPKT.cs new file mode 100644 index 0000000..fb1e345 --- /dev/null +++ b/src/Khd.Core.Plc/S7/TPKT.cs @@ -0,0 +1,61 @@ +namespace Khd.Core.Plc.S7 +{ + + /// + /// Describes a TPKT Packet + /// + internal class TPKT + { + + + public byte Version; + public byte Reserved1; + public int Length; + public byte[] Data; + private TPKT(byte version, byte reserved1, int length, byte[] data) + { + Version = version; + Reserved1 = reserved1; + Length = length; + Data = data; + } + + /// + /// Reads a TPKT from the socket Async + /// + /// The stream to read from + /// Task TPKT Instace + public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken) + { + var buf = new byte[4]; + int len = await stream.ReadExactAsync(buf, 0, 4, cancellationToken).ConfigureAwait(false); + if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid"); + + var version = buf[0]; + var reserved1 = buf[1]; + var length = buf[2] * 256 + buf[3]; //BigEndian + + var data = new byte[length - 4]; + len = await stream.ReadExactAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false); + if (len < data.Length) + throw new TPKTInvalidException("TPKT payload incomplete / invalid"); + + return new TPKT + ( + version: version, + reserved1: reserved1, + length: length, + data: data + ); + } + + public override string ToString() + { + return string.Format("Version: {0} Length: {1} Data: {2}", + Version, + Length, + BitConverter.ToString(Data) + ); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Bit.cs b/src/Khd.Core.Plc/S7/Types/Bit.cs new file mode 100644 index 0000000..98f86f6 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Bit.cs @@ -0,0 +1,42 @@ +using System.Collections; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Bit from S7 plc to C#. + /// + public static class Bit + { + /// + /// Converts a Bit to bool + /// + public static bool FromByte(byte v, byte bitAdr) + { + return (((int)v & (1 << bitAdr)) != 0); + } + + /// + /// Converts an array of bytes to a BitArray. + /// + /// The bytes to convert. + /// A BitArray with the same number of bits and equal values as . + public static BitArray ToBitArray(byte[] bytes) => ToBitArray(bytes, bytes.Length * 8); + + /// + /// Converts an array of bytes to a BitArray. + /// + /// The bytes to convert. + /// The number of bits to return. + /// A BitArray with bits. + public static BitArray ToBitArray(byte[] bytes, int length) + { + if (length > bytes.Length * 8) throw new ArgumentException($"Not enough data in bytes to return {length} bits.", nameof(bytes)); + + var bitArr = new BitArray(bytes); + var bools = new bool[length]; + for (var i = 0; i < length; i++) bools[i] = bitArr[i]; + + return new BitArray(bools); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Boolean.cs b/src/Khd.Core.Plc/S7/Types/Boolean.cs new file mode 100644 index 0000000..cb89962 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Boolean.cs @@ -0,0 +1,64 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to read, set and reset bits inside bytes + /// + public static class Boolean + { + /// + /// Returns the value of a bit in a bit, given the address of the bit + /// + public static bool GetValue(byte value, int bit) + { + return (((int)value & (1 << bit)) != 0); + } + + /// + /// Sets the value of a bit to 1 (true), given the address of the bit. Returns + /// a copy of the value with the bit set. + /// + /// The input value to modify. + /// The index (zero based) of the bit to set. + /// The modified value with the bit at index set. + public static byte SetBit(byte value, int bit) + { + SetBit(ref value, bit); + + return value; + } + + /// + /// Sets the value of a bit to 1 (true), given the address of the bit. + /// + /// The value to modify. + /// The index (zero based) of the bit to set. + public static void SetBit(ref byte value, int bit) + { + value = (byte)((value | (1 << bit)) & 0xFF); + } + + /// + /// Resets the value of a bit to 0 (false), given the address of the bit. Returns + /// a copy of the value with the bit cleared. + /// + /// The input value to modify. + /// The index (zero based) of the bit to clear. + /// The modified value with the bit at index cleared. + public static byte ClearBit(byte value, int bit) + { + ClearBit(ref value, bit); + + return value; + } + + /// + /// Resets the value of a bit to 0 (false), given the address of the bit + /// + /// The input value to modify. + /// The index (zero based) of the bit to clear. + public static void ClearBit(ref byte value, int bit) + { + value = (byte)(value & ~(1 << bit) & 0xFF); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Byte.cs b/src/Khd.Core.Plc/S7/Types/Byte.cs new file mode 100644 index 0000000..eb4c88c --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Byte.cs @@ -0,0 +1,31 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert from bytes to byte arrays + /// + public static class Byte + { + /// + /// Converts a byte to byte array + /// + public static byte[] ToByteArray(byte value) + { + return new byte[] { value }; ; + } + + /// + /// Converts a byte array to byte + /// + /// + /// + public static byte FromByteArray(byte[] bytes) + { + if (bytes.Length != 1) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 1 bytes."); + } + return bytes[0]; + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/ByteArray.cs b/src/Khd.Core.Plc/S7/Types/ByteArray.cs new file mode 100644 index 0000000..d2c195e --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/ByteArray.cs @@ -0,0 +1,61 @@ +namespace Khd.Core.Plc.S7.Types +{ + class ByteArray + { + List list = new List(); + + public byte this[int index] + { + get => list[index]; + set => list[index] = value; + } + + public byte[] Array + { + get { return list.ToArray(); } + } + + public int Length => list.Count; + + public ByteArray() + { + list = new List(); + } + + public ByteArray(int size) + { + list = new List(size); + } + + public void Clear() + { + list = new List(); + } + + public void Add(byte item) + { + list.Add(item); + } + + public void AddWord(ushort value) + { + list.Add((byte)(value >> 8)); + list.Add((byte)value); + } + + public void Add(byte[] items) + { + list.AddRange(items); + } + + public void Add(IEnumerable items) + { + list.AddRange(items); + } + + public void Add(ByteArray byteArray) + { + list.AddRange(byteArray.Array); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Class.cs b/src/Khd.Core.Plc/S7/Types/Class.cs new file mode 100644 index 0000000..ad49a7c --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Class.cs @@ -0,0 +1,337 @@ +using System.Reflection; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert a C# class to S7 data types + /// + public static class Class + { + private static IEnumerable GetAccessableProperties(Type classType) + { + return classType +#if NETSTANDARD1_3 + .GetTypeInfo().DeclaredProperties.Where(p => p.SetMethod != null); +#else + .GetProperties( + BindingFlags.SetProperty | + BindingFlags.Public | + BindingFlags.Instance) + .Where(p => p.GetSetMethod() != null); +#endif + + } + + private static double GetIncreasedNumberOfBytes(double numBytes, Type type) + { + switch (type.Name) + { + case "Boolean": + numBytes += 0.125; + break; + case "Byte": + numBytes = Math.Ceiling(numBytes); + numBytes++; + break; + case "Int16": + case "UInt16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 2; + break; + case "Int32": + case "UInt32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 4; + break; + case "Single": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 4; + break; + case "Double": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 8; + break; + default: + var propertyClass = Activator.CreateInstance(type); + numBytes = GetClassSize(propertyClass, numBytes, true); + break; + } + + return numBytes; + } + + /// + /// Gets the size of the class in bytes. + /// + /// An instance of the class + /// the number of bytes + public static double GetClassSize(object instance, double numBytes = 0.0, bool isInnerProperty = false) + { + var properties = GetAccessableProperties(instance.GetType()); + foreach (var property in properties) + { + if (property.PropertyType.IsArray) + { + Type elementType = property.PropertyType.GetElementType(); + Array array = (Array)property.GetValue(instance, null); + if (array.Length <= 0) + { + throw new Exception("Cannot determine size of class, because an array is defined which has no fixed size greater than zero."); + } + + IncrementToEven(ref numBytes); + for (int i = 0; i < array.Length; i++) + { + numBytes = GetIncreasedNumberOfBytes(numBytes, elementType); + } + } + else + { + numBytes = GetIncreasedNumberOfBytes(numBytes, property.PropertyType); + } + } + if (false == isInnerProperty) + { + // enlarge numBytes to next even number because S7-Structs in a DB always will be resized to an even byte count + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + } + return numBytes; + } + + private static object? GetPropertyValue(Type propertyType, byte[] bytes, ref double numBytes) + { + object? value = null; + + switch (propertyType.Name) + { + case "Boolean": + // get the value + int bytePos = (int)Math.Floor(numBytes); + int bitPos = (int)((numBytes - (double)bytePos) / 0.125); + if ((bytes[bytePos] & (int)Math.Pow(2, bitPos)) != 0) + value = true; + else + value = false; + numBytes += 0.125; + break; + case "Byte": + numBytes = Math.Ceiling(numBytes); + value = (byte)(bytes[(int)numBytes]); + numBytes++; + break; + case "Int16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // hier auswerten + ushort source = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); + value = source.ConvertToShort(); + numBytes += 2; + break; + case "UInt16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // hier auswerten + value = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); + numBytes += 2; + break; + case "Int32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // hier auswerten + uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 0]); + value = sourceUInt.ConvertToInt(); + numBytes += 4; + break; + case "UInt32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // hier auswerten + value = DWord.FromBytes( + bytes[(int)numBytes], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 3]); + numBytes += 4; + break; + case "Single": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // hier auswerten + value = Real.FromByteArray( + new byte[] { + bytes[(int)numBytes], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 3] }); + numBytes += 4; + break; + case "Double": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + var buffer = new byte[8]; + Array.Copy(bytes, (int)numBytes, buffer, 0, 8); + // hier auswerten + value = LReal.FromByteArray(buffer); + numBytes += 8; + break; + default: + var propClass = Activator.CreateInstance(propertyType); + numBytes = FromBytes(propClass, bytes, numBytes); + value = propClass; + break; + } + + return value; + } + + /// + /// Sets the object's values with the given array of bytes + /// + /// The object to fill in the given array of bytes + /// The array of bytes + public static double FromBytes(object sourceClass, byte[] bytes, double numBytes = 0, bool isInnerClass = false) + { + if (bytes == null) + return numBytes; + + var properties = GetAccessableProperties(sourceClass.GetType()); + foreach (var property in properties) + { + if (property.PropertyType.IsArray) + { + Array array = (Array)property.GetValue(sourceClass, null); + IncrementToEven(ref numBytes); + Type elementType = property.PropertyType.GetElementType(); + for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) + { + array.SetValue( + GetPropertyValue(elementType, bytes, ref numBytes), + i); + } + } + else + { + property.SetValue( + sourceClass, + GetPropertyValue(property.PropertyType, bytes, ref numBytes), + null); + } + } + + return numBytes; + } + + private static double SetBytesFromProperty(object propertyValue, byte[] bytes, double numBytes) + { + int bytePos = 0; + int bitPos = 0; + byte[]? bytes2 = null; + + switch (propertyValue.GetType().Name) + { + case "Boolean": + // get the value + bytePos = (int)Math.Floor(numBytes); + bitPos = (int)((numBytes - (double)bytePos) / 0.125); + if ((bool)propertyValue) + bytes[bytePos] |= (byte)Math.Pow(2, bitPos); // is true + else + bytes[bytePos] &= (byte)(~(byte)Math.Pow(2, bitPos)); // is false + numBytes += 0.125; + break; + case "Byte": + numBytes = (int)Math.Ceiling(numBytes); + bytePos = (int)numBytes; + bytes[bytePos] = (byte)propertyValue; + numBytes++; + break; + case "Int16": + bytes2 = Int.ToByteArray((Int16)propertyValue); + break; + case "UInt16": + bytes2 = Word.ToByteArray((UInt16)propertyValue); + break; + case "Int32": + bytes2 = DInt.ToByteArray((Int32)propertyValue); + break; + case "UInt32": + bytes2 = DWord.ToByteArray((UInt32)propertyValue); + break; + case "Single": + bytes2 = Real.ToByteArray((float)propertyValue); + break; + case "Double": + bytes2 = LReal.ToByteArray((double)propertyValue); + break; + default: + numBytes = ToBytes(propertyValue, bytes, numBytes); + break; + } + + if (bytes2 != null) + { + IncrementToEven(ref numBytes); + + bytePos = (int)numBytes; + for (int bCnt = 0; bCnt < bytes2.Length; bCnt++) + bytes[bytePos + bCnt] = bytes2[bCnt]; + numBytes += bytes2.Length; + } + + return numBytes; + } + + /// + /// Creates a byte array depending on the struct type. + /// + /// The struct object + /// A byte array or null if fails. + public static double ToBytes(object sourceClass, byte[] bytes, double numBytes = 0.0) + { + var properties = GetAccessableProperties(sourceClass.GetType()); + foreach (var property in properties) + { + if (property.PropertyType.IsArray) + { + Array array = (Array)property.GetValue(sourceClass, null); + IncrementToEven(ref numBytes); + Type elementType = property.PropertyType.GetElementType(); + for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) + { + numBytes = SetBytesFromProperty(array.GetValue(i), bytes, numBytes); + } + } + else + { + numBytes = SetBytesFromProperty(property.GetValue(sourceClass, null), bytes, numBytes); + } + } + return numBytes; + } + + private static void IncrementToEven(ref double numBytes) + { + numBytes = Math.Ceiling(numBytes); + if (numBytes % 2 > 0) numBytes++; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Counter.cs b/src/Khd.Core.Plc/S7/Types/Counter.cs new file mode 100644 index 0000000..39b95cb --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Counter.cs @@ -0,0 +1,61 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Counter from S7 plc to C# ushort (UInt16). + /// + public static class Counter + { + /// + /// Converts a Counter (2 bytes) to ushort (UInt16) + /// + public static UInt16 FromByteArray(byte[] bytes) + { + if (bytes.Length != 2) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes."); + } + // bytes[0] -> HighByte + // bytes[1] -> LowByte + return (UInt16)((bytes[0] << 8) | bytes[1]); + } + + + /// + /// Converts a ushort (UInt16) to word (2 bytes) + /// + public static byte[] ToByteArray(UInt16 value) + { + byte[] bytes = new byte[2]; + + bytes[0] = (byte)((value << 8) & 0xFF); + bytes[1] = (byte)((value) & 0xFF); + + return bytes; + } + + /// + /// Converts an array of ushort (UInt16) to an array of bytes + /// + public static byte[] ToByteArray(UInt16[] value) + { + ByteArray arr = new ByteArray(); + foreach (UInt16 val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of bytes to an array of ushort + /// + public static UInt16[] ToArray(byte[] bytes) + { + UInt16[] values = new UInt16[bytes.Length / 2]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 2; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++] }); + + return values; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/DInt.cs b/src/Khd.Core.Plc/S7/Types/DInt.cs new file mode 100644 index 0000000..9a51ba5 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/DInt.cs @@ -0,0 +1,63 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert DInt from S7 plc to C# int (Int32). + /// + public static class DInt + { + /// + /// Converts a S7 DInt (4 bytes) to int (Int32) + /// + public static Int32 FromByteArray(byte[] bytes) + { + if (bytes.Length != 4) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes."); + } + return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]; + } + + + /// + /// Converts a int (Int32) to S7 DInt (4 bytes) + /// + public static byte[] ToByteArray(Int32 value) + { + byte[] bytes = new byte[4]; + + bytes[0] = (byte)((value >> 24) & 0xFF); + bytes[1] = (byte)((value >> 16) & 0xFF); + bytes[2] = (byte)((value >> 8) & 0xFF); + bytes[3] = (byte)((value) & 0xFF); + + return bytes; + } + + /// + /// Converts an array of int (Int32) to an array of bytes + /// + public static byte[] ToByteArray(Int32[] value) + { + ByteArray arr = new ByteArray(); + foreach (Int32 val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of S7 DInt to an array of int (Int32) + /// + public static Int32[] ToArray(byte[] bytes) + { + Int32[] values = new Int32[bytes.Length / 4]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 4; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] }); + + return values; + } + + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/DWord.cs b/src/Khd.Core.Plc/S7/Types/DWord.cs new file mode 100644 index 0000000..f96e36e --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/DWord.cs @@ -0,0 +1,71 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert DWord from S7 plc to C#. + /// + public static class DWord + { + /// + /// Converts a S7 DWord (4 bytes) to uint (UInt32) + /// + public static UInt32 FromByteArray(byte[] bytes) + { + return (UInt32)(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]); + } + + + /// + /// Converts 4 bytes to DWord (UInt32) + /// + public static UInt32 FromBytes(byte b1, byte b2, byte b3, byte b4) + { + return (UInt32)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); + } + + + /// + /// Converts a uint (UInt32) to S7 DWord (4 bytes) + /// + public static byte[] ToByteArray(UInt32 value) + { + byte[] bytes = new byte[4]; + + bytes[0] = (byte)((value >> 24) & 0xFF); + bytes[1] = (byte)((value >> 16) & 0xFF); + bytes[2] = (byte)((value >> 8) & 0xFF); + bytes[3] = (byte)((value) & 0xFF); + + return bytes; + } + + + + + + + /// + /// Converts an array of uint (UInt32) to an array of S7 DWord (4 bytes) + /// + public static byte[] ToByteArray(UInt32[] value) + { + ByteArray arr = new ByteArray(); + foreach (UInt32 val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of S7 DWord to an array of uint (UInt32) + /// + public static UInt32[] ToArray(byte[] bytes) + { + UInt32[] values = new UInt32[bytes.Length / 4]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 4; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] }); + + return values; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/DataItem.cs b/src/Khd.Core.Plc/S7/Types/DataItem.cs new file mode 100644 index 0000000..c326b80 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/DataItem.cs @@ -0,0 +1,103 @@ +using Khd.Core.Plc.S7.Protocol.S7; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Create an instance of a memory block that can be read by using ReadMultipleVars + /// + public class DataItem + { + /// + /// Memory area to read + /// + public DataType DataType { get; set; } + + /// + /// Type of data to be read (default is bytes) + /// + public VarType VarType { get; set; } + + /// + /// Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45) + /// + public int DB { get; set; } + + /// + /// Address of the first byte to read + /// + public int StartByteAdr { get; set; } + + /// + /// Addess of bit to read from StartByteAdr + /// + public byte BitAdr { get; set; } + + /// + /// Number of variables to read + /// + public int Count { get; set; } + + /// + /// Contains the value of the memory area after the read has been executed + /// + public object? Value { get; set; } + + /// + /// Create an instance of DataItem + /// + public DataItem() + { + VarType = VarType.Byte; + Count = 1; + } + + /// + /// Create an instance of from the supplied address. + /// + /// The address to create the DataItem for. + /// A new instance with properties parsed from . + /// The property is not parsed from the address. + public static DataItem FromAddress(string address) + { + PLCAddress.Parse(address, out var dataType, out var dbNumber, out var varType, out var startByte, + out var bitNumber); + + return new DataItem + { + DataType = dataType, + DB = dbNumber, + VarType = varType, + StartByteAdr = startByte, + BitAdr = (byte)(bitNumber == -1 ? 0 : bitNumber) + }; + } + + /// + /// Create an instance of from the supplied address and value. + /// + /// The address to create the DataItem for. + /// The value to be applied to the DataItem. + /// A new instance with properties parsed from and the supplied value set. + public static DataItem FromAddressAndValue(string address, T value) + { + var dataItem = FromAddress(address); + dataItem.Value = value; + + if (typeof(T).IsArray) + { + var array = ((Array?)dataItem.Value); + if (array != null) + { + dataItem.Count = array.Length; + } + } + + return dataItem; + } + + internal static DataItemAddress GetDataItemAddress(DataItem dataItem) + { + return new DataItemAddress(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, Plc.VarTypeToByteLength(dataItem.VarType, dataItem.Count)); + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/DateTime.cs b/src/Khd.Core.Plc/S7/Types/DateTime.cs new file mode 100644 index 0000000..57e8535 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/DateTime.cs @@ -0,0 +1,153 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert between and S7 representation of datetime values. + /// + public static class DateTime + { + /// + /// The minimum value supported by the specification. + /// + public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1990, 1, 1); + + /// + /// The maximum value supported by the specification. + /// + public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2089, 12, 31, 23, 59, 59, 999); + + /// + /// Parses a value from bytes. + /// + /// Input bytes read from PLC. + /// A object representing the value read from PLC. + /// Thrown when the length of + /// is not 8 or any value in + /// is outside the valid range of values. + public static System.DateTime FromByteArray(byte[] bytes) + { + return FromByteArrayImpl(bytes); + } + + /// + /// Parses an array of values from bytes. + /// + /// Input bytes read from PLC. + /// An array of objects representing the values read from PLC. + /// Thrown when the length of + /// is not a multiple of 8 or any value in + /// is outside the valid range of values. + public static System.DateTime[] ToArray(byte[] bytes) + { + if (bytes.Length % 8 != 0) + throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, + $"Parsing an array of DateTime requires a multiple of 8 bytes of input data, input data is '{bytes.Length}' long."); + + var cnt = bytes.Length / 8; + var result = new System.DateTime[bytes.Length / 8]; + + for (var i = 0; i < cnt; i++) + result[i] = FromByteArrayImpl(new ArraySegment(bytes, i * 8, 8)); + + return result; + } + + private static System.DateTime FromByteArrayImpl(IList bytes) + { + if (bytes.Count != 8) + throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Count, + $"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Count} bytes long."); + + int DecodeBcd(byte input) => 10 * (input >> 4) + (input & 0b00001111); + + int ByteToYear(byte bcdYear) + { + var input = DecodeBcd(bcdYear); + if (input < 90) return input + 2000; + if (input < 100) return input + 1900; + + throw new ArgumentOutOfRangeException(nameof(bcdYear), bcdYear, + $"Value '{input}' is higher than the maximum '99' of S7 date and time representation."); + } + + int AssertRangeInclusive(int input, byte min, byte max, string field) + { + if (input < min) + throw new ArgumentOutOfRangeException(nameof(input), input, + $"Value '{input}' is lower than the minimum '{min}' allowed for {field}."); + if (input > max) + throw new ArgumentOutOfRangeException(nameof(input), input, + $"Value '{input}' is higher than the maximum '{max}' allowed for {field}."); + + return input; + } + + var year = ByteToYear(bytes[0]); + var month = AssertRangeInclusive(DecodeBcd(bytes[1]), 1, 12, "month"); + var day = AssertRangeInclusive(DecodeBcd(bytes[2]), 1, 31, "day of month"); + var hour = AssertRangeInclusive(DecodeBcd(bytes[3]), 0, 23, "hour"); + var minute = AssertRangeInclusive(DecodeBcd(bytes[4]), 0, 59, "minute"); + var second = AssertRangeInclusive(DecodeBcd(bytes[5]), 0, 59, "second"); + var hsec = AssertRangeInclusive(DecodeBcd(bytes[6]), 0, 99, "first two millisecond digits"); + var msec = AssertRangeInclusive(bytes[7] >> 4, 0, 9, "third millisecond digit"); + var dayOfWeek = AssertRangeInclusive(bytes[7] & 0b00001111, 1, 7, "day of week"); + + return new System.DateTime(year, month, day, hour, minute, second, hsec * 10 + msec); + } + + /// + /// Converts a value to a byte array. + /// + /// The DateTime value to convert. + /// A byte array containing the S7 date time representation of . + /// Thrown when the value of + /// is before + /// or after . + public static byte[] ToByteArray(System.DateTime dateTime) + { + byte EncodeBcd(int value) + { + return (byte)((value / 10 << 4) | value % 10); + } + + if (dateTime < SpecMinimumDateTime) + throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, + $"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 date time representation."); + + if (dateTime > SpecMaximumDateTime) + throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, + $"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 date time representation."); + + byte MapYear(int year) => (byte)(year < 2000 ? year - 1900 : year - 2000); + + int DayOfWeekToInt(DayOfWeek dayOfWeek) => (int)dayOfWeek + 1; + + return new[] + { + EncodeBcd(MapYear(dateTime.Year)), + EncodeBcd(dateTime.Month), + EncodeBcd(dateTime.Day), + EncodeBcd(dateTime.Hour), + EncodeBcd(dateTime.Minute), + EncodeBcd(dateTime.Second), + EncodeBcd(dateTime.Millisecond / 10), + (byte) (dateTime.Millisecond % 10 << 4 | DayOfWeekToInt(dateTime.DayOfWeek)) + }; + } + + /// + /// Converts an array of values to a byte array. + /// + /// The DateTime values to convert. + /// A byte array containing the S7 date time representations of . + /// Thrown when any value of + /// is before + /// or after . + public static byte[] ToByteArray(System.DateTime[] dateTimes) + { + var bytes = new List(dateTimes.Length * 8); + foreach (var dateTime in dateTimes) bytes.AddRange(ToByteArray(dateTime)); + + return bytes.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/Types/DateTimeLong.cs b/src/Khd.Core.Plc/S7/Types/DateTimeLong.cs new file mode 100644 index 0000000..eb677ce --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/DateTimeLong.cs @@ -0,0 +1,181 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert between and S7 representation of DateTimeLong (DTL) values. + /// + public static class DateTimeLong + { + public const int TypeLengthInBytes = 12; + /// + /// The minimum value supported by the specification. + /// + public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1970, 1, 1); + + /// + /// The maximum value supported by the specification. + /// + public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2262, 4, 11, 23, 47, 16, 854); + + /// + /// Parses a value from bytes. + /// + /// Input bytes read from PLC. + /// A object representing the value read from PLC. + /// + /// Thrown when the length of + /// is not 12 or any value in + /// is outside the valid range of values. + /// + public static System.DateTime FromByteArray(byte[] bytes) + { + return FromByteArrayImpl(bytes); + } + + /// + /// Parses an array of values from bytes. + /// + /// Input bytes read from PLC. + /// An array of objects representing the values read from PLC. + /// + /// Thrown when the length of + /// is not a multiple of 12 or any value in + /// is outside the valid range of values. + /// + public static System.DateTime[] ToArray(byte[] bytes) + { + if (bytes.Length % TypeLengthInBytes != 0) + { + throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, + $"Parsing an array of DateTimeLong requires a multiple of 12 bytes of input data, input data is '{bytes.Length}' long."); + } + + var cnt = bytes.Length / TypeLengthInBytes; + var result = new System.DateTime[cnt]; + + for (var i = 0; i < cnt; i++) + { + var slice = new byte[TypeLengthInBytes]; + Array.Copy(bytes, i * TypeLengthInBytes, slice, 0, TypeLengthInBytes); + result[i] = FromByteArrayImpl(slice); + } + + return result; + } + + private static System.DateTime FromByteArrayImpl(byte[] bytes) + { + if (bytes.Length != TypeLengthInBytes) + { + throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, + $"Parsing a DateTimeLong requires exactly 12 bytes of input data, input data is {bytes.Length} bytes long."); + } + + + var year = AssertRangeInclusive(Word.FromBytes(bytes[1], bytes[0]), 1970, 2262, "year"); + var month = AssertRangeInclusive(bytes[2], 1, 12, "month"); + var day = AssertRangeInclusive(bytes[3], 1, 31, "day of month"); + var dayOfWeek = AssertRangeInclusive(bytes[4], 1, 7, "day of week"); + var hour = AssertRangeInclusive(bytes[5], 0, 23, "hour"); + var minute = AssertRangeInclusive(bytes[6], 0, 59, "minute"); + var second = AssertRangeInclusive(bytes[7], 0, 59, "second"); + ; + + var nanoseconds = AssertRangeInclusive(DWord.FromBytes(bytes[11], bytes[10], bytes[9], bytes[8]), 0, + 999999999, "nanoseconds"); + + var time = new System.DateTime(year, month, day, hour, minute, second); + return time.AddTicks(nanoseconds / 100); + } + + /// + /// Converts a value to a byte array. + /// + /// The DateTime value to convert. + /// A byte array containing the S7 DateTimeLong representation of . + /// + /// Thrown when the value of + /// is before + /// or after . + /// + public static byte[] ToByteArray(System.DateTime dateTime) + { + if (dateTime < SpecMinimumDateTime) + { + throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, + $"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 DateTimeLong representation."); + } + + if (dateTime > SpecMaximumDateTime) + { + throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, + $"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 DateTimeLong representation."); + } + + var stream = new MemoryStream(TypeLengthInBytes); + // Convert Year + stream.Write(Word.ToByteArray(Convert.ToUInt16(dateTime.Year)), 0, 2); + + // Convert Month + stream.WriteByte(Convert.ToByte(dateTime.Month)); + + // Convert Day + stream.WriteByte(Convert.ToByte(dateTime.Day)); + + // Convert WeekDay. NET DateTime starts with Sunday = 0, while S7DT has Sunday = 1. + stream.WriteByte(Convert.ToByte(dateTime.DayOfWeek + 1)); + + // Convert Hour + stream.WriteByte(Convert.ToByte(dateTime.Hour)); + + // Convert Minutes + stream.WriteByte(Convert.ToByte(dateTime.Minute)); + + // Convert Seconds + stream.WriteByte(Convert.ToByte(dateTime.Second)); + + // Convert Nanoseconds. Net DateTime has a representation of 1 Tick = 100ns. + // Thus First take the ticks Mod 1 Second (1s = 10'000'000 ticks), and then Convert to nanoseconds. + stream.Write(DWord.ToByteArray(Convert.ToUInt32(dateTime.Ticks % 10000000 * 100)), 0, 4); + + return stream.ToArray(); + } + + /// + /// Converts an array of values to a byte array. + /// + /// The DateTime values to convert. + /// A byte array containing the S7 DateTimeLong representations of . + /// + /// Thrown when any value of + /// is before + /// or after . + /// + public static byte[] ToByteArray(System.DateTime[] dateTimes) + { + var bytes = new List(dateTimes.Length * TypeLengthInBytes); + foreach (var dateTime in dateTimes) + { + bytes.AddRange(ToByteArray(dateTime)); + } + + return bytes.ToArray(); + } + + private static T AssertRangeInclusive(T input, T min, T max, string field) where T : IComparable + { + if (input.CompareTo(min) < 0) + { + throw new ArgumentOutOfRangeException(nameof(input), input, + $"Value '{input}' is lower than the minimum '{min}' allowed for {field}."); + } + + if (input.CompareTo(max) > 0) + { + throw new ArgumentOutOfRangeException(nameof(input), input, + $"Value '{input}' is higher than the maximum '{max}' allowed for {field}."); + } + + return input; + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Plc/S7/Types/Double.cs b/src/Khd.Core.Plc/S7/Types/Double.cs new file mode 100644 index 0000000..42fbd6c --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Double.cs @@ -0,0 +1,66 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# double. + /// + [Obsolete("Class Double is obsolete. Use Real instead for 32bit floating point, or LReal for 64bit floating point.")] + public static class Double + { + /// + /// Converts a S7 Real (4 bytes) to double + /// + public static double FromByteArray(byte[] bytes) => Real.FromByteArray(bytes); + + /// + /// Converts a S7 DInt to double + /// + public static double FromDWord(Int32 value) + { + byte[] b = DInt.ToByteArray(value); + double d = FromByteArray(b); + return d; + } + + /// + /// Converts a S7 DWord to double + /// + public static double FromDWord(UInt32 value) + { + byte[] b = DWord.ToByteArray(value); + double d = FromByteArray(b); + return d; + } + + + /// + /// Converts a double to S7 Real (4 bytes) + /// + public static byte[] ToByteArray(double value) => Real.ToByteArray((float)value); + + /// + /// Converts an array of double to an array of bytes + /// + public static byte[] ToByteArray(double[] value) + { + ByteArray arr = new ByteArray(); + foreach (double val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of S7 Real to an array of double + /// + public static double[] ToArray(byte[] bytes) + { + double[] values = new double[bytes.Length / 4]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 4; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] }); + + return values; + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Int.cs b/src/Khd.Core.Plc/S7/Types/Int.cs new file mode 100644 index 0000000..7c7abed --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Int.cs @@ -0,0 +1,85 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Int from S7 plc to C#. + /// + public static class Int + { + /// + /// Converts a S7 Int (2 bytes) to short (Int16) + /// + public static short FromByteArray(byte[] bytes) + { + if (bytes.Length != 2) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes."); + } + // bytes[0] -> HighByte + // bytes[1] -> LowByte + return (short)((int)(bytes[1]) | ((int)(bytes[0]) << 8)); + } + + + /// + /// Converts a short (Int16) to a S7 Int byte array (2 bytes) + /// + public static byte[] ToByteArray(Int16 value) + { + byte[] bytes = new byte[2]; + + bytes[0] = (byte)(value >> 8 & 0xFF); + bytes[1] = (byte)(value & 0xFF); + + return bytes; + } + + /// + /// Converts an array of short (Int16) to a S7 Int byte array (2 bytes) + /// + public static byte[] ToByteArray(Int16[] value) + { + byte[] bytes = new byte[value.Length * 2]; + int bytesPos = 0; + + for (int i = 0; i < value.Length; i++) + { + bytes[bytesPos++] = (byte)((value[i] >> 8) & 0xFF); + bytes[bytesPos++] = (byte)(value[i] & 0xFF); + } + return bytes; + } + + /// + /// Converts an array of S7 Int to an array of short (Int16) + /// + public static Int16[] ToArray(byte[] bytes) + { + int shortsCount = bytes.Length / 2; + + Int16[] values = new Int16[shortsCount]; + + int counter = 0; + for (int cnt = 0; cnt < shortsCount; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++] }); + + return values; + } + + /// + /// Converts a C# int value to a C# short value, to be used as word. + /// + /// + /// + public static Int16 CWord(int value) + { + if (value > 32767) + { + value -= 32768; + value = 32768 - value; + value *= -1; + } + return (short)value; + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/LReal.cs b/src/Khd.Core.Plc/S7/Types/LReal.cs new file mode 100644 index 0000000..8d3ffc5 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/LReal.cs @@ -0,0 +1,54 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# double. + /// + public static class LReal + { + /// + /// Converts a S7 LReal (8 bytes) to double + /// + public static double FromByteArray(byte[] bytes) + { + if (bytes.Length != 8) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 8 bytes."); + } + var buffer = bytes; + + // sps uses bigending so we have to reverse if platform needs + if (BitConverter.IsLittleEndian) + { + Array.Reverse(buffer); + } + + return BitConverter.ToDouble(buffer, 0); + } + + /// + /// Converts a double to S7 LReal (8 bytes) + /// + public static byte[] ToByteArray(double value) + { + var bytes = BitConverter.GetBytes(value); + + // sps uses bigending so we have to check if platform is same + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + return bytes; + } + + /// + /// Converts an array of double to an array of bytes + /// + public static byte[] ToByteArray(double[] value) => TypeHelper.ToByteArray(value, ToByteArray); + + /// + /// Converts an array of S7 LReal to an array of double + /// + public static double[] ToArray(byte[] bytes) => TypeHelper.ToArray(bytes, FromByteArray); + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Real.cs b/src/Khd.Core.Plc/S7/Types/Real.cs new file mode 100644 index 0000000..375ae6d --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Real.cs @@ -0,0 +1,72 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# double. + /// + public static class Real + { + /// + /// Converts a S7 Real (4 bytes) to float + /// + public static float FromByteArray(byte[] bytes) + { + if (bytes.Length != 4) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes."); + } + + // sps uses bigending so we have to reverse if platform needs + if (BitConverter.IsLittleEndian) + { + // create deep copy of the array and reverse + bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] }; + } + + return BitConverter.ToSingle(bytes, 0); + } + + /// + /// Converts a float to S7 Real (4 bytes) + /// + public static byte[] ToByteArray(float value) + { + byte[] bytes = BitConverter.GetBytes(value); + + // sps uses bigending so we have to check if platform is same + if (!BitConverter.IsLittleEndian) return bytes; + + // create deep copy of the array and reverse + return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] }; + } + + /// + /// Converts an array of float to an array of bytes + /// + public static byte[] ToByteArray(float[] value) + { + var buffer = new byte[4 * value.Length]; + var stream = new MemoryStream(buffer); + foreach (var val in value) + { + stream.Write(ToByteArray(val), 0, 4); + } + + return buffer; + } + + /// + /// Converts an array of S7 Real to an array of float + /// + public static float[] ToArray(byte[] bytes) + { + var values = new float[bytes.Length / 4]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 4; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] }); + + return values; + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/S7String.cs b/src/Khd.Core.Plc/S7/Types/S7String.cs new file mode 100644 index 0000000..2a4eb8d --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/S7String.cs @@ -0,0 +1,68 @@ +using System.Text; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert from S7 strings to C# strings + /// An S7 String has a preceeding 2 byte header containing its capacity and length + /// + public static class S7String + { + /// + /// Converts S7 bytes to a string + /// + /// + /// + public static string FromByteArray(byte[] bytes) + { + if (bytes.Length < 2) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / too short"); + } + + int size = bytes[0]; + int length = bytes[1]; + if (length > size) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / length larger than capacity"); + } + + try + { + return Encoding.ASCII.GetString(bytes, 2, length); + } + catch (Exception e) + { + throw new PlcException(ErrorCode.ReadData, + $"Failed to parse {VarType.S7String} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.", + e); + } + + } + + /// + /// Converts a to S7 string with 2-byte header. + /// + /// The string to convert to byte array. + /// The length (in characters) allocated in PLC for the string. + /// A containing the string header and string value with a maximum length of + 2. + public static byte[] ToByteArray(string value, int reservedLength) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); + + var bytes = Encoding.ASCII.GetBytes(value); + if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength})."); + + var buffer = new byte[2 + reservedLength]; + Array.Copy(bytes, 0, buffer, 2, bytes.Length); + buffer[0] = (byte)reservedLength; + buffer[1] = (byte)bytes.Length; + return buffer; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/S7StringAttribute.cs b/src/Khd.Core.Plc/S7/Types/S7StringAttribute.cs new file mode 100644 index 0000000..2e4dd8a --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/S7StringAttribute.cs @@ -0,0 +1,65 @@ +namespace Khd.Core.Plc.S7.Types +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public sealed class S7StringAttribute : Attribute + { + private readonly S7StringType type; + private readonly int reservedLength; + + /// + /// Initializes a new instance of the class. + /// + /// The string type. + /// Reserved length of the string in characters. + /// Please use a valid value for the string type + public S7StringAttribute(S7StringType type, int reservedLength) + { + if (!Enum.IsDefined(typeof(S7StringType), type)) + throw new ArgumentException("Please use a valid value for the string type"); + + this.type = type; + this.reservedLength = reservedLength; + } + + /// + /// Gets the type of the string. + /// + /// + /// The string type. + /// + public S7StringType Type => type; + + /// + /// Gets the reserved length of the string in characters. + /// + /// + /// The reserved length of the string in characters. + /// + public int ReservedLength => reservedLength; + + /// + /// Gets the reserved length in bytes. + /// + /// + /// The reserved length in bytes. + /// + public int ReservedLengthInBytes => type == S7StringType.S7String ? reservedLength + 2 : (reservedLength * 2) + 4; + } + + + /// + /// String type. + /// + public enum S7StringType + { + /// + /// ASCII string. + /// + S7String = VarType.S7String, + + /// + /// Unicode string. + /// + S7WString = VarType.S7WString + } +} diff --git a/src/Khd.Core.Plc/S7/Types/S7WString.cs b/src/Khd.Core.Plc/S7/Types/S7WString.cs new file mode 100644 index 0000000..78c45f6 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/S7WString.cs @@ -0,0 +1,71 @@ +using System.Text; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert from S7 wstrings to C# strings + /// An S7 WString has a preceding 4 byte header containing its capacity and length + /// + public static class S7WString + { + /// + /// Converts S7 bytes to a string + /// + /// + /// + public static string FromByteArray(byte[] bytes) + { + if (bytes.Length < 4) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / too short"); + } + + int size = (bytes[0] << 8) | bytes[1]; + int length = (bytes[2] << 8) | bytes[3]; + + if (length > size) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / length larger than capacity"); + } + + try + { + return Encoding.BigEndianUnicode.GetString(bytes, 4, length * 2); + } + catch (Exception e) + { + throw new PlcException(ErrorCode.ReadData, + $"Failed to parse {VarType.S7WString} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.", + e); + } + + } + + /// + /// Converts a to S7 wstring with 4-byte header. + /// + /// The string to convert to byte array. + /// The length (in characters) allocated in PLC for the string. + /// A containing the string header and string value with a maximum length of + 4. + public static byte[] ToByteArray(string value, int reservedLength) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (reservedLength > 16382) throw new ArgumentException("The maximum string length supported is 16382."); + + var buffer = new byte[4 + reservedLength * 2]; + buffer[0] = (byte)((reservedLength >> 8) & 0xFF); + buffer[1] = (byte)(reservedLength & 0xFF); + buffer[2] = (byte)((value.Length >> 8) & 0xFF); + buffer[3] = (byte)(value.Length & 0xFF); + + var stringLength = Encoding.BigEndianUnicode.GetBytes(value, 0, value.Length, buffer, 4) / 2; + if (stringLength > reservedLength) throw new ArgumentException($"The provided string length ({stringLength} is larger than the specified reserved length ({reservedLength})."); + + return buffer; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Single.cs b/src/Khd.Core.Plc/S7/Types/Single.cs new file mode 100644 index 0000000..0ee9415 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Single.cs @@ -0,0 +1,66 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# float. + /// + [Obsolete("Class Single is obsolete. Use Real instead.")] + public static class Single + { + /// + /// Converts a S7 Real (4 bytes) to float + /// + public static float FromByteArray(byte[] bytes) => Real.FromByteArray(bytes); + + /// + /// Converts a S7 DInt to float + /// + public static float FromDWord(Int32 value) + { + byte[] b = DInt.ToByteArray(value); + float d = FromByteArray(b); + return d; + } + + /// + /// Converts a S7 DWord to float + /// + public static float FromDWord(UInt32 value) + { + byte[] b = DWord.ToByteArray(value); + float d = FromByteArray(b); + return d; + } + + + /// + /// Converts a double to S7 Real (4 bytes) + /// + public static byte[] ToByteArray(float value) => Real.ToByteArray(value); + + /// + /// Converts an array of float to an array of bytes + /// + public static byte[] ToByteArray(float[] value) + { + ByteArray arr = new ByteArray(); + foreach (float val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of S7 Real to an array of float + /// + public static float[] ToArray(byte[] bytes) + { + float[] values = new float[bytes.Length / 4]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 4; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] }); + + return values; + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/String.cs b/src/Khd.Core.Plc/S7/Types/String.cs new file mode 100644 index 0000000..b7e8b09 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/String.cs @@ -0,0 +1,37 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the methods to convert from S7 Array of Chars (like a const char[N] C-String) to C# strings + /// + public class String + { + /// + /// Converts a string to of bytes, padded with 0-bytes if required. + /// + /// The string to write to the PLC. + /// The amount of bytes reserved for the in the PLC. + public static byte[] ToByteArray(string value, int reservedLength) + { + var length = value?.Length; + if (length > reservedLength) length = reservedLength; + var bytes = new byte[reservedLength]; + + if (length == null || length == 0) return bytes; + + System.Text.Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 0); + + return bytes; + } + + /// + /// Converts S7 bytes to a string + /// + /// + /// + public static string FromByteArray(byte[] bytes) + { + return System.Text.Encoding.ASCII.GetString(bytes); + } + + } +} diff --git a/src/Khd.Core.Plc/S7/Types/StringEx.cs b/src/Khd.Core.Plc/S7/Types/StringEx.cs new file mode 100644 index 0000000..eefe07c --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/StringEx.cs @@ -0,0 +1,13 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + [Obsolete("Please use S7String class")] + public static class StringEx + { + /// + public static string FromByteArray(byte[] bytes) => S7String.FromByteArray(bytes); + + /// + public static byte[] ToByteArray(string value, int reservedLength) => S7String.ToByteArray(value, reservedLength); + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Struct.cs b/src/Khd.Core.Plc/S7/Types/Struct.cs new file mode 100644 index 0000000..bf03144 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Struct.cs @@ -0,0 +1,320 @@ +using System.Reflection; + +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the method to convert a C# struct to S7 data types + /// + public static class Struct + { + /// + /// Gets the size of the struct in bytes. + /// + /// the type of the struct + /// the number of bytes + public static int GetStructSize(Type structType) + { + double numBytes = 0.0; + + var infos = structType +#if NETSTANDARD1_3 + .GetTypeInfo().DeclaredFields; +#else + .GetFields(); +#endif + + foreach (var info in infos) + { + switch (info.FieldType.Name) + { + case "Boolean": + numBytes += 0.125; + break; + case "Byte": + numBytes = Math.Ceiling(numBytes); + numBytes++; + break; + case "Int16": + case "UInt16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 2; + break; + case "Int32": + case "UInt32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 4; + break; + case "Single": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 4; + break; + case "Double": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += 8; + break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += attribute.ReservedLengthInBytes; + break; + default: + numBytes += GetStructSize(info.FieldType); + break; + } + } + return (int)numBytes; + } + + /// + /// Creates a struct of a specified type by an array of bytes. + /// + /// The struct type + /// The array of bytes + /// The object depending on the struct type or null if fails(array-length != struct-length + public static object? FromBytes(Type structType, byte[] bytes) + { + if (bytes == null) + return null; + + if (bytes.Length != GetStructSize(structType)) + return null; + + // and decode it + int bytePos = 0; + int bitPos = 0; + double numBytes = 0.0; + object structValue = Activator.CreateInstance(structType); + + + var infos = structValue.GetType() +#if NETSTANDARD1_3 + .GetTypeInfo().DeclaredFields; +#else + .GetFields(); +#endif + + foreach (var info in infos) + { + switch (info.FieldType.Name) + { + case "Boolean": + // get the value + bytePos = (int)Math.Floor(numBytes); + bitPos = (int)((numBytes - (double)bytePos) / 0.125); + if ((bytes[bytePos] & (int)Math.Pow(2, bitPos)) != 0) + info.SetValue(structValue, true); + else + info.SetValue(structValue, false); + numBytes += 0.125; + break; + case "Byte": + numBytes = Math.Ceiling(numBytes); + info.SetValue(structValue, (byte)(bytes[(int)numBytes])); + numBytes++; + break; + case "Int16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + ushort source = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); + info.SetValue(structValue, source.ConvertToShort()); + numBytes += 2; + break; + case "UInt16": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + info.SetValue(structValue, Word.FromBytes(bytes[(int)numBytes + 1], + bytes[(int)numBytes])); + numBytes += 2; + break; + case "Int32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 0]); + info.SetValue(structValue, sourceUInt.ConvertToInt()); + numBytes += 4; + break; + case "UInt32": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + info.SetValue(structValue, DWord.FromBytes(bytes[(int)numBytes], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 3])); + numBytes += 4; + break; + case "Single": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + info.SetValue(structValue, Real.FromByteArray(new byte[] { bytes[(int)numBytes], + bytes[(int)numBytes + 1], + bytes[(int)numBytes + 2], + bytes[(int)numBytes + 3] })); + numBytes += 4; + break; + case "Double": + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + // get the value + var data = new byte[8]; + Array.Copy(bytes, (int)numBytes, data, 0, 8); + info.SetValue(structValue, LReal.FromByteArray(data)); + numBytes += 8; + break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + + // get the value + var sData = new byte[attribute.ReservedLengthInBytes]; + Array.Copy(bytes, (int)numBytes, sData, 0, sData.Length); + switch (attribute.Type) + { + case S7StringType.S7String: + info.SetValue(structValue, S7String.FromByteArray(sData)); + break; + case S7StringType.S7WString: + info.SetValue(structValue, S7WString.FromByteArray(sData)); + break; + default: + throw new ArgumentException("Please use a valid string type for the S7StringAttribute"); + } + + numBytes += sData.Length; + break; + default: + var buffer = new byte[GetStructSize(info.FieldType)]; + if (buffer.Length == 0) + continue; + Buffer.BlockCopy(bytes, (int)Math.Ceiling(numBytes), buffer, 0, buffer.Length); + info.SetValue(structValue, FromBytes(info.FieldType, buffer)); + numBytes += buffer.Length; + break; + } + } + return structValue; + } + + /// + /// Creates a byte array depending on the struct type. + /// + /// The struct object + /// A byte array or null if fails. + public static byte[] ToBytes(object structValue) + { + Type type = structValue.GetType(); + + int size = Struct.GetStructSize(type); + byte[] bytes = new byte[size]; + byte[]? bytes2 = null; + + int bytePos = 0; + int bitPos = 0; + double numBytes = 0.0; + + var infos = type +#if NETSTANDARD1_3 + .GetTypeInfo().DeclaredFields; +#else + .GetFields(); +#endif + + foreach (var info in infos) + { + bytes2 = null; + switch (info.FieldType.Name) + { + case "Boolean": + // get the value + bytePos = (int)Math.Floor(numBytes); + bitPos = (int)((numBytes - (double)bytePos) / 0.125); + if ((bool)info.GetValue(structValue)) + bytes[bytePos] |= (byte)Math.Pow(2, bitPos); // is true + else + bytes[bytePos] &= (byte)(~(byte)Math.Pow(2, bitPos)); // is false + numBytes += 0.125; + break; + case "Byte": + numBytes = (int)Math.Ceiling(numBytes); + bytePos = (int)numBytes; + bytes[bytePos] = (byte)info.GetValue(structValue); + numBytes++; + break; + case "Int16": + bytes2 = Int.ToByteArray((Int16)info.GetValue(structValue)); + break; + case "UInt16": + bytes2 = Word.ToByteArray((UInt16)info.GetValue(structValue)); + break; + case "Int32": + bytes2 = DInt.ToByteArray((Int32)info.GetValue(structValue)); + break; + case "UInt32": + bytes2 = DWord.ToByteArray((UInt32)info.GetValue(structValue)); + break; + case "Single": + bytes2 = Real.ToByteArray((float)info.GetValue(structValue)); + break; + case "Double": + bytes2 = LReal.ToByteArray((double)info.GetValue(structValue)); + break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + bytes2 = attribute.Type switch + { + S7StringType.S7String => S7String.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), + S7StringType.S7WString => S7WString.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), + _ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute") + }; + break; + } + if (bytes2 != null) + { + // add them + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + bytePos = (int)numBytes; + for (int bCnt = 0; bCnt < bytes2.Length; bCnt++) + bytes[bytePos + bCnt] = bytes2[bCnt]; + numBytes += bytes2.Length; + } + } + return bytes; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Timer.cs b/src/Khd.Core.Plc/S7/Types/Timer.cs new file mode 100644 index 0000000..b011b1d --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Timer.cs @@ -0,0 +1,80 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Converts the Timer data type to C# data type + /// + public static class Timer + { + /// + /// Converts the timer bytes to a double + /// + public static double FromByteArray(byte[] bytes) + { + double wert = 0; + + wert = ((bytes[0]) & 0x0F) * 100.0; + wert += ((bytes[1] >> 4) & 0x0F) * 10.0; + wert += ((bytes[1]) & 0x0F) * 1.0; + + // this value is not used... may for a nother exponation + //int unknown = (bytes[0] >> 6) & 0x03; + + switch ((bytes[0] >> 4) & 0x03) + { + case 0: + wert *= 0.01; + break; + case 1: + wert *= 0.1; + break; + case 2: + wert *= 1.0; + break; + case 3: + wert *= 10.0; + break; + } + + return wert; + } + + /// + /// Converts a ushort (UInt16) to an array of bytes formatted as time + /// + public static byte[] ToByteArray(UInt16 value) + { + byte[] bytes = new byte[2]; + bytes[1] = (byte)((int)value & 0xFF); + bytes[0] = (byte)((int)value >> 8 & 0xFF); + + return bytes; + } + + /// + /// Converts an array of ushorts (Uint16) to an array of bytes formatted as time + /// + public static byte[] ToByteArray(UInt16[] value) + { + ByteArray arr = new ByteArray(); + foreach (UInt16 val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of bytes formatted as time to an array of doubles + /// + /// + /// + public static double[] ToArray(byte[] bytes) + { + double[] values = new double[bytes.Length / 2]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 2; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++] }); + + return values; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/TypeHelper.cs b/src/Khd.Core.Plc/S7/Types/TypeHelper.cs new file mode 100644 index 0000000..53dc124 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/TypeHelper.cs @@ -0,0 +1,41 @@ +using System.Runtime.InteropServices; + +namespace Khd.Core.Plc.S7.Types +{ + internal static class TypeHelper + { + /// + /// Converts an array of T to an array of bytes + /// + public static byte[] ToByteArray(T[] value, Func converter) where T : struct + { + var buffer = new byte[Marshal.SizeOf(default(T)) * value.Length]; + var stream = new MemoryStream(buffer); + foreach (var val in value) + { + stream.Write(converter(val), 0, 4); + } + + return buffer; + } + + /// + /// Converts an array of T repesented as S7 binary data to an array of T + /// + public static T[] ToArray(byte[] bytes, Func converter) where T : struct + { + var typeSize = Marshal.SizeOf(default(T)); + var entries = bytes.Length / typeSize; + var values = new T[entries]; + + for (int i = 0; i < entries; ++i) + { + var buffer = new byte[typeSize]; + Array.Copy(bytes, i * typeSize, buffer, 0, typeSize); + values[i] = converter(buffer); + } + + return values; + } + } +} diff --git a/src/Khd.Core.Plc/S7/Types/Word.cs b/src/Khd.Core.Plc/S7/Types/Word.cs new file mode 100644 index 0000000..d8d78b8 --- /dev/null +++ b/src/Khd.Core.Plc/S7/Types/Word.cs @@ -0,0 +1,69 @@ +namespace Khd.Core.Plc.S7.Types +{ + /// + /// Contains the conversion methods to convert Words from S7 plc to C#. + /// + public static class Word + { + /// + /// Converts a word (2 bytes) to ushort (UInt16) + /// + public static UInt16 FromByteArray(byte[] bytes) + { + if (bytes.Length != 2) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes."); + } + + return (UInt16)((bytes[0] << 8) | bytes[1]); + } + + + /// + /// Converts 2 bytes to ushort (UInt16) + /// + public static UInt16 FromBytes(byte b1, byte b2) + { + return (UInt16)((b2 << 8) | b1); + } + + + /// + /// Converts a ushort (UInt16) to word (2 bytes) + /// + public static byte[] ToByteArray(UInt16 value) + { + byte[] bytes = new byte[2]; + + bytes[1] = (byte)(value & 0xFF); + bytes[0] = (byte)((value >> 8) & 0xFF); + + return bytes; + } + + /// + /// Converts an array of ushort (UInt16) to an array of bytes + /// + public static byte[] ToByteArray(UInt16[] value) + { + ByteArray arr = new ByteArray(); + foreach (UInt16 val in value) + arr.Add(ToByteArray(val)); + return arr.Array; + } + + /// + /// Converts an array of bytes to an array of ushort + /// + public static UInt16[] ToArray(byte[] bytes) + { + UInt16[] values = new UInt16[bytes.Length / 2]; + + int counter = 0; + for (int cnt = 0; cnt < bytes.Length / 2; cnt++) + values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++] }); + + return values; + } + } +} diff --git a/src/Khd.Core.Thrift.Client/Khd.Core.Thrift.Client.csproj b/src/Khd.Core.Thrift.Client/Khd.Core.Thrift.Client.csproj new file mode 100644 index 0000000..2a45cf1 --- /dev/null +++ b/src/Khd.Core.Thrift.Client/Khd.Core.Thrift.Client.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Khd.Core.Thrift.Client/Program.cs b/src/Khd.Core.Thrift.Client/Program.cs new file mode 100644 index 0000000..5255ef1 --- /dev/null +++ b/src/Khd.Core.Thrift.Client/Program.cs @@ -0,0 +1,29 @@ +using Thrift.Protocol; +using Thrift.Transport; + +internal class Program +{ + private static void Main(string[] args) + { + Console.WriteLine("Hello, Client!"); + SendTest("List1"); + } + public static void SendTest(string Message) + { + try + { + TTransport transport = new TSocket("localhost", 9091); + transport.Open(); + TProtocol protocol = new TBinaryProtocol(transport); + WcsThrift.Client client = new WcsThrift.Client(protocol); + //Console.WriteLine(client.hello("Sunzy")); + Console.WriteLine(client.SendCar(new List() { Message }, "P1001", "10")); + Console.ReadLine(); + } + catch (Exception ex) + { + // 处理异常 + Console.WriteLine($"Error: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Thrift.Server/Khd.Core.Thrift.Server.csproj b/src/Khd.Core.Thrift.Server/Khd.Core.Thrift.Server.csproj new file mode 100644 index 0000000..2a45cf1 --- /dev/null +++ b/src/Khd.Core.Thrift.Server/Khd.Core.Thrift.Server.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Khd.Core.Thrift.Server/Program.cs b/src/Khd.Core.Thrift.Server/Program.cs new file mode 100644 index 0000000..ec28df1 --- /dev/null +++ b/src/Khd.Core.Thrift.Server/Program.cs @@ -0,0 +1,27 @@ +using Thrift.Server; +using Thrift.Transport; +using ThriftService; + +internal class Program +{ + private static void Main(string[] args) + { + while (true) + { + try + { + WcsServer wcsServer = new WcsServer(); + WcsThrift.Processor processor = new WcsThrift.Processor(wcsServer); + TServerTransport serverTransport = new TServerSocket(9091); + TServer server = new TSimpleServer(processor, serverTransport); + Console.WriteLine("Starting the server..."); + server.Serve(); + } + catch (Exception ex) + { + Console.WriteLine(ex.StackTrace); + } + Console.WriteLine("done."); + } + } +} \ No newline at end of file diff --git a/src/Khd.Core.Thrift.Server/WcsServer.cs b/src/Khd.Core.Thrift.Server/WcsServer.cs new file mode 100644 index 0000000..5d4c9d7 --- /dev/null +++ b/src/Khd.Core.Thrift.Server/WcsServer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ThriftService +{ + public class WcsServer : WcsThrift.Iface + { + public string hello(string name) + { + Console.WriteLine($"hello {name}"); + return $"ServerResponse receive from client name:{name}"; + } + public string SendCar(List carlist, string order_code, string amount) + { + Console.WriteLine($"hello carlist[0]:{carlist.FirstOrDefault()}|order_code:{order_code}|amount:{amount}"); + return $"ServerResponse receive from client order_code:{order_code}"; + } + +} +} diff --git a/src/Khd.Core.Thrift/Khd.Core.Thrift.csproj b/src/Khd.Core.Thrift/Khd.Core.Thrift.csproj new file mode 100644 index 0000000..458cf77 --- /dev/null +++ b/src/Khd.Core.Thrift/Khd.Core.Thrift.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/src/Khd.Core.Thrift/WcsThrift.cs b/src/Khd.Core.Thrift/WcsThrift.cs new file mode 100644 index 0000000..c8cadd6 --- /dev/null +++ b/src/Khd.Core.Thrift/WcsThrift.cs @@ -0,0 +1,808 @@ +/** + * Autogenerated by Thrift Compiler (0.9.3) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.IO; +using Thrift; +using Thrift.Collections; +using System.Runtime.Serialization; +using Thrift.Protocol; +using Thrift.Transport; + +public partial class WcsThrift { + public interface Iface { + string hello(string name); + #if SILVERLIGHT + IAsyncResult Begin_hello(AsyncCallback callback, object state, string name); + string End_hello(IAsyncResult asyncResult); + #endif + string SendCar(List carlist, string order_code, string amount); + #if SILVERLIGHT + IAsyncResult Begin_SendCar(AsyncCallback callback, object state, List carlist, string order_code, string amount); + string End_SendCar(IAsyncResult asyncResult); + #endif + } + + public class Client : IDisposable, Iface { + public Client(TProtocol prot) : this(prot, prot) + { + } + + public Client(TProtocol iprot, TProtocol oprot) + { + iprot_ = iprot; + oprot_ = oprot; + } + + protected TProtocol iprot_; + protected TProtocol oprot_; + protected int seqid_; + + public TProtocol InputProtocol + { + get { return iprot_; } + } + public TProtocol OutputProtocol + { + get { return oprot_; } + } + + + #region " IDisposable Support " + private bool _IsDisposed; + + // IDisposable + public void Dispose() + { + Dispose(true); + } + + + protected virtual void Dispose(bool disposing) + { + if (!_IsDisposed) + { + if (disposing) + { + if (iprot_ != null) + { + ((IDisposable)iprot_).Dispose(); + } + if (oprot_ != null) + { + ((IDisposable)oprot_).Dispose(); + } + } + } + _IsDisposed = true; + } + #endregion + + + + #if SILVERLIGHT + public IAsyncResult Begin_hello(AsyncCallback callback, object state, string name) + { + return send_hello(callback, state, name); + } + + public string End_hello(IAsyncResult asyncResult) + { + oprot_.Transport.EndFlush(asyncResult); + return recv_hello(); + } + + #endif + + public string hello(string name) + { + #if !SILVERLIGHT + send_hello(name); + return recv_hello(); + + #else + var asyncResult = Begin_hello(null, null, name); + return End_hello(asyncResult); + + #endif + } + #if SILVERLIGHT + public IAsyncResult send_hello(AsyncCallback callback, object state, string name) + #else + public void send_hello(string name) + #endif + { + oprot_.WriteMessageBegin(new TMessage("hello", TMessageType.Call, seqid_)); + hello_args args = new hello_args(); + args.Name = name; + args.Write(oprot_); + oprot_.WriteMessageEnd(); + #if SILVERLIGHT + return oprot_.Transport.BeginFlush(callback, state); + #else + oprot_.Transport.Flush(); + #endif + } + + public string recv_hello() + { + TMessage msg = iprot_.ReadMessageBegin(); + if (msg.Type == TMessageType.Exception) { + TApplicationException x = TApplicationException.Read(iprot_); + iprot_.ReadMessageEnd(); + throw x; + } + hello_result result = new hello_result(); + result.Read(iprot_); + iprot_.ReadMessageEnd(); + if (result.__isset.success) { + return result.Success; + } + throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "hello failed: unknown result"); + } + + + #if SILVERLIGHT + public IAsyncResult Begin_SendCar(AsyncCallback callback, object state, List carlist, string order_code, string amount) + { + return send_SendCar(callback, state, carlist, order_code, amount); + } + + public string End_SendCar(IAsyncResult asyncResult) + { + oprot_.Transport.EndFlush(asyncResult); + return recv_SendCar(); + } + + #endif + + public string SendCar(List carlist, string order_code, string amount) + { + #if !SILVERLIGHT + send_SendCar(carlist, order_code, amount); + return recv_SendCar(); + + #else + var asyncResult = Begin_SendCar(null, null, carlist, order_code, amount); + return End_SendCar(asyncResult); + + #endif + } + #if SILVERLIGHT + public IAsyncResult send_SendCar(AsyncCallback callback, object state, List carlist, string order_code, string amount) + #else + public void send_SendCar(List carlist, string order_code, string amount) + #endif + { + oprot_.WriteMessageBegin(new TMessage("SendCar", TMessageType.Call, seqid_)); + SendCar_args args = new SendCar_args(); + args.Carlist = carlist; + args.Order_code = order_code; + args.Amount = amount; + args.Write(oprot_); + oprot_.WriteMessageEnd(); + #if SILVERLIGHT + return oprot_.Transport.BeginFlush(callback, state); + #else + oprot_.Transport.Flush(); + #endif + } + + public string recv_SendCar() + { + TMessage msg = iprot_.ReadMessageBegin(); + if (msg.Type == TMessageType.Exception) { + TApplicationException x = TApplicationException.Read(iprot_); + iprot_.ReadMessageEnd(); + throw x; + } + SendCar_result result = new SendCar_result(); + result.Read(iprot_); + iprot_.ReadMessageEnd(); + if (result.__isset.success) { + return result.Success; + } + throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "SendCar failed: unknown result"); + } + + } + public class Processor : TProcessor { + public Processor(Iface iface) + { + iface_ = iface; + processMap_["hello"] = hello_Process; + processMap_["SendCar"] = SendCar_Process; + } + + protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot); + private Iface iface_; + protected Dictionary processMap_ = new Dictionary(); + + public bool Process(TProtocol iprot, TProtocol oprot) + { + try + { + TMessage msg = iprot.ReadMessageBegin(); + ProcessFunction fn; + processMap_.TryGetValue(msg.Name, out fn); + if (fn == null) { + TProtocolUtil.Skip(iprot, TType.Struct); + iprot.ReadMessageEnd(); + TApplicationException x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, "Invalid method name: '" + msg.Name + "'"); + oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID)); + x.Write(oprot); + oprot.WriteMessageEnd(); + oprot.Transport.Flush(); + return true; + } + fn(msg.SeqID, iprot, oprot); + } + catch (IOException) + { + return false; + } + return true; + } + + public void hello_Process(int seqid, TProtocol iprot, TProtocol oprot) + { + hello_args args = new hello_args(); + args.Read(iprot); + iprot.ReadMessageEnd(); + hello_result result = new hello_result(); + result.Success = iface_.hello(args.Name); + oprot.WriteMessageBegin(new TMessage("hello", TMessageType.Reply, seqid)); + result.Write(oprot); + oprot.WriteMessageEnd(); + oprot.Transport.Flush(); + } + + public void SendCar_Process(int seqid, TProtocol iprot, TProtocol oprot) + { + SendCar_args args = new SendCar_args(); + args.Read(iprot); + iprot.ReadMessageEnd(); + SendCar_result result = new SendCar_result(); + result.Success = iface_.SendCar(args.Carlist, args.Order_code, args.Amount); + oprot.WriteMessageBegin(new TMessage("SendCar", TMessageType.Reply, seqid)); + result.Write(oprot); + oprot.WriteMessageEnd(); + oprot.Transport.Flush(); + } + + } + + + #if !SILVERLIGHT + [Serializable] + #endif + public partial class hello_args : TBase + { + private string _name; + + public string Name + { + get + { + return _name; + } + set + { + __isset.name = true; + this._name = value; + } + } + + + public Isset __isset; + #if !SILVERLIGHT + [Serializable] + #endif + public struct Isset { + public bool name; + } + + public hello_args() { + } + + public void Read (TProtocol iprot) + { + iprot.IncrementRecursionDepth(); + try + { + TField field; + iprot.ReadStructBegin(); + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) { + break; + } + switch (field.ID) + { + case 1: + if (field.Type == TType.String) { + Name = iprot.ReadString(); + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + iprot.ReadFieldEnd(); + } + iprot.ReadStructEnd(); + } + finally + { + iprot.DecrementRecursionDepth(); + } + } + + public void Write(TProtocol oprot) { + oprot.IncrementRecursionDepth(); + try + { + TStruct struc = new TStruct("hello_args"); + oprot.WriteStructBegin(struc); + TField field = new TField(); + if (Name != null && __isset.name) { + field.Name = "name"; + field.Type = TType.String; + field.ID = 1; + oprot.WriteFieldBegin(field); + oprot.WriteString(Name); + oprot.WriteFieldEnd(); + } + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); + } + finally + { + oprot.DecrementRecursionDepth(); + } + } + + public override string ToString() { + StringBuilder __sb = new StringBuilder("hello_args("); + bool __first = true; + if (Name != null && __isset.name) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Name: "); + __sb.Append(Name); + } + __sb.Append(")"); + return __sb.ToString(); + } + + } + + + #if !SILVERLIGHT + [Serializable] + #endif + public partial class hello_result : TBase + { + private string _success; + + public string Success + { + get + { + return _success; + } + set + { + __isset.success = true; + this._success = value; + } + } + + + public Isset __isset; + #if !SILVERLIGHT + [Serializable] + #endif + public struct Isset { + public bool success; + } + + public hello_result() { + } + + public void Read (TProtocol iprot) + { + iprot.IncrementRecursionDepth(); + try + { + TField field; + iprot.ReadStructBegin(); + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) { + break; + } + switch (field.ID) + { + case 0: + if (field.Type == TType.String) { + Success = iprot.ReadString(); + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + iprot.ReadFieldEnd(); + } + iprot.ReadStructEnd(); + } + finally + { + iprot.DecrementRecursionDepth(); + } + } + + public void Write(TProtocol oprot) { + oprot.IncrementRecursionDepth(); + try + { + TStruct struc = new TStruct("hello_result"); + oprot.WriteStructBegin(struc); + TField field = new TField(); + + if (this.__isset.success) { + if (Success != null) { + field.Name = "Success"; + field.Type = TType.String; + field.ID = 0; + oprot.WriteFieldBegin(field); + oprot.WriteString(Success); + oprot.WriteFieldEnd(); + } + } + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); + } + finally + { + oprot.DecrementRecursionDepth(); + } + } + + public override string ToString() { + StringBuilder __sb = new StringBuilder("hello_result("); + bool __first = true; + if (Success != null && __isset.success) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Success: "); + __sb.Append(Success); + } + __sb.Append(")"); + return __sb.ToString(); + } + + } + + + #if !SILVERLIGHT + [Serializable] + #endif + public partial class SendCar_args : TBase + { + private List _carlist; + private string _order_code; + private string _amount; + + public List Carlist + { + get + { + return _carlist; + } + set + { + __isset.carlist = true; + this._carlist = value; + } + } + + public string Order_code + { + get + { + return _order_code; + } + set + { + __isset.order_code = true; + this._order_code = value; + } + } + + public string Amount + { + get + { + return _amount; + } + set + { + __isset.amount = true; + this._amount = value; + } + } + + + public Isset __isset; + #if !SILVERLIGHT + [Serializable] + #endif + public struct Isset { + public bool carlist; + public bool order_code; + public bool amount; + } + + public SendCar_args() { + } + + public void Read (TProtocol iprot) + { + iprot.IncrementRecursionDepth(); + try + { + TField field; + iprot.ReadStructBegin(); + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) { + break; + } + switch (field.ID) + { + case 1: + if (field.Type == TType.List) { + { + Carlist = new List(); + TList _list0 = iprot.ReadListBegin(); + for( int _i1 = 0; _i1 < _list0.Count; ++_i1) + { + string _elem2; + _elem2 = iprot.ReadString(); + Carlist.Add(_elem2); + } + iprot.ReadListEnd(); + } + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 2: + if (field.Type == TType.String) { + Order_code = iprot.ReadString(); + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 3: + if (field.Type == TType.String) { + Amount = iprot.ReadString(); + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + iprot.ReadFieldEnd(); + } + iprot.ReadStructEnd(); + } + finally + { + iprot.DecrementRecursionDepth(); + } + } + + public void Write(TProtocol oprot) { + oprot.IncrementRecursionDepth(); + try + { + TStruct struc = new TStruct("SendCar_args"); + oprot.WriteStructBegin(struc); + TField field = new TField(); + if (Carlist != null && __isset.carlist) { + field.Name = "carlist"; + field.Type = TType.List; + field.ID = 1; + oprot.WriteFieldBegin(field); + { + oprot.WriteListBegin(new TList(TType.String, Carlist.Count)); + foreach (string _iter3 in Carlist) + { + oprot.WriteString(_iter3); + } + oprot.WriteListEnd(); + } + oprot.WriteFieldEnd(); + } + if (Order_code != null && __isset.order_code) { + field.Name = "order_code"; + field.Type = TType.String; + field.ID = 2; + oprot.WriteFieldBegin(field); + oprot.WriteString(Order_code); + oprot.WriteFieldEnd(); + } + if (Amount != null && __isset.amount) { + field.Name = "amount"; + field.Type = TType.String; + field.ID = 3; + oprot.WriteFieldBegin(field); + oprot.WriteString(Amount); + oprot.WriteFieldEnd(); + } + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); + } + finally + { + oprot.DecrementRecursionDepth(); + } + } + + public override string ToString() { + StringBuilder __sb = new StringBuilder("SendCar_args("); + bool __first = true; + if (Carlist != null && __isset.carlist) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Carlist: "); + __sb.Append(Carlist); + } + if (Order_code != null && __isset.order_code) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Order_code: "); + __sb.Append(Order_code); + } + if (Amount != null && __isset.amount) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Amount: "); + __sb.Append(Amount); + } + __sb.Append(")"); + return __sb.ToString(); + } + + } + + + #if !SILVERLIGHT + [Serializable] + #endif + public partial class SendCar_result : TBase + { + private string _success; + + public string Success + { + get + { + return _success; + } + set + { + __isset.success = true; + this._success = value; + } + } + + + public Isset __isset; + #if !SILVERLIGHT + [Serializable] + #endif + public struct Isset { + public bool success; + } + + public SendCar_result() { + } + + public void Read (TProtocol iprot) + { + iprot.IncrementRecursionDepth(); + try + { + TField field; + iprot.ReadStructBegin(); + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) { + break; + } + switch (field.ID) + { + case 0: + if (field.Type == TType.String) { + Success = iprot.ReadString(); + } else { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + iprot.ReadFieldEnd(); + } + iprot.ReadStructEnd(); + } + finally + { + iprot.DecrementRecursionDepth(); + } + } + + public void Write(TProtocol oprot) { + oprot.IncrementRecursionDepth(); + try + { + TStruct struc = new TStruct("SendCar_result"); + oprot.WriteStructBegin(struc); + TField field = new TField(); + + if (this.__isset.success) { + if (Success != null) { + field.Name = "Success"; + field.Type = TType.String; + field.ID = 0; + oprot.WriteFieldBegin(field); + oprot.WriteString(Success); + oprot.WriteFieldEnd(); + } + } + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); + } + finally + { + oprot.DecrementRecursionDepth(); + } + } + + public override string ToString() { + StringBuilder __sb = new StringBuilder("SendCar_result("); + bool __first = true; + if (Success != null && __isset.success) { + if(!__first) { __sb.Append(", "); } + __first = false; + __sb.Append("Success: "); + __sb.Append(Success); + } + __sb.Append(")"); + return __sb.ToString(); + } + + } + +} diff --git a/src/Khd.Core.Wcs/Global/PlcConfig.cs b/src/Khd.Core.Wcs/Global/PlcConfig.cs new file mode 100644 index 0000000..26aba1a --- /dev/null +++ b/src/Khd.Core.Wcs/Global/PlcConfig.cs @@ -0,0 +1,28 @@ +using Khd.Core.Domain.Models; +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Global +{ + public static class PlcConfig + { + public static int CpuType { get; set; } + public static string IP { get; set; } + public static int Port { get; set; } + public static short Rack { get; set; } + public static short Slot { get; set; } + } + public static class ConnectionStrings + { + public static string ConnectionString { get; set; } + } + public class CommonLock + { + public static object tableLock = new object(); + public static object Xlock = new object(); + } +} diff --git a/src/Khd.Core.Wcs/Global/StaticData.cs b/src/Khd.Core.Wcs/Global/StaticData.cs new file mode 100644 index 0000000..67fe263 --- /dev/null +++ b/src/Khd.Core.Wcs/Global/StaticData.cs @@ -0,0 +1,35 @@ +using Khd.Core.Domain.Dto.wcs; +using Khd.Core.Domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Global +{ + public static class StaticData + { + /// + /// 物料信息 + /// + public static List MateriaList = new List(); + /// + /// 库区信息 + /// + public static List BaseAreaList = new List(); + /// + /// 站台信息 + /// + public static List SiteNodeList = new List(); + /// + /// 小车信息 + /// + public static List CarList = new List(); + /// + /// 点位信息 + /// + public static List NodeSettingList = new List(); + + } +} diff --git a/src/Khd.Core.Wcs/Khd.Core.Wcs.csproj b/src/Khd.Core.Wcs/Khd.Core.Wcs.csproj new file mode 100644 index 0000000..80f3855 --- /dev/null +++ b/src/Khd.Core.Wcs/Khd.Core.Wcs.csproj @@ -0,0 +1,44 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + ..\..\dll\S7.Net.dll + True + + + + + + PreserveNewest + + + + + + + + diff --git a/src/Khd.Core.Wcs/MainCentralControl.cs b/src/Khd.Core.Wcs/MainCentralControl.cs new file mode 100644 index 0000000..0204912 --- /dev/null +++ b/src/Khd.Core.Wcs/MainCentralControl.cs @@ -0,0 +1,195 @@ +using Khd.Core.Domain.Dto.wcs; +using Khd.Core.Domain.Dto.webapi; +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Khd.Core.Plc.S7; +using Khd.Core.Wcs.Global; +using Khd.Core.Wcs.Wcs; +using Masuit.Tools; +using Masuit.Tools.Logging; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs +{ + public class MainCentralControl + { + public static object QingKongDianWei; + public static object WcsChuLiWanCheng; + public static object WcsMoRenQuXiang; + + private readonly IHost _host; + public MainCentralControl(IHost host) + { + this._host = host; + } + public void Start() + { + try + { + using (var scope = _host.Services.CreateScope()) + { + using (var dbContext = scope.ServiceProvider.GetRequiredService()) + { + //设置程序启动时清空点位的数据=>0为 Int16位 + QingKongDianWei = getValue("2", "0"); + //设置默认处理完成=>1为 Int16位 + WcsChuLiWanCheng = getValue("2", "1"); + //设置默认去向=>1为 Int16位 + WcsMoRenQuXiang = getValue("2", "1"); + //加载物料信息 + StaticData.MateriaList = dbContext.BaseMaterialinfo.Where(t => t.isDelete == 0).OrderBy(t => t.materialNo).ToList(); + //加载库区信息 + StaticData.BaseAreaList = dbContext.BaseArea.Where(t => t.isDelete == 0).OrderBy(t => t.areaOrder).ToList(); + //加载站台 + StaticData.SiteNodeList = dbContext.BaseSitenode.Where(t => t.isDelete == 0).OrderBy(t => t.siteNo).ToList(); + //加载小车 + StaticData.CarList = dbContext.BaseCar.Where(t => t.isDelete == 0).OrderBy(t => t.carNo).ToList(); + //加载点位 + StaticData.NodeSettingList = GetNodeSettingList(dbContext); + //加载配置项 + + //连接PLC判断 + var plc = new Khd.Core.Plc.S7.Plc((CpuType)PlcConfig.CpuType, PlcConfig.IP, PlcConfig.Port, + PlcConfig.Rack, PlcConfig.Slot); + try + { + plc.Open(); + if (plc.IsConnected) + if (true) + { + //启动处理订单线程 + BackUpData orderbak = new BackUpData(this._host); + orderbak.StartPoint(); + Console.WriteLine("处理订单线程启动完毕!"); + + //启动上件点线程1 + UpLine up1 = new UpLine(this._host, plc, "K46"); + up1.StartPoint(); + Console.WriteLine("2号上件点线程启动完毕!"); + + //启动上件点线程2 + UpLine up2 = new UpLine(this._host, plc, "K48"); + up2.StartPoint(); + Console.WriteLine("1号上件点线程启动完毕!"); + + //启动流转点线程1 + FlowPoint flow1 = new FlowPoint(this._host, plc, "K18"); + flow1.StartPoint(); + Console.WriteLine("K18流转点线程启动完毕!"); + + //启动流转点线程2 + FlowPoint flow2 = new FlowPoint(this._host, plc, "K22"); + flow2.StartPoint(); + Console.WriteLine("K22流转点线程启动完毕!"); + + //启动流转点线程3 + FlowPoint flow3 = new FlowPoint(this._host, plc, "K48"); + flow3.StartPoint(); + Console.WriteLine("K48流转点线程启动完毕!"); + + //启动下件点线程1 + OutWarePoint out1 = new OutWarePoint(this._host, plc, "K07"); + out1.StartPoint(); + Console.WriteLine("K07下件点线程启动完毕!"); + + //启动下件点线程2 + OutWarePoint out2 = new OutWarePoint(this._host, plc, "K02"); + out2.StartPoint(); + Console.WriteLine("K02下件点线程启动完毕!"); + + //启动入库点线程 + + //启动出库点线程 + + //启动出库线程 + + //报警监控线程 + } + } + catch (Exception ex) + { + //Console.WriteLine("PLC连接失败,重新连接"); + LogManager.Info($"程序启动失败 >>> {ex.Message}"); + return; + } + + } + } + } + catch (Exception ex) + { + LogManager.Error(ex); + } + } + /// + /// 加载点位 + /// + /// + /// + public List GetNodeSettingList(DefaultDbContext defaultDbContext) + { + try + { + var listSiteNode = defaultDbContext.BaseSitenode.Where(t => t.isDelete == 0).ToList(); + var listPlcPoint = defaultDbContext.BasePlcpoint.Where(t => t.isDelete == 0).ToList(); + var resultList = (from siteNode in listSiteNode + join plcPoint in listPlcPoint on siteNode.id equals plcPoint.sitenodeId + select new NodeSetting + { + // 根据需要设置NodeSetting的属性值 + id = (Guid)siteNode.id, + siteNo = siteNode.siteNo, + siteName = siteNode.siteName, + siteTasktype = siteNode.siteTasktype, + siteIpaddress = siteNode.siteIpaddress, + siteServerport = siteNode.siteServerport, + thriftPort = siteNode.thriftPort, + isDelete = siteNode.isDelete, + plcpointNo = plcPoint.plcpointNo, + plcpointName = plcPoint.plcpointName, + plcpointLength = plcPoint.plcpointLength, + plcpointAddress = plcPoint.plcpointAddress, + plcpointEquipmentId = plcPoint.plcpointEquipmentId, + plcpointEquipmentNo = plcPoint.plcpointEquipmentNo, + plcpointEquipmentName = plcPoint.plcpointEquipmentName, + plcpointType = plcPoint.plcpointType + }).ToList(); + if (resultList == null) + { + return null; + } + return resultList; + } + catch (Exception ex) + { + LogManager.Error(ex); + return null; + } + } + /// + /// 电气写入点位高低位转换 + /// + /// 点位地址位长度 + /// 写入数值 + /// + public static object getValue(string len, string value) + { + if (len == "2") + { + return Convert.ToInt16(value); + } + if (len == "4") + { + return Convert.ToInt32(value); + } + return 0; + } + } +} diff --git a/src/Khd.Core.Wcs/Program.cs b/src/Khd.Core.Wcs/Program.cs new file mode 100644 index 0000000..6b5aade --- /dev/null +++ b/src/Khd.Core.Wcs/Program.cs @@ -0,0 +1,104 @@ +using Masuit.Tools.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; +using Masuit.Tools; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Khd.Core.EntityFramework; +using Microsoft.EntityFrameworkCore; +using Khd.Core.Domain.Models; +using Z.EntityFramework.Plus; +using System.Runtime.InteropServices; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; +using Newtonsoft.Json.Linq; +using static System.Net.Mime.MediaTypeNames; +using System.Diagnostics; +using SharpCompress.Common; +using System.Timers; +using Microsoft.Extensions.Options; +using Microsoft.CodeAnalysis; +using Khd.Core.Wcs.Global; +using Khd.Core.Wcs; + +internal class Program +{ + public static IHost _host; + private static void Main(string[] args) + { + + init(); //初始化加载配置文件 + AppDomain.CurrentDomain.UnhandledException += GlobalExceptionHandler; //全局异常捕获 + try + { + bool ret; + System.Threading.Mutex mutex = new System.Threading.Mutex(true, "服务端", out ret); + if (ret) + { + IHost host = CreateHostBuilder(args).Build(); + _host = host; + Console.WriteLine("系统启动开始 :" + DateTime.Now); + + MainCentralControl main = new MainCentralControl(host); + main.Start(); + //while (true) + //{ + // Console.ReadLine(); + //} + mutex.ReleaseMutex(); // 释放 Mutex + } + else + { + Console.Write("系统重复开启,请先关闭之前窗口"); + } + } + catch (Exception ex) + { + Console.Write("系统启动异常:" + ex.Message + "\n" + ex.StackTrace); + } + + } + private static void GlobalExceptionHandler(object sender, UnhandledExceptionEventArgs e) + { + Exception exception = e.ExceptionObject as Exception; + if (exception != null) + { + Console.WriteLine("全局异常捕获:"); + Console.WriteLine(exception.Message); + Console.WriteLine(exception.StackTrace); + LogManager.Info("全局异常捕获:" + exception.Message); + } + else + { + Console.WriteLine("未知异常捕获:"); + Console.WriteLine(e.ExceptionObject); + LogManager.Info("未知异常捕获:" + e.ExceptionObject); + } + } + + /// + /// 初始化加载配置文件 + /// + private static void init() + { + IConfigurationRoot configuration = new ConfigurationBuilder() + .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) + .AddJsonFile("appsettings.json") + .Build(); + + PlcConfig.CpuType = configuration["PlcConfig:CpuType"].ToInt32(40); + PlcConfig.IP = configuration["PlcConfig:IP"].ToString(); + PlcConfig.Port = configuration["PlcConfig:Port"].ToInt32(102); + PlcConfig.Rack = (short)(configuration["PlcConfig:Rack"].ToInt32(0)); + PlcConfig.Slot = (short)(configuration["PlcConfig:Slot"].ToInt32(0)); + ConnectionStrings.ConnectionString = configuration["ConnectionStrings:DefaultConnection"].ToString(); + } + static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices((_, services) => + { + //services.AddDbContext(options => + // options.UseSqlServer(ConnectionStrings.ConnectionString)); + services.AddDbContext(options => + options.UseMySql(ConnectionStrings.ConnectionString, new MySqlServerVersion(new Version(8, 0, 31)))); + }); +} diff --git a/src/Khd.Core.Wcs/ThriftServer/WcsServer.cs b/src/Khd.Core.Wcs/ThriftServer/WcsServer.cs new file mode 100644 index 0000000..71e6629 --- /dev/null +++ b/src/Khd.Core.Wcs/ThriftServer/WcsServer.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ThriftService +{ + public class WcsServer : WcsThrift.Iface + { + public delegate string deletehello(string name); + public deletehello helloEvent; + public string hello(string name) + { + if (helloEvent != null) + { + return helloEvent(name); + } + return ""; + } + public delegate string deleteSendCar(List carlist, string order_code, string amount); + public deleteSendCar SendCarEvent; + public string SendCar(List carlist, string order_code, string amount) + { + if (SendCarEvent != null) + { + return SendCarEvent(carlist, order_code, amount); + } + return ""; + } + } +} diff --git a/src/Khd.Core.Wcs/Wcs/BackUpData.cs b/src/Khd.Core.Wcs/Wcs/BackUpData.cs new file mode 100644 index 0000000..04401a4 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/BackUpData.cs @@ -0,0 +1,82 @@ +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Masuit.Tools.Logging; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; +namespace Khd.Core.Wcs.Wcs +{ + public class BackUpData + { + private readonly IHost _host; + private static System.Timers.Timer timer; + Thread OrderBakThread; + public BackUpData(IHost host) + { + this._host = host; + } + /// + /// 启动线程 + /// + public void StartPoint() + { + OrderBakThread = new Thread(MonitorInLocatorPoint); + OrderBakThread.Start(); + } + public void MonitorInLocatorPoint() + { + // 创建一个定时器对象,设置时间间隔为24小时 + timer = new System.Timers.Timer(24 * 60 * 60 * 1000); // 1000毫秒 = 1秒 + //timer = new System.Timers.Timer(10*1000); // 1000毫秒 = 1秒 + + // 添加一个事件处理程序,当定时器间隔结束时调用方法 + timer.Elapsed += OnTimerElapsed; + + // 启动定时器 + timer.Enabled = true; + } + /// + /// 定时方法 + /// + /// + /// + private void OnTimerElapsed(object sender, ElapsedEventArgs e) + { + try + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + string sqlQuery = @$"INSERT INTO + base_production_order_split_bak + (id,site_code, line_code, line_name, order_code,prod_code,prod_desc,IsOver,est,Quantity,production_sequence) + SELECT + id,site_code, line_code, line_name, order_code,prod_code,prod_desc,IsOver,est,Quantity,production_sequence + FROM base_production_order_split + where + IsOver = 1 + and + est < date_add(NOW(), interval -1 DAY); + delete + FROM + base_production_order_split + WHERE + IsOver = 1 + and + est < date_add(NOW(), interval -1 DAY);"; + var ret = dbContext.Database.ExecuteSqlRaw(sqlQuery); + string saveLog = ret == 0 ? "备份任务SQL语句存在问题!" : "备份任务SQL语句执行成功!"; + LogManager.Info($"定时备份任务日志记录 >>> {saveLog}"); + } + catch (Exception ex) + { + LogManager.Info($"定时备份任务方法报错 >>> {ex.Message}"); + } + + } + } +} diff --git a/src/Khd.Core.Wcs/Wcs/DownLine.cs b/src/Khd.Core.Wcs/Wcs/DownLine.cs new file mode 100644 index 0000000..cd03829 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/DownLine.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 下件点线程 + /// + public class DownLine + { + } +} diff --git a/src/Khd.Core.Wcs/Wcs/FlowPoint.cs b/src/Khd.Core.Wcs/Wcs/FlowPoint.cs new file mode 100644 index 0000000..7200cb2 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/FlowPoint.cs @@ -0,0 +1,297 @@ +using AngleSharp.Dom; +using Khd.Core.Domain.Dto.wcs; +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Khd.Core.Wcs.Global; +using Masuit.Tools.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Khd.Core.Domain.Dto.webapi; + +using Khd.Core.Plc.S7; + +using Khd.Core.Wcs.Wcs; +using System.Data; +using Microsoft.EntityFrameworkCore; +using Z.EntityFramework.Plus; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 流转点线程 + /// + public class FlowPoint + { + private readonly IHost _host; + private readonly Plc.S7.Plc _plc; + List ScanPoint { get; set; }//点位信息 + BaseSitenode? sitenode { get; set; }//站台信息 + + NodeSetting? NodeSettingCarNo { get; set; } + NodeSetting? NodeSettingCarState { get; set; } + NodeSetting? NodeSettingCarRun { get; set; } + NodeSetting? NodeSettingWcsState { get; set; } + NodeSetting? NodeSettingWcsSend { get; set; } + Thread FlowPointThread; + public FlowPoint(IHost host, Plc.S7.Plc plc, string siteNo) + { + this._host = host; + this._plc = plc; + this.ScanPoint = StaticData.NodeSettingList.Where(t => t.siteNo == siteNo).ToList();//加载当前站点所对应的点位 + this.sitenode = StaticData.SiteNodeList.FirstOrDefault(t => t.siteNo == siteNo);//获取当前站台信息 + this.NodeSettingCarNo = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carno")); + this.NodeSettingCarState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carstate")); + this.NodeSettingCarRun = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carrun")); + this.NodeSettingWcsState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("plcsendK")); + this.NodeSettingWcsSend = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("wcsend")); + try + { + //默认启动,清理plc的上位机写入点位值 + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); + } + catch (Exception ex) + { + Console.WriteLine("站点" + siteNo + " 初始化数据异常" + ex.Message); + LogManager.Error(ex); + } + } + /// + /// 启动上件扫描监听 + /// + public void StartPoint() + { + + FlowPointThread = new Thread(MonitorInLocatorPoint); + FlowPointThread.Start(); + } + public void MonitorInLocatorPoint() + { + while (true) + { + try + { + //通用逻辑下,根据上件记录表,可区分业务,入库,出库,回库 + //- 入库 - 根据小车任务表,可区分流入哪个站点 + //- 出库 - 根据小车任务表,可区分流入哪个站点 + //- 回库 - 根据小车任务表,可区分流入哪个站点 + //- 特殊逻辑:下一站点多个站点,按多条线缓存数优先进少的; + + var carno = this._plc.Read(NodeSettingCarNo.plcpointAddress); + var carstate = this._plc.Read(NodeSettingCarState.plcpointAddress); + var carrun = this._plc.Read(NodeSettingCarRun.plcpointAddress); + var wcsstate = this._plc.Read(NodeSettingWcsState.plcpointAddress); + var wcsend = this._plc.Read(NodeSettingWcsSend.plcpointAddress); + if (carno != null && carstate != null && carrun != null && wcsstate != null && wcsend != null) + { + //清除点位信息 + if (Convert.ToInt32(carno) == 0 && Convert.ToInt32(wcsend) == 1 && Convert.ToInt32(wcsstate) == 1) + { + if (this.sitenode.siteNo == "K48") + { + continue; + } + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); //清空小车去向点位 + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); //清空wcs处理完成点位 + } + //正常读到小车信息 + if (Convert.ToInt32(carno) > 0 && Convert.ToInt32(wcsend) == 0 && Convert.ToInt32(wcsstate) == 0 && Convert.ToInt32(carstate) == 1 && Convert.ToInt32(carrun) == 0) + { + var carRun = GetTargetTo(this.sitenode.siteNo, Convert.ToInt32(carno)); + if (string.IsNullOrEmpty(carRun)) + { + if (this.sitenode.siteNo =="K48") + { + continue; + } + Console.WriteLine($" FlowPoint类GetTargetTo方法去向返回{carRun},查看错误日志内容!"); + Thread.Sleep(1000); + continue; + } + var ToInt16carRun = MainCentralControl.getValue("2", carRun); + this._plc.Write(this.NodeSettingCarRun.plcpointAddress, ToInt16carRun);//写入小车去向 + this._plc.Write(this.NodeSettingWcsSend.plcpointAddress, MainCentralControl.WcsChuLiWanCheng);//写入wcs处理完成 + LogManager.Info($"当前时间{DateTime.Now} >>> 挂具:{carno}经过站点:{this.sitenode.siteNo},WCS写入去向:{ToInt16carRun};"); + } + } + } + catch (Exception ex) + { + LogManager.Error(ex); + } + finally + { + Thread.Sleep(1000); + } + } + } + /// + /// 根据上件点,物料号,获取下一点位信息 + /// + /// 交互所属站点 + /// 到位挂具号码 + /// 1:给予PLC去向方法 2:\给予PLC物料信息方法 + /// + public string GetTargetTo(string siteNo, int carno) + { + try + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + if (siteNo == "K22") + { + var car_material = GetCarMessage(2, carno); + var flag = GetFlag(1, "UpState"); + if (!string.IsNullOrWhiteSpace(car_material) && (flag == "2" || flag == "1"))//挂具携带物料且上件点闸口正常时逻辑处理 + { + var CarRun = GetCarMessage(1, carno); + return CarRun; + } + else if (string.IsNullOrWhiteSpace(car_material) && flag == "1")//挂具为空且上件点闸口维护损坏时逻辑处理 + { + var CarRun = GetFlag(2, "UpState"); + return CarRun; + } + else if (string.IsNullOrWhiteSpace(car_material) && flag == "2")//空挂具走直通发往一线 + { + #region 代码注释折叠 + //var BaseProductionOrderSplitModelXB1 = dbContext.BaseProductionOrderSplit.Where(t => t.lineCode.Contains("01")).ToList(); + //var BaseProductionOrderSplitModelXB2 = dbContext.BaseProductionOrderSplit.Where(t => t.lineCode.Contains("02")).ToList(); + //int xianbie1shuliang = BaseProductionOrderSplitModelXB1.Count;//线别01的数量 + //int xianbie2shuliang = BaseProductionOrderSplitModelXB2.Count;//线别02的数量 + //if (xianbie1shuliang == 0) + //{ + // return "1"; + //} + //if (xianbie2shuliang == 0) + //{ + // return "2"; + //} + //if (xianbie1shuliang != 0 && xianbie2shuliang != 0)//一线二线同时存在订单时 + //{ + // if (xianbie1shuliang < xianbie2shuliang) + // { + // return "1"; + // } + // else if (xianbie1shuliang > xianbie2shuliang) + // { + // return "2"; + // } + // else//一二线订单数量相等时 + // { + // return "2"; + // } + //} + #endregion + return "1"; + } + else if (car_material == null || flag == null)//某个字段逻辑处理出错后日志输入结果 + { + //if (string.IsNullOrWhiteSpace(car_material)) LogManager.Info($"错误日志输出 >>> 未查找到车辆与订单的绑定信息!"); + //if (string.IsNullOrWhiteSpace(flag)) LogManager.Info($"错误日志输出 >>> 未查找到上件闸口状态信息!"); + return null; + } + } + else if (siteNo == "K18") + { + dbContext.BaseWaitdownline.Where(t => t.carNo == carno).Update(a => new BaseWaitdownline() { downline = 0 }); //修改车辆状态为未上线 (修改downline字段等于0) + //flag == 2 上件点闸口正常时写弯通2,挂具返回上件点, + //flag == 1上件点闸口人工维护损坏时写直通1,挂具返回K22流转点; + var flag = GetFlag(1, "UpState"); + return flag; + } + else if (siteNo == "K48") //如果是K46发过来的带件车辆逻辑处理 + { + var shifodaijian = GetCarMessage(2, carno); + if (!string.IsNullOrWhiteSpace(shifodaijian)) + { + return "2"; + } + else + { + return ""; + } + } + LogManager.Info("错误日志输出 >>> FlowPoint类GetTargetTo方法未确认返回值,从底部返回NULL!"); + return null; + + /// + /// 联查base_waitdownline 与 base_production_order_split,截取线别字段确定挂具在K22交互点去向 + /// + /// bianbie = 1 查找任务中的线别字段, bianbie = 2 查找车辆、Vin信息 + /// 挂具号 + /// + string GetCarMessage(int bianbie, int CarNo) + { + var listWaitDownLine = dbContext.BaseWaitdownline.Where(t => t.isDelete == 0 && t.carNo == CarNo).ToList(); + var listOrder = dbContext.BaseProductionOrderSplit.Where(t => t.isover == 0).ToList(); + var resultList = (from waitdownline in listWaitDownLine + join order in listOrder on waitdownline.materielNo equals order.orderCode + select new BaseProductionOrderSplit + { + id = order.id, + lineCode = order.lineCode, + orderCode = order.orderCode + }).ToList(); + if (resultList?.Count > 0) + { + if (bianbie == 1) + { + string XianBie = resultList[0].lineCode; + char lastChar = XianBie[XianBie.Length - 1]; + var num = Char.GetNumericValue(lastChar).ToString(); + //string qx = num == "1" ? "2" : "1"; + return num; + } + else + { + var vin = resultList[0].orderCode; + return vin; + } + + } + else { return null; } + } + + /// + /// K18流转点去向控制(上件点损坏时不返回上件) + /// + /// 查找字段辨别 1为查找闸口是否维护损坏, 2 为损坏时查找K22人工设定走向 + /// 闸口名称 + /// + string GetFlag(int bianbie, string FlagName) + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var baseflag = dbContext.BaseAmima.Where(t => t.name == FlagName).ToList(); + + if (baseflag?.Count > 0) + { + if (bianbie == 1) + { + var flag = baseflag[0].password == "2" ? "2" : "1"; + return flag;//返回上件损坏闸口值 + } + else + { + var flag = baseflag[0].direction.ToString() == "1" ? "1" : "2"; + return flag;//返回上件损坏闸口值后维护K22去向 + } + } + return null; + } + } + catch (Exception ex) + { + LogManager.Info($"GetTargetTo 方法报错 >>> {ex.Message}"); + return null; + } + } + } +} diff --git a/src/Khd.Core.Wcs/Wcs/InWarePoint.cs b/src/Khd.Core.Wcs/Wcs/InWarePoint.cs new file mode 100644 index 0000000..94c94a8 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/InWarePoint.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Wcs +{ + //入库点线程 + public class InWarePoint + { + } +} diff --git a/src/Khd.Core.Wcs/Wcs/OrderProcess.cs b/src/Khd.Core.Wcs/Wcs/OrderProcess.cs new file mode 100644 index 0000000..f97d4ee --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/OrderProcess.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 处理订单 + /// + public class OrderProcess + { + } +} diff --git a/src/Khd.Core.Wcs/Wcs/OutWare.cs b/src/Khd.Core.Wcs/Wcs/OutWare.cs new file mode 100644 index 0000000..97866f2 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/OutWare.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 出库线程 + /// + public class OutWare + { + } +} diff --git a/src/Khd.Core.Wcs/Wcs/OutWarePoint.cs b/src/Khd.Core.Wcs/Wcs/OutWarePoint.cs new file mode 100644 index 0000000..fb2827f --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/OutWarePoint.cs @@ -0,0 +1,253 @@ +using AngleSharp.Dom; +using Khd.Core.Domain.Dto.wcs; +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Khd.Core.Wcs.Global; +using Masuit.Tools.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Khd.Core.Domain.Dto.webapi; + +using Khd.Core.Plc.S7; + +using Khd.Core.Wcs.Wcs; +using System.Data; +using Microsoft.EntityFrameworkCore; +using Z.EntityFramework.Plus; +using Masuit.Tools; +using Microsoft.IdentityModel.Tokens; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 出库点线程 + /// + public class OutWarePoint + { + private readonly IHost _host; + private readonly Plc.S7.Plc _plc; + List ScanPoint { get; set; }//点位信息 + BaseSitenode? sitenode { get; set; }//站台信息 + + NodeSetting? NodeSettingCarNo { get; set; } + NodeSetting? NodeSettingCarState { get; set; } + NodeSetting? NodeSettingCarRun { get; set; } + NodeSetting? NodeSettingWcsState { get; set; } + NodeSetting? NodeSettingWcsSend { get; set; } + NodeSetting? NodeSettingWcsSendMaterial { get; set; } + NodeSetting? NodeSettingPLCSendSendMaterialstate { get; set; } + Thread FlowPointThread; + public OutWarePoint(IHost host, Plc.S7.Plc plc, string siteNo) + { + this._host = host; + this._plc = plc; + this.ScanPoint = StaticData.NodeSettingList.Where(t => t.siteNo == siteNo).ToList();//加载当前站点所对应的点位 + this.sitenode = StaticData.SiteNodeList.FirstOrDefault(t => t.siteNo == siteNo);//获取当前站台信息 + this.NodeSettingCarNo = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carno")); + this.NodeSettingCarState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carstate")); + this.NodeSettingCarRun = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carrun")); + this.NodeSettingWcsState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("plcsendK")); + this.NodeSettingWcsSend = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("wcsend")); + this.NodeSettingWcsSendMaterial = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("wcssendmessage")); + this.NodeSettingPLCSendSendMaterialstate = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("plcsendmessage")); + try + { + //默认启动,清理plc的上位机写入点位值 + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSendMaterial.plcpointAddress, MainCentralControl.QingKongDianWei); + } + catch (Exception ex) + { + Console.WriteLine("站点" + siteNo + " 初始化数据异常" + ex.Message); + LogManager.Error(ex); + } + } + /// + /// 启动上件扫描监听 + /// + public void StartPoint() + { + + FlowPointThread = new Thread(MonitorInLocatorPoint); + FlowPointThread.Start(); + } + public void MonitorInLocatorPoint() + { + while (true) + { + try + { + //通用逻辑下,根据上件记录表,可区分业务,入库,出库,回库 + //- 入库 - 根据小车任务表,可区分流入哪个站点 + //- 出库 - 根据小车任务表,可区分流入哪个站点 + //- 回库 - 根据小车任务表,可区分流入哪个站点 + //- 特殊逻辑:下一站点多个站点,按多条线缓存数优先进少的; + + var carno = this._plc.Read(NodeSettingCarNo.plcpointAddress); + var carstate = this._plc.Read(NodeSettingCarState.plcpointAddress); + var carrun = this._plc.Read(NodeSettingCarRun.plcpointAddress); + var wcsstate = this._plc.Read(NodeSettingWcsState.plcpointAddress); + var wcsend = this._plc.Read(NodeSettingWcsSend.plcpointAddress); + if (carno != null && carstate != null && carrun != null && wcsstate != null && wcsend != null) + { + //清除点位信息 + if (Convert.ToInt32(carno) == 0 && Convert.ToInt32(wcsend) == 1 && Convert.ToInt32(wcsstate) == 1) + { + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); //清空小车去向点位 + this._plc.Write(NodeSettingWcsSendMaterial.plcpointAddress, MainCentralControl.QingKongDianWei); //清空车身长度点位 + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); //清空wcs处理完成点位 + } + //正常读到小车信息 + if (Convert.ToInt32(carno) > 0 && Convert.ToInt32(wcsend) == 0 && Convert.ToInt32(wcsstate) == 0 && Convert.ToInt32(carstate) == 1 && Convert.ToInt32(carrun) == 0) + { + var downLength = GetTargetTo(Convert.ToInt32(carno)); + if (string.IsNullOrWhiteSpace(downLength)) + { + //Console.WriteLine("OutWarePoint类 WriteMaterialMessage方法未确认返回值,查看报错日志!"); + Thread.Sleep(1000); + continue; + } + object ToInt16downLength = MainCentralControl.getValue("2", downLength); + this._plc.Write(this.NodeSettingCarRun.plcpointAddress, MainCentralControl.WcsMoRenQuXiang);//写入小车去向 + this._plc.Write(this.NodeSettingWcsSendMaterial.plcpointAddress, ToInt16downLength); //写入车身长度 + this._plc.Write(this.NodeSettingWcsSend.plcpointAddress, MainCentralControl.WcsChuLiWanCheng);//写入wcs处理完成 + string zfc = downLength == "2900" ? "空挂具" : "挂具"; + LogManager.Info($"当前时间{DateTime.Now} >>> {zfc}:{carno}经过下件站点:{this.sitenode.siteNo},WCS写入去向:1,写入车身长度:{ToInt16downLength};"); + } + } + } + catch (Exception ex) + { + LogManager.Error(ex); + } + finally + { + Thread.Sleep(2000); + } + } + } + + /// + /// 根据上件点,物料号,获取下一点位信息 + /// + /// 交互所属站点 + /// 到位挂具号码 + /// 是否是订单结束处理的判断传参 + /// + private string GetTargetTo(int carno) + { + try + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var basecarModel = dbContext.BaseCar.Where(t => t.carNo == carno).FirstOrDefault(); + var waitdownlineModel = dbContext.BaseWaitdownline.Where(t => t.carNo == carno).FirstOrDefault(); + if (waitdownlineModel == null && basecarModel != null) + { + //添加车辆在Waitdownline表中的缓存信息 + BaseWaitdownline waitdownModel = new() + { + id = Guid.NewGuid(), + carId = basecarModel.id, + carNo = basecarModel.carNo, + carName = basecarModel.carName, + downline = 2, + materielNo = "", + materielNum = 0, + isDelete = 0, + createTime = DateTime.Now, + createBy = "DownLine" + }; + dbContext.Add(waitdownModel); + dbContext.SaveChanges(); + return "2900";//调试无信息挂具写入数值 + } + else if (waitdownlineModel != null) + { + if (!string.IsNullOrWhiteSpace(waitdownlineModel.materielNo))//挂具绑定物料表中,物料不为空时 + { + //查找车型长度后返回写入Plc + string sqlQuery = @$" + SELECT + b1.material_no AS materialNo, + b1.k46up_length AS k46upLength, + b1.k48up_length AS k48upLength, + b1.down_length AS downLength, + b2.order_code AS definefield1, + b2.line_code AS definefield2, + b2.prod_code AS definefield3 + FROM + base_materialinfo b1 + JOIN base_production_order_split b2 + ON b2.prod_code LIKE CONCAT('%', b1.material_no, '%') + WHERE + b2.order_code = '{waitdownlineModel.materielNo}'"; + var QueryOrder = dbContext.ExecuteSqlQuery(sqlQuery).ToList(); + if (QueryOrder?.Count > 0 && !string.IsNullOrWhiteSpace(QueryOrder[0].downLength)) + { + RelieveCarAndMaterial(carno, waitdownlineModel.materielNo);//下件点下件解除挂具与物料信息绑定关系 + return QueryOrder[0].downLength; + } + } + else if (string.IsNullOrWhiteSpace(waitdownlineModel.materielNo))//挂具绑定物料表中,物料为空时 + { + return "2900";//与电气沟通是空时写入长度最小值 + } + } + //添加下线记录后修正任务和等待下线记录表 + void RelieveCarAndMaterial(int carno, string vin) + { + //挂具在下件点交互完成后记录下线数据 + var orderMessage = dbContext.BaseProductionOrderSplit.Where(t => t.orderCode == vin).ToList(); + if (orderMessage?.Count > 0 && waitdownlineModel != null) + { + string XianBie = orderMessage[0].lineCode; + char lastChar = XianBie[XianBie.Length - 1]; + var XianBieNum = Char.GetNumericValue(lastChar).ToString(); + BaseFollowmessage baseFollowmessage = new() + { + id = Guid.NewGuid().ToString(), + sid = orderMessage[0].id, + vinCode = orderMessage[0].orderCode, + upSite = waitdownlineModel.createBy, + downSite = XianBieNum == "1" ? "K02" : "K07", + carNo = carno, + materialName = orderMessage[0].prodDesc, + lineCode = orderMessage[0].lineCode, + lineName = orderMessage[0].lineCode == "一线" ? "一线" : "二线", + isover = 1, + est = orderMessage[0].est, + quantity = orderMessage[0].quantity, + actualquantity = 1, + productionSequence = orderMessage[0].productionSequence, + createBy = "OrderOverInsert", + createDate = DateTime.Now + }; + dbContext.Add(baseFollowmessage); + dbContext.SaveChanges(); + //解除base_waitdownline表中挂具与任务VIN码绑定关系,修改车辆线上状态为已下线 + dbContext.BaseWaitdownline.Where(t => t.id == waitdownlineModel.id).Update(a => new BaseWaitdownline() { materielNo = string.Empty, materielNum = 0, createTime = DateTime.Now, downline = 2, createBy = string.Empty, definefield1 = string.Empty}); + //修改base_production_order_split表订单为结束状态 + dbContext.BaseProductionOrderSplit.Where(t => t.id == orderMessage[0].id).Update(a => new BaseProductionOrderSplit() { isover = 1 }); + } + } + //LogManager.Info($"OutWarePoint类 WriteMaterialMessage方法未确认返回值,从底部返回NULL!"); + return ""; + } + catch (Exception ex) + { + //LogManager.Info($"错误日志输出 >>> OutWarePoint类 WriteMaterialMessage 方法报错 {ex}"); + LogManager.Error(ex); + return null; + } + + } + } +} diff --git a/src/Khd.Core.Wcs/Wcs/UpLine.cs b/src/Khd.Core.Wcs/Wcs/UpLine.cs new file mode 100644 index 0000000..c1ca405 --- /dev/null +++ b/src/Khd.Core.Wcs/Wcs/UpLine.cs @@ -0,0 +1,338 @@ +using Khd.Core.Domain.Dto.wcs; +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Khd.Core.Wcs.Global; +using Masuit.Tools.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; +using Thrift.Server; +using Thrift.Transport; +using ThriftService; +using static AngleSharp.Css.Values.CssRadialGradientValue; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Thrift.Protocol; +using System.Security.Cryptography.Xml; +using Z.EntityFramework.Plus; +using System.Net.NetworkInformation; + +namespace Khd.Core.Wcs.Wcs +{ + /// + /// 上件点线程 + /// + public class UpLine : WcsServer + { + private readonly IHost _host; + private readonly Plc.S7.Plc _plc; + List ScanPoint { get; set; }//点位信息 + BaseSitenode? sitenode { get; set; }//站台信息 + + NodeSetting? NodeSettingCarNo { get; set; } + NodeSetting? NodeSettingCarState { get; set; } + NodeSetting? NodeSettingCarRun { get; set; } + NodeSetting? NodeSettingWcsState { get; set; } + NodeSetting? NodeSettingWcsSend { get; set; } + NodeSetting? NodeSettingWcsSendMaterial { get; set; } + NodeSetting? NodeSettingPLCSendSendMaterialstate { get; set; } + public UpLine(IHost host, Plc.S7.Plc plc, string siteNo) + { + this._host = host; + this._plc = plc; + this.ScanPoint = StaticData.NodeSettingList.Where(t => t.siteNo == siteNo).ToList();//加载当前站点所对应的点位 + this.sitenode = StaticData.SiteNodeList.FirstOrDefault(t => t.siteNo == siteNo);//获取当前站台信息 + this.NodeSettingCarNo = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carno")); + this.NodeSettingCarState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carstate")); + this.NodeSettingCarRun = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("carrun")); + this.NodeSettingWcsState = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("plcsendK")); + this.NodeSettingWcsSend = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("wcsend")); + this.NodeSettingWcsSendMaterial = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("wcssendmessage")); + this.NodeSettingPLCSendSendMaterialstate = this.ScanPoint.FirstOrDefault(t => t.plcpointNo.Contains("plcsendmessage")); + try + { + //默认启动,清理plc的上位机写入点位值 + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSendMaterial.plcpointAddress, MainCentralControl.QingKongDianWei); + } + catch (Exception ex) + { + Console.WriteLine("站点" + siteNo + " 初始化数据异常" + ex.Message); + LogManager.Error(ex); + } + } + + + /// + /// 启动线程 + /// + public void StartPoint() + { + Thread TestThread1 = new Thread(new ParameterizedThreadStart(StartScanThrift)); + TestThread1.IsBackground = true; + TestThread1.Start(this.sitenode); + Thread TestThread2 = new Thread(new ParameterizedThreadStart(StartSendCarThrift)); + TestThread2.IsBackground = true; + TestThread2.Start(this.sitenode); + Thread TestThread3 = new Thread(ClearSendCar); + TestThread3.IsBackground = true; + TestThread3.Start(); + } + /// + /// 启动推送车辆到位队列 + /// + private void StartScanThrift(object NodeSettingModel) + { + var sitenodemodel = NodeSettingModel as BaseSitenode; + int TSpoint = Convert.ToInt32(sitenodemodel.thriftPort); + string IpAddress = sitenodemodel.siteIpaddress; + TTransport transport = new TSocket(IpAddress, TSpoint); + while (true) + { + try + { + //启动thrift 推送服务 + //推送车辆到位情况 + transport.Open(); + TProtocol protocol = new TBinaryProtocol(transport); + WcsThrift.Client client = new WcsThrift.Client(protocol); + client.hello(ReceiveCarNo("")); + } + catch (Exception ex) + { + LogManager.Info($"wcs上件控制类推送车辆到位信息Thrift方法报错 >>> {ex.Message}"); + } + finally + { + transport.Close(); + Thread.Sleep(1000); + } + } + } + + /// + /// 启动小车扫描监听队列 + /// + private void StartSendCarThrift(object NodeSettingModel) + { + SendCar(null, null, null); + var sitenodemodel = NodeSettingModel as BaseSitenode; + int JSport = Convert.ToInt32(sitenodemodel.siteServerport); + while (true) + { + try + { + //启动thrift接收服务 + //接收挂具与Vin物料信息,上件绑定信息 + WcsServer wcsServer = new WcsServer(); + wcsServer.SendCarEvent += SendCar; + WcsThrift.Processor processor = new WcsThrift.Processor(wcsServer); + TServerTransport serverTransport = new TServerSocket(JSport); + TServer server = new TSimpleServer(processor, serverTransport); + Console.WriteLine($"站点:{this.sitenode.siteNo},当前时间:{DateTime.Now},接收客户端发车方法启动.. "); + server.Serve(); + } + catch (Exception ex) + { + LogManager.Info($"wcs上件控制类控制发车Thrift方法报错 >>> {ex.Message}"); + } + finally + { + Thread.Sleep(1000); + } + } + } + + /// + /// 发车 + /// + /// + /// + /// + /// + public string SendCar(List carlist, string order_code, string amount) + { + #region 测试屏蔽 + //Console.WriteLine($"服务端发车触发 >>> carlist[0]:{carlist.FirstOrDefault()},vin条码:{order_code},车辆长度:{amount}"); + //return "11"; + #endregion + //通用逻辑:(1 是否空车 2 物料号 3 上件数量) + // 通用逻辑下,下一站点固定(配置) + // -判断小车是否到位,小车已下发去向,清空下发数据信息 + // - 判断 小车是否到位,小车未下发去向 + // - 进库区判断: 库存是否满; + // -特殊逻辑:下一站点多个站点,按多条线缓存数优先进少的;按物料 + 属性区分进哪个站点,可配置 + try + { + var carno = this._plc.Read(NodeSettingCarNo.plcpointAddress); + var carstate = this._plc.Read(NodeSettingCarState.plcpointAddress); + var carrun = this._plc.Read(NodeSettingCarRun.plcpointAddress); + var wcsstate = this._plc.Read(NodeSettingWcsState.plcpointAddress); + var wcsend = this._plc.Read(NodeSettingWcsSend.plcpointAddress); + if (carno != null && carstate != null && carrun != null && wcsstate != null && wcsend != null) + { + //正常读到小车信息 + if (Convert.ToInt32(carno) > 0 && Convert.ToInt32(wcsend) == 0 && Convert.ToInt32(wcsstate) == 0 && Convert.ToInt32(carstate) == 1 && Convert.ToInt32(carrun) == 0) + { + //根据上件点,物料号,获取下一点位信息 + var TargetTo = GetTargetTo(Convert.ToInt32(carno), order_code, this.sitenode.siteNo); + //业务处理 + if (string.IsNullOrWhiteSpace(TargetTo) || string.IsNullOrWhiteSpace(order_code)) + { + //if (string.IsNullOrWhiteSpace(TargetTo)) LogManager.Info($"错误日志输出 >>> 站点:{this.sitenode.siteNo},挂具:{carno}上件车辆去向有误!"); + //if (string.IsNullOrWhiteSpace(order_code)) LogManager.Info($"错误日志输出 >>> 站点:{this.sitenode.siteNo},挂具:{carno}Thrift传输Vin码为空!"); + Thread.Sleep(1000); + return null; + } + //写入PLC + var ToInt16QuXiang = MainCentralControl.getValue("2", TargetTo); + var ToInt16ChangDu = MainCentralControl.getValue("2", amount); + this._plc.Write(this.NodeSettingCarRun.plcpointAddress, ToInt16QuXiang);//写入小车去向 + this._plc.Write(this.NodeSettingWcsSend.plcpointAddress, MainCentralControl.WcsChuLiWanCheng);//写入wms处理完成 + this._plc.Write(this.NodeSettingWcsSendMaterial.plcpointAddress, ToInt16ChangDu);//写入车身长度 + LogManager.Info($"当前时间 :{DateTime.Now} >>> 上件站点 :{this.sitenode.siteNo}发车 ,挂具号:{carno},去向:{ToInt16QuXiang},Wcs处理完成:{MainCentralControl.WcsChuLiWanCheng},vin条码:{order_code},车身长度:{ToInt16ChangDu}"); + return "1";//发车成功反馈 + } + } + } + catch (Exception ex) + { + LogManager.Error(ex); + } + finally + { + Thread.Sleep(1000); + } + return null; + } + //根据上件点,物料号,获取下一点位信息 + public string GetTargetTo(int carno, string materialNo, string sideno) + { + try + { + if (string.IsNullOrWhiteSpace(materialNo) || string.IsNullOrWhiteSpace(sideno)) + { + return null; + } + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + //1 读出点位信息,下一站点 + + //2 根据物料查下一站点 + + var WaitdownlineModel = dbContext.BaseWaitdownline.Where(t => t.carNo == carno).FirstOrDefault(); + var basecarModel = dbContext.BaseCar.Where(t => t.carNo == carno).FirstOrDefault(); + var orderModel = dbContext.BaseProductionOrderSplit.Where(t => t.orderCode == materialNo).FirstOrDefault(); + if (WaitdownlineModel == null && basecarModel != null && orderModel != null)//Waitdownline不存在当前车辆信息 + { + //添加挂具与物料绑定信息 + BaseWaitdownline waitdownModel = new() + { + id = Guid.NewGuid(), + carId = basecarModel.id, + carNo = basecarModel.carNo, + carName = basecarModel.carName, + downline = 1, + materielNo = materialNo, + materielNum = 1, + isDelete = 0, + createTime = DateTime.Now, + createBy = sideno, + definefield1 = orderModel.lineCode + }; + dbContext.Add(waitdownModel); + dbContext.SaveChanges(); + } + else//Waitdownline存在当前车辆信息即改 + { + //base_waitdownline表中挂具与任务VIN码绑定,修改车辆线上状态为已上线 + dbContext.BaseWaitdownline.Where(t => t.carNo == carno).Update(a => new BaseWaitdownline() { materielNo = materialNo, materielNum = 1, downline = 1, createBy = sideno, createTime = DateTime.Now, isDelete = 0, definefield1 = orderModel.lineCode }); + } + return "1"; + } + catch (Exception ex) + { + LogManager.Info($"UpLine类GetTargetTo方法报错 >>> {ex.Message}"); + return null; + } + + } + /// + /// 返回给客户端车辆到位及车辆信息 + /// + /// 无用参数 + /// + public string ReceiveCarNo(string name) + { + + try + { + var carno = this._plc.Read(NodeSettingCarNo.plcpointAddress); + var carstate = this._plc.Read(NodeSettingCarState.plcpointAddress); + var carrun = this._plc.Read(NodeSettingCarRun.plcpointAddress); + var wcsstate = this._plc.Read(NodeSettingWcsState.plcpointAddress); + var wcsend = this._plc.Read(NodeSettingWcsSend.plcpointAddress); + if (carno != null && carstate != null && carrun != null && wcsstate != null && wcsend != null) + { + if (Convert.ToInt32(carno) > 0 && Convert.ToInt32(wcsend) == 1 && Convert.ToInt32(wcsstate) == 1) + { + return ""; + } + //正常读到小车信息 + if (Convert.ToInt32(carno) > 0 && Convert.ToInt32(wcsend) == 0 && Convert.ToInt32(wcsstate) == 0 && Convert.ToInt32(carstate) == 1 && Convert.ToInt32(carrun) == 0) + { + string TuiSongCarNo = carno.ToString(); + return TuiSongCarNo; + } + } + Thread.Sleep(1000); + return ""; + } + catch (Exception ex) + { + LogManager.Info($""); + return ""; + } + } + /// + /// 启动小车发出后清空预设点位线程 + /// + private void ClearSendCar() + { + while (true) + { + try + { + var carno = this._plc.Read(NodeSettingCarNo.plcpointAddress); + var carstate = this._plc.Read(NodeSettingCarState.plcpointAddress); + var carrun = this._plc.Read(NodeSettingCarRun.plcpointAddress); + var wcsstate = this._plc.Read(NodeSettingWcsState.plcpointAddress); + var wcsend = this._plc.Read(NodeSettingWcsSend.plcpointAddress); + //清除点位信息 + if (Convert.ToInt32(carno) == 0 && Convert.ToInt32(wcsend) == 1 && Convert.ToInt32(wcsstate) == 1) + { + this._plc.Write(NodeSettingCarRun.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSend.plcpointAddress, MainCentralControl.QingKongDianWei); + this._plc.Write(NodeSettingWcsSendMaterial.plcpointAddress, MainCentralControl.QingKongDianWei); + } + } + catch (Exception ex) + { + LogManager.Info($"发车后清空点位线程报错 >>> {ex.Message}"); + } + finally + { + Thread.Sleep(1000); + } + } + } + } +} diff --git a/src/Khd.Core.Wcs/appsettings.json b/src/Khd.Core.Wcs/appsettings.json new file mode 100644 index 0000000..ec7c557 --- /dev/null +++ b/src/Khd.Core.Wcs/appsettings.json @@ -0,0 +1,25 @@ +{ + "ConnectionStrings": { + //雅迪工控机数据库 + //"DefaultConnection": "server=192.168.0.81;port=3306;database=khd_suspension_chain;uid=root;pwd=123456;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True" + //mysql 本地 + "DefaultConnection": "server=localhost;port=3306;database=khd_suspension_chain;uid=root;pwd=root;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True" + //khd服务器 + //"DefaultConnection": "server=106.12.13.113;port=3336;database=khd_suspension_chain;uid=khd;pwd=khd@123;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True" + }, + "PlcConfig": { + "IP": "192.168.0.1", + "Port": "102", + "CpuType": 30, + "Rack": "0", + "Slot": "0" + }, + "PLCSetting": { + "Mode": "0", + "EquipCode": "H61,H62,H63", + "Interval": "2000", + "AutoStart": "0", + "WaitTime": "60000", + "SiteCode": "999" + } +} diff --git a/src/Khd.Core.Wcs/update.cs b/src/Khd.Core.Wcs/update.cs new file mode 100644 index 0000000..00c1c0b --- /dev/null +++ b/src/Khd.Core.Wcs/update.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using HtmlAgilityPack; +using System.Linq; +using System.Diagnostics; +class Update +{ + public void Update1() + { + string iisServerBaseUrl = "http://192.168.202.23:8080/"; // IIS服务器基本URL + string iisFolder = ""; // IIS服务器上的文件夹路径 + string localFolderBase = @"D:\"; // 本地文件夹的基本路径 + string localFolder = FindLocalFolder(localFolderBase); + SynchronizeFiles(iisServerBaseUrl, iisFolder, localFolder); + + // 继续运行应用程序的主要逻辑 + Console.WriteLine("应用程序启动..."); + // 在这里添加你的应用程序的主要逻辑 + + Console.ReadLine(); + } + + public string FindLocalFolder(string localFolderBase) + { + for (int i = 1; i <= 8; i++) + { + string localFolder = Path.Combine(localFolderBase, $"C{i}"); + + if (Directory.Exists(localFolder)) + { + return localFolder; + } + } + + return null; + } + + public void SynchronizeFiles(string serverBaseUrl, string serverFolder, string localFolder) + { + using (HttpClient client = new HttpClient()) + { + try + { + HttpResponseMessage response = client.GetAsync(new Uri(serverBaseUrl + serverFolder)).Result; + response.EnsureSuccessStatusCode(); + + string responseContent = response.Content.ReadAsStringAsync().Result; + + // 使用HtmlAgilityPack解析HTML内容 + HtmlDocument doc = new HtmlDocument(); + doc.LoadHtml(responseContent); + + // 查找所有包含文件名和文件夹名的标签 + var links = doc.DocumentNode.SelectNodes("//a"); + + if (links != null) + { + foreach (var link in links) + { + string itemName = link.InnerText; + string itemUrl = serverBaseUrl + serverFolder + itemName; + string localPath = Path.Combine(localFolder, itemName); + + if (link.InnerText.StartsWith("")) + { + // 这是一个文件夹 + if (!Directory.Exists(localPath)) + { + Directory.CreateDirectory(localPath); + } + + // 递归处理文件夹中的内容 + SynchronizeFiles(serverBaseUrl, serverFolder + itemName + "/", localPath); + } + else + { + // 这是一个文件 + if (!string.Equals(itemName, "web.config", StringComparison.OrdinalIgnoreCase)) + { + // 检查文件类型是否需要更新 + string fileExtension = Path.GetExtension(itemName).ToLower(); + if (ShouldUpdateFile(fileExtension)) + { + if (File.Exists(localPath)) + { + bool filesAreEqual = AreFilesEqual(itemUrl, localPath); + + if (!filesAreEqual) + { + DownloadFile(itemUrl, localPath); + Console.WriteLine($"已下载并覆盖文件: {itemName}"); + } + else + { + Console.WriteLine($"该文件为最新版本: {itemName}!!!"); + } + } + else + { + DownloadFile(itemUrl, localPath); + Console.WriteLine($"已下载文件: {itemName}"); + } + } + else + { + Console.WriteLine($"不需要更新的文件类型: {fileExtension}"); + } + } + } + } + // 在配置文件更新后,执行以下代码重启程序 + Process.Start("Up.bat"); + } + else + { + Console.WriteLine("未找到文件列表"); + } + } + catch (HttpRequestException ex) + { + Console.WriteLine($"HTTP请求失败: {ex.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"发生错误: {ex.Message}"); + } + } + } + + public bool ShouldUpdateFile(string fileExtension) + { + // 添加需要更新的文件类型 + string[] allowedExtensions = { ".exe", ".dll", ".json", ".xml" }; + + // 如果文件类型在允许更新的文件类型列表中,则返回 true,否则返回 false + return allowedExtensions.Contains(fileExtension); + } + + + + + public void DownloadFile(string fileUrl, string localFilePath) + { + using (HttpClient client = new HttpClient()) + { + try + { + HttpResponseMessage response = client.GetAsync(new Uri(fileUrl)).Result; + response.EnsureSuccessStatusCode(); + + using (Stream contentStream = response.Content.ReadAsStreamAsync().Result) + { + using (Stream fileStream = File.Create(localFilePath)) + { + contentStream.CopyTo(fileStream); + } + } + + Console.WriteLine($"已成功下载文件: {localFilePath}"); + } + catch (HttpRequestException ex) + { + // 处理连接断开或其他HTTP请求错误 + Console.WriteLine($"HTTP请求失败: {ex.Message}"); + // 在这里可以添加重试逻辑或其他处理方式 + } + catch (Exception ex) + { + Console.WriteLine($"发生错误: {ex.Message}"); + } + } + } + + + public bool AreFilesEqual(string file1Url, string file2Path) + { + byte[] file1Content = File.ReadAllBytes(file2Path); + + using (HttpClient client = new HttpClient()) + { + byte[] file2Content = client.GetByteArrayAsync(file1Url).Result; + + return StructuralComparisons.StructuralEqualityComparer.Equals(file1Content, file2Content); + } + } +} diff --git a/src/Khd.Core.Wpf/App.xaml b/src/Khd.Core.Wpf/App.xaml new file mode 100644 index 0000000..c6b4434 --- /dev/null +++ b/src/Khd.Core.Wpf/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/src/Khd.Core.Wpf/App.xaml.cs b/src/Khd.Core.Wpf/App.xaml.cs new file mode 100644 index 0000000..e57530d --- /dev/null +++ b/src/Khd.Core.Wpf/App.xaml.cs @@ -0,0 +1,106 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using Masuit.Tools; +namespace Khd.Core.Wpf +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + public App() + { + //UI线程未捕获异常处理事件(UI主线程) + this.DispatcherUnhandledException += App_DispatcherUnhandledException; + + //非UI线程未捕获异常处理事件(例如自己创建的一个子线程) + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + //Task线程内未捕获异常处理事件 + TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;//Task异常 + + IConfigurationRoot configuration = new ConfigurationBuilder() + .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) + .AddJsonFile("appsettings.json") + .Build(); + + PlcConfig.CpuType = configuration["PlcConfig:CpuType"].ToInt32(40); + PlcConfig.IP = configuration["PlcConfig:IP"].ToString(); + PlcConfig.Port = configuration["PlcConfig:Port"].ToInt32(102); + PlcConfig.Rack = (short)(configuration["PlcConfig:Rack"].ToInt32(0)); + PlcConfig.Slot = (short)(configuration["PlcConfig:Slot"].ToInt32(0)); + ConnectionStrings.ConnectionString = configuration["ConnectionStrings:DefaultConnection"].ToString(); + //配置文件 加载 Thrift 推送端口 + ThriftConfig.ThriftIpAddress = configuration["ThriftConfig:ThriftIpAddress"].ToString(); + ThriftConfig.JiesShouDuanKou = int.Parse(configuration["ThriftConfig:JiesShouDuanKou"]); + ThriftConfig.TuiSongDuankou = int.Parse(configuration["ThriftConfig:TuiSongDuankou"]); + ThriftConfig.UpSite = configuration["ThriftConfig:upsite"].ToString(); + FlagConfig.UpFlagNum = int.Parse(configuration["FlagConfig:UpFlagNum"]); + } + void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + try + { + HandleException(e.Exception); + } + catch (Exception ex) + { + HandleException(ex); + } + finally + { + e.Handled = true; + } + } + private static void HandleException(Exception ex) + { + //Logger log = new Logger(); + //log.Log(ex.ToString()); + } + //非UI线程未捕获异常处理事件(例如自己创建的一个子线程) + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + try + { + var exception = e.ExceptionObject as Exception; + if (exception != null) + { + HandleException(exception); + } + } + catch (Exception ex) + { + HandleException(ex); + } + finally + { + //ignore + } + } + private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + try + { + var exception = e.Exception as Exception; + if (exception != null) + { + HandleException(exception); + } + } + catch (Exception ex) + { + HandleException(ex); + } + finally + { + e.SetObserved(); + } + } + } +} diff --git a/src/Khd.Core.Wpf/AssemblyInfo.cs b/src/Khd.Core.Wpf/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/src/Khd.Core.Wpf/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/src/Khd.Core.Wpf/CSS/DataGridClass.xaml b/src/Khd.Core.Wpf/CSS/DataGridClass.xaml new file mode 100644 index 0000000..655d060 --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/DataGridClass.xaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/CSS/NormalSmlBtnClass.xaml b/src/Khd.Core.Wpf/CSS/NormalSmlBtnClass.xaml new file mode 100644 index 0000000..3cefa1e --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/NormalSmlBtnClass.xaml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/CSS/SearchBtnClass.xaml b/src/Khd.Core.Wpf/CSS/SearchBtnClass.xaml new file mode 100644 index 0000000..1fd606d --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/SearchBtnClass.xaml @@ -0,0 +1,604 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/CSS/SearchTextClass.xaml b/src/Khd.Core.Wpf/CSS/SearchTextClass.xaml new file mode 100644 index 0000000..c5548ea --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/SearchTextClass.xaml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/CSS/Style.xaml b/src/Khd.Core.Wpf/CSS/Style.xaml new file mode 100644 index 0000000..13963e6 --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/Style.xaml @@ -0,0 +1,120 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/CSS/TabControl.xaml b/src/Khd.Core.Wpf/CSS/TabControl.xaml new file mode 100644 index 0000000..7265b8f --- /dev/null +++ b/src/Khd.Core.Wpf/CSS/TabControl.xaml @@ -0,0 +1,604 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml b/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml new file mode 100644 index 0000000..0364763 --- /dev/null +++ b/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml @@ -0,0 +1,425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml.cs b/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml.cs new file mode 100644 index 0000000..eb56d52 --- /dev/null +++ b/src/Khd.Core.Wpf/Form/CheShenChangDuGuanLi.xaml.cs @@ -0,0 +1,262 @@ +using Khd.Core.Domain.Models; +using Khd.Core.EntityFramework; +using Masuit.Tools.Logging; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Markup; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Media.Media3D; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Z.EntityFramework.Plus; + +namespace Khd.Core.Wpf.Form +{ + /// + /// CheShenChangDuGuanLi.xaml 的交互逻辑 + /// + public partial class CheShenChangDuGuanLi : Window + { + private readonly IHost _host; + public CheShenChangDuGuanLi(IHost host) + { + InitializeComponent(); + this._host = host; + } + /// + /// 页面加载方法 + /// + /// + /// + private void Window_Loaded(object sender, RoutedEventArgs e) + { + try + { + GetCarMessage(""); + } + catch (Exception ex) + { + LogManager.Info($"窗体加载事件报错 >>> {ex.Message}"); + } + } + private void GetCarMessage(string sql) + { + try + { + string wheresql = string.IsNullOrEmpty(sql) ? "" : $"and material_no like '%{sql}'"; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var data = dbContext.ExecuteSqlQuery + ($@"SELECT id, + material_no as materialNo, + k46up_length as k46upLength, + k48up_length as k48upLength, + down_length as downLength + FROM `base_materialinfo` + where is_delete = 0 {wheresql} order by material_no;"); + if (data?.Count > 0) + { + List materialinfo_list = new(); + foreach (var item in data) + { + BaseMaterialinfo materialinfo_model = new(); + materialinfo_model.materialNo = item.materialNo; + materialinfo_model.k46upLength = item.k46upLength; + materialinfo_model.k48upLength = item.k48upLength; + materialinfo_model.downLength = item.downLength; + materialinfo_list.Add(materialinfo_model); + } + this.dtg_LoadCarMessage.ItemsSource = materialinfo_list; + this.dtg_LoadCarMessage.Items.Refresh(); + } + } + catch (Exception ex) + { + LogManager.Info($"获取辆号信息加载到datagrid方法报错 >>> {ex.Message}"); + } + } + /// + /// 显示序号事件 + /// + /// + /// + private void dgData_LoadingRow(object sender, DataGridRowEventArgs e) + { e.Row.Header = e.Row.GetIndex() + 1; } + /// + /// datagrid编辑文本框触发事件 + /// + /// + /// + private void dtg_LoadCarMessage_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) + { + try + { + if (e != null) + { + if (e.EditAction == DataGridEditAction.Commit) + { + // 获取鼠标点击的单元格 + DataGridCellInfo cellInfo = dtg_LoadCarMessage.CurrentCell; + int columnIndex = cellInfo.Column.DisplayIndex; + BaseMaterialinfo data = this.dtg_LoadCarMessage.SelectedItem as BaseMaterialinfo; + var editedTextbox = e.EditingElement as System.Windows.Controls.TextBox; // 假设编辑的是文本框 + var newValue = editedTextbox.Text; // 获取编辑后的值 + string zfc; + switch (columnIndex) + { + case 1: + zfc = "material_no"; + break; + case 2: + zfc = "k46up_length"; + break; + case 3: + zfc = "k48up_length"; + break; + case 4: + zfc = "down_length"; + break; + default: + zfc = ""; + break; + } + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + if (!string.IsNullOrWhiteSpace(data.materialNo)) + { + string setsql = zfc == "material_no" ? $",material_name ='{newValue}'" : ""; + dbContext.ExecuteSqlQuery($@" UPDATE base_materialinfo set {zfc} = '{newValue}'{setsql} where material_no = '{data.materialNo}';"); + this.dtg_LoadCarMessage.ItemsSource = null; + GetCarMessage(""); + LogManager.Info($"当前事件 :{DateTime.Now},修改字段{zfc}为{newValue} >>> 日志记录!"); + } + } + } + } + catch (Exception ex) + { + LogManager.Info("datagrid编辑文本框触发事件报错 >>> " + ex.Message.ToString()); + } + } + private void DataGrid_KeyDown(object sender, KeyEventArgs e) + { + try + { + if (e.Key == Key.Enter) + { + // Move focus away from the DataGrid to exit edit mode + Keyboard.ClearFocus(); + dgData_LoadingRow(null, null); + Window_Loaded(null, null); + } + } + catch (Exception) + { + } + + } + + private void cloce_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + /// + /// 保存按钮保存数据 + /// + /// + /// + private void inster_Click(object sender, RoutedEventArgs e) + { + try + { + + var datagridMessage = dtg_LoadCarMessage.ItemsSource as List; + if (datagridMessage != null) + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var material_model = dbContext.BaseMaterialinfo.Where(t => t.isDelete == 0).ToList(); + foreach (var item in datagridMessage) + { + var number = material_model.Find(C => C.materialNo == item.materialNo); + if (number == null && !string.IsNullOrWhiteSpace(item.materialNo)) + { + BaseMaterialinfo model = new BaseMaterialinfo(); + model.id = Guid.NewGuid(); + model.materialNo = item.materialNo; + model.k46upLength = string.IsNullOrWhiteSpace(item.k46upLength) ? "" : item.k46upLength; + model.k48upLength = string.IsNullOrWhiteSpace(item.k48upLength) ? "" : item.k48upLength; + model.downLength = string.IsNullOrWhiteSpace(item.downLength) ? "" : item.downLength; + model.isDelete = 0; + model.materialName = item.materialNo; + model.createTime = DateTime.Now; + model.createBy = "AdmianInsert"; + dbContext.Add(model); + var insertresult = dbContext.SaveChanges(); + string showResult = insertresult == 1 ? "成功" : "失败"; + MessageBox.Show($"物料{item.materialNo}添加操作{showResult}!", "操作成功提示!"); + this.dtg_LoadCarMessage.ItemsSource = null; + GetCarMessage(""); + } + } + } + } + catch (Exception ex) + { + MessageBox.Show($"数据添加报错 >>> {ex.Message}","操作失败提示!"); + } + } + /// + /// 添加按钮点击事件 + /// + /// + /// + private void jia_Click(object sender, RoutedEventArgs e) + { + try + { + var listinfo = this.dtg_LoadCarMessage.ItemsSource as List; + BaseMaterialinfo model = new BaseMaterialinfo(); + listinfo.Add(model); + dtg_LoadCarMessage.ItemsSource = listinfo; + dtg_LoadCarMessage.Items.Refresh(); + var lastItem = listinfo[listinfo.Count - 1]; + // 将数据源的最后一个元素滚动到可视区域 + this.dtg_LoadCarMessage.ScrollIntoView(lastItem); + } + catch (Exception ex) + { + MessageBox.Show($"数据添加报错 >>> {ex.Message}"); + } + } + /// + /// 查询按钮 + /// + /// + /// + private void ChaXunButton_Click(object sender, RoutedEventArgs e) + { + try + { + var liangxing = this.tbx_wuliaoname.Text.Trim().ToUpper().ToString(); + GetCarMessage(liangxing); + } + catch (Exception) + { + } + } + } +} diff --git a/src/Khd.Core.Wpf/Form/FormBoard.xaml b/src/Khd.Core.Wpf/Form/FormBoard.xaml new file mode 100644 index 0000000..0bf6082 --- /dev/null +++ b/src/Khd.Core.Wpf/Form/FormBoard.xaml @@ -0,0 +1,1054 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Khd.Core.Wpf/Form/FormBoard.xaml.cs b/src/Khd.Core.Wpf/Form/FormBoard.xaml.cs new file mode 100644 index 0000000..873c7ad --- /dev/null +++ b/src/Khd.Core.Wpf/Form/FormBoard.xaml.cs @@ -0,0 +1,1235 @@ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Threading; +using Masuit.Tools.Logging; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Khd.Core.EntityFramework; +using Khd.Core.Plc.S7; +using Khd.Core.Wpf; +using Thrift.Server; +using Thrift.Transport; +using ThriftService; +using Thrift.Protocol; +using Khd.Core.Domain.Models; +using Z.EntityFramework.Plus; +using Khd.Core.Wpf.Form; +using OfficeOpenXml; +using Microsoft.Win32; +using System.IO; +using AngleSharp.Dom; +using System.Windows; + +namespace XGL.FormItem +{ + /// + /// FormBoard.xaml 的交互逻辑 + /// + public partial class FormBoard : Window + { + private readonly IHost _host; + private Plc _plc; + /// + /// 时间刷新计时器 + /// + + private DispatcherTimer ShowTimer;//刷新时间 + + private DispatcherTimer PLCMessage;//呈现PLC机柜信息 + private object order_code; + private int UpState;//对应上件站点的状态,0为良好 1为损坏 + //FormShowSelect formSelect; + // public static int WeekNo = CommonHelper.WeekOfYear(DateTime.Now, new System.Globalization.CultureInfo("zh-CN")); + + /// + /// 构造方法 + /// + public FormBoard(IHost host) + { + InitializeComponent(); + this._host = host; + } + /// + /// 页面加载事件 + /// + /// + /// + private void FormBoard_Loaded(object sender, RoutedEventArgs e) + { + try + { + //设置显示任务栏 + if (WindowState == WindowState.Normal) + { + MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight; + WindowState = WindowState.Maximized; + } + + + LoadMaterial_GetMessage(""); + + + //连接PLC判断 + var plc = new Khd.Core.Plc.S7.Plc((CpuType)PlcConfig.CpuType, PlcConfig.IP, PlcConfig.Port, + PlcConfig.Rack, PlcConfig.Slot); + plc.Open(); + if (!plc.IsConnected) + { + MessageBox.Show("PLC连接失败,重新连接"); + } + else + { + this._plc = plc; + } + + ShowTimer = new System.Windows.Threading.DispatcherTimer(); + ShowTimer.Tick += new EventHandler(ShowCurTimer);//起个Timer一直获取当前时间 + ShowTimer.Interval = new TimeSpan(0, 0, 0, 1, 0); + ShowTimer.Start(); + + #region 测试client,调用wcs服务端 + + //TTransport transport = new TSocket("localhost", 9091); + //transport.Open(); + //TProtocol protocol = new TBinaryProtocol(transport); + //WcsThrift.Client client = new WcsThrift.Client(protocol); + //client.hello("Sunzy"); + + #endregion + + //启动Thriftserver + Thread ThriftThread = new Thread(StartThriftServer);//接收服务端的推送信息 + ThriftThread.IsBackground = true; + ThriftThread.Start(); + + PLCMessage = new DispatcherTimer(); + PLCMessage.Tick += new EventHandler(PLCmessage); //委托获取点位方法 + PLCMessage.Interval = new TimeSpan(0, 0, 0, 0, 300); + PLCMessage.Start(); + + //string result = ThriftConfig.UpSite == "k46" ? " K46" : " K48"; + //this.lab_Title.Content = (string)this.lab_Title.Content + result; + + //载具到位状态 + this.labzjydw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjydw.Visibility = System.Windows.Visibility.Hidden; + this.labzjwdw.Visibility = System.Windows.Visibility.Visible; + this.imgzjwdw.Visibility = System.Windows.Visibility.Visible; + + if (ThriftConfig.UpSite == "k48") + { + this.spl_sunhuaizhakoumianban.Visibility = System.Windows.Visibility.Visible; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var getFlagState = dbContext.BaseAmima.Where(t => t.name == "UpState").FirstOrDefault(); + if (getFlagState != null) + { + int zhuangtai = getFlagState.password == "2" ? 2 : 1; + string quxiang = getFlagState.direction == 1 ? "直通" : "弯通"; + if (zhuangtai == 2) + { + this.lab_shangjiandianzhuangtai.Content = "上件点正常!K22按照任务线别方向流转."; + this.lab_shangjiandianzhuangtai.Foreground = new SolidColorBrush(Colors.Green); + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Hidden; + } + else + { + this.lab_shangjiandianzhuangtai.Content = $"上件点损坏!人工维护去向:{quxiang}"; + this.lab_shangjiandianzhuangtai.Foreground = new SolidColorBrush(Colors.Red); + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Visible; + } + } + } + else + { + this.spl_sunhuaizhakoumianban.Visibility = System.Windows.Visibility.Hidden; + } + + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + + } + public void StartThriftServer() + { + try + { + int point = Convert.ToInt32(ThriftConfig.JiesShouDuanKou); + WcsServer wcsServer = new WcsServer(); + //wcsServer.SendCarEvent += SendCar; + wcsServer.helloEvent += hello; + WcsThrift.Processor processor = new WcsThrift.Processor(wcsServer); + TServerTransport serverTransport = new TServerSocket(point); + TServer server = new TSimpleServer(processor, serverTransport); + server.Serve(); + } + catch (Exception ex) + { + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + //设置图片为未到位 + this.CarNo.Clear(); + this.labzjydw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjydw.Visibility = System.Windows.Visibility.Hidden; + this.labzjwdw.Visibility = System.Windows.Visibility.Visible; + this.imgzjwdw.Visibility = System.Windows.Visibility.Visible; + })); + LogManager.Error(ex); + } + + } + public string hello(string name) + { + //Console.WriteLine($"hello {name}"); + //return $"ServerResponse receive from client name:{name}"; + try + { + if (!string.IsNullOrWhiteSpace(name)) + { + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + //设置图片为已到位、显示挂具号 + this.CarNo.Text = name; + this.labzjydw.Visibility = System.Windows.Visibility.Visible; + this.imgzjydw.Visibility = System.Windows.Visibility.Visible; + this.labzjwdw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjwdw.Visibility = System.Windows.Visibility.Hidden; + })); + } + else + { + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + //设置图片为未到位 + this.labzjydw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjydw.Visibility = System.Windows.Visibility.Hidden; + this.labzjwdw.Visibility = System.Windows.Visibility.Visible; + this.imgzjwdw.Visibility = System.Windows.Visibility.Visible; + })); + } + } + catch (Exception ex) + { + LogManager.Info($"客户端接收服务端推送的小车信息方法报错 >>> {ex.Message}"); + } + return ""; + } + + /// + /// 发车方法 + /// + /// + /// Vin条码号 + /// 三轮车辆长度 + public void SendCar(List carlist, string order_code, string amount) + { + TTransport transport = new TSocket(ThriftConfig.ThriftIpAddress, ThriftConfig.TuiSongDuankou); + try + { + //Console.WriteLine($"hello carlist[0]:{carlist.FirstOrDefault()}|order_code:{order_code}|amount:{amount}"); + //return $"ServerResponse receive from client order_code:{order_code}"; + transport.Open(); + TProtocol protocol = new TBinaryProtocol(transport); + WcsThrift.Client client = new WcsThrift.Client(protocol); + var ret = client.SendCar(carlist, order_code, amount); + if (ret == "1")//与服务端交互成功发车时 + { + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + //发车成功提示反馈 + this.lba_ThrifTtitle.Foreground = new SolidColorBrush(Colors.Green); + this.lba_ThrifTtitle.Content = "发车成功!"; + //设置为未到位 + this.labzjwdw.Visibility = System.Windows.Visibility.Visible; + this.imgzjwdw.Visibility = System.Windows.Visibility.Visible; + this.labzjydw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjydw.Visibility = System.Windows.Visibility.Hidden; + })); + } + else//与服务端交互失败时 + { + + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + this.lba_ThrifTtitle.Foreground = new SolidColorBrush(Colors.Red); + this.lba_ThrifTtitle.Content = $"Thrift交互失败!"; + //发车失败依然显示到位 + this.labzjwdw.Visibility = System.Windows.Visibility.Hidden; + this.imgzjwdw.Visibility = System.Windows.Visibility.Hidden; + this.labzjydw.Visibility = System.Windows.Visibility.Visible; + this.imgzjydw.Visibility = System.Windows.Visibility.Visible; + })); + } + } + catch (Exception ex) + { + LogManager.Info($"发车方法报错 >>> {ex.Message}"); + } + finally + { + transport.Close(); + transport.Dispose(); + } + } + /// + /// 显示序号事件 + /// + /// + /// + private void dgData_LoadingRow(object sender, DataGridRowEventArgs e) + { e.Row.Header = e.Row.GetIndex() + 1; } + /// + /// 显示日期事件timer + /// + /// + /// + public void ShowCurTimer(object sender, EventArgs e) + { + try + { + LabDate.Content = DateTime.Now.ToString("yyyy年MM月dd日"); + LabTime.Content = DateTime.Now.ToString("HH:mm:ss"); + } + catch (Exception) + { + } + } + /// + /// 点击退出按钮 + /// + /// + /// + private void btnExit_Click(object sender, RoutedEventArgs e) + { + if (MessageBox.Show("是否确认关闭程序", "提示信息", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.No) + { + + } + else + { + System.Environment.Exit(System.Environment.ExitCode); + } + } + private void Close(object sender, EventArgs e) + { + System.Environment.Exit(System.Environment.ExitCode); + } + /// + /// dgData_LoadingRow 序号列表 + /// + /// + /// + private void dgData_LoadingRow1(object sender, DataGridRowEventArgs e) + { + e.Row.Header = e.Row.GetIndex() + 1; + dynamic item = e.Row.Item; + if (item != null) + { + if (order_code != null) + { + if (item.order_code.Equals(order_code)) + { + e.Row.Background = new SolidColorBrush(Colors.Green); + } + } + } + } + /// + /// 上件操作按钮点击事件 + /// + /// + /// + private void BtnUpMenu_Click(object sender, RoutedEventArgs e) + { + this.UpperStart.Visibility = System.Windows.Visibility.Visible;//显示上件操作页面 + this.imgshangjiancaouzo.Source = new BitmapImage(new Uri(@"..\Resources\kucun2.png", UriKind.Relative));//上件按钮点击变选定中的蓝色 + this.splPlcMessage.Visibility = System.Windows.Visibility.Collapsed;//隐藏plc交互信息页面 + this.imgPlcMessage.Source = new BitmapImage(new Uri(@"..\Resources\caozuo.png", UriKind.Relative));//上件按钮点击变选定中的蓝色 + } + /// + /// PLC信息按钮点击事件 + /// + /// + /// + private void BtnPlcMessage_Click(object sender, RoutedEventArgs e) + { + this.UpperStart.Visibility = System.Windows.Visibility.Collapsed;//隐藏上件操作页面 + this.imgshangjiancaouzo.Source = new BitmapImage(new Uri(@"..\Resources\kucun1.png", UriKind.Relative));//上件按钮变未选中白色 + this.splPlcMessage.Visibility = System.Windows.Visibility.Visible;//显示plc交互信息页面 + this.imgPlcMessage.Source = new BitmapImage(new Uri(@"..\Resources\caozuo2.png", UriKind.Relative));//plc交互信息页面变选定中的蓝色 + } + + /// + /// 电气机柜信息面板定时器调用方法 + /// + /// + /// + private void PLCmessage(object sender, EventArgs e) + { + try + { + #region 注释代码折叠 周义帆 2023/11/16 + //string XiaoCheID = "0"; + //string GuaJuDaoWei = "0"; + //string XiaoCheQuXiang = "0"; + //string PLCChuLiZhuangTai = "0"; + //string WCSChuLiZhuangTai = "0"; + //string wcsWriteMessage = ""; + //string plcWriteMessageCLZW = ""; + //#region K02交互点 + //string plcpointAddress = "DB301.DBW0"; + ////小车ID + + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK02XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW4"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK02GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW6"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK02CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW4"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK02XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW6"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK02WCSCLZT.Text = WCSChuLiZhuangTai; + //} + ////WCS写入物料信息 + //plcpointAddress = "DB5.DBW60"; + //wcsWriteMessage = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK02WriteMaterial.Text = wcsWriteMessage; + //} + ////PLC写入物料信息处理状态 + //plcpointAddress = "DB4.DBW60"; + //plcWriteMessageCLZW = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK02WriteMaterialPlc.Text = plcWriteMessageCLZW; + //} + //#endregion + + //#region K07交互点 + //plcpointAddress = "DB302.DBW0"; + ////小车ID + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK07XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW8"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK07GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW10"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK07CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW8"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK07XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW10"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK07WCSCLZT.Text = WCSChuLiZhuangTai; + //} + ////WCS写入物料信息 + //plcpointAddress = "DB5.DBW62"; + //wcsWriteMessage = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK07WriteMaterial.Text = wcsWriteMessage; + //} + ////PLC写入物料信息处理状态 + //plcpointAddress = "DB4.DBW62"; + //plcWriteMessageCLZW = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK07WriteMaterialPlc.Text = plcWriteMessageCLZW; + //} + //#endregion + + //#region K18交互点 + //plcpointAddress = "DB304.DBW0"; + ////小车ID + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK18XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW16"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK18GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW18"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK18CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW16"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK18XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW18"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK18WCSCLZT.Text = WCSChuLiZhuangTai; + //} + //#endregion + + //#region K22交互点 + //plcpointAddress = "DB306.DBW0"; + ////小车ID + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK22XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW24"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK22GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW26"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK22CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW24"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK22XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW26"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK22WCSCLZT.Text = WCSChuLiZhuangTai; + //} + //#endregion + + //#region K46交互点 + //plcpointAddress = "DB307.DBW0"; + ////小车ID + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK46XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW28"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK46GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW30"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK46CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW28"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK46XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW30"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK46WCSCLZT.Text = WCSChuLiZhuangTai; + //} + ////WCS写入物料信息 + //plcpointAddress = "DB5.DBW64"; + //wcsWriteMessage = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK46WriteMaterial.Text = wcsWriteMessage; + //} + ////PLC写入物料信息处理状态 + //plcpointAddress = "DB4.DBW64"; + //plcWriteMessageCLZW = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK46WriteMaterialPlc.Text = plcWriteMessageCLZW; + //} + //#endregion + + //#region K48交互点 + //plcpointAddress = "DB308.DBW0"; + ////小车ID + //XiaoCheID = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheID != null) + //{ + // this.tbxK48XCID.Text = XiaoCheID; + //} + ////挂具到位状态 + //plcpointAddress = "DB4.DBW32"; + //GuaJuDaoWei = _plc.Read(plcpointAddress).ToString(); + //if (GuaJuDaoWei != null) + //{ + // this.tbxK48GJDW.Text = GuaJuDaoWei; + //} + ////PLC处理状态 + //plcpointAddress = "DB4.DBW34"; + //PLCChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (PLCChuLiZhuangTai != null) + //{ + // this.tbxK48CLZT.Text = PLCChuLiZhuangTai; + //} + ////挂具去向 + //plcpointAddress = "DB5.DBW32"; + //XiaoCheQuXiang = _plc.Read(plcpointAddress).ToString(); + //if (XiaoCheQuXiang != null) + //{ + // this.tbxK48XCQX.Text = XiaoCheQuXiang; + //} + ////WCS处理状态 + //plcpointAddress = "DB5.DBW34"; + //WCSChuLiZhuangTai = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK48WCSCLZT.Text = WCSChuLiZhuangTai; + //} + ////WCS写入物料信息 + //plcpointAddress = "DB5.DBW66"; + //wcsWriteMessage = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK48WriteMaterial.Text = wcsWriteMessage; + //} + ////PLC写入物料信息处理状态 + //plcpointAddress = "DB4.DBW66"; + //plcWriteMessageCLZW = _plc.Read(plcpointAddress).ToString(); + //if (WCSChuLiZhuangTai != null) + //{ + // this.tbxK48WriteMaterialPlc.Text = plcWriteMessageCLZW; + //} + //#endregion + #endregion + + // 调用方法,传递 K02 交互点的信息 + ReadPlcData("DB301.DBW0", 1, "2", tbxK02XCID, tbxK02GJDW, tbxK02CLZT, tbxK02XCQX, tbxK02WCSCLZT); + // 调用方法,传递 K07 交互点的信息 + ReadPlcData("DB302.DBW0", 1, "2", tbxK07XCID, tbxK07GJDW, tbxK07CLZT, tbxK07XCQX, tbxK07WCSCLZT); + // 调用方法,传递 K18 交互点的信息 + ReadPlcData("DB304.DBW0", 1, "2", tbxK18XCID, tbxK18GJDW, tbxK18CLZT, tbxK18XCQX, tbxK18WCSCLZT); + // 调用方法,传递 K22 交互点的信息 + ReadPlcData("DB306.DBW0", 1, "2", tbxK22XCID, tbxK22GJDW, tbxK22CLZT, tbxK22XCQX, tbxK22WCSCLZT); + // 调用方法,传递 K46 交互点的信息 + ReadPlcData("DB307.DBW0", 1, "2", tbxK46XCID, tbxK46GJDW, tbxK46CLZT, tbxK46XCQX, tbxK46WCSCLZT); + // 调用方法,传递 K48 交互点的信息 + ReadPlcData("DB308.DBW0", 1, "2", tbxK48XCID, tbxK48GJDW, tbxK48CLZT, tbxK48XCQX, tbxK48WCSCLZT); + + #region 1号上件点给PLC信息 + ReadAndUpdatePlcData("DB5.DBW60", "DB4.DBW60", tbxK48WriteMaterial, tbxK48WriteMaterialPlc); + #endregion + + #region 2号上件点给PLC信息 + ReadAndUpdatePlcData("DB5.DBW62", "DB4.DBW62", tbxK46WriteMaterial, tbxK46WriteMaterialPlc); + #endregion + + #region 1线下件点给PLC信息 + ReadAndUpdatePlcData("DB5.DBW64", "DB4.DBW64", tbxK07WriteMaterial, tbxK07WriteMaterialPlc); + #endregion + + #region 2线上件点给PLC信息 + ReadAndUpdatePlcData("DB5.DBW66", "DB4.DBW66", tbxK02WriteMaterial, tbxK02WriteMaterialPlc); + #endregion + + #region 嵌套方法折叠 + void ReadAndUpdatePlcData(string addressWcs, string addressPlc, TextBox textBoxMaterial, TextBox textBoxMaterialPlc) + { + // WCS键入物料信息 + string wcsChuLiZhuangTai = _plc.Read(addressWcs).ToString(); + if (wcsChuLiZhuangTai != null && !textBoxMaterial.IsFocused) + { + textBoxMaterial.Text = wcsChuLiZhuangTai; + } + + // PLC接收键入物料信息状态 + string chuLiZhuangTai = _plc.Read(addressPlc).ToString(); + if (chuLiZhuangTai != null) + { + textBoxMaterialPlc.Text = chuLiZhuangTai; + } + } + #endregion + + //显示K46上件点状态 + string K46UpState = _plc.Read("DB4.DBW70").ToString(); + if (K46UpState != null && !this.tbx_K46State.IsFocused) + { + string k46state = K46UpState == "1" ? "启用上件" : "直接放行"; + this.tbx_K46State.Text = k46state; + } + //显示K48上件点状态 + string K48UpState = _plc.Read("DB4.DBW72").ToString(); + if (K48UpState != null && !this.tbx_K48State.IsFocused) + { + string k48state = K48UpState == "1" ? "启用上件" : "直接放行"; + this.tbx_K48State.Text = k48state; + } + string state = ThriftConfig.UpSite == "k46" ? K46UpState : K48UpState; + string result = state == "1" ? "启用上件" : "直接放行"; + this.lab_Title.Content = $"上件点:{ThriftConfig.UpSite},使用模式:{result}"; + this.tBoxMessage.IsEnabled = state == "1" ? true : false; + } + catch (Exception ex) + { + LogManager.Info($"电气机柜信息面板定时器调用方法报错 >>> {ex.Message}"); + } + } + + /// + /// PLC读取方法封装 + /// + /// plc地址位 + /// plc数据类型(int类型默认为1) + /// plc地址位长度(string类型默认为2) + /// 载具号文本输入框 + /// 载具到位文本输入框 + /// PLC车辆状态文本输入框 + /// 载具去向文本输入框 + /// WCS处理状态文本输入框 + private void ReadPlcData(string plcAddress, int plcDataType, string plcValueLength, + TextBox textBoxXcid, TextBox textBoxGjdw, TextBox textBoxClzt, + TextBox textBoxXcqx, TextBox textBoxWcsclzt) + { + try + { + // 载具号 + ReadAndSetText(plcAddress, textBoxXcid); + + // 载具到位 + //DQXX.plcpointAddress = $"DB4.DBW{GetOffset(plcAddress, 0)}"; + string gjdw = $"DB4.DBW{GetOffset(plcAddress, 0)}"; + ReadAndSetText(gjdw, textBoxGjdw); + + // PLC处理状态 + //DQXX.plcpointAddress = $"DB4.DBW{GetOffset(plcAddress, 2)}"; + string plcclzt = $"DB4.DBW{GetOffset(plcAddress, 2)}"; + ReadAndSetText(plcclzt, textBoxClzt); + + // 小车去向 + //DQXX.plcpointAddress = $"DB5.DBW{GetOffset(plcAddress, 0)}"; + string xcqx = $"DB5.DBW{GetOffset(plcAddress, 0)}"; + ReadAndSetText(xcqx, textBoxXcqx, textBoxXcqx.IsFocused); + + // WCS处理完成 + //DQXX.plcpointAddress = $"DB5.DBW{GetOffset(plcAddress, 2)}"; + string wcsclzt = $"DB5.DBW{GetOffset(plcAddress, 2)}"; + ReadAndSetText(wcsclzt, textBoxWcsclzt, textBoxWcsclzt.IsFocused); + //-------------------------------------------------------------------------以下为方法内部嵌套方法 + //嵌套方法1 + void ReadAndSetText(string dw, TextBox textBox, bool checkFocus = false) + { + string value = _plc.Read(dw).ToString(); + if (value != null && (!checkFocus || !textBox.IsFocused)) + { + textBox.Text = value; + } + } + //嵌套方法2 + int GetOffset(string PlcAddress, int baseOffset) + { + int result = int.Parse(PlcAddress.Substring(4, 1)) * 4 + baseOffset; + return result; + } + + } + catch (Exception ex) + { + LogManager.Info($"PLC读取方法封装报错 >>> {ex.Message}"); + } + + } + /// + /// 清除信息按钮 + /// + /// + /// + private void btnsendNullCar_Click(object sender, RoutedEventArgs e) + { + try + { + this.tBoxMessage.Clear(); + this.tbx_CheLiangChangDu.Clear(); + this.lba_ThrifTtitle.Content = string.Empty; + } + catch (Exception) + { + } + } + + + /// + /// 使用DataTable当做数据传递中介向DataGrid中传输数据源 + /// + private void LoadMaterial_GetMessage(string wheresql) + { + try + { + string pjsql = string.IsNullOrEmpty(wheresql) ? "" : $"where order_code like '%{wheresql}'"; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + string sql = $@"SELECT + line_code AS lineCode, + order_code AS orderCode, + prod_code AS prodCode, + est, + IsOver AS isover + FROM base_production_order_split + {pjsql} + ORDER BY est ,IsOver"; + var QueryOrder = dbContext.ExecuteSqlQuery(sql); + #region 注释代码折叠 + //($@"SELECT + + // a.line_code AS lineCode, + + // a.order_code AS orderCode, + + // a.prod_code AS prodCode, + + // a.est, + + // a.IsOver AS isover, + + // CONVERT(b.downline, CHAR) AS siteCode + + // FROM base_production_order_split a + + // LEFT JOIN base_waitdownline b + + // ON a.order_code = b.materiel_no {sql} ORDER BY est ,IsOver "); + //var QueryOrder = dbContext.BaseProductionOrderSplit.OrderBy(t => t.est).OrderBy(t => t.isover).ToList(); + #endregion + if (QueryOrder?.Count > 0) + { + DataTable dt = new DataTable(); + dt.Columns.Add("order_code"); + dt.Columns.Add("prod_code"); + dt.Columns.Add("line_code"); + dt.Columns.Add("est"); + dt.Columns.Add("isover"); + foreach (var i in QueryOrder) + { + DataRow dr = dt.NewRow(); + dr["order_code"] = i.orderCode.ToString(); + dr["prod_code"] = i.prodCode.ToString(); + dr["line_code"] = i.lineCode.ToString(); + dr["est"] = i.est.ToString(); + dr["isover"] = i.isover.ToString(); + switch (i.isover) + { + case 1: + dr["isover"] = "已完成"; + break; + case 0: + dr["isover"] = "未完成"; + break; + default: + dr["isover"] = "状态错误!"; + break; + } + dt.Rows.Add(dr); + } + this.LoadMaterial.ItemsSource = dt.DefaultView; + this.LoadMaterial.Items.Refresh(); + } + } + catch (Exception ex) + { + LogManager.Info($"dagird获取信息报错1 >>> {ex.Message}"); + } + + } + + /// + /// 查询按钮点击事件 + /// + /// + /// + private void ChaXunButton_Click(object sender, RoutedEventArgs e) + { + string vin = this.MoHuChaXunWuLiaoName.Text.Trim().ToUpper().ToString(); + this.LoadMaterial.ItemsSource = null; + LoadMaterial_GetMessage(vin); + } + /// + /// 清空按钮 + /// + /// + /// + private void QingKongButton_Click(object sender, RoutedEventArgs e) + { + this.MoHuChaXunWuLiaoName.Clear(); + this.LoadMaterial.ItemsSource = null; + LoadMaterial_GetMessage(""); + } + /// + /// 上件点状态损坏确认按钮点击事件 + /// + /// + /// + private void Btn_shangjiansunhuai_Click(object sender, RoutedEventArgs e) + { + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Visible; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.BaseAmima.Where(t => t.name == "UpState").Update(a => new BaseAmima() { password = "1" }); + var data = dbContext.BaseAmima.Where(t => t.name == "UpState").FirstOrDefault(); + string quxiang = data.direction == 1 ? "直通" : "弯通"; + this.lab_shangjiandianzhuangtai.Content = $"上件点状态:损坏!K22固定去向:" + quxiang; + this.lab_shangjiandianzhuangtai.Foreground = new SolidColorBrush(Colors.Red); + } + /// + /// 上件点状态良好确认按钮点击事件 + /// + /// + /// + private void Btn_shangjianweixiu_click(object sender, RoutedEventArgs e) + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + this.lab_shangjiandianzhuangtai.Content = $"上件点状态:正常!K22走任务线别去向!"; + this.lab_shangjiandianzhuangtai.Foreground = new SolidColorBrush(Colors.Green); + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Hidden; + dbContext.BaseAmima.Where(t => t.name == "UpState").Update(a => new BaseAmima() { password = "2" }); + } + /// + /// 上件点状态维护损坏后固定K22去向1 + /// + /// + /// + private void Btn_K22QuXiang1_Click(object sender, RoutedEventArgs e) + { + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Visible; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.BaseAmima.Where(t => t.name == "UpState").Update(a => new BaseAmima() { direction = 1 }); + Btn_shangjiansunhuai_Click(null, null); + } + + /// + /// 上件点状态维护损坏后固定K22去向2 + /// + /// + /// + private void Btn_K22QuXiang2_Click(object sender, RoutedEventArgs e) + { + this.spl_sunhuaizhakouquxiang.Visibility = System.Windows.Visibility.Visible; + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.BaseAmima.Where(t => t.name == "UpState").Update(a => new BaseAmima() { direction = 2 }); + Btn_shangjiansunhuai_Click(null, null); + } + /// + /// 修改车身长度信息点击事件 + /// + /// + /// + private void BtnXiuGaiCheShenChangDu_Click(object sender, RoutedEventArgs e) + { + CheShenChangDuGuanLi cscdgl = new(_host); + cscdgl.Show(); + } + /// + /// 扫码枪扫码后发车操作 + /// + /// + /// + private void txtInput_PreviewKeyDown(object sender, KeyEventArgs e) + { + try + { + //输入字符长度为17且有回车按键响应 + if (this.tBoxMessage.Text.Trim().Length == 17 && e.Key == Key.Enter) + { + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + string VinResult = this.tBoxMessage.Text.Trim().ToUpper().ToString(); + if (string.IsNullOrWhiteSpace(VinResult)) + { + return; + } + var orderModel = dbContext.BaseProductionOrderSplit.Where(t => t.orderCode == VinResult).FirstOrDefault(); + if (orderModel == null) + { + return; + } + var waitdownlineModel = dbContext.BaseWaitdownline.Where(t => t.definefield1 == orderModel.lineCode).ToList(); + int FlagNum = waitdownlineModel.Count(); + if (FlagNum >= FlagConfig.UpFlagNum) + { + //如果上件数量大于等于设置的闸口数量时 + SolidColorBrush myBrush = new SolidColorBrush(); + myBrush.Color = System.Windows.Media.Color.FromRgb(255, 130, 0); // 设置RGB值为(255, 0, 0)(红色) + this.lba_ThrifTtitle.Foreground = myBrush; + this.lba_ThrifTtitle.Content = $"上线数量:{FlagNum}>=缓存数量:{FlagConfig.UpFlagNum},限制发车!"; + LogManager.Info($"当前时间:{DateTime.Now},物料:{orderModel.orderCode},线别:{orderModel.lineCode},数量:{FlagNum} >= 缓存数量:{FlagConfig.UpFlagNum},限制发车操作!"); + return; + } + string sqlQuery = @$" + SELECT + b1.material_no AS materialNo, + b1.k46up_length AS k46upLength, + b1.k48up_length AS k48upLength, + b1.down_length AS downLength, + b2.order_code AS definefield1, + b2.line_code AS definefield2, + b2.prod_code AS definefield3 + FROM + base_materialinfo b1 + JOIN base_production_order_split b2 + ON b2.prod_code LIKE CONCAT('%', b1.material_no, '%') + WHERE + b2.order_code = '{VinResult}'"; + var QueryOrder = dbContext.ExecuteSqlQuery(sqlQuery); + #region 周义帆 20231202 注释sql折叠 + //(@$"SELECT b1.material_no as materialNo, + //b1.k46up_length as k46upLength, + //b1.k48up_length as k48upLength, + //b1.down_length as downLength, + //b2.order_code as definefield1, + //b2.line_code as definefield2, + //b2.prod_code as definefield3 + //FROM base_materialinfo b1 + //JOIN base_production_order_split b2 ON b2.prod_code LIKE CONCAT('%', b1.material_no, '%') + //WHERE b2.order_code = '{vinText}'; "); + #endregion + if (QueryOrder?.Count == 0) + { + MessageBox.Show($"Vin:{VinResult}不存在对应车型长度", "发车操作提示!"); + return; + } + string changduText = ThriftConfig.UpSite == "k46" ? QueryOrder[0].k46upLength : QueryOrder[0].k48upLength; + changduText = changduText == "" ? "Null" : changduText; + string zhandianText = ThriftConfig.UpSite == "k46" ? "K46" : "K48"; + this.tbx_CheLiangChangDu.Text = changduText; + string CarLengthResult = this.tbx_CheLiangChangDu.Text.Trim().ToString(); + if (!string.IsNullOrWhiteSpace(CarLengthResult) && !string.IsNullOrWhiteSpace(VinResult)) + { + List carList = new List + { + $"上件站点 >>> {zhandianText}" + }; + SendCar(carList, VinResult, CarLengthResult); + LogManager.Info($"当前时间;{DateTime.Now},发车方法调用记录 >>> 上件站点:{carList[0]} , Vin条码号:{VinResult},车身长度:{CarLengthResult}"); + carList.Clear(); + this.LoadMaterial.ItemsSource = null; + LoadMaterial_GetMessage(""); + } + } + } + catch (Exception ex) + { + MessageBox.Show($"扫码发车方法报错 >>> {ex.Message}"); + } + } + /// + /// 订单导入按钮点击事件 + /// + /// + /// + private void btn_dingdandaoru_Click(object sender, RoutedEventArgs e) + { + try + { + Application.Current.Dispatcher.Invoke(new Action( + delegate + { + // 设置 EPPlus 许可上下文 + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "Excel Files|*.xls;*.xlsx"; + if (openFileDialog.ShowDialog() == true) + { + FileInfo fileInfo = new FileInfo(openFileDialog.FileName); + + using (ExcelPackage package = new ExcelPackage(fileInfo)) + { + ExcelWorksheet worksheet = package.Workbook.Worksheets.FirstOrDefault(); + + if (worksheet != null) + { + // 获取 Excel 表头 + var columnHeaders = worksheet.Cells[1, 1, 1, worksheet.Dimension.End.Column] + .Select(cell => cell.Text) + .ToList(); + BaseProductionOrderSplit orderMoedel = new(); + // 映射数据库字段,这里需要根据你的实际需求进行调整 + var columnMappings = new Dictionary + { + // 添加映射 + {columnHeaders[0],"production_sequence"}, + {columnHeaders[1],"line_code"}, + {columnHeaders[3],"prod_code"}, + {columnHeaders[4],"Quantity"}, + {columnHeaders[5],"order_code"} + }; + int ordernum = 0; + // 遍历 Excel 数据行 + for (int row = 2; row <= worksheet.Dimension.End.Row; row++) + { + // 创建 SQL 插入语句 + //string insertCommand = $"INSERT INTO YourTableName ({string.Join(", ", columnMappings.Values)}) VALUES ("; + List values = new(); + + // 遍历 Excel 数据列 + for (int col = 1; col <= worksheet.Dimension.End.Column; col++) + { + string columnName = columnHeaders[col - 1]; + string databaseColumnName; + // 映射 Excel 列名到数据库列名 + if (columnMappings.TryGetValue(columnName, out databaseColumnName)) + { + // 获取 Excel 单元格的值 + var cellValue = worksheet.Cells[row, col].Text; + // 添加值到插入语句 + values.Add(cellValue); + //values.Add($"'{cellValue.Replace("'", "''")}'"); + //if (!string.IsNullOrWhiteSpace(cellValue)) + //{ + // values.Add(cellValue); + //} + } + } + // 使用 LINQ 查询空值 + bool containsNull = values.Any(item => item == "" || item == null); + if (containsNull) + { + MessageBox.Show("Excel表格中存在空值,不执行导入操作!", "订单导入操作提示!"); + LogManager.Info($"按钮订单导入事件粗无日志 >>> Excel表格中存在空值!不执行导入操作!"); + continue; + } + // 获得插入语句 + //string fullInsertCommand = $"{insertCommand} {string.Join(", ", values)} )"; + var xianbiename = Char.GetNumericValue(values[1][values[1].Length - 1]).ToString(); + int orderSequence = int.Parse(values[0].Trim()); + using var scope = _host.Services.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var orderModel = dbContext.BaseProductionOrderSplit.OrderBy(t => t.productionSequence).ToList(); + var data = orderModel.Find(t => t.orderCode == values[4].Trim().ToUpper()); + if (data == null) + { + BaseProductionOrderSplit InserOrderModel = new() + { + id = Guid.NewGuid().ToString(), + lineCode = values[1].Trim(), + lineName = xianbiename == "1" ? "二线" : "一线", + orderCode = values[4].Trim().ToUpper(), + prodCode = values[2].Trim(), + prodDesc = values[2].Trim(), + productionSequence = orderSequence, + est = DateTime.Now, + isover = 0, + quantity = 1 + }; + dbContext.Add(InserOrderModel); + dbContext.SaveChanges(); + ordernum++; + } + } + MessageBox.Show($"{ordernum}条订单导入系统!", "订单导入操作提示!"); + this.LoadMaterial.ItemsSource = null; + LoadMaterial_GetMessage(""); + } + } + } + })); + } + catch (Exception ex) + { + MessageBox.Show($"订单导入失败 >>> {ex.Message}", "订单导入操作提示!"); + LogManager.Info($"按钮订单导入事件报错 >>> {ex.Message}"); + } + } + /// + /// 接收到载具号后,清空残留信息 + /// + /// + /// + private void CarNo_TextChanged(object sender, TextChangedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(this.tBoxMessage.Text) && this.tBoxMessage.Text.Length == 17) + { + //清空缓存信息 + this.CarNo.Clear(); + this.tBoxMessage.Clear(); + this.tbx_CheLiangChangDu.Clear(); + this.lba_ThrifTtitle.Content = string.Empty; + } + } + } +} diff --git a/src/Khd.Core.Wpf/Global/PlcConfig.cs b/src/Khd.Core.Wpf/Global/PlcConfig.cs new file mode 100644 index 0000000..79f0cc5 --- /dev/null +++ b/src/Khd.Core.Wpf/Global/PlcConfig.cs @@ -0,0 +1,42 @@ + +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Khd.Core.Wpf +{ + public static class PlcConfig + { + public static int CpuType { get; set; } + public static string IP { get; set; } + public static int Port { get; set; } + public static short Rack { get; set; } + public static short Slot { get; set; } + } + public static class ConnectionStrings + { + public static string ConnectionString { get; set; } + } + public class CommonLock + { + public static object tableLock = new object(); + public static object Xlock = new object(); + } + public static class ThriftConfig + { + public static string ThriftIpAddress { get; set; } + public static int JiesShouDuanKou { get; set; } + public static int TuiSongDuankou { get; set; } + public static string UpSite { get; set; } + } + public static class FlagConfig + { + /// + /// 上件数量 + /// + public static int UpFlagNum { get; set; } + } +} diff --git a/src/Khd.Core.Wpf/Khd.Core.Wpf.csproj b/src/Khd.Core.Wpf/Khd.Core.Wpf.csproj new file mode 100644 index 0000000..ce4edfb --- /dev/null +++ b/src/Khd.Core.Wpf/Khd.Core.Wpf.csproj @@ -0,0 +1,462 @@ + + + + WinExe + net6.0-windows + enable + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + PreserveNewest + + + diff --git a/src/Khd.Core.Wpf/LoginPage.xaml b/src/Khd.Core.Wpf/LoginPage.xaml new file mode 100644 index 0000000..e93a5e7 --- /dev/null +++ b/src/Khd.Core.Wpf/LoginPage.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +