Compare commits
366
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8f6af7112 | ||
|
|
684513ee59 | ||
|
|
52bd0d01ce | ||
|
|
88b8bc5534 | ||
|
|
d3c8813012 | ||
|
|
054075a341 | ||
|
|
a0d53f9ec9 | ||
|
|
dfa8693be9 | ||
|
|
1e45f48c75 | ||
|
|
d833eca584 | ||
|
|
c966a7034c | ||
|
|
f051d51ae0 | ||
|
|
267159e0a9 | ||
|
|
26db172153 | ||
|
|
2e75f65b4a | ||
|
|
432cb91e5a | ||
|
|
b0083afa10 | ||
|
|
955b59a195 | ||
|
|
b904d0ca41 | ||
|
|
f34046df2a | ||
|
|
889782068e | ||
|
|
d12ee008db | ||
|
|
6c3c6cd0ef | ||
|
|
5803f06bdb | ||
|
|
a4acf30771 | ||
|
|
a4ef208233 | ||
|
|
be6b40b5cd | ||
|
|
8738edd0c5 | ||
|
|
c71fe75446 | ||
|
|
c619c7d3b8 | ||
|
|
f3e5527145 | ||
|
|
39941842d4 | ||
|
|
3488939995 | ||
|
|
0f2ef5a5bf | ||
|
|
55d2381e8a | ||
|
|
126322584f | ||
|
|
4d9ddef3ca | ||
|
|
7139cdbe80 | ||
|
|
93d7456ccd | ||
|
|
7a8ce7b6ab | ||
|
|
865528e344 | ||
|
|
02bb9ee9fa | ||
|
|
79de40515a | ||
|
|
e38c6c1199 | ||
|
|
dbb35a203e | ||
|
|
a88a094e86 | ||
|
|
ffaebbdf07 | ||
|
|
e7d04b55b3 | ||
|
|
8301971906 | ||
|
|
693cf7e4e2 | ||
|
|
2fa212e6ff | ||
|
|
a210a0d1f2 | ||
|
|
4cd191fcb2 | ||
|
|
e21833fb5f | ||
|
|
b746ceb34a | ||
|
|
83c1f0e817 | ||
|
|
a88a5a8db2 | ||
|
|
271200a8da | ||
|
|
b703297f5e | ||
|
|
637d1d021d | ||
|
|
dbe424b17a | ||
|
|
be0525a319 | ||
|
|
0ce130f0ef | ||
|
|
c1ee29cd06 | ||
|
|
28607e2729 | ||
|
|
f1bc71fb92 | ||
|
|
69b607f963 | ||
|
|
6ac3ea87ef | ||
|
|
38973e0030 | ||
|
|
37c89a4c9c | ||
|
|
0075362f92 | ||
|
|
38d28599ad | ||
|
|
c9a4b0f3ce | ||
|
|
b74f1fd644 | ||
|
|
26b7aa7e8b | ||
|
|
92f63e744d | ||
|
|
b0581d81ef | ||
|
|
540b9af871 | ||
|
|
d11f7107fe | ||
|
|
3c5a28f69d | ||
|
|
110b880304 | ||
|
|
7867044c0a | ||
|
|
88903f3dac | ||
|
|
4e223549ee | ||
|
|
2ea0df8faf | ||
|
|
061e85ca6b | ||
|
|
269e650b38 | ||
|
|
a3bfc7a43e | ||
|
|
c2ff40d816 | ||
|
|
eb09ae2eca | ||
|
|
d1b6247bb7 | ||
|
|
c5f52e06fa | ||
|
|
9700ba7900 | ||
|
|
bc72130d09 | ||
|
|
ad208f9633 | ||
|
|
ad2bfd3639 | ||
|
|
5e637ae02e | ||
|
|
63f8dd7467 | ||
|
|
aac3af8714 | ||
|
|
378917f59d | ||
|
|
76e7ccb4a6 | ||
|
|
953b02a6e8 | ||
|
|
71a66dca2d | ||
|
|
978c620287 | ||
|
|
8489dafdf7 | ||
|
|
cfc52f195b | ||
|
|
68a1eb9941 | ||
|
|
c1573390b5 | ||
|
|
5d60523e87 | ||
|
|
91c63e0792 | ||
|
|
0940df2f1a | ||
|
|
16a76720b8 | ||
|
|
7101fd679e | ||
|
|
86299da682 | ||
|
|
39b951b709 | ||
|
|
a475bd80f3 | ||
|
|
4e64bcd117 | ||
|
|
fbd0219a4a | ||
|
|
104a68d9dc | ||
|
|
33a5e479a9 | ||
|
|
5482d4ac96 | ||
|
|
0d984a77b3 | ||
|
|
7e95e18207 | ||
|
|
38fa86d243 | ||
|
|
4c1ce73de9 | ||
|
|
eea80d39c0 | ||
|
|
15dc325275 | ||
|
|
ca333d1772 | ||
|
|
a4ece06b05 | ||
|
|
992452052e | ||
|
|
75192bbd09 | ||
|
|
6199bd48c5 | ||
|
|
25434d70d4 | ||
|
|
de6dd45dcb | ||
|
|
74f2e99ed3 | ||
|
|
99b1f4a171 | ||
|
|
8c10aa3752 | ||
|
|
7170da4a70 | ||
|
|
842f009b42 | ||
|
|
7f30ca0cd1 | ||
|
|
93e207c211 | ||
|
|
64cafc088a | ||
|
|
8a0da27a07 | ||
|
|
5a8478e782 | ||
|
|
368cd95114 | ||
|
|
33ef914984 | ||
|
|
3eb0b87f0d | ||
|
|
c13cb725f0 | ||
|
|
c6d4a016b5 | ||
|
|
eda0644b4b | ||
|
|
711dfc03ef | ||
|
|
93459ccfab | ||
|
|
5b73835d94 | ||
|
|
009aefb7c9 | ||
|
|
d2e18d615d | ||
|
|
69b475eb77 | ||
|
|
9848b97082 | ||
|
|
0b429ffbc2 | ||
|
|
ef146efaf0 | ||
|
|
431061a685 | ||
|
|
b6edd9c0a0 | ||
|
|
2210bb8240 | ||
|
|
8d7c1adf37 | ||
|
|
3376ac41b4 | ||
|
|
f9181fee74 | ||
|
|
4c77d6c06e | ||
|
|
b7db39853b | ||
|
|
66157ba988 | ||
|
|
65ab19290f | ||
|
|
e7e6fc7920 | ||
|
|
94c7279da1 | ||
|
|
2136695b80 | ||
|
|
c0f9b0ffa0 | ||
|
|
c4ef22436f | ||
|
|
0b73871518 | ||
|
|
f7dc6fee5e | ||
|
|
ba848a167d | ||
|
|
dcb0bf9a88 | ||
|
|
f6433134c1 | ||
|
|
7e3df77b5b | ||
|
|
ff5954d85b | ||
|
|
08dff1272b | ||
|
|
c09e266b2c | ||
|
|
d5c8fd1c8f | ||
|
|
a34740e838 | ||
|
|
836ba5a41f | ||
|
|
545a747ebc | ||
|
|
23c83ab452 | ||
|
|
bc9e583c29 | ||
|
|
18cef617da | ||
|
|
4ee736cff5 | ||
|
|
4e40dc8601 | ||
|
|
a5a68ec457 | ||
|
|
e26d88d437 | ||
|
|
98ba7d2dee | ||
|
|
78835c1af6 | ||
|
|
2522d1da9f | ||
|
|
8983c01591 | ||
|
|
61b1320d4f | ||
|
|
2c8a0922d1 | ||
|
|
b078d9586d | ||
|
|
6e35de5f4d | ||
|
|
2dbe4cded4 | ||
|
|
10865c6878 | ||
|
|
ee1353fe54 | ||
|
|
5cefd7fc3a | ||
|
|
ab0e5ea82a | ||
|
|
b2276c086c | ||
|
|
4fa462661c | ||
|
|
2b30d0cf34 | ||
|
|
df2c56e6c4 | ||
|
|
a997c3d7be | ||
|
|
3c964f2aab | ||
|
|
c445b3e098 | ||
|
|
2ea2a2592e | ||
|
|
27072598cd | ||
|
|
4a2d4772d7 | ||
|
|
2bf00606d3 | ||
|
|
3773e1d96c | ||
|
|
19ddc753c7 | ||
|
|
0c41c750e8 | ||
|
|
4bfd5c8e11 | ||
|
|
f0fb70e38b | ||
|
|
6e5bcafec0 | ||
|
|
d5cb18c910 | ||
|
|
3a2069b255 | ||
|
|
80c651dbfd | ||
|
|
5a3b9b57c4 | ||
|
|
ead350eef3 | ||
|
|
b43657a4db | ||
|
|
8fffb9e366 | ||
|
|
0fdd229141 | ||
|
|
2f1ada3f5b | ||
|
|
988a8c6642 | ||
|
|
42ee9eaee3 | ||
|
|
3f28051d79 | ||
|
|
65f13cc868 | ||
|
|
cd8ba3cda3 | ||
|
|
a5936c8f19 | ||
|
|
2842425673 | ||
|
|
06a401a1ff | ||
|
|
3a2fc50fec | ||
|
|
38a58ed8b7 | ||
|
|
c7cba20de0 | ||
|
|
6311df49c3 | ||
|
|
f1d32a39ba | ||
|
|
224b53f774 | ||
|
|
1e2777c5e7 | ||
|
|
c3c21649fb | ||
|
|
99ccabc142 | ||
|
|
f1e6e94450 | ||
|
|
320cddb177 | ||
|
|
42002e3fa4 | ||
|
|
77cabfd712 | ||
|
|
c933e2d759 | ||
|
|
f3f0a46627 | ||
|
|
b5fb4c2947 | ||
|
|
f6cce9df3c | ||
|
|
c0385c0782 | ||
|
|
db43bdb652 | ||
|
|
6bdc10f4a7 | ||
|
|
9692dc7f4f | ||
|
|
6795793858 | ||
|
|
a2c564d789 | ||
|
|
3bc9e731e8 | ||
|
|
95c5f09238 | ||
|
|
0a45d66c0b | ||
|
|
96408eaef3 | ||
|
|
cae3ca7cdf | ||
|
|
c8adc093e0 | ||
|
|
4395516216 | ||
|
|
9281b52464 | ||
|
|
6571de7c32 | ||
|
|
a75d66e028 | ||
|
|
e839148a6f | ||
|
|
65b502a9b1 | ||
|
|
9cde62b2e2 | ||
|
|
cc86c38246 | ||
|
|
357df87f49 | ||
|
|
4b33da565c | ||
|
|
d11f855325 | ||
|
|
eef1f31a21 | ||
|
|
b878ee96a5 | ||
|
|
97eed27458 | ||
|
|
fe08550dc3 | ||
|
|
8a0ae8080f | ||
|
|
ab4025e55f | ||
|
|
22fffe602c | ||
|
|
e4c0219783 | ||
|
|
3362fdfaff | ||
|
|
6093db4502 | ||
|
|
5e7598667b | ||
|
|
4fe8729b85 | ||
|
|
b1dd6666f1 | ||
|
|
a3613889e1 | ||
|
|
7b87d63531 | ||
|
|
d838c78a8a | ||
|
|
7a8bef501a | ||
|
|
9863b54bc8 | ||
|
|
e57c6a6a02 | ||
|
|
afa215534c | ||
|
|
780d44166e | ||
|
|
35e4078dda | ||
|
|
47e09584f2 | ||
|
|
fc8e82c0e8 | ||
|
|
b729b08da2 | ||
|
|
3209ff303c | ||
|
|
41693d9c83 | ||
|
|
8f02f4ff31 | ||
|
|
1525bb7c1a | ||
|
|
2aeaf42325 | ||
|
|
bf4fb469da | ||
|
|
efd0515295 | ||
|
|
be8ac28bc7 | ||
|
|
56dc89ac82 | ||
|
|
62ffc94c49 | ||
|
|
50a38bc557 | ||
|
|
3e643854bc | ||
|
|
0daefb93bb | ||
|
|
f5731c67a0 | ||
|
|
d2ae5f3e83 | ||
|
|
c4d332b68b | ||
|
|
1daa0212ef | ||
|
|
1748a83ec8 | ||
|
|
b3d6b5a59f | ||
|
|
891795b101 | ||
|
|
a20b0761fd | ||
|
|
3b9e8d754d | ||
|
|
fed3281525 | ||
|
|
c1d966d86a | ||
|
|
b2d2f8c13b | ||
|
|
4e5e3d63e1 | ||
|
|
25602fb914 | ||
|
|
602d0d5308 | ||
|
|
f95198e40e | ||
|
|
d2b2bbb94b | ||
|
|
a4246c7ea4 | ||
|
|
498fc21412 | ||
|
|
3f07a506ad | ||
|
|
fb74e2d785 | ||
|
|
3a2290570d | ||
|
|
3b68e3f9fd | ||
|
|
3184b1da8e | ||
|
|
b70a10e697 | ||
|
|
3433bbbebe | ||
|
|
4c0a797e98 | ||
|
|
203c2498ac | ||
|
|
2023ea5221 | ||
|
|
cbb3d4f735 | ||
|
|
8acf219ba2 | ||
|
|
68884cd7df | ||
|
|
1629d82d65 | ||
|
|
17ab11bdf1 | ||
|
|
d2ef959f15 | ||
|
|
21b00e9b83 | ||
|
|
3d7aaaaa3d | ||
|
|
705f7760af | ||
|
|
3b385d592d | ||
|
|
cf6a16656b | ||
|
|
f50f9bfa5b | ||
|
|
77d8c95930 | ||
|
|
57c2019405 | ||
|
|
46432bc61b | ||
|
|
a4360f6993 | ||
|
|
97e0a764b8 | ||
|
|
7409af70ac |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
|
||||||
|
<Options Language="Neutral">
|
||||||
|
<Groups />
|
||||||
|
</Options>
|
||||||
|
</Root>
|
||||||
+35
-4
@@ -1,3 +1,5 @@
|
|||||||
|
AppDiagnostic/bin/
|
||||||
|
AppDiagnostic/obj/
|
||||||
Common/bin/
|
Common/bin/
|
||||||
Common/obj/
|
Common/obj/
|
||||||
Config/bin/
|
Config/bin/
|
||||||
@@ -30,6 +32,10 @@ LabelPrinting/bin/
|
|||||||
LabelPrinting/obj/
|
LabelPrinting/obj/
|
||||||
MergeResultsDBs/bin/
|
MergeResultsDBs/bin/
|
||||||
MergeResultsDBs/obj/
|
MergeResultsDBs/obj/
|
||||||
|
NfcC7_Dll/bin/
|
||||||
|
NfcC7_Dll/obj/
|
||||||
|
NfcC7_DLL.Tests/bin/
|
||||||
|
NfcC7_DLL.Tests/obj/
|
||||||
OrderManagement/bin/
|
OrderManagement/bin/
|
||||||
OrderManagement/obj/
|
OrderManagement/obj/
|
||||||
ProductionTracing/bin/
|
ProductionTracing/bin/
|
||||||
@@ -48,14 +54,18 @@ ResultsParser/bin/
|
|||||||
ResultsParser/obj/
|
ResultsParser/obj/
|
||||||
S640TestApp/bin/
|
S640TestApp/bin/
|
||||||
S640TestApp/obj/
|
S640TestApp/obj/
|
||||||
SharedDatabase/bin
|
SharedComponents/bin/
|
||||||
SharedDatabase/obj
|
SharedComponents/obj/
|
||||||
SchematicDrawing/bin
|
SharedDatabase/bin/
|
||||||
SchematicDrawing/obj
|
SharedDatabase/obj/
|
||||||
|
SchematicDrawing/bin/
|
||||||
|
SchematicDrawing/obj/
|
||||||
Statistics/bin/
|
Statistics/bin/
|
||||||
Statistics/obj/
|
Statistics/obj/
|
||||||
TBF/bin/
|
TBF/bin/
|
||||||
TBF/obj/
|
TBF/obj/
|
||||||
|
TBFTests/bin/
|
||||||
|
TBFTests/obj/
|
||||||
ToFirstMonitor/bin/
|
ToFirstMonitor/bin/
|
||||||
ToFirstMonitor/obj/
|
ToFirstMonitor/obj/
|
||||||
ToSecondMonitor/bin/
|
ToSecondMonitor/bin/
|
||||||
@@ -66,7 +76,28 @@ WorkflowConfigurator/bin/
|
|||||||
WorkflowConfigurator/obj/
|
WorkflowConfigurator/obj/
|
||||||
Workplace/bin/
|
Workplace/bin/
|
||||||
Workplace/obj/
|
Workplace/obj/
|
||||||
|
DataStreamInterfaceTest/bin/
|
||||||
|
DataStreamInterfaceTest/obj/
|
||||||
|
DataStreamMeter/bin/
|
||||||
|
DataStreamMeter/obj/
|
||||||
|
Events/bin/
|
||||||
|
Events/obj/
|
||||||
|
TracingDB/bin/
|
||||||
|
TracingDB/obj/
|
||||||
|
Users/bin/
|
||||||
|
Users/obj/
|
||||||
.vs/
|
.vs/
|
||||||
.idea/
|
.idea/
|
||||||
*.suo
|
*.suo
|
||||||
*.bak
|
*.bak
|
||||||
|
TBF.sln.DotSettings.user
|
||||||
|
/ExternalProjects
|
||||||
|
/GenesisCordonelTester
|
||||||
|
/packages
|
||||||
|
/GenesisCordonelInterface/bin/Debug/GenesisCordonelInterfaceLogger.log
|
||||||
|
/GenesisCordonelInterface/bin/Debug/GenesisCordonelTesterLogger.log
|
||||||
|
/GenesisCordonelInterface/bin/Debug/SirtConfig.json
|
||||||
|
/GenesisCordonelInterface/bin/Release
|
||||||
|
/GenesisCordonelInterface/obj/Debug
|
||||||
|
/GenesisCordonelInterface/obj/Release
|
||||||
|
/ExternalProjects
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||||
|
</startup>
|
||||||
|
<log4net>
|
||||||
|
<appender name="LogCacheAppender" type="Namespace.LogCacheAppender, AssemblyName">
|
||||||
|
<param name="Cache" value="YourCacheInstance" />
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<level value="DEBUG" />
|
||||||
|
<appender-ref ref="LogCacheAppender" />
|
||||||
|
</root>
|
||||||
|
</log4net>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>AppDiagnostic</RootNamespace>
|
||||||
|
<AssemblyName>AppDiagnostic</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Deployment" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="BufferedListView.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="DiagApi.cs" />
|
||||||
|
<Compile Include="LogAdapter.cs" />
|
||||||
|
<Compile Include="LogFilter.cs" />
|
||||||
|
<Compile Include="MainForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainForm.Designer.cs">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<EmbeddedResource Include="MainForm.resx">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
</Compile>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SharedComponents\SharedComponents.csproj">
|
||||||
|
<Project>{8f942729-f454-4c99-ba6c-746962065ae3}</Project>
|
||||||
|
<Name>SharedComponents</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\TBF_icon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
public class BufferedListView : ListView
|
||||||
|
{
|
||||||
|
public BufferedListView()
|
||||||
|
{
|
||||||
|
// Zapne dvojité bufferovanie pre ListView
|
||||||
|
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
this.UpdateStyles();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
public class DiagApi
|
||||||
|
{
|
||||||
|
private static MainForm diagWindow; // Udržiava referenciu na existujúce okno
|
||||||
|
|
||||||
|
public DiagApi()
|
||||||
|
{
|
||||||
|
if (diagWindow == null || diagWindow.IsDisposed) // Ak okno neexistuje, vytvoríme ho
|
||||||
|
{
|
||||||
|
diagWindow = new MainForm();
|
||||||
|
diagWindow.FormClosing += (s, e) =>
|
||||||
|
{
|
||||||
|
diagWindow = null; // Keď sa zavrie, vyčistíme referenciu
|
||||||
|
};
|
||||||
|
diagWindow.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diagWindow.BringToFront(); // Ak už beží, len ho presunieme na vrch
|
||||||
|
diagWindow.WindowState = FormWindowState.Normal; // Ak je minimalizované, obnovíme ho
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
using SharedComponents;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
public class LogAdapter
|
||||||
|
{
|
||||||
|
private ListView _listView;
|
||||||
|
public string _filterText = string.Empty;
|
||||||
|
private bool _isUserScrolling = false; // Indikátor, že používateľ manuálne scrolluje
|
||||||
|
private bool _autoScrollEnabled = true; // Indikátor, že automatické scrollovanie je povolené
|
||||||
|
private Timer _refreshTimer;
|
||||||
|
private const int RefreshInterval = 1000;
|
||||||
|
|
||||||
|
public LogAdapter(ListView listView)
|
||||||
|
{
|
||||||
|
_listView = listView;
|
||||||
|
|
||||||
|
// Nastavenie ListView
|
||||||
|
_listView.View = View.Details;
|
||||||
|
_listView.Columns.Clear();
|
||||||
|
_listView.Columns.Add("Logs");
|
||||||
|
|
||||||
|
_listView.Scrollable = true;
|
||||||
|
|
||||||
|
// Nastavenie šírky stĺpca na celú šírku ListView
|
||||||
|
AdjustColumnWidth();
|
||||||
|
|
||||||
|
// Udalosť pre dynamickú zmenu veľkosti stĺpca pri zmene veľkosti ListView
|
||||||
|
_listView.Resize += (sender, args) => AdjustColumnWidth();
|
||||||
|
|
||||||
|
// Pridanie udalosti pre manuálne skrolovanie
|
||||||
|
_listView.MouseWheel += ListView_MouseWheel;
|
||||||
|
|
||||||
|
// Inicializácia časovača na pravidelný refresh
|
||||||
|
_refreshTimer = new Timer { Interval = RefreshInterval };
|
||||||
|
_refreshTimer.Tick += (sender, args) => RefreshListView();
|
||||||
|
_refreshTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ListView_MouseWheel(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
_isUserScrolling = true;
|
||||||
|
|
||||||
|
// Ak sa manuálnym scrollovaním používateľ dostane na koniec, obnovíme automatické scrollovanie
|
||||||
|
if (IsOnLastItem())
|
||||||
|
{
|
||||||
|
_isUserScrolling = false;
|
||||||
|
_autoScrollEnabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_autoScrollEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshListView()
|
||||||
|
{
|
||||||
|
//LiveLogCache.Instance.AddLog(string.Format("-------------------------------RunDeviceAfter()-------------------------------"));
|
||||||
|
|
||||||
|
if (_listView.InvokeRequired)
|
||||||
|
{
|
||||||
|
_listView.Invoke(new Action(RefreshListView));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_listView.BeginUpdate();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Získanie pozície prvého viditeľného prvku pre stabilizáciu scrollovania
|
||||||
|
int topIndexBeforeRefresh = _listView.TopItem?.Index ?? 0;
|
||||||
|
|
||||||
|
// Pred pridaním nových položiek si pamätáme, či bol používateľ na poslednej položke
|
||||||
|
bool wasOnLastItem = IsOnLastItem();
|
||||||
|
|
||||||
|
// Uchovanie aktuálne označených položiek (indexov)
|
||||||
|
var selectedIndices = _listView.SelectedIndices.Cast<int>().ToList();
|
||||||
|
|
||||||
|
// Načítanie logov
|
||||||
|
var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.GetCount())
|
||||||
|
.Where(log => string.IsNullOrEmpty(_filterText) ||
|
||||||
|
log.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Vymazanie existujúcich položiek a pridanie nových
|
||||||
|
_listView.Items.Clear();
|
||||||
|
|
||||||
|
foreach (var log in logs)
|
||||||
|
{
|
||||||
|
var item = new ListViewItem(log)
|
||||||
|
{
|
||||||
|
BackColor = _listView.Items.Count % 2 == 0 ? Color.White : Color.FromArgb(240, 240, 240)
|
||||||
|
};
|
||||||
|
_listView.Items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obnovenie označených položiek
|
||||||
|
foreach (var index in selectedIndices)
|
||||||
|
{
|
||||||
|
if (index < _listView.Items.Count)
|
||||||
|
{
|
||||||
|
_listView.Items[index].Selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ak bol používateľ na poslednom prvku, nastavíme focus a scroll na posledný prvok
|
||||||
|
if (_autoScrollEnabled && wasOnLastItem && _listView.Items.Count > 0)
|
||||||
|
{
|
||||||
|
var lastItemIndex = _listView.Items.Count - 1;
|
||||||
|
_listView.EnsureVisible(lastItemIndex);
|
||||||
|
_listView.Items[lastItemIndex].Focused = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ak používateľ nebol na spodku, vrátime sa na predchádzajúcu pozíciu scrollu
|
||||||
|
if (topIndexBeforeRefresh < _listView.Items.Count)
|
||||||
|
{
|
||||||
|
_listView.TopItem = _listView.Items[topIndexBeforeRefresh];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_listView.EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void ApplyFilter(string filterText)
|
||||||
|
{
|
||||||
|
_filterText = filterText?.Trim() ?? string.Empty;
|
||||||
|
RefreshListView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsOnLastItem()
|
||||||
|
{
|
||||||
|
if (_listView.Items.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Získame poslednú položku
|
||||||
|
int lastItemIndex = _listView.Items.Count - 1;
|
||||||
|
var lastItem = _listView.Items[lastItemIndex];
|
||||||
|
|
||||||
|
// Overíme, či je posledná položka úplne viditeľná
|
||||||
|
return lastItem.Bounds.Bottom <= _listView.ClientRectangle.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableAutoScroll()
|
||||||
|
{
|
||||||
|
_autoScrollEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableAutoScroll()
|
||||||
|
{
|
||||||
|
_autoScrollEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AdjustColumnWidth()
|
||||||
|
{
|
||||||
|
if (_listView.Columns.Count > 0)
|
||||||
|
{
|
||||||
|
_listView.Columns[0].Width = _listView.ClientSize.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using SharedComponents;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
public class LogFilter
|
||||||
|
{
|
||||||
|
private ListView _listView;
|
||||||
|
private string _filterText = string.Empty; // Pre uloženie aktuálneho filtrovaného reťazca
|
||||||
|
|
||||||
|
// Konstruktor
|
||||||
|
public LogFilter(ListView listView)
|
||||||
|
{
|
||||||
|
_listView = listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metóda na aplikovanie filtra na ListView
|
||||||
|
public void ApplyFilter(string filterText)
|
||||||
|
{
|
||||||
|
_filterText = filterText.ToLower(); // Ukladáme text filtra bez ohľadu na veľkosť písmen
|
||||||
|
|
||||||
|
// Po aplikovaní filtra obnovíme zobrazenie
|
||||||
|
RefreshListView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshListView()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Skontrolujeme, či sme na hlavnom vlákne a v prípade potreby použijeme Invoke
|
||||||
|
if (_listView.InvokeRequired)
|
||||||
|
{
|
||||||
|
// Použijeme Invoke, ak nie sme na hlavnom vlákne
|
||||||
|
_listView.Invoke(new Action(RefreshListView));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Vymazanie existujúcich položiek
|
||||||
|
_listView.Items.Clear();
|
||||||
|
|
||||||
|
// Načítame všetky logy (ideálne z cache alebo databázy)
|
||||||
|
var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.Logs.Count);
|
||||||
|
|
||||||
|
// Pridáme len tie logy, ktoré spĺňajú filter
|
||||||
|
foreach (var log in logs)
|
||||||
|
{
|
||||||
|
if (log.ToLower().Contains(_filterText)) // Kontrola, či log obsahuje filter text
|
||||||
|
{
|
||||||
|
var listViewItem = new ListViewItem(log);
|
||||||
|
|
||||||
|
// Striedanie farieb riadkov
|
||||||
|
if (_listView.Items.Count % 2 == 0) // Párny index => biela
|
||||||
|
{
|
||||||
|
listViewItem.BackColor = Color.White;
|
||||||
|
}
|
||||||
|
else // Nepárny index => svetlá sivá
|
||||||
|
{
|
||||||
|
listViewItem.BackColor = Color.FromArgb(240, 240, 240); // Veľmi svetlá sivá
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pridanie efektu pre novú položku
|
||||||
|
ApplyNewItemEffect(listViewItem);
|
||||||
|
|
||||||
|
// Pridanie položky do ListView
|
||||||
|
_listView.Items.Add(listViewItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Zachytíme výnimku, ak sa niečo pokazí, a zobrazíme ju užívateľovi
|
||||||
|
MessageBox.Show($"An error occurred while refreshing the log view: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Môžete pridať ďalšie metódy na zobrazenie alebo efekt pre nové položky, ak je to potrebné
|
||||||
|
private void ApplyNewItemEffect(ListViewItem item)
|
||||||
|
{
|
||||||
|
// Prípadný efekt pre nový pridaný log
|
||||||
|
// Môžete pridať animáciu alebo zmenu farby atď.
|
||||||
|
item.ForeColor = Color.Green; // Napríklad zmeníme farbu písma na zelenú
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+278
@@ -0,0 +1,278 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
partial class MainForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
//private BufferedListView memoListView;
|
||||||
|
private System.Windows.Forms.ColumnHeader columnHeaderTime;
|
||||||
|
private System.Windows.Forms.ColumnHeader columnHeaderMessage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.columnHeaderTime = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||||
|
this.columnHeaderMessage = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||||
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
|
this.refreshMemoButton = new System.Windows.Forms.Button();
|
||||||
|
this.button6 = new System.Windows.Forms.Button();
|
||||||
|
this.button7 = new System.Windows.Forms.Button();
|
||||||
|
this.filterManagementPanel = new System.Windows.Forms.Panel();
|
||||||
|
this.button5 = new System.Windows.Forms.Button();
|
||||||
|
this.filterButton = new System.Windows.Forms.Button();
|
||||||
|
this.filterTextBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
|
this.wndControlPanel = new System.Windows.Forms.Panel();
|
||||||
|
this.button2 = new System.Windows.Forms.Button();
|
||||||
|
this.flowControlsPanel = new System.Windows.Forms.Panel();
|
||||||
|
this.runButton = new System.Windows.Forms.Button();
|
||||||
|
this.stopButton = new System.Windows.Forms.Button();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.memoListView = new BufferedListView();
|
||||||
|
this.panel1.SuspendLayout();
|
||||||
|
this.filterManagementPanel.SuspendLayout();
|
||||||
|
this.wndControlPanel.SuspendLayout();
|
||||||
|
this.flowControlsPanel.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// columnHeaderTime
|
||||||
|
//
|
||||||
|
this.columnHeaderTime.Text = "Time";
|
||||||
|
this.columnHeaderTime.Width = 150;
|
||||||
|
//
|
||||||
|
// columnHeaderMessage
|
||||||
|
//
|
||||||
|
this.columnHeaderMessage.Text = "Message";
|
||||||
|
this.columnHeaderMessage.Width = 350;
|
||||||
|
//
|
||||||
|
// panel1
|
||||||
|
//
|
||||||
|
this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.panel1.Controls.Add(this.refreshMemoButton);
|
||||||
|
this.panel1.Controls.Add(this.button6);
|
||||||
|
this.panel1.Controls.Add(this.button7);
|
||||||
|
this.panel1.Location = new System.Drawing.Point(189, 356);
|
||||||
|
this.panel1.Name = "panel1";
|
||||||
|
this.panel1.Size = new System.Drawing.Size(283, 26);
|
||||||
|
this.panel1.TabIndex = 16;
|
||||||
|
//
|
||||||
|
// refreshMemoButton
|
||||||
|
//
|
||||||
|
this.refreshMemoButton.Location = new System.Drawing.Point(108, 0);
|
||||||
|
this.refreshMemoButton.Name = "refreshMemoButton";
|
||||||
|
this.refreshMemoButton.Size = new System.Drawing.Size(84, 26);
|
||||||
|
this.refreshMemoButton.TabIndex = 17;
|
||||||
|
this.refreshMemoButton.Text = "Refresh memo";
|
||||||
|
this.refreshMemoButton.UseVisualStyleBackColor = true;
|
||||||
|
this.refreshMemoButton.Click += new System.EventHandler(this.refreshMemoButton_Click);
|
||||||
|
//
|
||||||
|
// button6
|
||||||
|
//
|
||||||
|
this.button6.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.button6.Name = "button6";
|
||||||
|
this.button6.Size = new System.Drawing.Size(102, 26);
|
||||||
|
this.button6.TabIndex = 4;
|
||||||
|
this.button6.Text = "Clean diag cache";
|
||||||
|
this.button6.UseVisualStyleBackColor = true;
|
||||||
|
this.button6.Click += new System.EventHandler(this.button6_Click);
|
||||||
|
//
|
||||||
|
// button7
|
||||||
|
//
|
||||||
|
this.button7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.button7.Location = new System.Drawing.Point(198, 0);
|
||||||
|
this.button7.Name = "button7";
|
||||||
|
this.button7.Size = new System.Drawing.Size(72, 26);
|
||||||
|
this.button7.TabIndex = 5;
|
||||||
|
this.button7.Text = "Save logs";
|
||||||
|
this.button7.UseVisualStyleBackColor = true;
|
||||||
|
this.button7.Click += new System.EventHandler(this.button7_Click);
|
||||||
|
//
|
||||||
|
// filterManagementPanel
|
||||||
|
//
|
||||||
|
this.filterManagementPanel.Controls.Add(this.button5);
|
||||||
|
this.filterManagementPanel.Controls.Add(this.filterButton);
|
||||||
|
this.filterManagementPanel.Controls.Add(this.filterTextBox);
|
||||||
|
this.filterManagementPanel.Controls.Add(this.label2);
|
||||||
|
this.filterManagementPanel.Location = new System.Drawing.Point(127, 6);
|
||||||
|
this.filterManagementPanel.Name = "filterManagementPanel";
|
||||||
|
this.filterManagementPanel.Size = new System.Drawing.Size(393, 26);
|
||||||
|
this.filterManagementPanel.TabIndex = 15;
|
||||||
|
//
|
||||||
|
// button5
|
||||||
|
//
|
||||||
|
this.button5.Enabled = false;
|
||||||
|
this.button5.Location = new System.Drawing.Point(294, 4);
|
||||||
|
this.button5.Name = "button5";
|
||||||
|
this.button5.Size = new System.Drawing.Size(81, 20);
|
||||||
|
this.button5.TabIndex = 11;
|
||||||
|
this.button5.Text = "Add new filter";
|
||||||
|
this.button5.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// filterButton
|
||||||
|
//
|
||||||
|
this.filterButton.Location = new System.Drawing.Point(225, 4);
|
||||||
|
this.filterButton.Name = "filterButton";
|
||||||
|
this.filterButton.Size = new System.Drawing.Size(64, 20);
|
||||||
|
this.filterButton.TabIndex = 10;
|
||||||
|
this.filterButton.Text = "Set filter";
|
||||||
|
this.filterButton.UseVisualStyleBackColor = true;
|
||||||
|
this.filterButton.Click += new System.EventHandler(this.filterButton_Click_1);
|
||||||
|
//
|
||||||
|
// filterTextBox
|
||||||
|
//
|
||||||
|
this.filterTextBox.Location = new System.Drawing.Point(33, 4);
|
||||||
|
this.filterTextBox.Name = "filterTextBox";
|
||||||
|
this.filterTextBox.Size = new System.Drawing.Size(187, 20);
|
||||||
|
this.filterTextBox.TabIndex = 7;
|
||||||
|
//
|
||||||
|
// label2
|
||||||
|
//
|
||||||
|
this.label2.AutoSize = true;
|
||||||
|
this.label2.Location = new System.Drawing.Point(3, 8);
|
||||||
|
this.label2.Name = "label2";
|
||||||
|
this.label2.Size = new System.Drawing.Size(29, 13);
|
||||||
|
this.label2.TabIndex = 6;
|
||||||
|
this.label2.Text = "Filter";
|
||||||
|
//
|
||||||
|
// wndControlPanel
|
||||||
|
//
|
||||||
|
this.wndControlPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.wndControlPanel.Controls.Add(this.button2);
|
||||||
|
this.wndControlPanel.Location = new System.Drawing.Point(831, 356);
|
||||||
|
this.wndControlPanel.Name = "wndControlPanel";
|
||||||
|
this.wndControlPanel.Size = new System.Drawing.Size(70, 26);
|
||||||
|
this.wndControlPanel.TabIndex = 14;
|
||||||
|
//
|
||||||
|
// button2
|
||||||
|
//
|
||||||
|
this.button2.Location = new System.Drawing.Point(3, 0);
|
||||||
|
this.button2.Name = "button2";
|
||||||
|
this.button2.Size = new System.Drawing.Size(64, 26);
|
||||||
|
this.button2.TabIndex = 2;
|
||||||
|
this.button2.Text = "Close";
|
||||||
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||||
|
//
|
||||||
|
// flowControlsPanel
|
||||||
|
//
|
||||||
|
this.flowControlsPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.flowControlsPanel.Controls.Add(this.runButton);
|
||||||
|
this.flowControlsPanel.Controls.Add(this.stopButton);
|
||||||
|
this.flowControlsPanel.Location = new System.Drawing.Point(12, 356);
|
||||||
|
this.flowControlsPanel.Name = "flowControlsPanel";
|
||||||
|
this.flowControlsPanel.Size = new System.Drawing.Size(133, 26);
|
||||||
|
this.flowControlsPanel.TabIndex = 13;
|
||||||
|
//
|
||||||
|
// runButton
|
||||||
|
//
|
||||||
|
this.runButton.Enabled = false;
|
||||||
|
this.runButton.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.runButton.Name = "runButton";
|
||||||
|
this.runButton.Size = new System.Drawing.Size(64, 26);
|
||||||
|
this.runButton.TabIndex = 4;
|
||||||
|
this.runButton.Text = "Run";
|
||||||
|
this.runButton.UseVisualStyleBackColor = true;
|
||||||
|
this.runButton.Click += new System.EventHandler(this.runButton_Click);
|
||||||
|
//
|
||||||
|
// stopButton
|
||||||
|
//
|
||||||
|
this.stopButton.Location = new System.Drawing.Point(69, 0);
|
||||||
|
this.stopButton.Name = "stopButton";
|
||||||
|
this.stopButton.Size = new System.Drawing.Size(64, 26);
|
||||||
|
this.stopButton.TabIndex = 5;
|
||||||
|
this.stopButton.Text = "Stop";
|
||||||
|
this.stopButton.UseVisualStyleBackColor = true;
|
||||||
|
this.stopButton.Click += new System.EventHandler(this.stopButton_Click);
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(12, 19);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(76, 13);
|
||||||
|
this.label1.TabIndex = 12;
|
||||||
|
this.label1.Text = "Flow of events";
|
||||||
|
//
|
||||||
|
// memoListView
|
||||||
|
//
|
||||||
|
this.memoListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.memoListView.FullRowSelect = true;
|
||||||
|
this.memoListView.GridLines = true;
|
||||||
|
this.memoListView.HideSelection = false;
|
||||||
|
this.memoListView.Location = new System.Drawing.Point(12, 38);
|
||||||
|
this.memoListView.Name = "memoListView";
|
||||||
|
this.memoListView.Size = new System.Drawing.Size(889, 312);
|
||||||
|
this.memoListView.TabIndex = 0;
|
||||||
|
this.memoListView.UseCompatibleStateImageBehavior = false;
|
||||||
|
this.memoListView.View = System.Windows.Forms.View.Details;
|
||||||
|
|
||||||
|
this.memoListView.DoubleClick += new System.EventHandler(this.MemoListView_DoubleClick);
|
||||||
|
//
|
||||||
|
// MainForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(913, 388);
|
||||||
|
this.Controls.Add(this.memoListView);
|
||||||
|
this.Controls.Add(this.panel1);
|
||||||
|
this.Controls.Add(this.filterManagementPanel);
|
||||||
|
this.Controls.Add(this.wndControlPanel);
|
||||||
|
this.Controls.Add(this.flowControlsPanel);
|
||||||
|
this.Controls.Add(this.label1);
|
||||||
|
this.Name = "MainForm";
|
||||||
|
this.Text = "Application live diagnostic";
|
||||||
|
this.panel1.ResumeLayout(false);
|
||||||
|
this.filterManagementPanel.ResumeLayout(false);
|
||||||
|
this.filterManagementPanel.PerformLayout();
|
||||||
|
this.wndControlPanel.ResumeLayout(false);
|
||||||
|
this.flowControlsPanel.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel panel1;
|
||||||
|
private System.Windows.Forms.Button button6;
|
||||||
|
private System.Windows.Forms.Button button7;
|
||||||
|
private System.Windows.Forms.Panel filterManagementPanel;
|
||||||
|
private System.Windows.Forms.Button button5;
|
||||||
|
private System.Windows.Forms.Button filterButton;
|
||||||
|
private System.Windows.Forms.TextBox filterTextBox;
|
||||||
|
private System.Windows.Forms.Label label2;
|
||||||
|
private System.Windows.Forms.Panel wndControlPanel;
|
||||||
|
private System.Windows.Forms.Button button2;
|
||||||
|
private System.Windows.Forms.Panel flowControlsPanel;
|
||||||
|
private System.Windows.Forms.Button runButton;
|
||||||
|
private System.Windows.Forms.Button stopButton;
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
|
private System.Windows.Forms.Button refreshMemoButton;
|
||||||
|
private BufferedListView memoListView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
using log4net.Config;
|
||||||
|
using log4net;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using log4net.Appender;
|
||||||
|
using SharedComponents;
|
||||||
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||||||
|
using System.IO; // Pre File.WriteAllLines a prácu so súbormi
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
public partial class MainForm : Form
|
||||||
|
{
|
||||||
|
private bool stopUpdate = false; // Premenná na zastavenie aktualizácie
|
||||||
|
private int lastDisplayedLogIndex = 0;
|
||||||
|
private bool autoScrollEnabled = true; // Automatický posun, predvolene povolený
|
||||||
|
private MainForm appDiagnosticForm;
|
||||||
|
private Timer refreshTimer;
|
||||||
|
private LogAdapter _logAdapter; // Pre LogAdapter
|
||||||
|
private LogFilter _logFilter; // LogFilter objekt pre filtráciu
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
this.Icon = Properties.Resources.TBF_icon;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
// Inicializujeme LogAdapter a priradíme ho k memoListView
|
||||||
|
_logAdapter = new LogAdapter(memoListView);
|
||||||
|
|
||||||
|
RefreshLogView();
|
||||||
|
|
||||||
|
// Pripojenie na udalosť pri pridaní nového logu
|
||||||
|
// nakoľko sa memo refreshuje cyklicky, tak refresh na udalosť nepotrebujem
|
||||||
|
//LiveLogCache.Instance.LogAdded += OnLogAdded;
|
||||||
|
|
||||||
|
// Vytvoríme LogFilter objekt
|
||||||
|
//_logFilter = new LogFilter(memoListView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLogAdded(string log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RefreshLogView();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error in OnLogAdded: {ex.Message}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshLogView()
|
||||||
|
{
|
||||||
|
// Skontrolujte, či voláme z iného vlákna
|
||||||
|
if (InvokeRequired)
|
||||||
|
{
|
||||||
|
Invoke((Action)RefreshLogView);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoListView.BeginUpdate(); // Zabraňuje vizuálnym aktualizáciám počas vykresľovania
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Skontrolujte, či je užívateľ na poslednom viditeľnom zázname
|
||||||
|
if (memoListView.Items.Count > 0)
|
||||||
|
{
|
||||||
|
var lastVisibleIndex = memoListView.TopItem.Index + memoListView.ClientRectangle.Height / memoListView.Items[0].Bounds.Height;
|
||||||
|
autoScrollEnabled = lastVisibleIndex >= memoListView.Items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Získajte nové logy od posledného indexu
|
||||||
|
var logs = LiveLogCache.Instance.GetLogs(lastDisplayedLogIndex, LiveLogCache.Instance.Logs.Count - lastDisplayedLogIndex);
|
||||||
|
|
||||||
|
// Pridajte len nové logy
|
||||||
|
foreach (var log in logs)
|
||||||
|
{
|
||||||
|
memoListView.Items.Add(new ListViewItem(log));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktualizujte posledný zobrazený index
|
||||||
|
lastDisplayedLogIndex = LiveLogCache.Instance.Logs.Count;
|
||||||
|
|
||||||
|
// Ak je autoScrollEnabled, posuňte sa na posledný záznam
|
||||||
|
if (autoScrollEnabled && memoListView.Items.Count > 0)
|
||||||
|
{
|
||||||
|
memoListView.EnsureVisible(memoListView.Items.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
memoListView.EndUpdate(); // Umožní vizuálne aktualizácie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Táto metóda sa spustí pri scrollovaní v memoListView (tzn. ak sa používateľ dostane na spodok listu)
|
||||||
|
private void memoListView_Scroll(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Skontrolujte, či používateľ dosiahol spodok ListView
|
||||||
|
if (memoListView.Items.Count > 0 &&
|
||||||
|
memoListView.Items[memoListView.Items.Count - 1].Bounds.Bottom <= memoListView.ClientSize.Height)
|
||||||
|
{
|
||||||
|
// Ak áno, načítame ďalšie logy
|
||||||
|
RefreshLogView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button6_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//LiveLogCache.Instance.AddLog("Nový log z hlavnej aplikácie");
|
||||||
|
|
||||||
|
LiveLogCache.Instance.ClearLogs();
|
||||||
|
ClearListView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (stopUpdate) // Ak je časovač zastavený, spustíme ho
|
||||||
|
{
|
||||||
|
stopUpdate = false; // Zastav aktualizáciu
|
||||||
|
runButton.Enabled = false; // Zakážeme tlačidlo Start počas behu
|
||||||
|
stopButton.Enabled = true; // Povolenie tlačidla Stop
|
||||||
|
refreshTimer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (!stopUpdate) // Ak časovač beží, môžeme ho zastaviť
|
||||||
|
{
|
||||||
|
stopUpdate = true; // Spusti aktualizáciu
|
||||||
|
runButton.Enabled = true; // Povolenie tlačidla Start
|
||||||
|
stopButton.Enabled = false; // Zakážeme tlačidlo Stop
|
||||||
|
refreshTimer.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearListView()
|
||||||
|
{
|
||||||
|
if (InvokeRequired)
|
||||||
|
{
|
||||||
|
Invoke(new Action(ClearListView));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoListView.Items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshMemoButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button7_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
SaveLogsToFile();
|
||||||
|
//LiveLogCache.Instance.AddLog("Toto je nový log.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
// Odpojenie udalosti, keď sa okno zatvára, inak môže po opätovnom spustení okna a pridaní nového itemu do listu zhhodiť program
|
||||||
|
// nakoľko sa memo refreshuje cyklicky, tak refresh na udalosť nepotrebujem
|
||||||
|
//LiveLogCache.Instance.LogAdded -= OnLogAdded;
|
||||||
|
|
||||||
|
_logAdapter = null;
|
||||||
|
|
||||||
|
base.OnFormClosing(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterButton_Click_1(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Aplikujeme filter na základe textu z filterTextBox
|
||||||
|
string filterText = filterTextBox.Text;
|
||||||
|
//_logFilter.ApplyFilter(filterText); // Aplikovanie filtra
|
||||||
|
_logAdapter.ApplyFilter(filterText);
|
||||||
|
}
|
||||||
|
private void SubForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Vaša logika pri uzatváraní formy
|
||||||
|
// this.Hide(); // Podforma sa len skryje
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Ošetrenie výnimky
|
||||||
|
MessageBox.Show("Chyba pri zatváraní pod-aplikácie: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveLogsToFile()
|
||||||
|
{
|
||||||
|
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
|
||||||
|
{
|
||||||
|
saveFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
|
||||||
|
saveFileDialog.Title = "Save TBF live logs";
|
||||||
|
saveFileDialog.DefaultExt = "txt";
|
||||||
|
saveFileDialog.FileName = "TBFLiveLogs.txt";
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Načítanie všetkých logov z LiveLogCache
|
||||||
|
var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.GetCount());
|
||||||
|
|
||||||
|
// Uloženie logov do vybraného súboru
|
||||||
|
File.WriteAllLines(saveFileDialog.FileName, logs);
|
||||||
|
|
||||||
|
MessageBox.Show("Logs saved successfully.", "Save TBF live logs", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"An error occurred while saving TBF live logs: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
this.Close();
|
||||||
|
//this.Hide(); // Podforma sa len skryje
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MemoListView_DoubleClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (memoListView.SelectedItems.Count > 0)
|
||||||
|
{
|
||||||
|
var selectedItem = memoListView.SelectedItems[0];
|
||||||
|
int selectedIndex = memoListView.Items.IndexOf(selectedItem);
|
||||||
|
|
||||||
|
if (selectedIndex >= 0)
|
||||||
|
{
|
||||||
|
// Zrušenie filtra
|
||||||
|
_logAdapter._filterText = string.Empty;
|
||||||
|
|
||||||
|
// Načítanie logov od indexu vybraného riadku
|
||||||
|
var logs = LiveLogCache.Instance.GetLogs(selectedIndex, LiveLogCache.Instance.GetCount() - selectedIndex);
|
||||||
|
|
||||||
|
// Obnovenie ListView s novými údajmi
|
||||||
|
memoListView.BeginUpdate();
|
||||||
|
memoListView.Items.Clear();
|
||||||
|
foreach (var log in logs)
|
||||||
|
{
|
||||||
|
var item = new ListViewItem(log)
|
||||||
|
{
|
||||||
|
BackColor = memoListView.Items.Count % 2 == 0 ? Color.White : Color.FromArgb(240, 240, 240)
|
||||||
|
};
|
||||||
|
memoListView.Items.Add(item);
|
||||||
|
}
|
||||||
|
memoListView.EndUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace AppDiagnostic
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for the application.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("AppDiagnostic")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("AppDiagnostic")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2024")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("fa9abad1-7184-4295-ade8-d44f2e3de6b2")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace AppDiagnostic.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AppDiagnostic.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Icon TBF_icon {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("TBF_icon", resourceCulture);
|
||||||
|
return ((System.Drawing.Icon)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="TBF_icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\TBF_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace AppDiagnostic.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="log4net" version="3.0.3" targetFramework="net472" />
|
||||||
|
</packages>
|
||||||
@@ -33,9 +33,14 @@
|
|||||||
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.5.2.2\lib\net46\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.IO.Compression" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
@@ -90,7 +95,9 @@
|
|||||||
<Compile Include="Utils.cs" />
|
<Compile Include="Utils.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Forms\ListViewEx.resx">
|
<EmbeddedResource Include="Forms\ListViewEx.resx">
|
||||||
<DependentUpon>ListViewEx.cs</DependentUpon>
|
<DependentUpon>ListViewEx.cs</DependentUpon>
|
||||||
|
|||||||
+21
-2
@@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
@@ -289,11 +290,20 @@ namespace Common
|
|||||||
CompoundAux,
|
CompoundAux,
|
||||||
Compound,
|
Compound,
|
||||||
HeatMeterVolume,
|
HeatMeterVolume,
|
||||||
HeatMeterEnergy,
|
HeatMeterMass,//...MF
|
||||||
|
HeatMeterEnergy,
|
||||||
SingleOrCompound, /// Only used when querying results
|
SingleOrCompound, /// Only used when querying results
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Medium
|
public enum PulsesTypeByQuantity : short
|
||||||
|
{
|
||||||
|
[Description("dm3")] Volume_dm3,
|
||||||
|
[Description("kg")] Mass_kg,
|
||||||
|
|
||||||
|
Count
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Medium
|
||||||
{
|
{
|
||||||
[Description("")] NotSpecified,
|
[Description("")] NotSpecified,
|
||||||
#if LANG_PL
|
#if LANG_PL
|
||||||
@@ -829,6 +839,15 @@ namespace Common
|
|||||||
E63 = (1L << 62),
|
E63 = (1L << 62),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//...MF
|
||||||
|
public enum FlowType : int
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
volume,
|
||||||
|
mass,
|
||||||
|
Count,
|
||||||
|
}
|
||||||
|
|
||||||
#region iPERL enums
|
#region iPERL enums
|
||||||
|
|
||||||
public enum Side
|
public enum Side
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace Common.Iperl
|
|||||||
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
|
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
|
||||||
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
|
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
|
||||||
public Int32 FlipTime() { return Impedance; }
|
public Int32 FlipTime() { return Impedance; }
|
||||||
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
|
public decimal TimestampDec() { return (decimal)TimestampExt; }
|
||||||
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
|
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
|
||||||
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
|
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
|
||||||
public string Label()
|
public string Label()
|
||||||
|
|||||||
+92
-26
@@ -2,6 +2,7 @@
|
|||||||
/// Copyright (c) 2016-2021 Sensus Slovensko a.s.
|
/// Copyright (c) 2016-2021 Sensus Slovensko a.s.
|
||||||
///
|
///
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
@@ -33,6 +34,20 @@ namespace Common
|
|||||||
[Description("m3/m")] m3pm, /// 1 m3/m = 60 m3/h
|
[Description("m3/m")] m3pm, /// 1 m3/m = 60 m3/h
|
||||||
[Description("cf/s")] cfs, /// 1 cubic foot per second = 101.9406477312 m3/h
|
[Description("cf/s")] cfs, /// 1 cubic foot per second = 101.9406477312 m3/h
|
||||||
|
|
||||||
|
|
||||||
|
[Description("g/s")] gps, /// 1 g/s = 0.0036 t/h
|
||||||
|
[Description("g/min")] gpm, /// 1 g/min = 0.00006 t/h
|
||||||
|
[Description("g/h")] gph, /// 1 g/h = 0.000001 t/h
|
||||||
|
[Description("kg/s")] kgps, /// 1 kg/s = 3.6 t/h
|
||||||
|
[Description("kg/min")] kgpm, /// 1 kg/min = 0.06 t/h
|
||||||
|
[Description("kg/h")] kgph, /// 1 kg/h = 0.001 t/h
|
||||||
|
[Description("t/s")] tps, /// 1 t/s = 3600 t/h
|
||||||
|
[Description("t/min")] tpm, /// 1 t/min = 60 t/h
|
||||||
|
[Description("t/h")] tph, /// 1 t/h = 1 t/h
|
||||||
|
[Description("lb/s")] lbps, /// 1 lb/s ≈ 1.633 t/h
|
||||||
|
[Description("lb/min")] lbpm, /// 1 lb/min ≈ 0.027216 t/h
|
||||||
|
[Description("lb/h")] lbph, /// 1 lb/h ≈ 0.0004536 t/h
|
||||||
|
|
||||||
[Description("g")] g, /// 0.001 kg
|
[Description("g")] g, /// 0.001 kg
|
||||||
[Description("oz")] oz, /// 0.0283495231 kg
|
[Description("oz")] oz, /// 0.0283495231 kg
|
||||||
[Description("lb")] lb, /// 0.45359237 kg
|
[Description("lb")] lb, /// 0.45359237 kg
|
||||||
@@ -221,9 +236,13 @@ namespace Common
|
|||||||
[Description("Corrente")] Current,
|
[Description("Corrente")] Current,
|
||||||
[Description("Voltaggio")] Voltage,
|
[Description("Voltaggio")] Voltage,
|
||||||
#else
|
#else
|
||||||
|
[Description("RegisterReader")] RegisterReader,
|
||||||
|
[Description("MultiFunctionalVariables")] MultiFunctionalVariables,
|
||||||
|
[Description("VolumeFlow")] VolumeFlow,
|
||||||
[Description("Volume")] Volume,
|
[Description("Volume")] Volume,
|
||||||
[Description("Flow")] Flow,
|
[Description("Flow")] Flow,
|
||||||
[Description("Mass")] Mass,
|
[Description("MassFlow")] MassFlow,
|
||||||
|
[Description("Mass")] Mass,
|
||||||
[Description("Time")] Time,
|
[Description("Time")] Time,
|
||||||
[Description("Temperature")] Temperature,
|
[Description("Temperature")] Temperature,
|
||||||
[Description("Pressure")] Pressure,
|
[Description("Pressure")] Pressure,
|
||||||
@@ -234,6 +253,8 @@ namespace Common
|
|||||||
[Description("Energy")] Energy,
|
[Description("Energy")] Energy,
|
||||||
[Description("Pulses")] Pulses,
|
[Description("Pulses")] Pulses,
|
||||||
[Description("Pulses/liter")] PulsePerLtr,
|
[Description("Pulses/liter")] PulsePerLtr,
|
||||||
|
[Description("Pulses/kg")] PulsePerKilogram, //...MF
|
||||||
|
[Description("Pulses/Unit")] PulsePerUnit,//...MF
|
||||||
[Description("Pulses/kWh")] PulsePerKWh,
|
[Description("Pulses/kWh")] PulsePerKWh,
|
||||||
[Description("Conductivity")] Conductivity,
|
[Description("Conductivity")] Conductivity,
|
||||||
|
|
||||||
@@ -266,54 +287,97 @@ namespace Common
|
|||||||
|
|
||||||
public static bool IsQuantity(Unit unit, Quantity quantity)
|
public static bool IsQuantity(Unit unit, Quantity quantity)
|
||||||
{
|
{
|
||||||
return (quantity == GetQuantity(unit));
|
return GetQuantity(unit).Contains(quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Quantity GetQuantity(Unit unit)
|
public static Quantity[] GetQuantity(Unit unit)
|
||||||
{
|
{
|
||||||
switch (unit)
|
switch (unit)
|
||||||
{
|
{
|
||||||
case Unit.pulse:
|
case Unit.pulse:
|
||||||
case Unit.degree:
|
case Unit.degree:
|
||||||
return Quantity.Pulses;
|
return new[] { Quantity.Pulses };
|
||||||
|
|
||||||
case Unit.ml:
|
case Unit.ml:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.l:
|
case Unit.l:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.dm3:
|
case Unit.dm3:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.USgal:
|
case Unit.USgal:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.UKgal:
|
case Unit.UKgal:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.cf:
|
case Unit.cf:
|
||||||
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.m3:
|
case Unit.m3:
|
||||||
return Quantity.Volume;
|
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||||
|
|
||||||
case Unit.lph:
|
case Unit.lph:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.cfph:
|
case Unit.cfph:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.lpm:
|
case Unit.lpm:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.USgalpm:
|
case Unit.USgalpm:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.m3ph:
|
case Unit.m3ph:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.lps:
|
case Unit.lps:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.USgalps:
|
case Unit.USgalps:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.m3pm:
|
case Unit.m3pm:
|
||||||
|
return new[] { Quantity.Flow };
|
||||||
case Unit.cfs:
|
case Unit.cfs:
|
||||||
return Quantity.Flow;
|
return new[] { Quantity.Flow };
|
||||||
|
|
||||||
|
case Unit.gps:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.gpm:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.gph:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.kgps:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.kgpm:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.kgph:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.tps:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.tpm:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.tph:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.lbps:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.lbpm:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
case Unit.lbph:
|
||||||
|
return new[] { Quantity.MassFlow};
|
||||||
|
|
||||||
case Unit.g:
|
case Unit.g:
|
||||||
|
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.oz:
|
case Unit.oz:
|
||||||
|
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.lb:
|
case Unit.lb:
|
||||||
|
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.kg:
|
case Unit.kg:
|
||||||
|
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||||
case Unit.t:
|
case Unit.t:
|
||||||
return Quantity.Mass;
|
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||||
|
|
||||||
case Unit.ms:
|
case Unit.ms:
|
||||||
case Unit.s:
|
case Unit.s:
|
||||||
case Unit.min:
|
case Unit.min:
|
||||||
case Unit.hour:
|
case Unit.hour:
|
||||||
return Quantity.Time;
|
return new[] { Quantity.Time };
|
||||||
|
|
||||||
case Unit.C:
|
case Unit.C:
|
||||||
case Unit.F:
|
case Unit.F:
|
||||||
case Unit.K:
|
case Unit.K:
|
||||||
return Quantity.Temperature;
|
return new[] { Quantity.Temperature };
|
||||||
|
|
||||||
case Unit.Pa:
|
case Unit.Pa:
|
||||||
case Unit.hPa:
|
case Unit.hPa:
|
||||||
@@ -323,14 +387,14 @@ namespace Common
|
|||||||
case Unit.psi:
|
case Unit.psi:
|
||||||
case Unit.bar:
|
case Unit.bar:
|
||||||
case Unit.MPa:
|
case Unit.MPa:
|
||||||
return Quantity.Pressure;
|
return new[] { Quantity.Pressure };
|
||||||
|
|
||||||
case Unit.RPct:
|
case Unit.RPct:
|
||||||
return Quantity.Humidity;
|
return new[] { Quantity.Humidity };
|
||||||
|
|
||||||
case Unit.Promile:
|
case Unit.Promile:
|
||||||
case Unit.Pct:
|
case Unit.Pct:
|
||||||
return Quantity.Error;
|
return new[] { Quantity.Error };
|
||||||
|
|
||||||
case Unit.mm:
|
case Unit.mm:
|
||||||
case Unit.cm:
|
case Unit.cm:
|
||||||
@@ -339,23 +403,23 @@ namespace Common
|
|||||||
case Unit.foot:
|
case Unit.foot:
|
||||||
case Unit.yard:
|
case Unit.yard:
|
||||||
case Unit.m:
|
case Unit.m:
|
||||||
return Quantity.Length;
|
return new[] { Quantity.Length };
|
||||||
|
|
||||||
case Unit.kgpm3:
|
case Unit.kgpm3:
|
||||||
case Unit.kgpl:
|
case Unit.kgpl:
|
||||||
return Quantity.Density;
|
return new[] { Quantity.Density };
|
||||||
|
|
||||||
case Unit.J:
|
case Unit.J:
|
||||||
case Unit.kJ:
|
case Unit.kJ:
|
||||||
case Unit.MJ:
|
case Unit.MJ:
|
||||||
case Unit.Wh:
|
case Unit.Wh:
|
||||||
case Unit.kWh:
|
case Unit.kWh:
|
||||||
case Unit.MWh:
|
case Unit.MWh:
|
||||||
return Quantity.Energy;
|
return new[] { Quantity.Energy };
|
||||||
|
|
||||||
case Unit.uSpcm:
|
case Unit.uSpcm:
|
||||||
case Unit.mSpm:
|
case Unit.mSpm:
|
||||||
return Quantity.Conductivity;
|
return new[] { Quantity.Conductivity };
|
||||||
|
|
||||||
case Unit.ppl:
|
case Unit.ppl:
|
||||||
case Unit.ppdm3:
|
case Unit.ppdm3:
|
||||||
@@ -365,27 +429,29 @@ namespace Common
|
|||||||
case Unit.dm3pp:
|
case Unit.dm3pp:
|
||||||
case Unit.lpdeg:
|
case Unit.lpdeg:
|
||||||
case Unit.dm3pdeg:
|
case Unit.dm3pdeg:
|
||||||
return Quantity.PulsePerLtr;
|
return new[] { Quantity.PulsePerLtr };
|
||||||
|
|
||||||
case Unit.ppkWh:
|
case Unit.ppkWh:
|
||||||
case Unit.kWhpp:
|
case Unit.kWhpp:
|
||||||
return Quantity.PulsePerKWh;
|
return new[] { Quantity.PulsePerKWh };
|
||||||
|
|
||||||
case Unit.A:
|
case Unit.A:
|
||||||
case Unit.mA:
|
case Unit.mA:
|
||||||
return Quantity.Current;
|
return new[] { Quantity.Current };
|
||||||
|
|
||||||
case Unit.V:
|
case Unit.V:
|
||||||
case Unit.mV:
|
case Unit.mV:
|
||||||
return Quantity.Voltage;
|
return new[] { Quantity.Voltage };
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Quantity.Number;
|
return new[] { Quantity.Number };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsVolume(Unit unit) { return IsQuantity(unit, Quantity.Volume); }
|
public static bool IsVolumeFlow(Unit unit) { return IsQuantity(unit, Quantity.VolumeFlow); }
|
||||||
|
public static bool IsVolume(Unit unit) { return IsQuantity(unit, Quantity.Volume); }
|
||||||
public static bool IsFlow(Unit unit) { return IsQuantity(unit, Quantity.Flow); }
|
public static bool IsFlow(Unit unit) { return IsQuantity(unit, Quantity.Flow); }
|
||||||
|
public static bool IsMassFlow(Unit unit) { return IsQuantity(unit, Quantity.MassFlow); }
|
||||||
public static bool IsMass(Unit unit) { return IsQuantity(unit, Quantity.Mass); }
|
public static bool IsMass(Unit unit) { return IsQuantity(unit, Quantity.Mass); }
|
||||||
public static bool IsTime(Unit unit) { return IsQuantity(unit, Quantity.Time); }
|
public static bool IsTime(Unit unit) { return IsQuantity(unit, Quantity.Time); }
|
||||||
public static bool IsTemperature(Unit unit) { return IsQuantity(unit, Quantity.Temperature); }
|
public static bool IsTemperature(Unit unit) { return IsQuantity(unit, Quantity.Temperature); }
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ namespace Common
|
|||||||
string passwordOfDay = Convert.ToString(number, 8);
|
string passwordOfDay = Convert.ToString(number, 8);
|
||||||
|
|
||||||
return (userName.Equals("milan") && password.Equals("kraken")) ||
|
return (userName.Equals("milan") && password.Equals("kraken")) ||
|
||||||
|
(userName.Equals("bumi") && password.Equals("70630")) ||
|
||||||
(userName.Equals("igor") && password.Equals("mojronko8")) ||
|
(userName.Equals("igor") && password.Equals("mojronko8")) ||
|
||||||
(userName.Equals("lubo1212") && password.Equals("Tatry52")) ||
|
(userName.Equals("lubo1212") && password.Equals("Tatry52")) ||
|
||||||
(userName.Equals("Michal") && password.Equals("Plok789456123")) ||
|
(userName.Equals("Michal") && password.Equals("Plok789456123")) ||
|
||||||
@@ -310,6 +311,11 @@ namespace Common
|
|||||||
(userName.Equals("jan") && password.Equals("jaugust")) ||
|
(userName.Equals("jan") && password.Equals("jaugust")) ||
|
||||||
(userName.Equals("JCermak") && password.Equals("Zt6911")) ||
|
(userName.Equals("JCermak") && password.Equals("Zt6911")) ||
|
||||||
(userName.Equals("gilles") && password.Equals("alibaba")) ||
|
(userName.Equals("gilles") && password.Equals("alibaba")) ||
|
||||||
|
(userName.Equals("michal") && password.Equals("mojnelson8")) ||
|
||||||
|
(userName.Equals("marek") && password.Equals("mareciino")) ||
|
||||||
|
#if DEBUG
|
||||||
|
(userName.Equals("Sensus Developers") && password.Equals("5fbg12gf5hn8nhy1fr")) ||
|
||||||
|
#endif
|
||||||
(userName.Equals(passwordOfDay) && password.Equals(passwordOfDay));
|
(userName.Equals(passwordOfDay) && password.Equals(passwordOfDay));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="log4net" version="2.0.15" targetFramework="net472" />
|
<package id="log4net" version="2.0.15" targetFramework="net472" />
|
||||||
|
<package id="NLog" version="5.2.2" targetFramework="net472" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW</DefineConstants>
|
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CalendarEvent\ICalendarEvent.cs" />
|
<Compile Include="CalendarEvent\ICalendarEvent.cs" />
|
||||||
|
<Compile Include="Data.cs" />
|
||||||
<Compile Include="Entities\BenchPath.cs" />
|
<Compile Include="Entities\BenchPath.cs" />
|
||||||
<Compile Include="Entities\CustomEvent.cs" />
|
<Compile Include="Entities\CustomEvent.cs" />
|
||||||
<Compile Include="Entities\Component.cs" />
|
<Compile Include="Entities\Component.cs" />
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
<Compile Include="Entities\VirtualBenchStep.cs" />
|
<Compile Include="Entities\VirtualBenchStep.cs" />
|
||||||
<Compile Include="CalendarEvent\Utils.cs" />
|
<Compile Include="CalendarEvent\Utils.cs" />
|
||||||
<Compile Include="FluentCommon.cs" />
|
<Compile Include="FluentCommon.cs" />
|
||||||
|
<Compile Include="Formulas.cs" />
|
||||||
<Compile Include="Mappings\BenchPathMap.cs" />
|
<Compile Include="Mappings\BenchPathMap.cs" />
|
||||||
<Compile Include="Mappings\ComponentMap.cs" />
|
<Compile Include="Mappings\ComponentMap.cs" />
|
||||||
<Compile Include="Mappings\ComponentProcedureMap.cs" />
|
<Compile Include="Mappings\ComponentProcedureMap.cs" />
|
||||||
@@ -115,6 +117,7 @@
|
|||||||
<Compile Include="Mappings\UncertaintyMap.cs" />
|
<Compile Include="Mappings\UncertaintyMap.cs" />
|
||||||
<Compile Include="Mappings\UserMap.cs" />
|
<Compile Include="Mappings\UserMap.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="PurchaseResources.cs" />
|
||||||
<Compile Include="Resources\Strings.Designer.cs">
|
<Compile Include="Resources\Strings.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
|
|||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
//formulas.cs has been added cause iPerl from master-2 ...MF
|
||||||
|
|
||||||
|
namespace Config
|
||||||
|
{
|
||||||
|
public class Data
|
||||||
|
{
|
||||||
|
public const string AdminUsername = "admin";
|
||||||
|
public const string AdminPassword = "staratura";
|
||||||
|
public const string SQLiteDbFName = "SQLite.db";
|
||||||
|
|
||||||
|
#if DN100 || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT || CEVAK_200
|
||||||
|
public const int WMsCount = 3;
|
||||||
|
public const int LineSize = 3;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 1;
|
||||||
|
#elif MUNICH
|
||||||
|
public const int WMsCount = 3;
|
||||||
|
public const int LineSize = 3;
|
||||||
|
public const int CompoundWMsCount = 3;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 3;
|
||||||
|
#elif MALTA_WSD25
|
||||||
|
public const int WMsCount = 6;
|
||||||
|
public const int LineSize = 6;
|
||||||
|
public const int CompoundWMsCount = 0;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 1;
|
||||||
|
#elif BADGER_MALA_TRAT || BERLIN || FUZHOU_150 || FUZHOU_300 || GELSENWASSER || GENESIS || IZRAEL_200 || PETERSBURG_200 || SENTEC || SLM_150 || TORINO_50
|
||||||
|
public const int WMsCount = 6;
|
||||||
|
public const int LineSize = 6;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 1;
|
||||||
|
#elif PUCHONG_200
|
||||||
|
public const int WMsCount = 6;
|
||||||
|
public const int LineSize = 6;
|
||||||
|
public const int CompoundWMsCount = 3;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 3;
|
||||||
|
#elif RUM_MOB
|
||||||
|
public const int WMsCount = 8;
|
||||||
|
public const int LineSize = 8;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif TURA_SPECIAL
|
||||||
|
public const int WMsCount = 6;
|
||||||
|
public const int LineSize = 6;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif DEWA_300 || FUZHOU_100 || ROMA_200 || LUXEMBURG_40
|
||||||
|
public const int WMsCount = 10;
|
||||||
|
public const int LineSize = 10;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif DUBAJ_50
|
||||||
|
public const int WMsCount = 10;
|
||||||
|
public const int LineSize = 5;
|
||||||
|
public const int CompoundWMsCount = 0;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif SLM_END || WARSAW_END
|
||||||
|
public const int WMsCount = 10;
|
||||||
|
public const int LineSize = 5;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = 4;
|
||||||
|
#elif SLM_50
|
||||||
|
public const int WMsCount = 12;
|
||||||
|
public const int LineSize = 12;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 6;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif ALZIR_25 || BAHRAIN_50 || CEVAK_40 || FEWA_50 || FILIPINY_50 || FUZHOU_50 || HONGKONG_50 || IZRAEL_25 || JUZNA_AFRIKA_50 || KEMPNO_50 || KRAKOW_50 || MILWAUKEE || MURES_40 || PETERSBURG_50 || TORUN_50 || WARSAW_50 || ZAMBIA || ZODINO
|
||||||
|
public const int WMsCount = 20;
|
||||||
|
public const int LineSize = 10;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif TURA_IPERL || TURA_IPERL_NEW
|
||||||
|
public const int WMsCount = 40;
|
||||||
|
public const int LineSize = 20;
|
||||||
|
public const int CompoundWMsCount = 1;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#elif IZRAEL_50
|
||||||
|
public const int WMsCount = 40;
|
||||||
|
public const int LineSize = 10;
|
||||||
|
public const int CompoundWMsCount = 0;
|
||||||
|
public const int HeatMetersCount = 0;
|
||||||
|
public const int MaxPartNr = WMsCount / LineSize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static double RealDensity = 0; /// true water density [kg/m3]
|
||||||
|
public static double AtTemperature = 0; /// measured at temperature [°C]
|
||||||
|
public static double Buoyancy = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,9 @@ namespace Config.Entities
|
|||||||
public virtual Unit FlowUnit { get; set; } /// Not mapped to database, used in UI
|
public virtual Unit FlowUnit { get; set; } /// Not mapped to database, used in UI
|
||||||
public virtual string Selector { get; set; }
|
public virtual string Selector { get; set; }
|
||||||
public virtual string RegValve { get; set; }
|
public virtual string RegValve { get; set; }
|
||||||
public virtual string FlowMeter { get; set; }
|
public virtual int RegulMinStep { get; set; }
|
||||||
public virtual float PidCoef { get; set; } /// PID coefficient for the regulation path
|
public virtual string FlowMeter { get; set; }
|
||||||
|
public virtual float PidCoef { get; set; } /// PID coefficient for the regulation path
|
||||||
public virtual string StartValve { get; set; }
|
public virtual string StartValve { get; set; }
|
||||||
public virtual string Diverter { get; set; }
|
public virtual string Diverter { get; set; }
|
||||||
public virtual string TempMtrDiv { get; set; }
|
public virtual string TempMtrDiv { get; set; }
|
||||||
@@ -39,6 +40,7 @@ namespace Config.Entities
|
|||||||
{
|
{
|
||||||
ValvesOpen = string.Empty;
|
ValvesOpen = string.Empty;
|
||||||
ValvesClose = string.Empty;
|
ValvesClose = string.Empty;
|
||||||
|
RegulMinStep = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputPath(string name, int itemNr)
|
public OutputPath(string name, int itemNr)
|
||||||
@@ -46,7 +48,9 @@ namespace Config.Entities
|
|||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
ItemNr = itemNr;
|
ItemNr = itemNr;
|
||||||
}
|
RegulMinStep = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public virtual OutputPath Clone(string name, int itemNr)
|
public virtual OutputPath Clone(string name, int itemNr)
|
||||||
{
|
{
|
||||||
@@ -58,7 +62,8 @@ namespace Config.Entities
|
|||||||
result.FlowUnit = FlowUnit;
|
result.FlowUnit = FlowUnit;
|
||||||
result.Selector = Selector;
|
result.Selector = Selector;
|
||||||
result.RegValve = RegValve;
|
result.RegValve = RegValve;
|
||||||
result.FlowMeter = FlowMeter;
|
result.RegulMinStep = RegulMinStep;
|
||||||
|
result.FlowMeter = FlowMeter;
|
||||||
result.PidCoef = PidCoef;
|
result.PidCoef = PidCoef;
|
||||||
result.StartValve = StartValve;
|
result.StartValve = StartValve;
|
||||||
result.Diverter = Diverter;
|
result.Diverter = Diverter;
|
||||||
|
|||||||
@@ -128,10 +128,11 @@ namespace Config
|
|||||||
case GID.Administrators:
|
case GID.Administrators:
|
||||||
#if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL
|
#if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL
|
||||||
case GID.TraceabilityManagement:
|
case GID.TraceabilityManagement:
|
||||||
#elif LANG_PL
|
//#elif LANG_PL
|
||||||
|
#endif
|
||||||
case GID.MetrologicalAuthority:
|
case GID.MetrologicalAuthority:
|
||||||
case GID.WaterMeterAuthority:
|
case GID.WaterMeterAuthority:
|
||||||
#endif
|
|
||||||
admin.AddGroup(group);
|
admin.AddGroup(group);
|
||||||
session.SaveOrUpdate(group); /// Save this group
|
session.SaveOrUpdate(group); /// Save this group
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,410 @@
|
|||||||
|
///
|
||||||
|
/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
|
||||||
|
///
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using log4net;
|
||||||
|
using Common;
|
||||||
|
|
||||||
|
//formulas.cs has been added cause iPerl from master-2 ...MF
|
||||||
|
|
||||||
|
namespace Config
|
||||||
|
{
|
||||||
|
public static class Formulas
|
||||||
|
{
|
||||||
|
private static readonly ILog log = LogManager.GetLogger(typeof(Formulas));
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Wrappers
|
||||||
|
///
|
||||||
|
public static double RealDensity() { return Data.RealDensity; }
|
||||||
|
public static double AtTemperature() { return Data.AtTemperature; }
|
||||||
|
public static double Buoyancy() { return Data.Buoyancy; }
|
||||||
|
|
||||||
|
|
||||||
|
/// Private tables with coeficients to calculate specific enthalpy
|
||||||
|
static readonly int[] Ii;
|
||||||
|
static readonly int[] Ji;
|
||||||
|
static readonly double[] ni;
|
||||||
|
|
||||||
|
/// Private table with coeficients to calculate temperature of a platinum thermometer
|
||||||
|
static readonly double[] Di;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
static Formulas()
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// Initialize tables to calculate specific enthalpies
|
||||||
|
///
|
||||||
|
Ii = new int[34] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32 };
|
||||||
|
Ji = new int[34] { -2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41 };
|
||||||
|
ni = new double[34] {
|
||||||
|
0.14632971213167, /// 1
|
||||||
|
-0.84548187169114, /// 2
|
||||||
|
-0.37563603672040E1, /// 3
|
||||||
|
0.33855169168385E1, /// 4
|
||||||
|
-0.95791963387872, /// 5
|
||||||
|
0.15772038513228, /// 6
|
||||||
|
-0.16616417199501E-1, /// 7
|
||||||
|
0.81214629983568E-3, /// 8
|
||||||
|
0.28319080123804E-3, /// 9
|
||||||
|
-0.60706301565874E-3, /// 10
|
||||||
|
-0.18990068218419E-1, /// 11
|
||||||
|
-0.32529748770505E-1, /// 12
|
||||||
|
-0.21841717175414E-1, /// 13
|
||||||
|
-0.52838357969930E-4, /// 14
|
||||||
|
-0.47184321073267E-3, /// 15
|
||||||
|
-0.30001780793026E-3, /// 16
|
||||||
|
0.47661393906987E-4, /// 17
|
||||||
|
-0.44141845330846E-5, /// 18
|
||||||
|
-0.72694996297594E-15, /// 19
|
||||||
|
-0.31679644845054E-4, /// 20
|
||||||
|
-0.28270797985312E-5, /// 21
|
||||||
|
-0.85205128120103E-9, /// 22
|
||||||
|
-0.22425281908000E-5, /// 23
|
||||||
|
-0.65171222895601E-6, /// 24
|
||||||
|
-0.14341729937924E-12, /// 25
|
||||||
|
-0.40516996860117E-6, /// 26
|
||||||
|
-0.12734301741641E-8, /// 27
|
||||||
|
-0.17424871230634E-9, /// 28
|
||||||
|
-0.68762131295531E-18, /// 29
|
||||||
|
0.14478307828521E-19, /// 30
|
||||||
|
0.26335781662795E-22, /// 31
|
||||||
|
-0.11947622640071E-22, /// 32
|
||||||
|
0.18228094581404E-23, /// 33
|
||||||
|
-0.93537087292458E-25, /// 34
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialize a table to calculate temperature of a platinum thermometer from resistance
|
||||||
|
///
|
||||||
|
Di = new double[]
|
||||||
|
{
|
||||||
|
439.932854,
|
||||||
|
472.418020,
|
||||||
|
37.684494,
|
||||||
|
7.472018,
|
||||||
|
2.920828,
|
||||||
|
0.005184,
|
||||||
|
-0.963864,
|
||||||
|
-0.188732,
|
||||||
|
0.191203,
|
||||||
|
0.049025,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate density of distilled water from temperature
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">ITS-90 temperature in [°C]</param>
|
||||||
|
/// <returns>Density in [kg/m3]</returns>
|
||||||
|
public static double DistilledWaterDensityFromTemp(double t)
|
||||||
|
{
|
||||||
|
if (t <= 40)
|
||||||
|
{
|
||||||
|
const double c0 = 999.839564;
|
||||||
|
const double c1 = 0.067998613;
|
||||||
|
const double c2 = -0.0091101468;
|
||||||
|
const double c3 = 0.00010058299;
|
||||||
|
const double c4 = -0.0000011275659;
|
||||||
|
const double c5 = 6.5985371e-09;
|
||||||
|
|
||||||
|
return ((((c5 * t + c4) * t + c3) * t + c2) * t + c1) * t + c0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const double a0 = 9.9983952E2;
|
||||||
|
const double a1 = 1.6952577E1;
|
||||||
|
const double a2 = -7.9905127E-3;
|
||||||
|
const double a3 = -4.6241757E-5;
|
||||||
|
const double a4 = 1.0584601E-7;
|
||||||
|
const double a5 = -2.8103006E-10;
|
||||||
|
const double b = 1.6887236E-2;
|
||||||
|
|
||||||
|
return (((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t + a0) / (1.0 + b * t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate density of distilled water from temperature (obsolete)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">IPTS-68 temperature in [°C]</param>
|
||||||
|
/// <returns>Density in [kg/m3]</returns>
|
||||||
|
public static double DistilledWaterDensityFromTempIPTS68(double t)
|
||||||
|
{
|
||||||
|
const double a0 = 999.842594;
|
||||||
|
const double a1 = 0.06793952;
|
||||||
|
const double a2 = -0.009095290;
|
||||||
|
const double a3 = 0.0001001685;
|
||||||
|
const double a4 = -0.000001120083;
|
||||||
|
const double a5 = 6.536332e-09;
|
||||||
|
|
||||||
|
return ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t + a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate density by comparing calculated data and data from a certificate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="realDensity">Density from a certificate in [kg/m3]</param>
|
||||||
|
/// <param name="atTemperature">Temperature from a certificate in [°C]</param>
|
||||||
|
/// <returns>Density correction in [kg/m3]</returns>
|
||||||
|
public static double DensityCorrection(double realDensity, double atTemperature)
|
||||||
|
{
|
||||||
|
/// Calculated data
|
||||||
|
double calculatedDensity = DistilledWaterDensityFromTemp(atTemperature);
|
||||||
|
|
||||||
|
return realDensity - calculatedDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate corrected (real) water density from temperature
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">Temperature in [°C]</param>
|
||||||
|
/// <returns>Density in [kg/m3]</returns>
|
||||||
|
public static double WaterDensityFromTemp(double t)
|
||||||
|
{
|
||||||
|
return DistilledWaterDensityFromTemp(t) + DensityCorrection(RealDensity(), AtTemperature());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate corrected (real) water density from temperature
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">Temperature in [°C]</param>
|
||||||
|
/// <returns>Density in [kg/m3]</returns>
|
||||||
|
public static double WaterDensityFromTempPress(double temp, double pressure)
|
||||||
|
{
|
||||||
|
double x0 = 5.08821E-10;
|
||||||
|
double x1 = 1.2639418;
|
||||||
|
double x2 = 0.2660269;
|
||||||
|
double x3 = 0.3734838;
|
||||||
|
double x4 = 2.0205242;
|
||||||
|
double theta = temp / 100.0;
|
||||||
|
double B = x0 * (((x3 * theta + x2) * theta + x1) * theta + 1) / (1 + x4 * theta);
|
||||||
|
|
||||||
|
return WaterDensityFromTemp(temp) * (1 + B * Units.ConvertTo(Unit.Pa, pressure));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static float AirDensityFromAmbientVales(float tempC, float pressureBar, float humiPct)
|
||||||
|
{
|
||||||
|
double pressurePa = 100000.0 * (double)pressureBar; /// [Pa]
|
||||||
|
double tempKelvin = 273.15 + (double)tempC;
|
||||||
|
double coef1 = 1.2811805 / 10000.0 * tempKelvin * tempKelvin
|
||||||
|
- 1.950987 / 100.0 * tempKelvin
|
||||||
|
+ 34.04926034
|
||||||
|
- 6.353631 * 1000.0 / tempKelvin;
|
||||||
|
double coef3 = humiPct / 100.0 * System.Math.Exp(coef1) / pressurePa;
|
||||||
|
double airDensityKgm3 = 0.00348353 * pressurePa * (1.0 - 0.378 * coef3) / tempKelvin; /// kg/m3
|
||||||
|
return (float)airDensityKgm3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert 'pulses' to 'volume', prevent division by zero
|
||||||
|
/// </summary>
|
||||||
|
public static double VolumeFromPulses(int pulses, double pulsesPerLiter)
|
||||||
|
{
|
||||||
|
if (pulsesPerLiter <= double.Epsilon) return 0;
|
||||||
|
return Convert.ToDouble(pulses) / pulsesPerLiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the error in % from 'measured' and 'true' volume, prevent division by zero
|
||||||
|
/// </summary>
|
||||||
|
public static double ErrorFromVolumes(double measuredVolume, double trueVolume)
|
||||||
|
{
|
||||||
|
if (-float.Epsilon <= trueVolume && trueVolume <= float.Epsilon)
|
||||||
|
{
|
||||||
|
if (-float.Epsilon <= measuredVolume && measuredVolume <= float.Epsilon)
|
||||||
|
{
|
||||||
|
log.WarnFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, -100.0);
|
||||||
|
return -100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WarnFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, 99.0);
|
||||||
|
return 99.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double error = 100.0 * (measuredVolume - trueVolume) / trueVolume;
|
||||||
|
log.InfoFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates corrected value from a list of corrections by interpolation.
|
||||||
|
/// It is assumed that values in the list 'corrections' are sorted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rawMeasurement">Raw uncorrected value</param>
|
||||||
|
/// <param name="corrections">Sorted (value, correction) pairs</param>
|
||||||
|
/// <returns>Corrected value</returns>
|
||||||
|
public static double CorrectedValue(double rawValue, IList<IMeasurementCorrection> corrections)
|
||||||
|
{
|
||||||
|
return rawValue + GetCorrection(rawValue, corrections);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a correction from a list of corrections by interpolation.
|
||||||
|
/// It is assumed that values in the list 'corrections' are sorted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rawMeasurement">Raw uncorrected value</param>
|
||||||
|
/// <param name="corrections">Sorted (value, correction) pairs</param>
|
||||||
|
/// <returns>Corrected value</returns>
|
||||||
|
public static double GetCorrection(double rawValue, IList<IMeasurementCorrection> corrections)
|
||||||
|
{
|
||||||
|
if ((corrections == null) || (corrections.Count == 0)) return 0; /// No correction
|
||||||
|
|
||||||
|
if (rawValue < corrections[0].Measurement)
|
||||||
|
{
|
||||||
|
/// rawValue is below the lowest value in the correction table
|
||||||
|
return corrections[0].Correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = corrections.Count;
|
||||||
|
for (int i = 1; i < count; i++)
|
||||||
|
{
|
||||||
|
if (rawValue < corrections[i].Measurement)
|
||||||
|
{
|
||||||
|
double d1 = rawValue - corrections[i - 1].Measurement;
|
||||||
|
double d2 = corrections[i].Measurement - rawValue;
|
||||||
|
|
||||||
|
if (d1 + d2 <= float.Epsilon)
|
||||||
|
{
|
||||||
|
/// Neigboring values in the corection table are close to each other -> calculate the average
|
||||||
|
return (corrections[i - 1].Correction + corrections[i].Correction) / 2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/// Interpolate the correction from neigboring values in the corection table
|
||||||
|
return (corrections[i - 1].Correction * d2 + corrections[i].Correction * d1) / (d1 + d2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// rawValue is above the highest value in the correction table
|
||||||
|
return corrections[count - 1].Correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a measurement error to a correction (used when preparing correction tables).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="measuredValue">Measured value (in arbitrary units)</param>
|
||||||
|
/// <param name="error">Measurement error in %</param>
|
||||||
|
/// <returns>Correction in the same units as the measured value</returns>
|
||||||
|
public static double CorrectionFromError(double measuredValue, double error)
|
||||||
|
{
|
||||||
|
double trueValue = measuredValue / (1 + error/100);
|
||||||
|
double correction = trueValue - measuredValue;
|
||||||
|
return correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the heat coefficient for water
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pressure">Pressure [bar]</param>
|
||||||
|
/// <param name="T_in">Inlet temperature [°C]</param>
|
||||||
|
/// <param name="T_out">Outlet temperature [°C]</param>
|
||||||
|
/// <param name="flowMeasuredAtInlet">true = flow measured @inlet, false = flow measured @outlet</param>
|
||||||
|
/// <returns> Heat coefficient for water [J/(m3 K)]</returns>
|
||||||
|
public static double HeatCoefficientWater(double pressure, double T_in, double T_out, bool flowMeasuredAtInlet)
|
||||||
|
{
|
||||||
|
if (T_in == T_out) return 0;
|
||||||
|
|
||||||
|
const double R = 461.526; /// [J kg^-1 K^-1]
|
||||||
|
const double p_star_Pa = 16.53E6; /// [Pa] (=16.53 MPa)
|
||||||
|
const double T_star = 1386.0; /// [K]
|
||||||
|
|
||||||
|
double T_in_K = Units.ConvertTo(Unit.K, T_in);
|
||||||
|
double T_out_K = Units.ConvertTo(Unit.K, T_out);
|
||||||
|
double tau_in = T_star / T_in_K;
|
||||||
|
double tau_out = T_star / T_out_K;
|
||||||
|
double pi = Units.ConvertTo(Unit.Pa, pressure) / p_star_Pa;
|
||||||
|
|
||||||
|
double h_in = tau_in * GammaTau(pi, tau_in) * R * T_in_K;
|
||||||
|
double h_out = tau_out * GammaTau(pi, tau_out) * R * T_out_K;
|
||||||
|
|
||||||
|
double ni = flowMeasuredAtInlet ? GammaPi(pi, tau_in) * R * T_in_K / p_star_Pa
|
||||||
|
: GammaPi(pi, tau_out) * R * T_out_K / p_star_Pa;
|
||||||
|
|
||||||
|
return (h_in - h_out) / (ni * (T_in - T_out));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gamma(pi) see also STN EN 1434-1 Annex A (A.4)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pi">pi = p / p* where p* = 16.53 MPa</param>
|
||||||
|
/// <param name="tau">tau = T* / T where T* = 1386 K</param>
|
||||||
|
/// <returns>gamma(pi)</returns>
|
||||||
|
static double GammaPi(double pi, double tau)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
for (int i = 0; i < 34; i++)
|
||||||
|
{
|
||||||
|
result -= ni[i] * Ii[i] * Math.Pow(7.1 - pi, Ii[i] - 1) * Math.Pow(tau - 1.222, Ji[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gamma(tau) see also STN EN 1434-1 Annex A (A.7)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pi">pi = p / p* where p* = 16.53 MPa</param>
|
||||||
|
/// <param name="tau">tau = T* / T where T* = 1386 K</param>
|
||||||
|
/// <returns>gamma(tau)</returns>
|
||||||
|
static double GammaTau(double pi, double tau)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
for (int i = 0; i < 34; i++)
|
||||||
|
{
|
||||||
|
result += ni[i] * Math.Pow(7.1 - pi, Ii[i]) * Ji[i] * Math.Pow(tau - 1.222, Ji[i] - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Conversion of measured resistance of a platinum thermometer to temperature according to ITS-90
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="R">Measured resistance in [°C]</param>
|
||||||
|
/// <param name="R001C">Calibrated resistance in Ohm at 0.01°C</param>
|
||||||
|
/// <param name="a7">Calibrated ITS-90 coefficient a7</param>
|
||||||
|
/// <param name="b7">Calibrated ITS-90 coefficient b7</param>
|
||||||
|
/// <param name="c7">Calibrated ITS-90 coefficient c7</param>
|
||||||
|
/// <returns>Temperature in [°C]</returns>
|
||||||
|
public static double PlatinumResistanceTM_ITS90_R2T(double R, double R001C, double a7, double b7, double c7)
|
||||||
|
{
|
||||||
|
double w = R / R001C; /// ratio
|
||||||
|
double r1 = w - 1.0;
|
||||||
|
double dw = r1 * (a7 + r1 * (b7 + r1 * c7)); /// = a7*r1 + b7*r1^2 + c7*r1^3
|
||||||
|
double wr = w - dw;
|
||||||
|
double x = (wr - 2.64) / 1.64;
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for (int i = Di.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
sum = sum * x + Di[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Conversion of measured resistance of a platinum thermometer to temperature using Callendar-Van Dusen equations
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="R">Measured resistance in [°C]</param>
|
||||||
|
/// <param name="R0">Calibrated resistance in Ohm at 0°C</param>
|
||||||
|
/// <param name="A">Calibration coefficient a</param>
|
||||||
|
/// <param name="B">Calibration coefficient b</param>
|
||||||
|
/// <returns>Temperature in [°C]</returns>
|
||||||
|
public static double PlatinumResistanceTM_ITS27_R2T(double R, double R0, double A, double B)
|
||||||
|
{
|
||||||
|
if (R0 * R0 * A * A - 4 * R0 * B * (R0 - R) <= 0) return 0; /// Out of range
|
||||||
|
|
||||||
|
return (-(R0 * A) + Math.Sqrt(R0 * R0 * A * A - 4 * R0 * B * (R0 - R))) / (2 * R0 * B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,8 @@ namespace Config.Mappings
|
|||||||
Map(x => x.Qto);
|
Map(x => x.Qto);
|
||||||
Map(x => x.Selector);
|
Map(x => x.Selector);
|
||||||
Map(x => x.RegValve);
|
Map(x => x.RegValve);
|
||||||
Map(x => x.FlowMeter);
|
Map(x => x.RegulMinStep).Nullable();//older databases may not have this column
|
||||||
|
Map(x => x.FlowMeter);
|
||||||
Map(x => x.PidCoef);
|
Map(x => x.PidCoef);
|
||||||
Map(x => x.StartValve);
|
Map(x => x.StartValve);
|
||||||
Map(x => x.Diverter);
|
Map(x => x.Diverter);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Config
|
||||||
|
{
|
||||||
|
public class PurchaseResources
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
|
||||||
Binary file not shown.
+1
-1
@@ -18,7 +18,7 @@ namespace Decrypt
|
|||||||
{
|
{
|
||||||
if ((args.Length < 1) || !File.Exists(args[0]))
|
if ((args.Length < 1) || !File.Exists(args[0]))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Usage: Decode <file>");
|
Console.WriteLine(@"Usage: Decode <file>");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-2
@@ -1,3 +1,28 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /></startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ namespace Encrypt
|
|||||||
{
|
{
|
||||||
if ((args.Length < 1) || !File.Exists(args[0]))
|
if ((args.Length < 1) || !File.Exists(args[0]))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Usage: Encode <file>");
|
Console.WriteLine(@"Usage: Encode <file>");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|||||||
+27
-2
@@ -1,3 +1,28 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /></startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.API
|
||||||
|
{
|
||||||
|
public class Enums
|
||||||
|
{
|
||||||
|
public enum DataStorageReaderTypes : sbyte
|
||||||
|
{
|
||||||
|
LoginPasswordsReader,
|
||||||
|
CalibrationParamsReader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,785 @@
|
|||||||
|
using CordonelPreadjustmentUi;
|
||||||
|
using CordonelPreadjustmentUi.Processes.Itinerary;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Config;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||||
|
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
|
||||||
|
using Xylem.Common.Ui.CordonelPreadjustmentUi;
|
||||||
|
using static GenesisCordonelInterface.API.PublicModels;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Public-facing facade for external applications integrating with
|
||||||
|
/// Genesis Cordonel Interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class exposes a simplified and controlled API for external callers.
|
||||||
|
/// It validates public input, maps public request models to internal models,
|
||||||
|
/// forwards operations to the internal GCI implementation and exposes status
|
||||||
|
/// notifications for meter batch changes.
|
||||||
|
///
|
||||||
|
/// This layer should stay thin. Business logic and meter communication are
|
||||||
|
/// handled by <see cref="InterfaceGCIToLaatzen"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public class InterfaceOutsideToGCI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Internal GCI implementation used by this public facade.
|
||||||
|
/// </summary>
|
||||||
|
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when meter batch status information changes.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
|
||||||
|
private readonly IMeterLoginPasswordReader loginPasswordsReader;
|
||||||
|
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the public GCI facade.
|
||||||
|
/// </summary>
|
||||||
|
public InterfaceOutsideToGCI()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the public GCI facade.
|
||||||
|
/// </summary>
|
||||||
|
public InterfaceOutsideToGCI(
|
||||||
|
InterfaceGCIToLaatzen innerMeterApi,
|
||||||
|
IMeterLoginPasswordReader passwordReader,
|
||||||
|
IPreAdjustmentCalibrationParamsReader calibrationReader)
|
||||||
|
{
|
||||||
|
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
|
||||||
|
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
|
||||||
|
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
|
||||||
|
|
||||||
|
//init handlers
|
||||||
|
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RequestedCalibrationParamsFromDb(
|
||||||
|
object sender,
|
||||||
|
EventArgsProcessProgress e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DataQuery query = new DataQuery();
|
||||||
|
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
|
||||||
|
|
||||||
|
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query).ConfigureAwait(false);
|
||||||
|
|
||||||
|
e.Value.PushedCalibrationParams = calibrationParams;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
e.Value.DebugMessage(
|
||||||
|
$"Calibration params reading failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
e.Value.CalibrationParamsReadEvent.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Laatzen ToolBox actions
|
||||||
|
|
||||||
|
#region ================================== PORT DETECTION ==================================
|
||||||
|
|
||||||
|
public PortDetectionResult DetectStreamingPort(int slot)
|
||||||
|
{
|
||||||
|
var result = _innerMeterAPI?.DetectStreamingPort(slot);
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PortDetectionResult> DetectStreamingPortAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var result = await _innerMeterAPI?.DetectStreamingPortAsync(slot, token);
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortDetectionResult DetectRequestPort(int slot)
|
||||||
|
{
|
||||||
|
var result = _innerMeterAPI?.DetectRequestPort(slot);
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PortDetectionResult> DetectRequestPortAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== INIT/UPDATE/GET slot ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a meter slot using the provided slot configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">
|
||||||
|
/// Slot initialization request containing slot id, configuration source,
|
||||||
|
/// password source, request port and streaming port.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Result describing whether the slot was created, updated, already existed or failed.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown when <paramref name="request"/> is null.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciInitSlotResult> InitSlotAsync(
|
||||||
|
GciInitSlotRequest request,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.InitSlotAsync(
|
||||||
|
request.SlotId,
|
||||||
|
ModelsMapping.MapConfigSource(request.ConfigSource),
|
||||||
|
ModelsMapping.MapPasswordSource(request.PasswordSource),
|
||||||
|
ModelsMapping.MapPort(request.RequestPort),
|
||||||
|
ModelsMapping.MapPort(request.StreamingPort),
|
||||||
|
token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates configuration of an existing meter slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">
|
||||||
|
/// Slot configuration request containing updated configuration source,
|
||||||
|
/// password source and port settings.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Result describing whether the slot update succeeded or failed.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Thrown when <paramref name="request"/> is null.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciInitSlotResult> UpdateSlotAsync(
|
||||||
|
GciInitSlotRequest request,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.UpdateSlotAsync(
|
||||||
|
request.SlotId,
|
||||||
|
ModelsMapping.MapConfigSource(request.ConfigSource),
|
||||||
|
ModelsMapping.MapPasswordSource(request.PasswordSource),
|
||||||
|
ModelsMapping.MapPort(request.RequestPort),
|
||||||
|
ModelsMapping.MapPort(request.StreamingPort),
|
||||||
|
token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets information about one meter slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slotId">Slot id to query. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Slot information including existence, connection state, login state,
|
||||||
|
/// PCB id and configured communication ports.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slotId"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciSlotInfo> GetSlotAsync(
|
||||||
|
int slotId,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slotId <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets information about all currently initialized meter slots.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Collection of slot information records for all known meters.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<GciAllSlotsInfo> GetAllSlotsAsync(
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var result = await _innerMeterAPI.GetAllMetersInfo(token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans one meter slot and releases its runtime resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id to clean. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Result describing whether the slot cleanup succeeded or failed.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slot"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciCleanSlotResult> CleanSlotAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.CleanSlotAsync(slot, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans all initialized meter slots and releases related runtime resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Result describing whether cleanup of all slots succeeded or failed.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<GciCleanAllSlotsResult> CleanAllSlotsAsync(
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var result = await _innerMeterAPI.CleanAllSlotsAsync(token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== PASSWORD ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets runtime password for the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="password">Password to assign to the meter.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>Result containing password update status.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when slot id is invalid or password is empty.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciSetPasswordResult> SetPasswordAsync(
|
||||||
|
int slot,
|
||||||
|
string password,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
throw new ArgumentException("Password is empty.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== LOGIN ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs in to the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>Login result containing login state and status message.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slot"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.LoginOneSlotAsync(slot, token).ConfigureAwait(false);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== CONNECTION ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connects the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>Connection result containing connection state and status message.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slot"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciConnectResult> ConnectOneSlotAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>Disconnect result containing final connection state and status message.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slot"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciDisconnectResult> DisconnectAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.DisconnectAsync(slot, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== PCB ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads PCB identifier from the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>Result containing PCB id and read status.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="slot"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciGetPcbIdResult> GetPcbIdAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.");
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== READ ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a firmware register value from the meter assigned to the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="registerName">Register identifier to read.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Register read result containing raw register value and operation status.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when slot id or register name is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<RegisterReadResult> ReadRegisterAsync(
|
||||||
|
int slot,
|
||||||
|
string registerName,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(registerName))
|
||||||
|
throw new ArgumentException("Register name is empty.", nameof(registerName));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI
|
||||||
|
.ReadRegisterAsync(slot, registerName, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== WRITE ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a value to a firmware register.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="registerName">Register identifier to write.</param>
|
||||||
|
/// <param name="value">Value to write.</param>
|
||||||
|
/// <param name="storeToDevice">
|
||||||
|
/// Indicates whether configuration should be permanently stored.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="refreshSystemState">
|
||||||
|
/// Indicates whether firmware system state should be refreshed after write.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Register write result describing write status.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when slot id or register name is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<RegisterWriteResult> WriteRegisterAsync(
|
||||||
|
int slot,
|
||||||
|
string registerName,
|
||||||
|
object value,
|
||||||
|
bool storeToDevice = false,
|
||||||
|
bool refreshSystemState = false,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(registerName))
|
||||||
|
throw new ArgumentException("Register name is empty.", nameof(registerName));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI
|
||||||
|
.WriteRegisterAsync(
|
||||||
|
slot,
|
||||||
|
registerName,
|
||||||
|
value,
|
||||||
|
storeToDevice,
|
||||||
|
refreshSystemState,
|
||||||
|
token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== Password ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates meter password for the specified slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||||
|
/// <param name="password">New password.</param>
|
||||||
|
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password update result.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when slot id or password is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(
|
||||||
|
int slot,
|
||||||
|
string password,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (slot <= 0)
|
||||||
|
throw new ArgumentException("Invalid slot id.", nameof(slot));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
throw new ArgumentException("Password is empty.", nameof(password));
|
||||||
|
|
||||||
|
var result = await _innerMeterAPI
|
||||||
|
.SetMeterPasswordAsync(slot, password, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
//RaiseMeterBatchStatusChanged();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== DEBUG STATUS ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets runtime diagnostic information for all active workers.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Collection containing worker state, queue information,
|
||||||
|
/// current operation and activity timestamps.
|
||||||
|
/// </returns>
|
||||||
|
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI?.GetWorkerDebugStatuses();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets runtime diagnostic information for all meter slots.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Collection containing slot state, connection state,
|
||||||
|
/// selected state and communication configuration.
|
||||||
|
/// </returns>
|
||||||
|
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises meter batch status change notification.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Intended to notify external consumers after changes in meter state.
|
||||||
|
/// </remarks>
|
||||||
|
public void RaiseMeterBatchStatusChanged()
|
||||||
|
{
|
||||||
|
var statuses = GetMeterBatchDebugStatuses();
|
||||||
|
|
||||||
|
var handler = MeterBatchStatusChanged;
|
||||||
|
if (handler != null)
|
||||||
|
handler(statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== SLOT SELECTION ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets selection state for a slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id.</param>
|
||||||
|
/// <param name="selected">Selection state.</param>
|
||||||
|
public void SetSlotSelected(int slot, bool selected)
|
||||||
|
{
|
||||||
|
_innerMeterAPI?.SetSlotSelected(slot, selected);
|
||||||
|
RaiseMeterBatchStatusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified slot is selected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">Slot id.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if slot is selected; otherwise false.
|
||||||
|
/// </returns>
|
||||||
|
public bool IsSlotSelected(int slot)
|
||||||
|
{
|
||||||
|
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all selected slot identifiers.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Ordered collection of selected slot ids.
|
||||||
|
/// </returns>
|
||||||
|
public List<int> GetSelectedSlots()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI?.GetSelectedSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== Register names ==================================
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all available firmware register identifiers.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Ordered collection of register names.
|
||||||
|
/// </returns>
|
||||||
|
public List<string> GetAllRegisterNames()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI?.GetAllRegisterNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Laatzen Preadjustment processes
|
||||||
|
|
||||||
|
#region ================================== PreAdjustment ==================================
|
||||||
|
public PreAdjustmentInitializationResult Preadjustment_Initialization(
|
||||||
|
ProcessProgress pp,
|
||||||
|
List<MeterStateControl> mc)
|
||||||
|
{
|
||||||
|
//init handlers
|
||||||
|
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
|
||||||
|
|
||||||
|
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||||
|
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
|
||||||
|
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_DetectDirect(selectedSlots, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_PreparationDirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool PreAdjustment_PushTemperature(
|
||||||
|
double temperature)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
|
||||||
|
int slot,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
|
||||||
|
{
|
||||||
|
return _innerMeterAPI.PreAdjustment_CompletionDirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ================================== UNI DATA STORAGE READER ==================================
|
||||||
|
|
||||||
|
//LoginPasswords reading
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meter login password from configured GCI data storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing PCB ID or another configured lookup value.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token used to cancel the asynchronous operation.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password if found; otherwise null.
|
||||||
|
/// </returns>
|
||||||
|
public Task<string> ReadMeterLoginPasswordAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
|
||||||
|
query,
|
||||||
|
token);
|
||||||
|
}
|
||||||
|
|
||||||
|
//CalibrationParams reading
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads pre-adjustment calibration parameters
|
||||||
|
/// from configured GCI data storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing meter size or another configured lookup value.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token used to cancel the asynchronous operation.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary:
|
||||||
|
///
|
||||||
|
/// Key:
|
||||||
|
/// Calibration parameter name
|
||||||
|
///
|
||||||
|
/// Value:
|
||||||
|
/// Calibration parameter value
|
||||||
|
/// </returns>
|
||||||
|
public Task<Dictionary<string, UInt32>> ReadPreAdjustmentCalibrationParamsAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return calibrationParamsReader.ReadCalibrationParamsAsync(
|
||||||
|
query,
|
||||||
|
token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||||
|
using static GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||||
|
using static GenesisCordonelInterface.API.PublicModels;
|
||||||
|
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Public data contract layer for the Genesis Cordonel Interface (GCI).
|
||||||
|
///
|
||||||
|
/// This class defines all Data Transfer Objects (DTOs) that are exposed
|
||||||
|
/// to external consumers (e.g. TBF, UI, or other integration layers).
|
||||||
|
///
|
||||||
|
/// Responsibilities:
|
||||||
|
/// - Provide stable, dependency-free models for external usage
|
||||||
|
/// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries)
|
||||||
|
/// from external systems
|
||||||
|
/// - Define request/response contracts for all supported operations
|
||||||
|
/// - Contain mapping methods between public DTOs and internal domain models
|
||||||
|
///
|
||||||
|
/// Architecture:
|
||||||
|
/// External world (TBF / UI)
|
||||||
|
/// ↓
|
||||||
|
/// GciPublicModels (this layer)
|
||||||
|
/// ↓
|
||||||
|
/// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.)
|
||||||
|
///
|
||||||
|
/// Notes:
|
||||||
|
/// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.)
|
||||||
|
/// - All mapping between internal and external representations must be done here
|
||||||
|
/// - DTOs are designed to be simple, serializable, and stable over time
|
||||||
|
/// - Any change in internal implementation should not affect these models
|
||||||
|
///
|
||||||
|
/// Pattern:
|
||||||
|
/// Each operation follows a consistent structure:
|
||||||
|
/// Request → Operation → Result
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// GciInitSlotRequest → InitSlot → GciInitSlotResult
|
||||||
|
/// GetSlot → GciSlotInfo
|
||||||
|
/// GetPcbId → GciGetPcbIdResult
|
||||||
|
///
|
||||||
|
/// This layer acts as a boundary between domain logic and integration logic.
|
||||||
|
/// </summary>
|
||||||
|
public class ModelsMapping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Mapping methods between public DTOs and internal GCI models.
|
||||||
|
/// Ensures separation between external contracts and internal domain objects.
|
||||||
|
/// </summary>
|
||||||
|
#region ================================== OUTERN/INTERN and back models mapping ==================================
|
||||||
|
public static PasswordSource MapPasswordSource(PublicModels.GciPasswordSource src)
|
||||||
|
{
|
||||||
|
return (PasswordSource)src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigSource MapConfigSource(PublicModels.GciConfigSource src)
|
||||||
|
{
|
||||||
|
return (ConfigSource)src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig? MapPort(PublicModels.GciPortConfig port)
|
||||||
|
{
|
||||||
|
if (port == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig result = new Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig();
|
||||||
|
|
||||||
|
result.PortName = port.PortName;
|
||||||
|
result.Type = port.Type;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciInitSlotResult MapInitResult(PublicModels.GciInitSlotResult result)
|
||||||
|
{
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new PublicModels.GciInitSlotResult
|
||||||
|
{
|
||||||
|
SlotId = result.SlotId,
|
||||||
|
Success = result.Success,
|
||||||
|
Message = result.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciPasswordSource MapPasswordSourceBack(PasswordSource src)
|
||||||
|
{
|
||||||
|
return (PublicModels.GciPasswordSource)src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciConfigSource MapConfigSourceBack(ConfigSource src)
|
||||||
|
{
|
||||||
|
return (PublicModels.GciConfigSource)src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciPortConfig MapPortBack(PortConfig? port)
|
||||||
|
{
|
||||||
|
if (!port.HasValue)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
PortConfig value = port.Value;
|
||||||
|
|
||||||
|
return new PublicModels.GciPortConfig
|
||||||
|
{
|
||||||
|
PortName = value.PortName,
|
||||||
|
Type = value.Type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciPortConfig MapPortBack(IPort port)
|
||||||
|
{
|
||||||
|
if (port == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new PublicModels.GciPortConfig
|
||||||
|
{
|
||||||
|
PortName = port.GetPortName(),
|
||||||
|
Type = port.GetType().Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicModels.GciPortConfig MapPortBack(string portName, string portTyoe)
|
||||||
|
{
|
||||||
|
return new PublicModels.GciPortConfig
|
||||||
|
{
|
||||||
|
PortName = portName,
|
||||||
|
Type = portTyoe
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MeterBatchDebugStatus ToMeterBatchDebugStatus(GciSlotInfo slot)
|
||||||
|
{
|
||||||
|
if (slot == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new MeterBatchDebugStatus
|
||||||
|
{
|
||||||
|
Slot = slot.SlotId,
|
||||||
|
Selected = true,
|
||||||
|
PcbId = slot.PcbId,
|
||||||
|
IsConnected = false,
|
||||||
|
IsLoggedOn = false,
|
||||||
|
RequestPort = slot.RequestPort == null ? "" : slot.RequestPort.PortName,
|
||||||
|
StreamingPort = slot.StreamingPort == null ? "" : slot.StreamingPort.PortName,
|
||||||
|
FwVersion = "",
|
||||||
|
InterfaceVersion = ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<MeterBatchDebugStatus> ToMeterBatchDebugStatusList(
|
||||||
|
IEnumerable<GciSlotInfo> slots)
|
||||||
|
{
|
||||||
|
if (slots == null)
|
||||||
|
return new List<MeterBatchDebugStatus>();
|
||||||
|
|
||||||
|
return slots
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Select(ToMeterBatchDebugStatus)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,504 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
||||||
|
using static GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||||
|
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.API
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Public data contract layer for the Genesis Cordonel Interface (GCI).
|
||||||
|
///
|
||||||
|
/// This class defines all Data Transfer Objects (DTOs) that are exposed
|
||||||
|
/// to external consumers (e.g. TBF, UI, or other integration layers).
|
||||||
|
///
|
||||||
|
/// Responsibilities:
|
||||||
|
/// - Provide stable, dependency-free models for external usage
|
||||||
|
/// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries)
|
||||||
|
/// from external systems
|
||||||
|
/// - Define request/response contracts for all supported operations
|
||||||
|
/// - Contain mapping methods between public DTOs and internal domain models
|
||||||
|
///
|
||||||
|
/// Architecture:
|
||||||
|
/// External world (TBF / UI)
|
||||||
|
/// ↓
|
||||||
|
/// GciPublicModels (this layer)
|
||||||
|
/// ↓
|
||||||
|
/// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.)
|
||||||
|
///
|
||||||
|
/// Notes:
|
||||||
|
/// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.)
|
||||||
|
/// - All mapping between internal and external representations must be done here
|
||||||
|
/// - DTOs are designed to be simple, serializable, and stable over time
|
||||||
|
/// - Any change in internal implementation should not affect these models
|
||||||
|
///
|
||||||
|
/// Pattern:
|
||||||
|
/// Each operation follows a consistent structure:
|
||||||
|
/// Request → Operation → Result
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// GciInitSlotRequest → InitSlot → GciInitSlotResult
|
||||||
|
/// GetSlot → GciSlotInfo
|
||||||
|
/// GetPcbId → GciGetPcbIdResult
|
||||||
|
///
|
||||||
|
/// This layer acts as a boundary between domain logic and integration logic.
|
||||||
|
/// </summary>
|
||||||
|
public class PublicModels
|
||||||
|
{
|
||||||
|
public static bool HideSensitiveValues { get; set; } = true;
|
||||||
|
|
||||||
|
private static string FormatPassword(string password)
|
||||||
|
{
|
||||||
|
if (!HideSensitiveValues)
|
||||||
|
return password ?? "<null>";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(password))
|
||||||
|
return "<empty>";
|
||||||
|
|
||||||
|
return "********";
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Public DTOs exposed to external systems.
|
||||||
|
/// These models represent the contract of the GCI API.
|
||||||
|
/// They must remain stable and independent of internal implementation.
|
||||||
|
/// </summary>
|
||||||
|
#region ================================== PUBLIC MODELS ===========================================
|
||||||
|
|
||||||
|
public class RegisterReadResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string RegisterName { get; set; }
|
||||||
|
public string RawHex { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"Success={0}, RegisterName={1}, RawHex={2}, ErrorMessage={3}",
|
||||||
|
Success,
|
||||||
|
RegisterName,
|
||||||
|
RawHex,
|
||||||
|
ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorkerDebugStatus
|
||||||
|
{
|
||||||
|
public int Slot { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int QueueLength { get; set; }
|
||||||
|
public bool IsBusy { get; set; }
|
||||||
|
public string CurrentOperation { get; set; }
|
||||||
|
public string LastError { get; set; }
|
||||||
|
public DateTime LastActivity { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MeterBatchDebugStatus
|
||||||
|
{
|
||||||
|
public int Slot { get; set; }
|
||||||
|
public bool Selected { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public bool IsConnected { get; set; }
|
||||||
|
public bool IsLoggedOn { get; set; }
|
||||||
|
public string RequestPort { get; set; }
|
||||||
|
public string StreamingPort { get; set; }
|
||||||
|
public string RequestPortType { get; set; }
|
||||||
|
public string FwVersion { get; set; }
|
||||||
|
public string InterfaceVersion { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegisterWriteResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string RegisterName { get; set; }
|
||||||
|
public object WrittenValue { get; set; }
|
||||||
|
public bool StoreToDevice { get; set; }
|
||||||
|
public bool RefreshSystemState { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Success={Success}, " +
|
||||||
|
$"Register={RegisterName ?? "<null>"}, " +
|
||||||
|
$"Value={WrittenValue ?? "<null>"}, " +
|
||||||
|
$"StoreToDevice={StoreToDevice}, " +
|
||||||
|
$"RefreshSystemState={RefreshSystemState}, " +
|
||||||
|
$"Error={ErrorMessage ?? "<none>"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PortDetectionResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public int Slot { get; set; }
|
||||||
|
public string PortName { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"Slot={0}, Success={1}, PortName={2}, PcbId={3}, ErrorMessage={4}",
|
||||||
|
Slot,
|
||||||
|
Success,
|
||||||
|
PortName,
|
||||||
|
PcbId,
|
||||||
|
ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciSlotInfo
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Exists { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public bool IsConnected { get; set; }
|
||||||
|
public bool IsLoggedOn { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public GciConfigSource ConfigSource { get; set; }
|
||||||
|
public GciPasswordSource PasswordSource { get; set; }
|
||||||
|
public GciPortConfig RequestPort { get; set; }
|
||||||
|
public GciPortConfig StreamingPort { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Exists={1}, Success={2}, IsConnected={3}, IsLoggedOn={4}, Message={5}, PcbId={6}, Password={7}, ConfigSource={8}, PasswordSource={9}, RequestPort={10}, StreamingPort={11}",
|
||||||
|
SlotId,
|
||||||
|
Exists,
|
||||||
|
Success,
|
||||||
|
IsConnected,
|
||||||
|
IsLoggedOn,
|
||||||
|
Message,
|
||||||
|
PcbId,
|
||||||
|
FormatPassword(Password),
|
||||||
|
ConfigSource,
|
||||||
|
PasswordSource,
|
||||||
|
RequestPort,
|
||||||
|
StreamingPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciAllSlotsInfo
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public List<GciSlotInfo> Slots { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Success={Success}, Count={Slots?.Count ?? 0}, Message={Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Result
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, Message={2}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GciPasswordSource
|
||||||
|
{
|
||||||
|
RestApi = 0,
|
||||||
|
OfflineFile = 1,
|
||||||
|
InterfaceInputPassword = 2,
|
||||||
|
}
|
||||||
|
public enum GciConfigSource
|
||||||
|
{
|
||||||
|
FileConfig = 0,
|
||||||
|
InterfaceInputConfig = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of port settings
|
||||||
|
/// </summary>
|
||||||
|
public class GciPortConfig
|
||||||
|
{
|
||||||
|
public string PortName { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("PortName={0}, Type={1}", PortName, Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GciPortConfig CreatePortConfig(string portName, string type)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(portName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new GciPortConfig
|
||||||
|
{
|
||||||
|
PortName = portName,
|
||||||
|
Type = MapPortType(type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MapPortType(string type)
|
||||||
|
{
|
||||||
|
switch ((type ?? "").Trim().ToUpperInvariant())
|
||||||
|
{
|
||||||
|
case "RFID":
|
||||||
|
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.RfidSerialPort";
|
||||||
|
|
||||||
|
case "UART":
|
||||||
|
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.UartSerialPort";
|
||||||
|
|
||||||
|
case "IRDA":
|
||||||
|
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciInitSlotRequest
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public GciConfigSource ConfigSource { get; set; }
|
||||||
|
public GciPasswordSource PasswordSource { get; set; }
|
||||||
|
public GciPortConfig RequestPort { get; set; }
|
||||||
|
public GciPortConfig StreamingPort { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}, Password={5}",
|
||||||
|
SlotId,
|
||||||
|
ConfigSource,
|
||||||
|
PasswordSource,
|
||||||
|
RequestPort,
|
||||||
|
StreamingPort,
|
||||||
|
FormatPassword(Password));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciInitSlotResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
|
||||||
|
public bool Created { get; set; }
|
||||||
|
|
||||||
|
public bool Updated { get; set; }
|
||||||
|
|
||||||
|
public bool AlreadyExists { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, Message={2}, PcbId={3}, Created={4}, Updated={5}, AlreadyExists={6}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
Message,
|
||||||
|
PcbId,
|
||||||
|
Created,
|
||||||
|
Updated,
|
||||||
|
AlreadyExists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciCleanAllSlotsResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"Success={0}, Message={1}",
|
||||||
|
Success,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class GciCleanSlotResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, Message={2}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciGetPcbIdResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, PcbId={2}, Message={3}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
PcbId,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class GciConnectResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public bool IsConnected { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, PcbId={2}, IsConnected={3}, Message={4}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
PcbId,
|
||||||
|
IsConnected,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciLoginResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string PcbId { get; set; }
|
||||||
|
public bool IsLoggedOn { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Slot={SlotId}, Success={Success}, PcbId={PcbId}, IsLoggedOn={IsLoggedOn}, Message={Message ?? "<none>"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciRegisterSnapshot
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string RawValue { get; set; }
|
||||||
|
public string Min { get; set; }
|
||||||
|
public string Max { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string IsAvailable { get; set; }
|
||||||
|
public string Privilege { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciDisconnectResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, Message={2}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GciSetPasswordResult
|
||||||
|
{
|
||||||
|
public int SlotId { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"SlotId={0}, Success={1}, Password={2}, Message={3}",
|
||||||
|
SlotId,
|
||||||
|
Success,
|
||||||
|
FormatPassword(Password),
|
||||||
|
Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PreAdjustmentProcessResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public int Slot { get; set; }
|
||||||
|
public string ProcessName { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$"Success={Success}, " +
|
||||||
|
$"Slot={Slot}, " +
|
||||||
|
$"Process={ProcessName}, " +
|
||||||
|
$"Error={ErrorMessage ?? "None"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PreadjustmentDetectResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public int DetectedMeterCount { get; set; }
|
||||||
|
public int DetectedThermometerCount { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$"Success={Success}, " +
|
||||||
|
$"Meters={DetectedMeterCount}, " +
|
||||||
|
$"Thermometers={DetectedThermometerCount}, " +
|
||||||
|
$"Error={ErrorMessage ?? "None"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PreAdjustmentInitializationResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Slots { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$"Success={Success}, " +
|
||||||
|
$"Slots={Slots}, " +
|
||||||
|
$"Error={ErrorMessage ?? "None"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Xylem.Common.CommonCore" publicKeyToken="null" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-2.8.18.17954" newVersion="2.8.18.17954" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"_Comment": "GCI DataStorage configuration",
|
||||||
|
|
||||||
|
"DataStorage": {
|
||||||
|
|
||||||
|
"MeterLoginPasswords": {
|
||||||
|
|
||||||
|
"___Documentation___": {
|
||||||
|
"Description": "Reads login passwords for meters by PCB ID",
|
||||||
|
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||||
|
"DataSource": "Database connection string",
|
||||||
|
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||||
|
},
|
||||||
|
|
||||||
|
"Name": "MeterLoginPasswords",
|
||||||
|
"Type": "LocalDatabase",
|
||||||
|
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||||
|
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||||
|
},
|
||||||
|
|
||||||
|
"PreAdjustmentCalibrationParams": {
|
||||||
|
|
||||||
|
"___Documentation___": {
|
||||||
|
"Description": "Reads pre-adjustment calibration parameters by meter size",
|
||||||
|
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||||
|
"DataSource": "Database connection string",
|
||||||
|
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
|
||||||
|
},
|
||||||
|
|
||||||
|
"Name": "PreAdjustmentCalibrationParams",
|
||||||
|
"Type": "LocalDatabase",
|
||||||
|
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||||
|
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
namespace GenesisCordonelInterface.Core.Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Root GCI configuration object loaded from gci_config.json.
|
||||||
|
///
|
||||||
|
/// This class represents the top-level configuration structure
|
||||||
|
/// and serves as an entry point for all configurable GCI features.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// "DataStorageSection":
|
||||||
|
/// {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </summary>
|
||||||
|
public class GciConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data storage configuration section.
|
||||||
|
///
|
||||||
|
/// Contains definitions for all configured readers and writers,
|
||||||
|
/// such as:
|
||||||
|
///
|
||||||
|
/// - MeterLoginPasswords
|
||||||
|
/// - PreAdjustmentCalibrationParams
|
||||||
|
/// - future storage providers
|
||||||
|
/// </summary>
|
||||||
|
public DataStorage.Config.GciDataStorageConfig DataStorageSection { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads GCI configuration from gci_config.json.
|
||||||
|
///
|
||||||
|
/// Responsibilities:
|
||||||
|
/// - Locate configuration file
|
||||||
|
/// - Deserialize JSON into strongly typed objects
|
||||||
|
/// - Normalize relative file paths
|
||||||
|
/// - Preserve database connection strings and REST URLs
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// Config/
|
||||||
|
/// gci_config.json
|
||||||
|
///
|
||||||
|
/// Data/
|
||||||
|
/// meter_passwords.csv
|
||||||
|
///
|
||||||
|
/// Relative paths:
|
||||||
|
/// Data\file.csv
|
||||||
|
///
|
||||||
|
/// become:
|
||||||
|
///
|
||||||
|
/// C:\App\bin\Debug\Data\file.csv
|
||||||
|
///
|
||||||
|
/// Database sources remain unchanged:
|
||||||
|
///
|
||||||
|
/// Server=(localdb)\MojaDB;Database=...
|
||||||
|
/// </summary>
|
||||||
|
public static class GciConfigLoader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads default GCI configuration from:
|
||||||
|
///
|
||||||
|
/// Config\gci_config.json
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Loaded GCI configuration.
|
||||||
|
/// </returns>
|
||||||
|
public static GciConfig LoadDefault()
|
||||||
|
{
|
||||||
|
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
|
string configPath = Path.Combine(
|
||||||
|
baseDirectory,
|
||||||
|
"Config",
|
||||||
|
"gci_config.json");
|
||||||
|
|
||||||
|
return Load(configPath, baseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads GCI configuration from specified file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configPath">
|
||||||
|
/// Path to configuration json file.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base directory used for resolving relative paths.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Loaded configuration object.
|
||||||
|
/// </returns>
|
||||||
|
public static GciConfig Load(
|
||||||
|
string configPath,
|
||||||
|
string baseDirectory = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(configPath))
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Config path must not be empty.",
|
||||||
|
nameof(configPath));
|
||||||
|
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"GCI config file was not found.",
|
||||||
|
configPath);
|
||||||
|
|
||||||
|
string json = File.ReadAllText(configPath);
|
||||||
|
|
||||||
|
GciConfig config =
|
||||||
|
JsonConvert.DeserializeObject<GciConfig>(json);
|
||||||
|
|
||||||
|
if (config == null)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"GCI config could not be loaded.");
|
||||||
|
|
||||||
|
NormalizeDataStoragePaths(
|
||||||
|
config,
|
||||||
|
baseDirectory ?? Path.GetDirectoryName(configPath));
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes paths for all configured data storage entries.
|
||||||
|
///
|
||||||
|
/// Converts relative file paths into absolute paths.
|
||||||
|
/// Database connection strings remain untouched.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Loaded configuration object.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base path used for relative resolution.
|
||||||
|
/// </param>
|
||||||
|
private static void NormalizeDataStoragePaths(
|
||||||
|
GciConfig config,
|
||||||
|
string baseDirectory)
|
||||||
|
{
|
||||||
|
if (config.DataStorageSection == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NormalizePath(
|
||||||
|
config.DataStorageSection.MeterLoginPasswords,
|
||||||
|
baseDirectory);
|
||||||
|
|
||||||
|
NormalizePath(
|
||||||
|
config.DataStorageSection.PreAdjustmentCalibrationParams,
|
||||||
|
baseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts relative file paths into absolute paths.
|
||||||
|
///
|
||||||
|
/// Applies only to:
|
||||||
|
/// - LocalCsv
|
||||||
|
/// - RemoteCsv
|
||||||
|
/// - LocalJson
|
||||||
|
/// - RemoteJson
|
||||||
|
///
|
||||||
|
/// Does not modify:
|
||||||
|
/// - Database connection strings
|
||||||
|
/// - REST URLs
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storageConfig">
|
||||||
|
/// Storage configuration.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base path for resolution.
|
||||||
|
/// </param>
|
||||||
|
private static void NormalizePath(
|
||||||
|
DataStorageConfig storageConfig,
|
||||||
|
string baseDirectory)
|
||||||
|
{
|
||||||
|
if (storageConfig == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Database connection strings and URLs
|
||||||
|
// must never be treated as file paths.
|
||||||
|
if (storageConfig.Type == DataStorageType.LocalDatabase ||
|
||||||
|
storageConfig.Type == DataStorageType.RemoteDatabase ||
|
||||||
|
storageConfig.Type == DataStorageType.RestApi)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Path.IsPathRooted(storageConfig.DataSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
storageConfig.DataSource =
|
||||||
|
Path.GetFullPath(
|
||||||
|
Path.Combine(
|
||||||
|
baseDirectory,
|
||||||
|
storageConfig.DataSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"_Comment": "GCI DataStorage configuration",
|
||||||
|
|
||||||
|
"DataStorageSection": {
|
||||||
|
|
||||||
|
"MeterLoginPasswords": {
|
||||||
|
|
||||||
|
"___Documentation___": {
|
||||||
|
"Description": "Reads login passwords for meters by PCB ID",
|
||||||
|
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||||
|
"DataSource": "Database connection string",
|
||||||
|
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||||
|
},
|
||||||
|
|
||||||
|
"Name": "MeterLoginPasswords",
|
||||||
|
"Type": "LocalDatabase",
|
||||||
|
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||||
|
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||||
|
},
|
||||||
|
|
||||||
|
"PreAdjustmentCalibrationParams": {
|
||||||
|
|
||||||
|
"___Documentation___": {
|
||||||
|
"Description": "Reads pre-adjustment calibration parameters by meter size",
|
||||||
|
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||||
|
"DataSource": "Database connection string",
|
||||||
|
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
|
||||||
|
},
|
||||||
|
|
||||||
|
"Name": "PreAdjustmentCalibrationParams",
|
||||||
|
"Type": "LocalDatabase",
|
||||||
|
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||||
|
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Root GCI configuration object loaded from gci_config.json.
|
||||||
|
///
|
||||||
|
/// This class represents the top-level configuration structure
|
||||||
|
/// and serves as an entry point for all configurable GCI features.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// "DataStorage":
|
||||||
|
/// {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </summary>
|
||||||
|
public class GciConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data storage configuration section.
|
||||||
|
///
|
||||||
|
/// Contains definitions for all configured readers and writers,
|
||||||
|
/// such as:
|
||||||
|
///
|
||||||
|
/// - MeterLoginPasswords
|
||||||
|
/// - PreAdjustmentCalibrationParams
|
||||||
|
/// - future storage providers
|
||||||
|
/// </summary>
|
||||||
|
public GciDataStorageConfig DataStorage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads GCI configuration from gci_config.json.
|
||||||
|
///
|
||||||
|
/// Responsibilities:
|
||||||
|
/// - Locate configuration file
|
||||||
|
/// - Deserialize JSON into strongly typed objects
|
||||||
|
/// - Normalize relative file paths
|
||||||
|
/// - Preserve database connection strings and REST URLs
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// Config/
|
||||||
|
/// gci_config.json
|
||||||
|
///
|
||||||
|
/// Data/
|
||||||
|
/// meter_passwords.csv
|
||||||
|
///
|
||||||
|
/// Relative paths:
|
||||||
|
/// Data\file.csv
|
||||||
|
///
|
||||||
|
/// become:
|
||||||
|
///
|
||||||
|
/// C:\App\bin\Debug\Data\file.csv
|
||||||
|
///
|
||||||
|
/// Database sources remain unchanged:
|
||||||
|
///
|
||||||
|
/// Server=(localdb)\MojaDB;Database=...
|
||||||
|
/// </summary>
|
||||||
|
public static class GciConfigLoader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads default GCI configuration from:
|
||||||
|
///
|
||||||
|
/// Config\gci_config.json
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Loaded GCI configuration.
|
||||||
|
/// </returns>
|
||||||
|
public static GciConfig LoadDefault()
|
||||||
|
{
|
||||||
|
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
|
string configPath = Path.Combine(
|
||||||
|
baseDirectory,
|
||||||
|
"Config",
|
||||||
|
"gci_config.json");
|
||||||
|
|
||||||
|
return Load(configPath, baseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads GCI configuration from specified file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configPath">
|
||||||
|
/// Path to configuration json file.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base directory used for resolving relative paths.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Loaded configuration object.
|
||||||
|
/// </returns>
|
||||||
|
public static GciConfig Load(
|
||||||
|
string configPath,
|
||||||
|
string baseDirectory = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(configPath))
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Config path must not be empty.",
|
||||||
|
nameof(configPath));
|
||||||
|
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"GCI config file was not found.",
|
||||||
|
configPath);
|
||||||
|
|
||||||
|
string json = File.ReadAllText(configPath);
|
||||||
|
|
||||||
|
GciConfig config =
|
||||||
|
JsonConvert.DeserializeObject<GciConfig>(json);
|
||||||
|
|
||||||
|
if (config == null)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"GCI config could not be loaded.");
|
||||||
|
|
||||||
|
NormalizeDataStoragePaths(
|
||||||
|
config,
|
||||||
|
baseDirectory ?? Path.GetDirectoryName(configPath));
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalizes paths for all configured data storage entries.
|
||||||
|
///
|
||||||
|
/// Converts relative file paths into absolute paths.
|
||||||
|
/// Database connection strings remain untouched.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Loaded configuration object.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base path used for relative resolution.
|
||||||
|
/// </param>
|
||||||
|
private static void NormalizeDataStoragePaths(
|
||||||
|
GciConfig config,
|
||||||
|
string baseDirectory)
|
||||||
|
{
|
||||||
|
if (config.DataStorage == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NormalizePath(
|
||||||
|
config.DataStorage.MeterLoginPasswords,
|
||||||
|
baseDirectory);
|
||||||
|
|
||||||
|
NormalizePath(
|
||||||
|
config.DataStorage.PreAdjustmentCalibrationParams,
|
||||||
|
baseDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts relative file paths into absolute paths.
|
||||||
|
///
|
||||||
|
/// Applies only to:
|
||||||
|
/// - LocalCsv
|
||||||
|
/// - RemoteCsv
|
||||||
|
/// - LocalJson
|
||||||
|
/// - RemoteJson
|
||||||
|
///
|
||||||
|
/// Does not modify:
|
||||||
|
/// - Database connection strings
|
||||||
|
/// - REST URLs
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storageConfig">
|
||||||
|
/// Storage configuration.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="baseDirectory">
|
||||||
|
/// Base path for resolution.
|
||||||
|
/// </param>
|
||||||
|
private static void NormalizePath(
|
||||||
|
DataStorageConfig storageConfig,
|
||||||
|
string baseDirectory)
|
||||||
|
{
|
||||||
|
if (storageConfig == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Database connection strings and URLs
|
||||||
|
// must never be treated as file paths.
|
||||||
|
if (storageConfig.Type == DataStorageType.LocalDatabase ||
|
||||||
|
storageConfig.Type == DataStorageType.RemoteDatabase ||
|
||||||
|
storageConfig.Type == DataStorageType.RestApi)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Path.IsPathRooted(storageConfig.DataSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
storageConfig.DataSource =
|
||||||
|
Path.GetFullPath(
|
||||||
|
Path.Combine(
|
||||||
|
baseDirectory,
|
||||||
|
storageConfig.DataSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all configured GCI data storage sources.
|
||||||
|
///
|
||||||
|
/// Each property represents one logical use-case
|
||||||
|
/// and points to its storage configuration.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// MeterLoginPasswords
|
||||||
|
/// -> SQL database
|
||||||
|
///
|
||||||
|
/// PreAdjustmentCalibrationParams
|
||||||
|
/// -> CSV file
|
||||||
|
/// </summary>
|
||||||
|
public class GciDataStorageConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for meter login password lookup.
|
||||||
|
/// </summary>
|
||||||
|
public DataStorageConfig MeterLoginPasswords { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for pre-adjustment calibration data lookup.
|
||||||
|
/// </summary>
|
||||||
|
public DataStorageConfig PreAdjustmentCalibrationParams { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines common contract for all GCI data storage readers.
|
||||||
|
///
|
||||||
|
/// Implementations can read data from different storage types:
|
||||||
|
/// - SQL database
|
||||||
|
/// - CSV file
|
||||||
|
/// - JSON file
|
||||||
|
/// - REST API
|
||||||
|
///
|
||||||
|
/// Consumers should use this interface instead of concrete readers.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataStorageReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads data from configured storage using provided query.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Query object containing lookup parameters.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Storage-specific result object.
|
||||||
|
/// </returns>
|
||||||
|
object GetData(Models.DataQuery query);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether configured data source is accessible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result of source test.
|
||||||
|
/// </returns>
|
||||||
|
Models.ReaderDiagnosticResult TestSource(bool enableDiagnostics);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether configured query is valid for the data source.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result of query test.
|
||||||
|
/// </returns>
|
||||||
|
Models.ReaderDiagnosticResult TestQuery(bool enableDiagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Factory responsible for creating appropriate
|
||||||
|
/// data storage reader implementations.
|
||||||
|
///
|
||||||
|
/// The reader type is selected according to:
|
||||||
|
///
|
||||||
|
/// DataStorageConfig.Type
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// LocalDatabase
|
||||||
|
/// -> DatabaseDataStorageReader
|
||||||
|
///
|
||||||
|
/// LocalCsv
|
||||||
|
/// -> CsvDataStorageReader
|
||||||
|
///
|
||||||
|
/// LocalJson
|
||||||
|
/// -> JsonDataStorageReader
|
||||||
|
///
|
||||||
|
/// RestApi
|
||||||
|
/// -> RestApiDataStorageReader
|
||||||
|
///
|
||||||
|
/// This factory hides implementation details from
|
||||||
|
/// higher layers and keeps consumers independent
|
||||||
|
/// of storage technology.
|
||||||
|
///
|
||||||
|
/// Trace:
|
||||||
|
///
|
||||||
|
/// MeterLoginPasswordReader
|
||||||
|
/// -> DataStorageReaderFactory.Create()
|
||||||
|
/// -> DatabaseDataStorageReader
|
||||||
|
/// -> CsvDataStorageReader
|
||||||
|
/// -> JsonDataStorageReader
|
||||||
|
/// -> RestApiDataStorageReader
|
||||||
|
/// </summary>
|
||||||
|
public static class DataStorageReaderFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates appropriate reader implementation
|
||||||
|
/// according to storage configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Data storage configuration loaded from gci_config.json.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Configured storage reader implementation.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// Configuration is null.
|
||||||
|
/// </exception>
|
||||||
|
/// <exception cref="NotSupportedException">
|
||||||
|
/// Unsupported storage type.
|
||||||
|
/// </exception>
|
||||||
|
public static IDataStorageReader Create(
|
||||||
|
DataStorageConfig config)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
|
||||||
|
switch (config.Type)
|
||||||
|
{
|
||||||
|
case DataStorageType.RemoteDatabase:
|
||||||
|
case DataStorageType.LocalDatabase:
|
||||||
|
|
||||||
|
return new Providers.DatabaseDataStorageReader(config);
|
||||||
|
|
||||||
|
case DataStorageType.RemoteCsv:
|
||||||
|
case DataStorageType.LocalCsv:
|
||||||
|
|
||||||
|
return new Providers.CsvDataStorageReader(config);
|
||||||
|
|
||||||
|
case DataStorageType.RemoteJson:
|
||||||
|
case DataStorageType.LocalJson:
|
||||||
|
|
||||||
|
return new Providers.JsonDataStorageReader(config);
|
||||||
|
|
||||||
|
case DataStorageType.RestApi:
|
||||||
|
|
||||||
|
return new Providers.RestApiDataStorageReader(config);
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
throw new NotSupportedException(
|
||||||
|
$"Unsupported DataStorageType: '{config.Type}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents query input for data storage readers.
|
||||||
|
///
|
||||||
|
/// Query parameters are used as lookup values
|
||||||
|
/// during storage search operations.
|
||||||
|
///
|
||||||
|
/// A single parameter:
|
||||||
|
///
|
||||||
|
/// 231630279
|
||||||
|
///
|
||||||
|
/// executes one lookup.
|
||||||
|
///
|
||||||
|
/// Multiple parameters:
|
||||||
|
///
|
||||||
|
/// 231630279
|
||||||
|
/// 231630243
|
||||||
|
/// 231630148
|
||||||
|
///
|
||||||
|
/// can execute batch operations.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// QueryTemplate:
|
||||||
|
///
|
||||||
|
/// SELECT [Password]
|
||||||
|
/// WHERE [PcbId]=QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Query:
|
||||||
|
///
|
||||||
|
/// QueryParams:
|
||||||
|
/// 231630279
|
||||||
|
///
|
||||||
|
/// Result:
|
||||||
|
///
|
||||||
|
/// Password belonging to 231630279.
|
||||||
|
/// </summary>
|
||||||
|
public class DataQuery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of query values.
|
||||||
|
///
|
||||||
|
/// Single item:
|
||||||
|
///
|
||||||
|
/// QueryParams[0]
|
||||||
|
///
|
||||||
|
/// represents one lookup.
|
||||||
|
///
|
||||||
|
/// Multiple items may be processed
|
||||||
|
/// as batch requests.
|
||||||
|
/// </summary>
|
||||||
|
public List<string> QueryParams { get; }
|
||||||
|
= new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for one storage source.
|
||||||
|
/// Loaded from gci_config.json
|
||||||
|
/// </summary>
|
||||||
|
public class DataStorageConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logical storage name.
|
||||||
|
/// Example:
|
||||||
|
/// MeterLoginPasswords
|
||||||
|
/// CalibrationParameters
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Storage implementation type.
|
||||||
|
/// </summary>
|
||||||
|
public DataStorageType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connection string,
|
||||||
|
/// file path,
|
||||||
|
/// URL etc.
|
||||||
|
/// </summary>
|
||||||
|
public string DataSource { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query template.
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SQL:
|
||||||
|
/// SELECT Password
|
||||||
|
/// FROM Passwords
|
||||||
|
/// WHERE PcbId=QUERYPARAM
|
||||||
|
///
|
||||||
|
/// CSV:
|
||||||
|
/// SELECT [Password]
|
||||||
|
/// WHERE [PcbId]=QUERYPARAM
|
||||||
|
/// </summary>
|
||||||
|
public string QueryTemplate { get; set; }
|
||||||
|
|
||||||
|
public DataStorageConfig(
|
||||||
|
string name,
|
||||||
|
DataStorageType type,
|
||||||
|
string dataSource,
|
||||||
|
string queryTemplate)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Type = type;
|
||||||
|
DataSource = dataSource;
|
||||||
|
QueryTemplate = queryTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies supported data storage implementations.
|
||||||
|
///
|
||||||
|
/// The type determines which reader implementation
|
||||||
|
/// will be created by DataStorageReaderFactory.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// LocalDatabase
|
||||||
|
/// -> DatabaseDataStorageReader
|
||||||
|
///
|
||||||
|
/// LocalCsv
|
||||||
|
/// -> CsvDataStorageReader
|
||||||
|
///
|
||||||
|
/// RestApi
|
||||||
|
/// -> RestApiDataStorageReader
|
||||||
|
///
|
||||||
|
/// Source:
|
||||||
|
///
|
||||||
|
/// gci_config.json
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// "Type":"LocalDatabase"
|
||||||
|
/// </summary>
|
||||||
|
public enum DataStorageType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// REST API endpoint.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// https://api.company.com/passwords
|
||||||
|
/// </summary>
|
||||||
|
RestApi,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Database located on remote server.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SQL server on network machine.
|
||||||
|
/// </summary>
|
||||||
|
RemoteDatabase,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Database located on local machine.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SQL LocalDB
|
||||||
|
/// SQLite
|
||||||
|
/// local SQL Server instance
|
||||||
|
/// </summary>
|
||||||
|
LocalDatabase,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON source stored remotely.
|
||||||
|
/// </summary>
|
||||||
|
RemoteJson,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON file stored locally.
|
||||||
|
/// </summary>
|
||||||
|
LocalJson,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CSV source stored remotely.
|
||||||
|
/// </summary>
|
||||||
|
RemoteCsv,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CSV file stored locally.
|
||||||
|
/// </summary>
|
||||||
|
LocalCsv
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents result returned by database-based
|
||||||
|
/// data storage readers.
|
||||||
|
///
|
||||||
|
/// Supports both:
|
||||||
|
///
|
||||||
|
/// - Single-row lookups
|
||||||
|
/// (e.g. MeterLoginPasswords)
|
||||||
|
///
|
||||||
|
/// - Multi-row queries
|
||||||
|
/// (e.g. PreAdjustmentCalibrationParams)
|
||||||
|
/// </summary>
|
||||||
|
internal class DatabaseSearchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether at least one record
|
||||||
|
/// was found.
|
||||||
|
/// </summary>
|
||||||
|
public bool Found { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executed SQL query text.
|
||||||
|
/// Mainly intended for diagnostics
|
||||||
|
/// and troubleshooting.
|
||||||
|
/// </summary>
|
||||||
|
public string Query { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// First returned row represented as
|
||||||
|
/// column/value pairs.
|
||||||
|
///
|
||||||
|
/// Preserved for backward compatibility
|
||||||
|
/// with existing readers expecting
|
||||||
|
/// a single database record.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> Values { get; }
|
||||||
|
= new Dictionary<string, object>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All returned rows represented as
|
||||||
|
/// a collection of column/value dictionaries.
|
||||||
|
///
|
||||||
|
/// Intended for queries returning
|
||||||
|
/// multiple records.
|
||||||
|
/// </summary>
|
||||||
|
public List<Dictionary<string, object>> Rows { get; }
|
||||||
|
= new List<Dictionary<string, object>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
+141
@@ -0,0 +1,141 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents diagnostic result of data storage operations.
|
||||||
|
///
|
||||||
|
/// Used for:
|
||||||
|
/// - source validation
|
||||||
|
/// - query validation
|
||||||
|
/// - connection tests
|
||||||
|
/// - detailed diagnostic logging
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// TestSource()
|
||||||
|
/// -> connection successful
|
||||||
|
///
|
||||||
|
/// TestQuery()
|
||||||
|
/// -> column validation
|
||||||
|
///
|
||||||
|
/// Can also carry additional payload data.
|
||||||
|
/// </summary>
|
||||||
|
public class ReaderDiagnosticResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether operation completed successfully.
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Human readable summary message.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// "Connection successful."
|
||||||
|
///
|
||||||
|
/// or:
|
||||||
|
///
|
||||||
|
/// "Column not found."
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detailed diagnostic output lines.
|
||||||
|
///
|
||||||
|
/// Used for debugging and troubleshooting.
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Diagnostics { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional operation payload.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// loaded CSV lines
|
||||||
|
/// SQL test value
|
||||||
|
/// parsed content
|
||||||
|
/// </summary>
|
||||||
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates empty diagnostic result.
|
||||||
|
/// </summary>
|
||||||
|
public ReaderDiagnosticResult()
|
||||||
|
{
|
||||||
|
Diagnostics = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates successful diagnostic result.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">
|
||||||
|
/// Success message.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Successful result object.
|
||||||
|
/// </returns>
|
||||||
|
public static ReaderDiagnosticResult SuccessResult(
|
||||||
|
string message = "OK")
|
||||||
|
{
|
||||||
|
return new ReaderDiagnosticResult
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates failed diagnostic result.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">
|
||||||
|
/// Failure message.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Failed result object.
|
||||||
|
/// </returns>
|
||||||
|
public static ReaderDiagnosticResult Failure(
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
return new ReaderDiagnosticResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts diagnostic information into display text.
|
||||||
|
///
|
||||||
|
/// Output contains:
|
||||||
|
///
|
||||||
|
/// diagnostic lines
|
||||||
|
/// +
|
||||||
|
/// summary message
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Formatted text representation.
|
||||||
|
/// </returns>
|
||||||
|
public string ToDisplayDiag()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
if (Diagnostics != null &&
|
||||||
|
Diagnostics.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string line in Diagnostics)
|
||||||
|
sb.AppendLine(line);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(Message))
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(Message))
|
||||||
|
sb.AppendLine(Message);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+394
@@ -0,0 +1,394 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads and searches CSV based data sources.
|
||||||
|
///
|
||||||
|
/// Supported query syntax:
|
||||||
|
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Supports:
|
||||||
|
/// - column names: [ColumnName]
|
||||||
|
/// - column indexes: COLUMN(n)
|
||||||
|
///
|
||||||
|
/// Trace:
|
||||||
|
/// MeterLoginPasswordReader
|
||||||
|
/// -> CsvDataStorageReader.GetData()
|
||||||
|
/// -> ConnectToSource()
|
||||||
|
/// -> ExecuteQuery()
|
||||||
|
/// </summary>
|
||||||
|
public class CsvDataStorageReader : IDataStorageReader
|
||||||
|
{
|
||||||
|
private readonly DataStorageConfig config;
|
||||||
|
|
||||||
|
private string resolvedPath;
|
||||||
|
private string[] loadedLines;
|
||||||
|
private string[] loadedHeaders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates CSV data storage reader using provided configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Data storage configuration loaded from gci_config.json.
|
||||||
|
/// </param>
|
||||||
|
public CsvDataStorageReader(DataStorageConfig config)
|
||||||
|
{
|
||||||
|
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads values from CSV using configured QueryTemplate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Query parameters used for lookup.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// ReaderDiagnosticResult containing lookup result.
|
||||||
|
/// </returns>
|
||||||
|
public object GetData(DataQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
ReaderDiagnosticResult sourceResult = ConnectToSource(true);
|
||||||
|
if (!sourceResult.Success)
|
||||||
|
throw new InvalidOperationException(sourceResult.Message);
|
||||||
|
|
||||||
|
ReaderDiagnosticResult queryResult = ExecuteQuery(query, true);
|
||||||
|
if (!queryResult.Success)
|
||||||
|
throw new InvalidOperationException(queryResult.Message);
|
||||||
|
|
||||||
|
return queryResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates CSV source accessibility and content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result.
|
||||||
|
/// </returns>
|
||||||
|
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
return ConnectToSource(enableDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates configured QueryTemplate against CSV header.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result.
|
||||||
|
/// </returns>
|
||||||
|
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
ReaderDiagnosticResult sourceResult = ConnectToSource(enableDiagnostics);
|
||||||
|
if (!sourceResult.Success)
|
||||||
|
return sourceResult;
|
||||||
|
|
||||||
|
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log(result, enableDiagnostics, "Starting QueryTemplate validation.");
|
||||||
|
|
||||||
|
SearchOrderDefinition definition =
|
||||||
|
SearchOrderParser.Parse(config.QueryTemplate);
|
||||||
|
|
||||||
|
int selectIndex = ResolveColumnIndex(
|
||||||
|
loadedHeaders,
|
||||||
|
definition.SelectColumn);
|
||||||
|
|
||||||
|
int whereIndex = ResolveColumnIndex(
|
||||||
|
loadedHeaders,
|
||||||
|
definition.WhereColumn);
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "QueryTemplate parsed successfully.");
|
||||||
|
Log(result, enableDiagnostics, "Select column index: " + selectIndex);
|
||||||
|
Log(result, enableDiagnostics, "Where column index: " + whereIndex);
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
result.Message = "QueryTemplate validation finished successfully.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = ex.Message;
|
||||||
|
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens CSV file and loads its content.
|
||||||
|
/// Validates file existence and header.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Source validation result.
|
||||||
|
/// </returns>
|
||||||
|
private ReaderDiagnosticResult ConnectToSource(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log(result, enableDiagnostics, "Starting CSV source connection test.");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||||
|
throw new InvalidOperationException("CSV data source is empty.");
|
||||||
|
|
||||||
|
resolvedPath = Path.GetFullPath(config.DataSource);
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "Resolved full path: " + resolvedPath);
|
||||||
|
|
||||||
|
if (!File.Exists(resolvedPath))
|
||||||
|
throw new FileNotFoundException("CSV file was not found.", resolvedPath);
|
||||||
|
|
||||||
|
loadedLines = File.ReadAllLines(resolvedPath);
|
||||||
|
|
||||||
|
if (loadedLines == null || loadedLines.Length == 0)
|
||||||
|
throw new InvalidOperationException("CSV file is empty.");
|
||||||
|
|
||||||
|
loadedHeaders = SplitCsvLine(loadedLines[0]);
|
||||||
|
|
||||||
|
if (loadedHeaders == null || loadedHeaders.Length == 0)
|
||||||
|
throw new InvalidOperationException("CSV header is empty.");
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "CSV line count: " + loadedLines.Length);
|
||||||
|
Log(result, enableDiagnostics, "CSV header column count: " + loadedHeaders.Length);
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
result.Message = "CSV source connection finished successfully.";
|
||||||
|
result.Data = loadedLines;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = ex.Message;
|
||||||
|
result.Data = null;
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes configured QueryTemplate against loaded CSV data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Query values used for matching.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Query result.
|
||||||
|
/// </returns>
|
||||||
|
private ReaderDiagnosticResult ExecuteQuery(
|
||||||
|
DataQuery query,
|
||||||
|
bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (query.QueryParams == null || query.QueryParams.Count == 0)
|
||||||
|
throw new InvalidOperationException("DataQuery.QueryParams is empty.");
|
||||||
|
|
||||||
|
SearchOrderDefinition definition =
|
||||||
|
SearchOrderParser.Parse(config.QueryTemplate);
|
||||||
|
|
||||||
|
int selectIndex = ResolveColumnIndex(
|
||||||
|
loadedHeaders,
|
||||||
|
definition.SelectColumn);
|
||||||
|
|
||||||
|
int whereIndex = ResolveColumnIndex(
|
||||||
|
loadedHeaders,
|
||||||
|
definition.WhereColumn);
|
||||||
|
|
||||||
|
List<string> matchedValues = new List<string>();
|
||||||
|
|
||||||
|
foreach (string param in query.QueryParams)
|
||||||
|
{
|
||||||
|
string queryValue = (param ?? string.Empty).Trim();
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (int lineIndex = 1; lineIndex < loadedLines.Length; lineIndex++)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(loadedLines[lineIndex]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string[] values = SplitCsvLine(loadedLines[lineIndex]);
|
||||||
|
|
||||||
|
if (whereIndex >= values.Length)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string currentValue = (values[whereIndex] ?? string.Empty).Trim();
|
||||||
|
|
||||||
|
if (!string.Equals(
|
||||||
|
currentValue,
|
||||||
|
queryValue,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string returnValue =
|
||||||
|
selectIndex < values.Length
|
||||||
|
? values[selectIndex]
|
||||||
|
: string.Empty;
|
||||||
|
|
||||||
|
matchedValues.Add(returnValue);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
Log(result, enableDiagnostics, "No match found for: " + queryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
|
||||||
|
if (query.QueryParams.Count == 1)
|
||||||
|
{
|
||||||
|
result.Data = matchedValues.Count > 0 ? matchedValues[0] : null;
|
||||||
|
result.Message = matchedValues.Count > 0
|
||||||
|
? "Value found."
|
||||||
|
: "No match found.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Data = matchedValues;
|
||||||
|
result.Message =
|
||||||
|
"Batch query finished. Matches found: " +
|
||||||
|
matchedValues.Count +
|
||||||
|
" of " +
|
||||||
|
query.QueryParams.Count +
|
||||||
|
".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = ex.Message;
|
||||||
|
result.Data = null;
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves column index either by name or by explicit index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="headers">
|
||||||
|
/// CSV header columns.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="columnReference">
|
||||||
|
/// Parsed QueryTemplate column definition.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Zero-based column index.
|
||||||
|
/// </returns>
|
||||||
|
private int ResolveColumnIndex(
|
||||||
|
string[] headers,
|
||||||
|
ColumnReference columnReference)
|
||||||
|
{
|
||||||
|
if (columnReference == null)
|
||||||
|
throw new InvalidOperationException("Column reference is null.");
|
||||||
|
|
||||||
|
if (columnReference.HasIndex)
|
||||||
|
{
|
||||||
|
int index = columnReference.Index.Value;
|
||||||
|
|
||||||
|
if (index < 0 || index >= headers.Length)
|
||||||
|
throw new InvalidOperationException("Column index is out of range.");
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnReference.HasName)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < headers.Length; i++)
|
||||||
|
{
|
||||||
|
if (string.Equals(
|
||||||
|
headers[i]?.Trim(),
|
||||||
|
columnReference.Name?.Trim(),
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Column '" + columnReference.Name + "' was not found in CSV header.");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("Column reference is not defined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Splits CSV line using common separators.
|
||||||
|
/// Supports:
|
||||||
|
/// ';'
|
||||||
|
/// ','
|
||||||
|
/// '\t'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line">
|
||||||
|
/// Input CSV line.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Parsed columns.
|
||||||
|
/// </returns>
|
||||||
|
private string[] SplitCsvLine(string line)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
return new string[0];
|
||||||
|
|
||||||
|
if (line.Contains(";"))
|
||||||
|
return line.Split(';');
|
||||||
|
|
||||||
|
if (line.Contains(","))
|
||||||
|
return line.Split(',');
|
||||||
|
|
||||||
|
if (line.Contains("\t"))
|
||||||
|
return line.Split('\t');
|
||||||
|
|
||||||
|
return new[] { line };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds diagnostic line when diagnostics are enabled.
|
||||||
|
/// </summary>
|
||||||
|
private void Log(
|
||||||
|
ReaderDiagnosticResult result,
|
||||||
|
bool enableDiagnostics,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
if (!enableDiagnostics || result == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result.Diagnostics.Add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+397
@@ -0,0 +1,397 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads data from SQL Server based data storage.
|
||||||
|
///
|
||||||
|
/// The reader uses configuration loaded from gci_config.json.
|
||||||
|
///
|
||||||
|
/// QueryTemplate must contain QUERYPARAM placeholder.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SELECT [Password]
|
||||||
|
/// FROM [dbo].[SkeletonKeys]
|
||||||
|
/// WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// or
|
||||||
|
///
|
||||||
|
/// SELECT [ParameterName], [ParameterValue]
|
||||||
|
/// FROM [dbo].[PreAdjustmentCalibrationParams]
|
||||||
|
/// WHERE [Dn_InternalId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// The placeholder is internally converted
|
||||||
|
/// to SQL parameter @value.
|
||||||
|
///
|
||||||
|
/// Supports both:
|
||||||
|
///
|
||||||
|
/// - Single-row lookups
|
||||||
|
/// - Multi-row result sets
|
||||||
|
/// </summary>
|
||||||
|
public class DatabaseDataStorageReader : IDataStorageReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data storage configuration containing
|
||||||
|
/// connection string and query template.
|
||||||
|
/// </summary>
|
||||||
|
private readonly DataStorageConfig config;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates SQL Server data storage reader
|
||||||
|
/// using provided configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Data storage configuration loaded
|
||||||
|
/// from gci_config.json.
|
||||||
|
/// </param>
|
||||||
|
public DatabaseDataStorageReader(
|
||||||
|
DataStorageConfig config)
|
||||||
|
{
|
||||||
|
this.config =
|
||||||
|
config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes configured SQL query and returns
|
||||||
|
/// matching database records.
|
||||||
|
///
|
||||||
|
/// Single-row queries populate:
|
||||||
|
/// DatabaseSearchResult.Values
|
||||||
|
///
|
||||||
|
/// Multi-row queries populate:
|
||||||
|
/// DatabaseSearchResult.Rows
|
||||||
|
///
|
||||||
|
/// For backward compatibility, the first row
|
||||||
|
/// is also stored in Values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Query object containing lookup parameter.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// DatabaseSearchResult containing returned
|
||||||
|
/// database records.
|
||||||
|
///
|
||||||
|
/// Values contains the first returned row.
|
||||||
|
///
|
||||||
|
/// Rows contains the complete result set.
|
||||||
|
/// </returns>
|
||||||
|
public object GetData(DataQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
ReaderDiagnosticResult sourceResult =
|
||||||
|
TestSource(true);
|
||||||
|
|
||||||
|
if (!sourceResult.Success)
|
||||||
|
throw new InvalidOperationException(sourceResult.Message);
|
||||||
|
|
||||||
|
string sqlText =
|
||||||
|
PrepareSqlText(config.QueryTemplate);
|
||||||
|
|
||||||
|
object queryValue =
|
||||||
|
ExtractQueryValue(query);
|
||||||
|
|
||||||
|
using (SqlConnection connection =
|
||||||
|
new SqlConnection(config.DataSource))
|
||||||
|
|
||||||
|
using (SqlCommand command =
|
||||||
|
new SqlCommand(sqlText, connection))
|
||||||
|
{
|
||||||
|
AddQueryParameter(command, queryValue);
|
||||||
|
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
// Full result set is required because some
|
||||||
|
// storage definitions return multiple records
|
||||||
|
// (e.g. PreAdjustmentCalibrationParams).
|
||||||
|
using (SqlDataReader reader =
|
||||||
|
command.ExecuteReader())
|
||||||
|
{
|
||||||
|
DatabaseSearchResult result =
|
||||||
|
new DatabaseSearchResult
|
||||||
|
{
|
||||||
|
Query = sqlText
|
||||||
|
};
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
result.Found = true;
|
||||||
|
|
||||||
|
// Represents one database row.
|
||||||
|
Dictionary<string, object> row =
|
||||||
|
new Dictionary<string, object>();
|
||||||
|
|
||||||
|
for (int i = 0; i < reader.FieldCount; i++)
|
||||||
|
{
|
||||||
|
object value =
|
||||||
|
reader.GetValue(i);
|
||||||
|
|
||||||
|
object normalizedValue =
|
||||||
|
value == DBNull.Value
|
||||||
|
? null
|
||||||
|
: value;
|
||||||
|
|
||||||
|
row[reader.GetName(i)] =
|
||||||
|
normalizedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preserve first row for legacy consumers
|
||||||
|
// expecting a single returned database record
|
||||||
|
// (e.g. MeterLoginPasswordReader).
|
||||||
|
if (result.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
foreach (var item in row)
|
||||||
|
{
|
||||||
|
result.Values[item.Key] =
|
||||||
|
item.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store complete database result set.
|
||||||
|
result.Rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
result.Found = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether SQL Server connection
|
||||||
|
/// can be opened.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result of SQL connection test.
|
||||||
|
/// </returns>
|
||||||
|
public ReaderDiagnosticResult TestSource(
|
||||||
|
bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
ReaderDiagnosticResult result =
|
||||||
|
new ReaderDiagnosticResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Data source is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
"Opening SQL connection.");
|
||||||
|
|
||||||
|
using (SqlConnection connection =
|
||||||
|
new SqlConnection(config.DataSource))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
"Connection opened successfully.");
|
||||||
|
|
||||||
|
using (SqlCommand command =
|
||||||
|
new SqlCommand("SELECT 1", connection))
|
||||||
|
{
|
||||||
|
object value =
|
||||||
|
command.ExecuteScalar();
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
"Test query result: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
result.Message =
|
||||||
|
"Connection to SQL Server OK.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message =
|
||||||
|
"Failed to connect to SQL Server. " + ex.Message;
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether configured SQL query
|
||||||
|
/// can be prepared and executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="enableDiagnostics">
|
||||||
|
/// Enables detailed diagnostic output.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Diagnostic result of query execution test.
|
||||||
|
/// </returns>
|
||||||
|
public ReaderDiagnosticResult TestQuery(
|
||||||
|
bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
ReaderDiagnosticResult result =
|
||||||
|
new ReaderDiagnosticResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(config.QueryTemplate))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Query template is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string sqlText =
|
||||||
|
PrepareSqlText(config.QueryTemplate);
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "Original template:");
|
||||||
|
Log(result, enableDiagnostics, config.QueryTemplate);
|
||||||
|
|
||||||
|
Log(result, enableDiagnostics, "Prepared SQL:");
|
||||||
|
Log(result, enableDiagnostics, sqlText);
|
||||||
|
|
||||||
|
using (SqlConnection connection =
|
||||||
|
new SqlConnection(config.DataSource))
|
||||||
|
|
||||||
|
using (SqlCommand command =
|
||||||
|
new SqlCommand(sqlText, connection))
|
||||||
|
{
|
||||||
|
AddQueryParameter(command, "TEST");
|
||||||
|
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
object value =
|
||||||
|
command.ExecuteScalar();
|
||||||
|
|
||||||
|
result.Data = value;
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
"Query executed successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
result.Message =
|
||||||
|
"Query executed successfully.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message =
|
||||||
|
"Query execution failed. " + ex.Message;
|
||||||
|
|
||||||
|
Log(
|
||||||
|
result,
|
||||||
|
enableDiagnostics,
|
||||||
|
ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts configured QueryTemplate
|
||||||
|
/// to executable SQL text.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryTemplate">
|
||||||
|
/// SQL query template containing
|
||||||
|
/// QUERYPARAM placeholder.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// SQL text with QUERYPARAM replaced
|
||||||
|
/// by @value parameter.
|
||||||
|
/// </returns>
|
||||||
|
private static string PrepareSqlText(
|
||||||
|
string queryTemplate)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(queryTemplate))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Query template must not be empty.",
|
||||||
|
nameof(queryTemplate));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queryTemplate.Contains("QUERYPARAM"))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Query template must contain QUERYPARAM placeholder.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryTemplate.Replace(
|
||||||
|
"QUERYPARAM",
|
||||||
|
"@value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts first query parameter value.
|
||||||
|
/// </summary>
|
||||||
|
private static object ExtractQueryValue(
|
||||||
|
DataQuery query)
|
||||||
|
{
|
||||||
|
if (query.QueryParams == null ||
|
||||||
|
query.QueryParams.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"DataQuery does not contain any query parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.QueryParams[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds lookup parameter to SQL command.
|
||||||
|
/// </summary>
|
||||||
|
private static void AddQueryParameter(
|
||||||
|
SqlCommand command,
|
||||||
|
object value)
|
||||||
|
{
|
||||||
|
command.Parameters.Clear();
|
||||||
|
|
||||||
|
SqlParameter parameter =
|
||||||
|
command.Parameters.Add(
|
||||||
|
"@value",
|
||||||
|
SqlDbType.Variant);
|
||||||
|
|
||||||
|
parameter.Value =
|
||||||
|
value ?? DBNull.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds diagnostic line when diagnostics
|
||||||
|
/// are enabled.
|
||||||
|
/// </summary>
|
||||||
|
private static void Log(
|
||||||
|
ReaderDiagnosticResult result,
|
||||||
|
bool enableDiagnostics,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
if (!enableDiagnostics || result == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result.Diagnostics.Add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||||
|
{
|
||||||
|
public class JsonDataStorageReader : IDataStorageReader
|
||||||
|
{
|
||||||
|
private readonly DataStorageConfig config;
|
||||||
|
|
||||||
|
public JsonDataStorageReader(DataStorageConfig config)
|
||||||
|
{
|
||||||
|
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetData(DataQuery query)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||||
|
{
|
||||||
|
public class RestApiDataStorageReader : IDataStorageReader
|
||||||
|
{
|
||||||
|
private readonly DataStorageConfig config;
|
||||||
|
|
||||||
|
public RestApiDataStorageReader(DataStorageConfig config)
|
||||||
|
{
|
||||||
|
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetData(DataQuery query)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parsed representation of QueryTemplate expression.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// becomes:
|
||||||
|
///
|
||||||
|
/// SelectColumn:
|
||||||
|
/// [Password]
|
||||||
|
///
|
||||||
|
/// WhereColumn:
|
||||||
|
/// [PcbId]
|
||||||
|
/// </summary>
|
||||||
|
public class SearchOrderDefinition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Column to be returned from lookup result.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SELECT [Password]
|
||||||
|
///
|
||||||
|
/// returns:
|
||||||
|
///
|
||||||
|
/// [Password]
|
||||||
|
/// </summary>
|
||||||
|
public ColumnReference SelectColumn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Column used for row matching.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// uses:
|
||||||
|
///
|
||||||
|
/// [PcbId]
|
||||||
|
/// </summary>
|
||||||
|
public ColumnReference WhereColumn { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents one column definition inside QueryTemplate.
|
||||||
|
///
|
||||||
|
/// Supports two forms:
|
||||||
|
///
|
||||||
|
/// [ColumnName]
|
||||||
|
///
|
||||||
|
/// or:
|
||||||
|
///
|
||||||
|
/// COLUMN(number)
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// [Password]
|
||||||
|
///
|
||||||
|
/// COLUMN(2)
|
||||||
|
/// </summary>
|
||||||
|
public class ColumnReference
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Column name used in named lookup mode.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// [Password]
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zero-based column index used in indexed lookup mode.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// COLUMN(2)
|
||||||
|
/// </summary>
|
||||||
|
public int? Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether column uses name-based lookup.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether column uses index-based lookup.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasIndex
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Index.HasValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns human readable representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Column expression text.
|
||||||
|
/// </returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (HasName)
|
||||||
|
return "[" + Name + "]";
|
||||||
|
|
||||||
|
if (HasIndex)
|
||||||
|
return "COLUMN(" + Index.Value + ")";
|
||||||
|
|
||||||
|
return "<undefined column reference>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+167
@@ -0,0 +1,167 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parses SQL-like QueryTemplate expressions used by GCI data storage readers.
|
||||||
|
///
|
||||||
|
/// Supported syntax:
|
||||||
|
///
|
||||||
|
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// SELECT COLUMN(1) WHERE COLUMN(0) = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Mixed forms are also supported:
|
||||||
|
///
|
||||||
|
/// SELECT [Password] WHERE COLUMN(0)=QUERYPARAM
|
||||||
|
///
|
||||||
|
/// The parser converts text expressions into structured
|
||||||
|
/// SearchOrderDefinition objects which are later consumed
|
||||||
|
/// by CSV and other readers.
|
||||||
|
/// </summary>
|
||||||
|
public static class SearchOrderParser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Full QueryTemplate validation pattern.
|
||||||
|
///
|
||||||
|
/// Expected syntax:
|
||||||
|
///
|
||||||
|
/// SELECT [Column] WHERE [Column] = QUERYPARAM
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex FullPattern = new Regex(
|
||||||
|
@"^\s*SELECT\s+(?<select>\[[^\]]+\]|COLUMN\(\d+\))\s+WHERE\s+(?<where>\[[^\]]+\]|COLUMN\(\d+\))\s*=\s*QUERYPARAM\s*$",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Matches:
|
||||||
|
///
|
||||||
|
/// [ColumnName]
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex NamedColumnPattern = new Regex(
|
||||||
|
@"^\[(?<name>[^\]]+)\]$",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Matches:
|
||||||
|
///
|
||||||
|
/// COLUMN(number)
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex IndexedColumnPattern = new Regex(
|
||||||
|
@"^COLUMN\((?<index>\d+)\)$",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses QueryTemplate text into structured definition.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||||
|
///
|
||||||
|
/// Result:
|
||||||
|
///
|
||||||
|
/// SelectColumn:
|
||||||
|
/// Password
|
||||||
|
///
|
||||||
|
/// WhereColumn:
|
||||||
|
/// PcbId
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchOrder">
|
||||||
|
/// QueryTemplate expression.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Parsed query definition.
|
||||||
|
/// </returns>
|
||||||
|
public static SearchOrderDefinition Parse(string searchOrder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(searchOrder))
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"QueryTemplate is empty.");
|
||||||
|
|
||||||
|
Match match = FullPattern.Match(searchOrder);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Invalid QueryTemplate syntax. Expected: SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchOrderDefinition
|
||||||
|
{
|
||||||
|
SelectColumn =
|
||||||
|
ParseColumnReference(
|
||||||
|
match.Groups["select"].Value),
|
||||||
|
|
||||||
|
WhereColumn =
|
||||||
|
ParseColumnReference(
|
||||||
|
match.Groups["where"].Value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses single column definition.
|
||||||
|
///
|
||||||
|
/// Supported forms:
|
||||||
|
///
|
||||||
|
/// [ColumnName]
|
||||||
|
///
|
||||||
|
/// COLUMN(number)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Raw token extracted from QueryTemplate.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Parsed column reference.
|
||||||
|
/// </returns>
|
||||||
|
private static ColumnReference ParseColumnReference(
|
||||||
|
string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Column reference token is empty.");
|
||||||
|
|
||||||
|
Match nameMatch =
|
||||||
|
NamedColumnPattern.Match(token);
|
||||||
|
|
||||||
|
if (nameMatch.Success)
|
||||||
|
{
|
||||||
|
return new ColumnReference
|
||||||
|
{
|
||||||
|
Name =
|
||||||
|
nameMatch
|
||||||
|
.Groups["name"]
|
||||||
|
.Value
|
||||||
|
.Trim(),
|
||||||
|
|
||||||
|
Index = null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Match indexMatch =
|
||||||
|
IndexedColumnPattern.Match(token);
|
||||||
|
|
||||||
|
if (indexMatch.Success)
|
||||||
|
{
|
||||||
|
return new ColumnReference
|
||||||
|
{
|
||||||
|
Name = null,
|
||||||
|
|
||||||
|
Index =
|
||||||
|
int.Parse(
|
||||||
|
indexMatch
|
||||||
|
.Groups["index"]
|
||||||
|
.Value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Invalid column reference '" +
|
||||||
|
token +
|
||||||
|
"'. Use [ColumnName] or COLUMN(number).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines contract for reading meter login passwords
|
||||||
|
/// from configured GCI data storage.
|
||||||
|
///
|
||||||
|
/// Implementations hide storage details and provide
|
||||||
|
/// a unified API for password lookup.
|
||||||
|
///
|
||||||
|
/// Supported storage types:
|
||||||
|
/// - SQL database
|
||||||
|
/// - CSV
|
||||||
|
/// - JSON
|
||||||
|
/// - REST API
|
||||||
|
/// </summary>
|
||||||
|
public interface IMeterLoginPasswordReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meter login password synchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryParam">
|
||||||
|
/// PCB ID or another configured lookup value.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password if found; otherwise null.
|
||||||
|
/// </returns>
|
||||||
|
string GetPassword(DataQuery query);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meter login password asynchronously.
|
||||||
|
/// Thread-safe implementation may serialize
|
||||||
|
/// access to the underlying storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryParam">
|
||||||
|
/// PCB ID or another configured lookup value.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password if found; otherwise null.
|
||||||
|
/// </returns>
|
||||||
|
Task<string> ReadMeterLoginPasswordAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meter login password from configured GCI data storage.
|
||||||
|
/// </summary>
|
||||||
|
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Universal data storage reader selected by configuration.
|
||||||
|
/// Can represent database, CSV, JSON or REST reader.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IDataStorageReader reader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that only one password lookup is executed at a time.
|
||||||
|
/// </summary>
|
||||||
|
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates password reader using provided data storage configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">Data storage configuration loaded from gci_config.json.</param>
|
||||||
|
public MeterLoginPasswordReader(DataStorageConfig config)
|
||||||
|
{
|
||||||
|
reader = DataStorageReaderFactory.Create(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously reads meter login password using
|
||||||
|
/// the provided data query.
|
||||||
|
///
|
||||||
|
/// Thread-safe implementation may serialize
|
||||||
|
/// access to the underlying storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing lookup parameter values.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password if found; otherwise null.
|
||||||
|
/// </returns>
|
||||||
|
public async Task<string> ReadMeterLoginPasswordAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
if (query.QueryParams.Count == 0)
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Query does not contain any parameter.",
|
||||||
|
nameof(query));
|
||||||
|
|
||||||
|
await readLock.WaitAsync(token);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await Task.Run(
|
||||||
|
() => GetPassword(query),
|
||||||
|
token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads meter login password using
|
||||||
|
/// the provided data query.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing lookup parameter values.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Password if found; otherwise null.
|
||||||
|
/// </returns>
|
||||||
|
public string GetPassword(
|
||||||
|
DataQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
if (query.QueryParams.Count == 0)
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Query does not contain any parameter.",
|
||||||
|
nameof(query));
|
||||||
|
|
||||||
|
object result =
|
||||||
|
reader.GetData(query);
|
||||||
|
|
||||||
|
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||||
|
{
|
||||||
|
return diagnosticResult.Data?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result is DatabaseSearchResult dbResult)
|
||||||
|
{
|
||||||
|
if (!dbResult.Found ||
|
||||||
|
dbResult.Values == null ||
|
||||||
|
dbResult.Values.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (object value in dbResult.Values.Values)
|
||||||
|
{
|
||||||
|
return value?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to pre-adjustment calibration parameters
|
||||||
|
/// stored in the configured GCI data source.
|
||||||
|
///
|
||||||
|
/// Expected structure:
|
||||||
|
///
|
||||||
|
/// MeterSize | ParameterName | ParameterValue
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// 2 | GENESISFLOW_MinValidToF | 21990232
|
||||||
|
/// 2 | GENESISFLOW_Timeout | 1
|
||||||
|
///
|
||||||
|
/// Parameters are grouped by MeterSize and returned
|
||||||
|
/// as key/value pairs where:
|
||||||
|
///
|
||||||
|
/// Key = ParameterName
|
||||||
|
/// Value = ParameterValue
|
||||||
|
/// </summary>
|
||||||
|
public interface IPreAdjustmentCalibrationParamsReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pre-adjustment calibration parameters
|
||||||
|
/// using the provided data query synchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing meter size as lookup parameter.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary where key is GENESISFLOW parameter name
|
||||||
|
/// and value is stored parameter value.
|
||||||
|
/// </returns>
|
||||||
|
Dictionary<string, UInt32> GetCalibrationParams(
|
||||||
|
DataQuery query);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pre-adjustment calibration parameters
|
||||||
|
/// using the provided data query asynchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing meter size as lookup parameter.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary where key is GENESISFLOW parameter name
|
||||||
|
/// and value is stored parameter value.
|
||||||
|
/// </returns>
|
||||||
|
Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads pre-adjustment calibration parameters
|
||||||
|
/// from configured GCI data storage.
|
||||||
|
///
|
||||||
|
/// The underlying storage type is selected
|
||||||
|
/// through IDataStorageReader and may represent:
|
||||||
|
///
|
||||||
|
/// - SQL database
|
||||||
|
/// - CSV
|
||||||
|
/// - JSON
|
||||||
|
/// - REST API
|
||||||
|
///
|
||||||
|
/// Expected storage structure:
|
||||||
|
///
|
||||||
|
/// MeterSize | ParameterName | ParameterValue
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// 2 | GENESISFLOW_MinValidToF | 21990232
|
||||||
|
/// 2 | GENESISFLOW_Timeout | 1
|
||||||
|
///
|
||||||
|
/// Returned data are represented as:
|
||||||
|
///
|
||||||
|
/// Key = ParameterName
|
||||||
|
/// Value = ParameterValue
|
||||||
|
/// </summary>
|
||||||
|
public class PreAdjustmentCalibrationParamsReader
|
||||||
|
: IPreAdjustmentCalibrationParamsReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Universal data storage reader selected by configuration.
|
||||||
|
/// Can represent database, CSV, JSON or REST reader.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IDataStorageReader reader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that only one calibration parameter lookup
|
||||||
|
/// is executed at a time.
|
||||||
|
/// </summary>
|
||||||
|
private readonly SemaphoreSlim readLock =
|
||||||
|
new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates calibration parameter reader using
|
||||||
|
/// provided data storage configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">
|
||||||
|
/// Data storage configuration loaded from gci_config.json.
|
||||||
|
/// </param>
|
||||||
|
public PreAdjustmentCalibrationParamsReader(
|
||||||
|
DataStorageConfig config)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
|
||||||
|
reader = DataStorageReaderFactory.Create(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pre-adjustment calibration parameters
|
||||||
|
/// using the provided data query asynchronously.
|
||||||
|
///
|
||||||
|
/// The query is expected to contain meter size
|
||||||
|
/// as the first lookup parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing lookup parameter values.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="token">
|
||||||
|
/// Cancellation token.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary:
|
||||||
|
///
|
||||||
|
/// Key:
|
||||||
|
/// GENESISFLOW parameter name
|
||||||
|
///
|
||||||
|
/// Value:
|
||||||
|
/// Stored parameter value
|
||||||
|
/// </returns>
|
||||||
|
public async Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
|
||||||
|
DataQuery query,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
ValidateQuery(query);
|
||||||
|
|
||||||
|
return await Task.Run(
|
||||||
|
() => GetCalibrationParams(query),
|
||||||
|
token).ConfigureAwait(false); ;
|
||||||
|
|
||||||
|
/*await readLock.WaitAsync(token);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await Task.Run(
|
||||||
|
() => GetCalibrationParams(query),
|
||||||
|
token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readLock.Release();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pre-adjustment calibration parameters
|
||||||
|
/// using the provided data query.
|
||||||
|
///
|
||||||
|
/// The query is expected to contain meter size
|
||||||
|
/// as the first lookup parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query containing lookup parameter values.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary:
|
||||||
|
///
|
||||||
|
/// Key:
|
||||||
|
/// GENESISFLOW parameter name
|
||||||
|
///
|
||||||
|
/// Value:
|
||||||
|
/// Stored parameter value
|
||||||
|
/// </returns>
|
||||||
|
public Dictionary<string, UInt32> GetCalibrationParams(
|
||||||
|
DataQuery query)
|
||||||
|
{
|
||||||
|
ValidateQuery(query);
|
||||||
|
|
||||||
|
object result =
|
||||||
|
reader.GetData(query);
|
||||||
|
|
||||||
|
return ConvertResultToDictionary(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates that data query exists
|
||||||
|
/// and contains at least one lookup parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">
|
||||||
|
/// Data query to validate.
|
||||||
|
/// </param>
|
||||||
|
private static void ValidateQuery(
|
||||||
|
DataQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
if (query.QueryParams == null ||
|
||||||
|
query.QueryParams.Count == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Query does not contain any parameter.",
|
||||||
|
nameof(query));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts provider-specific result objects
|
||||||
|
/// returned by IDataStorageReader into a unified
|
||||||
|
/// dictionary representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">
|
||||||
|
/// Raw result returned by the configured storage reader.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Dictionary containing calibration parameter
|
||||||
|
/// name/value pairs.
|
||||||
|
/// </returns>
|
||||||
|
private Dictionary<string, UInt32> ConvertResultToDictionary(
|
||||||
|
object result)
|
||||||
|
{
|
||||||
|
Dictionary<string, UInt32> values =
|
||||||
|
new Dictionary<string, UInt32>();
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
return values;
|
||||||
|
|
||||||
|
if (result is DatabaseSearchResult dbResult)
|
||||||
|
{
|
||||||
|
if (!dbResult.Found)
|
||||||
|
return values;
|
||||||
|
|
||||||
|
foreach (Dictionary<string, object> row in dbResult.Rows)
|
||||||
|
{
|
||||||
|
if (!row.TryGetValue("ParameterName", out object parameterNameObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!row.TryGetValue("ParameterValue", out object parameterValueObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string parameterName =
|
||||||
|
parameterNameObject?.ToString();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(parameterName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (TryConvertToUInt32(parameterValueObject, out UInt32 parameterValue))
|
||||||
|
{
|
||||||
|
values[parameterName] =
|
||||||
|
parameterValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||||
|
{
|
||||||
|
if (diagnosticResult.Data is Dictionary<string, UInt32> uintDictionary)
|
||||||
|
return uintDictionary;
|
||||||
|
|
||||||
|
if (diagnosticResult.Data is Dictionary<string, string> stringDictionary)
|
||||||
|
{
|
||||||
|
foreach (var item in stringDictionary)
|
||||||
|
{
|
||||||
|
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||||
|
values[item.Key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagnosticResult.Data is Dictionary<string, object> objectDictionary)
|
||||||
|
{
|
||||||
|
foreach (var item in objectDictionary)
|
||||||
|
{
|
||||||
|
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||||
|
values[item.Key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result is Dictionary<string, UInt32> directUIntDictionary)
|
||||||
|
return directUIntDictionary;
|
||||||
|
|
||||||
|
if (result is Dictionary<string, string> directStringDictionary)
|
||||||
|
{
|
||||||
|
foreach (var item in directStringDictionary)
|
||||||
|
{
|
||||||
|
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||||
|
values[item.Key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result is Dictionary<string, object> directObjectDictionary)
|
||||||
|
{
|
||||||
|
foreach (var item in directObjectDictionary)
|
||||||
|
{
|
||||||
|
if (TryConvertToUInt32(item.Value, out UInt32 value))
|
||||||
|
values[item.Key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryConvertToUInt32(
|
||||||
|
object value,
|
||||||
|
out UInt32 result)
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (value is UInt32 uintValue)
|
||||||
|
{
|
||||||
|
result = uintValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is int intValue && intValue >= 0)
|
||||||
|
{
|
||||||
|
result = Convert.ToUInt32(intValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is long longValue &&
|
||||||
|
longValue >= 0 &&
|
||||||
|
longValue <= UInt32.MaxValue)
|
||||||
|
{
|
||||||
|
result = Convert.ToUInt32(longValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UInt32.TryParse(
|
||||||
|
value.ToString(),
|
||||||
|
out result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using GenesisCordonelInterface.API;
|
||||||
|
using GenesisCordonelInterface.Core.Config;
|
||||||
|
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using UdsReaderType_CalibrationParams = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams.PreAdjustmentCalibrationParamsReader;
|
||||||
|
using UdsReaderType_LoginPasswords = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords.MeterLoginPasswordReader;
|
||||||
|
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
|
||||||
|
using GciEnums = GenesisCordonelInterface.API.Enums;
|
||||||
|
using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
|
||||||
|
using GciDataStorageReadingModels = GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||||
|
using GciGUIType = GenesisCordonelInterface.UI.MainView;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core
|
||||||
|
{
|
||||||
|
public class Engine
|
||||||
|
{
|
||||||
|
GciConfig gciConfig;
|
||||||
|
|
||||||
|
public UdsReaderType_LoginPasswords loginPasswordsDataStorageReader;
|
||||||
|
public UdsReaderType_CalibrationParams calibrationParamsStorageReader;
|
||||||
|
//readonly UdsWriterType writer;
|
||||||
|
|
||||||
|
//diag GUI for GCI
|
||||||
|
GciGUIType gciGUI;
|
||||||
|
Form gciGuiHostForm;
|
||||||
|
|
||||||
|
//diag GUI for GciBridge
|
||||||
|
public UserControl gciBridgeGUIUserControl;
|
||||||
|
public UI.MainForm gciBridgeGuiForm;
|
||||||
|
|
||||||
|
public InterfaceOutsideToGCI gciExternalInterface;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the public GCI facade.
|
||||||
|
/// </summary>
|
||||||
|
public Engine()
|
||||||
|
{
|
||||||
|
gciConfig = GciConfigLoader.LoadDefault();
|
||||||
|
|
||||||
|
loginPasswordsDataStorageReader = new UdsReaderType_LoginPasswords
|
||||||
|
(
|
||||||
|
new GciDataStorageReadingModels.DataStorageConfig
|
||||||
|
(
|
||||||
|
nameof(GciEnums.DataStorageReaderTypes.LoginPasswordsReader),
|
||||||
|
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
|
||||||
|
gciConfig.DataStorageSection.MeterLoginPasswords.DataSource,
|
||||||
|
gciConfig.DataStorageSection.MeterLoginPasswords.QueryTemplate
|
||||||
|
)
|
||||||
|
);
|
||||||
|
calibrationParamsStorageReader = new UdsReaderType_CalibrationParams
|
||||||
|
(
|
||||||
|
new GciDataStorageReadingModels.DataStorageConfig
|
||||||
|
(
|
||||||
|
nameof(GciEnums.DataStorageReaderTypes.CalibrationParamsReader),
|
||||||
|
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
|
||||||
|
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.DataSource,
|
||||||
|
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.QueryTemplate
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
gciExternalInterface = new InterfaceOutsideToGCI(
|
||||||
|
new InterfaceGCIToLaatzen(),
|
||||||
|
loginPasswordsDataStorageReader,
|
||||||
|
calibrationParamsStorageReader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
Navod na logovanie projektu Laatzen/Common/ pre Genesis Cordonel meradla:
|
||||||
|
1. Princip:
|
||||||
|
- logovanie je zabezpecene pomocou nastroja NLog, ktory pouziva Laatzen
|
||||||
|
- Laatzen vsak pouziva nad NLogom svoj wrapper NLogHelper.cs a teda sa na pracu pri ich projektoch konfiguruje NLog pomocou ich suboru Nlogconfig.xml,
|
||||||
|
no nakolko ja som nechcel zasahovat do ich konfiguracneho suboru NLogu, pouzil som svoj, ktory je definovany v mojom projekte GenesisCordonelInterface,
|
||||||
|
s nazvom nlog.config
|
||||||
|
- z dovodu, ze som teda nechcel zasahovat do ich konfiguracie a zaroven som musel NLog na svoje ucely (presmerovanie logov do mema v mojom projekte),
|
||||||
|
musel som teda konfigurovat svojim konfiguracnym suborom, a zaroven plati ze pouzivam raw NLog.
|
||||||
|
- btw ked som pouzil ich wrapper a zaroven moj konfiguracny subor NLogu, tak pri spusten appky ho aj nacitalo, no nasledne sa nacitali Laatzenacke projekty
|
||||||
|
a nacital sa aj ich konfigurak Nlogu, ci sa prepisal ten moj. Pouzil som ten raw Nlog a uz to bolo ok
|
||||||
|
- takto je mozne presmerovat pomocou nizsie uvedenej metody logy az do mema v mojom projekte a tiez aj do suboru, pricom vsetko definujem v nlog.config
|
||||||
|
mojho projektu a nie v projekte Laatzenu
|
||||||
|
2. prilozene subory UiLogBus.cs a UiTarget.cs musia byt nakopirovane do projektu v ramci Solution s nazvom Common od Laatzenu
|
||||||
|
- kopiruju sa do top adresara projektu
|
||||||
|
- musia obsahovat ""namespace Xylem.Common.Utils.Logging"
|
||||||
|
- v mieste kde chcem logovat, je potrebne:
|
||||||
|
- zadefinovat:
|
||||||
|
private readonly ILogger Logger = NLogHelper.CreateOrGetLogger("GenesisCordonelInterface");
|
||||||
|
- pouzit log:
|
||||||
|
Logger.Error(ex, $"Slot #{slotNR} failed: {ex.Message}");
|
||||||
|
Logger.Trace("CONFIG: Setup loaded.");
|
||||||
|
Logger.Info("CONFIG: Setup loaded.");
|
||||||
|
...whatever
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
/*namespace Xylem.Common.Utils.Logging
|
||||||
|
{
|
||||||
|
public static class UiLogBus
|
||||||
|
{
|
||||||
|
public static event Action<string> MessageReceived;
|
||||||
|
|
||||||
|
public static void Publish(string message)
|
||||||
|
{
|
||||||
|
var handler = MessageReceived;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using NLog;
|
||||||
|
using NLog.Targets;
|
||||||
|
|
||||||
|
/*namespace Xylem.Common.Utils.Logging
|
||||||
|
{
|
||||||
|
[Target("UiTarget")]
|
||||||
|
public class UiTarget : TargetWithLayout
|
||||||
|
{
|
||||||
|
protected override void Write(LogEventInfo logEvent)
|
||||||
|
{
|
||||||
|
string message = Layout.Render(logEvent);
|
||||||
|
UiLogBus.Publish(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.Threading
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
ApiWorker – per-slot sequential execution worker
|
||||||
|
|
||||||
|
This class provides a lightweight background worker that executes actions
|
||||||
|
sequentially on a dedicated thread.
|
||||||
|
|
||||||
|
PRIMARY PURPOSE
|
||||||
|
---------------
|
||||||
|
ApiWorker is designed to safely execute hardware-related operations
|
||||||
|
(e.g. meter communication) without blocking the UI thread and without
|
||||||
|
allowing concurrent access to the same device.
|
||||||
|
|
||||||
|
Each ApiWorker instance typically represents:
|
||||||
|
1 worker = 1 slot = 1 meter = 1 communication channel
|
||||||
|
|
||||||
|
KEY PROPERTIES
|
||||||
|
--------------
|
||||||
|
- Single dedicated background thread
|
||||||
|
- FIFO queue (first-in, first-out)
|
||||||
|
- Sequential execution (NO parallelism inside one worker)
|
||||||
|
- Thread-safe enqueueing
|
||||||
|
- Task-based async interface for callers
|
||||||
|
|
||||||
|
WHY THIS IS IMPORTANT
|
||||||
|
--------------------
|
||||||
|
Hardware communication (serial ports, meters, etc.) is usually NOT thread-safe.
|
||||||
|
If multiple commands are executed in parallel, communication may break or corrupt data.
|
||||||
|
|
||||||
|
ApiWorker guarantees:
|
||||||
|
- operations are executed one-by-one
|
||||||
|
- order is preserved
|
||||||
|
- no race conditions on the device
|
||||||
|
|
||||||
|
HIGH-LEVEL FLOW
|
||||||
|
---------------
|
||||||
|
Caller (UI/API)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
RunAsync(...)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
TaskCompletionSource created
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Action wrapped into queue item
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Added to BlockingCollection queue
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Worker thread consumes queue
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Action executed (blocking HW call)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Result propagated via TaskCompletionSource
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Caller receives result via await
|
||||||
|
|
||||||
|
GRAPH
|
||||||
|
-----
|
||||||
|
Caller thread (UI)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
RunAsync()
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Queue (BlockingCollection)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
-----------------------------
|
||||||
|
| Worker Thread (background)|
|
||||||
|
| while(queue) |
|
||||||
|
| Execute Action |
|
||||||
|
-----------------------------
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Task result (await)
|
||||||
|
|
||||||
|
THREADING MODEL
|
||||||
|
---------------
|
||||||
|
- Producer/Consumer pattern
|
||||||
|
- Producer: any thread calling RunAsync
|
||||||
|
- Consumer: single worker thread
|
||||||
|
- Synchronization handled by BlockingCollection
|
||||||
|
|
||||||
|
MAIN COMPONENTS
|
||||||
|
---------------
|
||||||
|
1. BlockingCollection<Action> queue
|
||||||
|
- thread-safe queue
|
||||||
|
- stores work items
|
||||||
|
- supports blocking consumption
|
||||||
|
|
||||||
|
2. Dedicated Thread
|
||||||
|
- runs WorkerLoop()
|
||||||
|
- continuously processes queue
|
||||||
|
|
||||||
|
3. TaskCompletionSource<T>
|
||||||
|
- bridges sync execution → async API
|
||||||
|
- allows caller to await result
|
||||||
|
|
||||||
|
METHODS
|
||||||
|
-------
|
||||||
|
|
||||||
|
RunAsync<T>(Func<T>)
|
||||||
|
--------------------
|
||||||
|
- Enqueues a function returning a value
|
||||||
|
- Wraps it into Action
|
||||||
|
- Executes on worker thread
|
||||||
|
- Returns Task<T> to caller
|
||||||
|
|
||||||
|
RunAsync(Action)
|
||||||
|
----------------
|
||||||
|
- Convenience overload for void methods
|
||||||
|
- Internally wraps into Func<object>
|
||||||
|
|
||||||
|
WorkerLoop()
|
||||||
|
------------
|
||||||
|
- Infinite loop consuming queue
|
||||||
|
- Executes actions one-by-one
|
||||||
|
- Stops when queue is completed
|
||||||
|
|
||||||
|
Dispose()
|
||||||
|
---------
|
||||||
|
- Stops accepting new items
|
||||||
|
- Cleans up queue
|
||||||
|
- Does NOT forcibly stop running task
|
||||||
|
|
||||||
|
CANCELLATION
|
||||||
|
------------
|
||||||
|
- CancellationToken is checked BEFORE execution
|
||||||
|
- If cancelled → Task is cancelled
|
||||||
|
- Does NOT interrupt running operation
|
||||||
|
|
||||||
|
IMPORTANT LIMITATIONS
|
||||||
|
--------------------
|
||||||
|
- No parallel execution inside one worker (by design)
|
||||||
|
- Long-running action blocks worker thread
|
||||||
|
- No built-in timeout handling
|
||||||
|
- Dispose does not abort running work
|
||||||
|
|
||||||
|
WHEN TO USE
|
||||||
|
-----------
|
||||||
|
✔ Per-device communication (serial, TCP, HW)
|
||||||
|
✔ Ordered execution required
|
||||||
|
✔ UI must stay responsive
|
||||||
|
|
||||||
|
WHEN NOT TO USE
|
||||||
|
---------------
|
||||||
|
✘ CPU parallel processing (use Task.Run / Parallel)
|
||||||
|
✘ High-throughput parallel workloads
|
||||||
|
✘ Fire-and-forget background tasks
|
||||||
|
|
||||||
|
SUMMARY
|
||||||
|
-------
|
||||||
|
ApiWorker is a simple, robust solution for:
|
||||||
|
"Execute commands sequentially per resource, asynchronously from UI"
|
||||||
|
|
||||||
|
It is a perfect fit for:
|
||||||
|
- hardware interfaces
|
||||||
|
- device drivers
|
||||||
|
- IO-bound serialized workflows
|
||||||
|
*/
|
||||||
|
|
||||||
|
public sealed class ApiWorker : IDisposable
|
||||||
|
{
|
||||||
|
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface-threading");
|
||||||
|
/// <summary>
|
||||||
|
/// Thread-safe FIFO queue holding work items.
|
||||||
|
/// </summary>
|
||||||
|
private readonly BlockingCollection<Action> queue = new BlockingCollection<Action>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dedicated worker thread processing the queue.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Thread thread;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether this worker has been disposed.
|
||||||
|
/// </summary>
|
||||||
|
private bool disposed;
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public int QueueLength
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (disposed || queue == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return queue.Count;
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool IsBusy { get; private set; }
|
||||||
|
public string CurrentOperation { get; private set; }
|
||||||
|
public string LastError { get; private set; }
|
||||||
|
public DateTime LastActivity { get; private set; }
|
||||||
|
public bool IsDisposed
|
||||||
|
{
|
||||||
|
get { return disposed; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ApiWorker with its own background thread.
|
||||||
|
/// </summary>
|
||||||
|
public ApiWorker(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
LastActivity = DateTime.Now;
|
||||||
|
|
||||||
|
thread = new Thread(WorkerLoop)
|
||||||
|
{
|
||||||
|
IsBackground = true,
|
||||||
|
Name = name
|
||||||
|
};
|
||||||
|
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueues a function returning a value for sequential execution.
|
||||||
|
/// </summary>
|
||||||
|
public Task<T> RunAsync<T>(
|
||||||
|
Func<T> action,
|
||||||
|
CancellationToken token = default,
|
||||||
|
string operationName = null)
|
||||||
|
{
|
||||||
|
if (action == null)
|
||||||
|
throw new ArgumentNullException(nameof(action));
|
||||||
|
|
||||||
|
if (disposed)
|
||||||
|
throw new ObjectDisposedException(nameof(ApiWorker));
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<T>(
|
||||||
|
TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ENQUEUE {operationName} slot worker={Name} time={DateTime.Now:HH:mm:ss.fff}");
|
||||||
|
queue.Add(() =>
|
||||||
|
{
|
||||||
|
if (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
tcs.TrySetCanceled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
CurrentOperation = operationName ?? action.Method.Name;
|
||||||
|
LastActivity = DateTime.Now;
|
||||||
|
LastError = null;
|
||||||
|
|
||||||
|
var result = action();
|
||||||
|
|
||||||
|
tcs.TrySetResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LastError = ex.Message;
|
||||||
|
tcs.TrySetException(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
CurrentOperation = null;
|
||||||
|
LastActivity = DateTime.Now;
|
||||||
|
}
|
||||||
|
}, token);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException ex)
|
||||||
|
{
|
||||||
|
tcs.TrySetException(ex);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
tcs.TrySetException(new ObjectDisposedException(nameof(ApiWorker), ex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RunAsync(
|
||||||
|
Action action,
|
||||||
|
CancellationToken token = default(CancellationToken),
|
||||||
|
string operationName = null)
|
||||||
|
{
|
||||||
|
return RunAsync<object>(() =>
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
return null;
|
||||||
|
}, token, operationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueues a void action for sequential execution.
|
||||||
|
/// </summary>
|
||||||
|
public Task RunAsync(Action action, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return RunAsync<object>(() =>
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
return null;
|
||||||
|
}, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main worker loop processing queued actions.
|
||||||
|
/// </summary>
|
||||||
|
private void WorkerLoop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var item in queue.GetConsumingEnumerable())
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
item();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// worker is shutting down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the worker and releases resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
disposed = true;
|
||||||
|
|
||||||
|
queue.CompleteAdding();
|
||||||
|
|
||||||
|
if (Thread.CurrentThread != thread && thread.IsAlive)
|
||||||
|
thread.Join(1000);
|
||||||
|
|
||||||
|
queue.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Core.Threading
|
||||||
|
{
|
||||||
|
public sealed class RetryResult<T>
|
||||||
|
{
|
||||||
|
public T Result { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public int Attempts { get; set; }
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
|
public bool TimedOut { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"Success={0}, Attempts={1}, Duration={2} ms, TimedOut={3}, Result={4}",
|
||||||
|
Success,
|
||||||
|
Attempts,
|
||||||
|
(int)Duration.TotalMilliseconds,
|
||||||
|
TimedOut,
|
||||||
|
Result == null ? "<null>" : Result.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RetryWorker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Executes an asynchronous operation with retry and timeout protection.
|
||||||
|
///
|
||||||
|
/// Important:
|
||||||
|
/// The timeout here protects the caller from waiting forever, but it does not forcibly abort
|
||||||
|
/// the underlying hardware operation. Because hardware/COM calls may continue running after
|
||||||
|
/// the timeout signal, this method waits for the original action to finish before starting
|
||||||
|
/// the next retry. This prevents overlapping COM requests on the same device.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<RetryResult<T>> RunWithRetryAsync<T>(
|
||||||
|
Func<Task<T>> action,
|
||||||
|
Func<T, bool> isSuccess,
|
||||||
|
Action<string> log,
|
||||||
|
Action<string, T> logResult,
|
||||||
|
string operationName,
|
||||||
|
int maxAttempts = 3,
|
||||||
|
int delayMs = 500,
|
||||||
|
int timeoutMs = 30000)
|
||||||
|
{
|
||||||
|
var started = DateTime.Now;
|
||||||
|
T lastResult = default(T);
|
||||||
|
bool timedOut = false;
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= maxAttempts; attempt++)
|
||||||
|
{
|
||||||
|
Task<T> actionTask = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log?.Invoke($"{operationName} started. Attempt {attempt}/{maxAttempts}");
|
||||||
|
|
||||||
|
// Start real operation, for example Connect/GetPcbId/Login.
|
||||||
|
actionTask = action();
|
||||||
|
|
||||||
|
// Start independent timeout timer.
|
||||||
|
var timeoutTask = Task.Delay(timeoutMs);
|
||||||
|
|
||||||
|
// Wait until either operation completes or timeout expires.
|
||||||
|
var completedTask = await Task.WhenAny(actionTask, timeoutTask)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (completedTask == timeoutTask)
|
||||||
|
{
|
||||||
|
timedOut = true;
|
||||||
|
|
||||||
|
log?.Invoke(
|
||||||
|
$"{operationName} timed out. Attempt {attempt}/{maxAttempts}. " +
|
||||||
|
"Waiting for the running operation to finish before retry.");
|
||||||
|
|
||||||
|
// Critical part:
|
||||||
|
// Do NOT immediately start another retry.
|
||||||
|
// The hardware operation may still be active in ApiWorker.
|
||||||
|
// Starting another attempt immediately could corrupt communication.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lastResult = await actionTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log?.Invoke(
|
||||||
|
$"{operationName} finished after timeout with exception: " +
|
||||||
|
$"{ex.GetType().Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Operation completed before timeout.
|
||||||
|
lastResult = await actionTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide whether the returned result means success.
|
||||||
|
if (lastResult != null && isSuccess(lastResult))
|
||||||
|
{
|
||||||
|
return new RetryResult<T>
|
||||||
|
{
|
||||||
|
Result = lastResult,
|
||||||
|
Success = true,
|
||||||
|
Attempts = attempt,
|
||||||
|
Duration = DateTime.Now - started,
|
||||||
|
TimedOut = timedOut
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
logResult?.Invoke(
|
||||||
|
$"{operationName} failed. Attempt {attempt}/{maxAttempts}",
|
||||||
|
lastResult);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Covers exceptions thrown before timeout handling or by action startup.
|
||||||
|
log?.Invoke(
|
||||||
|
$"{operationName} exception on attempt {attempt}/{maxAttempts}: " +
|
||||||
|
$"{ex.GetType().Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay before next retry, except after the final attempt.
|
||||||
|
if (attempt < maxAttempts)
|
||||||
|
{
|
||||||
|
log?.Invoke($"{operationName} waiting {delayMs} ms before retry.");
|
||||||
|
|
||||||
|
await Task.Delay(delayMs).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RetryResult<T>
|
||||||
|
{
|
||||||
|
Result = lastResult,
|
||||||
|
Success = false,
|
||||||
|
Attempts = maxAttempts,
|
||||||
|
Duration = DateTime.Now - started,
|
||||||
|
TimedOut = timedOut
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureSuccess<T>(
|
||||||
|
RetryResult<T> retryResult,
|
||||||
|
string operationName)
|
||||||
|
{
|
||||||
|
if (!retryResult.Success)
|
||||||
|
throw new Exception($"{operationName} failed after {retryResult.Attempts} attempts.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{C955D8AC-76B8-42D8-A83F-8AEB56CF2567}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>GenesisCordonelInterface</RootNamespace>
|
||||||
|
<AssemblyName>GenesisCordonelInterface</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<LangVersion>7.3</LangVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Common">
|
||||||
|
<HintPath>..\packages\Common\Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Logic.ProductionToProductMapper">
|
||||||
|
<HintPath>..\packages\Common\Logic.ProductionToProductMapper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.5.2.2\lib\net46\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.IO.Compression" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Deployment" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Xylem.Common.CommonCore">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.CommonCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.CommonCore.Configuration">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.CommonCore.Configuration.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.CommonCore.ThreadWatcher">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.CommonCore.ThreadWatcher.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.Interfaces.Ports.PortCore">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.Interfaces.Ports.PortCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.Interfaces.Ports.SerialPorts">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.Applications">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.Applications.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.Registers">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.Registers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.MagFlux.Protocols.RequestProtocol.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.WaterMeterCore">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.WaterMeterCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Logic.ProductionOrderCore">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Logic.ProductionOrderCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Logic.SoftwareAccessHelper">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Logic.SoftwareAccessHelper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Utils.ByteArrayStyle">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Utils.ByteArrayStyle.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Utils.Crc16Ccitt">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Utils.Crc16Ccitt.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Utils.Logging">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Utils.Logging.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Xylem.Common.Utils.ProcessExec">
|
||||||
|
<HintPath>..\packages\Common\Xylem.Common.Utils.ProcessExec.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="API\ModelsMapping.cs" />
|
||||||
|
<Compile Include="API\InterfaceOutsideToGCI.cs" />
|
||||||
|
<Compile Include="API\InterfaceGCIToLaatzen.cs" />
|
||||||
|
<Compile Include="API\PublicModels.cs" />
|
||||||
|
<Compile Include="Core\Config\GciConfig.cs" />
|
||||||
|
<Compile Include="Core\Config\GciConfigLoader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Config\GciDataStorageConfig.cs" />
|
||||||
|
<Compile Include="API\Enums.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Providers\CsvDataStorageReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Providers\DatabaseDataStorageReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Models\DatabaseSearchResult.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Models\DataQuery.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageConfig.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\DataStorageReaderFactory.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageType.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Contracts\IDataStorageReader..cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Providers\JsonDataStorageReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Models\ReaderDiagnosticResult.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Providers\RestApiDataStorageReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderDefinition.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderParser.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\IMeterLoginPasswordReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\MeterLoginPasswordReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\IPreAdjustmentCalibrationParamsReader.cs" />
|
||||||
|
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\PreAdjustmentCalibrationParamsReader.cs" />
|
||||||
|
<Compile Include="Core\Engine\Engine.cs" />
|
||||||
|
<Compile Include="Core\Logging\UiLogBus.cs" />
|
||||||
|
<Compile Include="Core\Logging\UiTarget.cs" />
|
||||||
|
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
|
||||||
|
<Compile Include="Core\Threading\RetryWorker\RetryWorker.cs" />
|
||||||
|
<Compile Include="UI\Debug\MeterBatchConfigPanel.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\Debug\MeterBatchConfigPanel.Designer.cs">
|
||||||
|
<DependentUpon>MeterBatchConfigPanel.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\Debug\WorkerDebugPanel.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\Debug\WorkerDebugPanel.Designer.cs">
|
||||||
|
<DependentUpon>WorkerDebugPanel.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\Grid\MeterGridColumnConfig.cs" />
|
||||||
|
<Compile Include="UI\Grid\MeterGridConfigProvider.cs" />
|
||||||
|
<Compile Include="UI\Grid\MeterGridManager.cs" />
|
||||||
|
<Compile Include="UI\Grid\MeterRowDto.cs" />
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmConfigurations.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmConfigurations.Designer.cs">
|
||||||
|
<DependentUpon>FrmConfigurations.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_CordonelPreadjustmentUI\FrmCordonelPreadjustmentUI.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_CordonelPreadjustmentUI\FrmCordonelPreadjustmentUI.Designer.cs">
|
||||||
|
<DependentUpon>FrmCordonelPreadjustmentUI.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmRegisterStore.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmRegisterStore.Designer.cs">
|
||||||
|
<DependentUpon>FrmRegisterStore.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmSetup.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_GenesisToolBox\FrmSetup.Designer.cs">
|
||||||
|
<DependentUpon>FrmSetup.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\MainForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\MainForm.Designer.cs">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="UI\LaatzenAPI_CordonelPreadjustmentUI\PreAdjustmentControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\LaatzenAPI_CordonelPreadjustmentUI\PreAdjustmentControl.Designer.cs">
|
||||||
|
<DependentUpon>PreAdjustmentControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\MainView.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\MainView.Designer.cs">
|
||||||
|
<DependentUpon>MainView.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.Designer.cs">
|
||||||
|
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MeterInitView.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MeterInitView.Designer.cs">
|
||||||
|
<DependentUpon>MeterInitView.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.Designer.cs">
|
||||||
|
<DependentUpon>MetersActionView.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Content Include="docs\images\GciBridge_component_GUI.png" />
|
||||||
|
<Content Include="docs\images\GCI__Onboarding_Overview_drawio.svg" />
|
||||||
|
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg" />
|
||||||
|
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg" />
|
||||||
|
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Data_collection_domain.svg" />
|
||||||
|
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Meter_management_domain.svg" />
|
||||||
|
<Content Include="docs\images\logo\logo.png" />
|
||||||
|
<Content Include="docs\images\logo\logo.svg" />
|
||||||
|
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.png" />
|
||||||
|
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.svg" />
|
||||||
|
<Content Include="docs\styles\main.css" />
|
||||||
|
<Content Include="RuntimePackage\Build\Copy.targets.xml" />
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="UI\LaatzenAPI_GenesisToolBox\FrmSetup.resx">
|
||||||
|
<DependentUpon>FrmSetup.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="UI\MainForm.resx">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Content Include="Core\Logging\ImportantInstructions.txt" />
|
||||||
|
<Content Include="nlog.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<EmbeddedResource Include="UI\LaatzenAPI_CordonelPreadjustmentUI\PreAdjustmentControl.resx">
|
||||||
|
<DependentUpon>PreAdjustmentControl.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.resx">
|
||||||
|
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Content Include="Core\Config\gci_config.json">
|
||||||
|
<Link>Config\gci_config.json</Link>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<None Include="Core\Config\gci_config.json" />
|
||||||
|
<None Include="docs\articles\API\index.md" />
|
||||||
|
<None Include="docs\articles\API\PublicModels.md" />
|
||||||
|
<None Include="docs\articles\API\InterfaceGCIToLaatzen.md" />
|
||||||
|
<None Include="docs\articles\API\InterfaceOutsideToGCI.md" />
|
||||||
|
<None Include="docs\docfx.json" />
|
||||||
|
<None Include="docs\index.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__current_state.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__home.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__target_state.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__migration.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__refactoring.md" />
|
||||||
|
<None Include="docs\pages\development\page_dev__target_architecture.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__app_environment.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__internal_architecture.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__datastorage.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__home.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__interfaces.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__runtime.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__implementation_to_tbf.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__implementation_universal.md" />
|
||||||
|
<None Include="docs\pages\gci\page_gci__workers.md" />
|
||||||
|
<None Include="docs\pages\platform\page_platform__configuration_domain.md" />
|
||||||
|
<None Include="docs\pages\platform\page_platform__data_collection_domain.md" />
|
||||||
|
<None Include="docs\pages\platform\page_platform__tbf_implementation.md" />
|
||||||
|
<None Include="docs\pages\platform\page_platform__current_state.md" />
|
||||||
|
<None Include="docs\pages\platform\page_platform__home.md" />
|
||||||
|
<None Include="docs\toc.yml" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Config\" />
|
||||||
|
<Folder Include="Core\DataStorage\Writing\NewFolder1\" />
|
||||||
|
<Folder Include="RuntimePackage\Package\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<WCFMetadata Include="Connected Services\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\CordonelPreadjustmentUi\CordonelPreadjustmentUi.csproj">
|
||||||
|
<Project>{D0C8D887-ED52-40AB-A069-90BCE0E801E2}</Project>
|
||||||
|
<Name>CordonelPreadjustmentUi</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Ui\GenesisToolBox\GenesisToolBox.csproj">
|
||||||
|
<Project>{95A50087-99EE-4349-9A17-C547290DB42F}</Project>
|
||||||
|
<Name>GenesisToolBox</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<!-- Import custom GCI runtime packaging logic -->
|
||||||
|
<Import Project="RuntimePackage\Build\Copy.targets.xml" />
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using GenesisCordonelInterface.UI;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Xylem.Common.Utils.Logging;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface
|
||||||
|
{
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
private static readonly ILogger Logger = NLogHelper.CreateOrGetLogger("GenesisCordonelInterface");
|
||||||
|
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("GenesisCordonelInterface")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Windows Org")]
|
||||||
|
[assembly: AssemblyProduct("GenesisCordonelInterface")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Windows Org 2026")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("c955d8ac-76b8-42d8-a83f-8aeb56cf2567")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GenesisCordonelInterface.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace GenesisCordonelInterface.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
|
||||||
|
<Target Name="CopyGciRuntimeToRuntimePackage" AfterTargets="Build">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<GciRuntimePackageDir>$(ProjectDir)RuntimePackage\Package\</GciRuntimePackageDir>
|
||||||
|
<TbfPackageCommonDir>$(ProjectDir)..\packages\Common\</TbfPackageCommonDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<MakeDir Directories="$(GciRuntimePackageDir)" />
|
||||||
|
<MakeDir Directories="$(TbfPackageCommonDir)" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.dll" />
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.pdb" />
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.xml" />
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.exe" />
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.config" />
|
||||||
|
<GciRuntimeFiles Include="$(TargetDir)*.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Message Text="Copying GCI runtime package to $(GciRuntimePackageDir)" Importance="High" />
|
||||||
|
<Copy
|
||||||
|
SourceFiles="@(GciRuntimeFiles)"
|
||||||
|
DestinationFolder="$(GciRuntimePackageDir)"
|
||||||
|
SkipUnchangedFiles="false"
|
||||||
|
OverwriteReadOnlyFiles="true" />
|
||||||
|
|
||||||
|
<Message Text="Copying GCI runtime package to TBF package common: $(TbfPackageCommonDir)" Importance="High" />
|
||||||
|
<Copy
|
||||||
|
SourceFiles="@(GciRuntimeFiles)"
|
||||||
|
DestinationFolder="$(TbfPackageCommonDir)"
|
||||||
|
SkipUnchangedFiles="false"
|
||||||
|
OverwriteReadOnlyFiles="true" />
|
||||||
|
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||||
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Xylem.Common.CommonCore" publicKeyToken="null" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-2.8.18.17954" newVersion="2.8.18.17954" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
Binary file not shown.
Binary file not shown.
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
|
||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"Erase": [
|
||||||
|
"1\\tstfile",
|
||||||
|
"1\\fdrdata",
|
||||||
|
"1\\logdata",
|
||||||
|
"1\\blklist",
|
||||||
|
"1\\pulsedbg",
|
||||||
|
"1\\upg*",
|
||||||
|
"1\\img*"
|
||||||
|
],
|
||||||
|
"Restore": [
|
||||||
|
{
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user