Compare commits
428
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8b5732046 | ||
|
|
02de7c6c57 | ||
|
|
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 | ||
|
|
783d51e71a | ||
|
|
a710ab7457 | ||
|
|
6a428e3c76 | ||
|
|
2e56b8e5cf | ||
|
|
1a4c18fa35 | ||
|
|
153d031125 | ||
|
|
5d4c7dfe68 | ||
|
|
021beeb47f | ||
|
|
1140d67c75 | ||
|
|
f0d26a36be | ||
|
|
6afb10351e | ||
|
|
2f3259d063 | ||
|
|
30a9396e4a | ||
|
|
2a94347f3e | ||
|
|
30f5e6c935 | ||
|
|
0ad8bc6d75 | ||
|
|
c076d6b343 | ||
|
|
64e32751c0 | ||
|
|
279c8eefb4 | ||
|
|
b931ed62ff | ||
|
|
05665288ab | ||
|
|
8af89136e7 | ||
|
|
5b6461fa0a | ||
|
|
1f51924bc8 | ||
|
|
b1f3b72a96 | ||
|
|
dfc0661300 | ||
|
|
f747755301 | ||
|
|
ad2c1d615f | ||
|
|
11ad6d39ee | ||
|
|
0f394f622b | ||
|
|
09c542e1e6 | ||
|
|
d772a3d531 | ||
|
|
87206ebe79 | ||
|
|
f4c77feeef | ||
|
|
ae6d77efc1 | ||
|
|
8318ed33e1 | ||
|
|
c4c86b36d2 | ||
|
|
990b695db6 | ||
|
|
33f050ef71 | ||
|
|
026a95e3ac | ||
|
|
4fd9030812 | ||
|
|
45d138ece3 | ||
|
|
4a884a09a1 | ||
|
|
a524915f28 | ||
|
|
7982526790 | ||
|
|
b866c45e9b | ||
|
|
0073ac8b9e | ||
|
|
36d661c75e | ||
|
|
e3f1f2711b | ||
|
|
f9ae8c54e1 | ||
|
|
ad7abe5471 | ||
|
|
a027f3f544 | ||
|
|
0930df5e64 | ||
|
|
1c5bf76bb3 | ||
|
|
d2d697ae80 | ||
|
|
60f30e4c41 | ||
|
|
ecccfab7a6 | ||
|
|
1691c634c6 | ||
|
|
900a2d660c | ||
|
|
eaf60f3292 | ||
|
|
bc0de96ad9 | ||
|
|
a770f5a9b8 |
@@ -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>
|
||||
+38
-4
@@ -1,3 +1,5 @@
|
||||
AppDiagnostic/bin/
|
||||
AppDiagnostic/obj/
|
||||
Common/bin/
|
||||
Common/obj/
|
||||
Config/bin/
|
||||
@@ -26,8 +28,14 @@ GenericTest/bin/
|
||||
GenericTest/obj/
|
||||
GraphLib/bin/
|
||||
GraphLib/obj/
|
||||
LabelPrinting/bin/
|
||||
LabelPrinting/obj/
|
||||
MergeResultsDBs/bin/
|
||||
MergeResultsDBs/obj/
|
||||
NfcC7_Dll/bin/
|
||||
NfcC7_Dll/obj/
|
||||
NfcC7_DLL.Tests/bin/
|
||||
NfcC7_DLL.Tests/obj/
|
||||
OrderManagement/bin/
|
||||
OrderManagement/obj/
|
||||
ProductionTracing/bin/
|
||||
@@ -46,14 +54,18 @@ ResultsParser/bin/
|
||||
ResultsParser/obj/
|
||||
S640TestApp/bin/
|
||||
S640TestApp/obj/
|
||||
SharedDatabase/bin
|
||||
SharedDatabase/obj
|
||||
SchematicDrawing/bin
|
||||
SchematicDrawing/obj
|
||||
SharedComponents/bin/
|
||||
SharedComponents/obj/
|
||||
SharedDatabase/bin/
|
||||
SharedDatabase/obj/
|
||||
SchematicDrawing/bin/
|
||||
SchematicDrawing/obj/
|
||||
Statistics/bin/
|
||||
Statistics/obj/
|
||||
TBF/bin/
|
||||
TBF/obj/
|
||||
TBFTests/bin/
|
||||
TBFTests/obj/
|
||||
ToFirstMonitor/bin/
|
||||
ToFirstMonitor/obj/
|
||||
ToSecondMonitor/bin/
|
||||
@@ -64,6 +76,28 @@ WorkflowConfigurator/bin/
|
||||
WorkflowConfigurator/obj/
|
||||
Workplace/bin/
|
||||
Workplace/obj/
|
||||
DataStreamInterfaceTest/bin/
|
||||
DataStreamInterfaceTest/obj/
|
||||
DataStreamMeter/bin/
|
||||
DataStreamMeter/obj/
|
||||
Events/bin/
|
||||
Events/obj/
|
||||
TracingDB/bin/
|
||||
TracingDB/obj/
|
||||
Users/bin/
|
||||
Users/obj/
|
||||
.vs/
|
||||
.idea/
|
||||
*.suo
|
||||
*.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>
|
||||
+11
-4
@@ -17,7 +17,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@@ -30,12 +30,17 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="log4net">
|
||||
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
|
||||
<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>
|
||||
</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.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -90,7 +95,9 @@
|
||||
<Compile Include="Utils.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Forms\ListViewEx.resx">
|
||||
<DependentUpon>ListViewEx.cs</DependentUpon>
|
||||
|
||||
+79
-3
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Common
|
||||
{
|
||||
@@ -199,10 +200,56 @@ namespace Common
|
||||
QnClassColdWater,
|
||||
QnClassHotWater,
|
||||
QpQi,
|
||||
CompoundQ3RQ3R,
|
||||
CompoundQnClassQnClass,
|
||||
CompoundQ3RQnClass,
|
||||
CompoundQnClassQ3R,
|
||||
CompoundQpQiQpQi,
|
||||
Manual,
|
||||
Count,
|
||||
}
|
||||
|
||||
public enum FlowId
|
||||
{
|
||||
Any,
|
||||
Q4,
|
||||
Q3,
|
||||
Q2,
|
||||
Q1,
|
||||
Q4Main,
|
||||
Q3Main,
|
||||
Q2Main,
|
||||
Q1Main,
|
||||
Q4Aux,
|
||||
Q3Aux,
|
||||
Q2Aux,
|
||||
Q1Aux,
|
||||
Qmax,
|
||||
Qn,
|
||||
Qt,
|
||||
Qmin,
|
||||
QmaxMain,
|
||||
QnMain,
|
||||
QtMain,
|
||||
QminMain,
|
||||
QmaxAux,
|
||||
QnAux,
|
||||
QtAux,
|
||||
QminAux,
|
||||
Qp,
|
||||
Qp10,
|
||||
Qi,
|
||||
QpMain,
|
||||
Qp10Main,
|
||||
QiMain,
|
||||
QpAux,
|
||||
Qp10Aux,
|
||||
QiAux,
|
||||
Qdetected,
|
||||
Qfall,
|
||||
Qrise,
|
||||
}
|
||||
|
||||
public enum ErrorLimitType
|
||||
{
|
||||
Manual,
|
||||
@@ -243,11 +290,20 @@ namespace Common
|
||||
CompoundAux,
|
||||
Compound,
|
||||
HeatMeterVolume,
|
||||
HeatMeterEnergy,
|
||||
HeatMeterMass,//...MF
|
||||
HeatMeterEnergy,
|
||||
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,
|
||||
#if LANG_PL
|
||||
@@ -474,8 +530,10 @@ namespace Common
|
||||
public enum TestProfile
|
||||
{
|
||||
UserDefined,
|
||||
UserDefinedCompound,
|
||||
UserDefinedHeatMeter,
|
||||
Protected, /// The first protected one
|
||||
ProtectedCompound,
|
||||
ProtectedHeatMeter,
|
||||
}
|
||||
|
||||
@@ -650,7 +708,16 @@ namespace Common
|
||||
Count,
|
||||
}
|
||||
|
||||
public enum ImageRotation
|
||||
public enum DivResolution : int
|
||||
{
|
||||
[Description("1 ms (0.25 s)")] _1ms = 0,
|
||||
[Description("2 ms (0.5 s)")] _2ms = 1,
|
||||
[Description("4 ms (1 s)")] _4ms = 2,
|
||||
[Description("8 ms (2 s)")] _8ms = 3,
|
||||
Count,
|
||||
}
|
||||
|
||||
public enum ImageRotation
|
||||
{
|
||||
[Description("0 deg")] None,
|
||||
[Description("90 deg")] Deg90,
|
||||
@@ -772,6 +839,15 @@ namespace Common
|
||||
E63 = (1L << 62),
|
||||
}
|
||||
|
||||
//...MF
|
||||
public enum FlowType : int
|
||||
{
|
||||
none,
|
||||
volume,
|
||||
mass,
|
||||
Count,
|
||||
}
|
||||
|
||||
#region iPERL enums
|
||||
|
||||
public enum Side
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
///
|
||||
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
|
||||
namespace Common.Forms
|
||||
{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
///
|
||||
/// Copyright (c) 2021-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
@@ -53,20 +56,20 @@ namespace Common.Forms
|
||||
public event EventHandler<MessageEventArgs> UpdateMessageHandler;
|
||||
void OnUpdateMessage(object sender, MessageEventArgs args)
|
||||
{
|
||||
if (args.Message != null) label1.Text = args.Message;
|
||||
if (args != null && args.Message != null) label1.Text = args.Message;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called from the state machine when an operation forces a modeless dialog close.
|
||||
/// </summary>
|
||||
public static void CloseForm()
|
||||
public void CloseForm()
|
||||
{
|
||||
if (CloseFormHandler == null) return;
|
||||
try { CloseFormHandler(null, null); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
static public event EventHandler<EventArgs> CloseFormHandler;
|
||||
public event EventHandler<EventArgs> CloseFormHandler;
|
||||
void OnCloseForm(object sender, EventArgs args)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Common.Iperl
|
||||
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
|
||||
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
|
||||
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 decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
|
||||
public string Label()
|
||||
|
||||
+131
-23
@@ -2,6 +2,7 @@
|
||||
/// Copyright (c) 2016-2021 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Common
|
||||
{
|
||||
@@ -33,6 +34,20 @@ namespace Common
|
||||
[Description("m3/m")] m3pm, /// 1 m3/m = 60 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("oz")] oz, /// 0.0283495231 kg
|
||||
[Description("lb")] lb, /// 0.45359237 kg
|
||||
@@ -105,6 +120,14 @@ namespace Common
|
||||
///
|
||||
[Description("pulse/kWh")] ppkWh, /// * 1 pulse/kWh
|
||||
[Description("kWh/pulse")] kWhpp, /// 1 kWh/pulse
|
||||
|
||||
///
|
||||
[Description("A")] A, /// *1 A
|
||||
[Description("mA")] mA, /// 1 mA = 0.001 A
|
||||
///
|
||||
[Description("V")] V, /// *1 V
|
||||
[Description("mV")] mV, /// 1 mV = 0.001 V
|
||||
|
||||
|
||||
Count
|
||||
}
|
||||
@@ -135,6 +158,8 @@ namespace Common
|
||||
[Description("Boolean")] Boolean,
|
||||
[Description("Datum und Uhrzeit")] DateTime,
|
||||
[Description("Aufgezählt")] Enumerated,
|
||||
[Description("Strom")] Current,
|
||||
[Description("Spannung")] Voltage,
|
||||
#elif LANG_PL
|
||||
[Description("Objętość")] Volume,
|
||||
[Description("Przepływ")] Flow,
|
||||
@@ -158,6 +183,8 @@ namespace Common
|
||||
[Description("Boolean")] Boolean,
|
||||
[Description("Data i czas")] DateTime,
|
||||
[Description("Wyliczone")] Enumerated,
|
||||
[Description("Prąd")] Current,
|
||||
[Description("Napięcie")] Voltage,
|
||||
#elif LANG_CS
|
||||
[Description("Objem")] Volume,
|
||||
[Description("Průtok")] Flow,
|
||||
@@ -181,6 +208,8 @@ namespace Common
|
||||
[Description("Boolean")] Boolean,
|
||||
[Description("Datum a čas")] DateTime,
|
||||
[Description("Vyjmenované")] Enumerated,
|
||||
[Description("Proud")] Current,
|
||||
[Description("Napětí")] Voltage,
|
||||
#elif LANG_IT
|
||||
[Description("Volume")] Volume,
|
||||
[Description("Flusso")] Flow,
|
||||
@@ -204,10 +233,16 @@ namespace Common
|
||||
[Description("Boolean")] Boolean,
|
||||
[Description("Data e ora")] DateTime,
|
||||
[Description("Enumerato")] Enumerated,
|
||||
[Description("Corrente")] Current,
|
||||
[Description("Voltaggio")] Voltage,
|
||||
#else
|
||||
[Description("RegisterReader")] RegisterReader,
|
||||
[Description("MultiFunctionalVariables")] MultiFunctionalVariables,
|
||||
[Description("VolumeFlow")] VolumeFlow,
|
||||
[Description("Volume")] Volume,
|
||||
[Description("Flow")] Flow,
|
||||
[Description("Mass")] Mass,
|
||||
[Description("MassFlow")] MassFlow,
|
||||
[Description("Mass")] Mass,
|
||||
[Description("Time")] Time,
|
||||
[Description("Temperature")] Temperature,
|
||||
[Description("Pressure")] Pressure,
|
||||
@@ -218,6 +253,8 @@ namespace Common
|
||||
[Description("Energy")] Energy,
|
||||
[Description("Pulses")] Pulses,
|
||||
[Description("Pulses/liter")] PulsePerLtr,
|
||||
[Description("Pulses/kg")] PulsePerKilogram, //...MF
|
||||
[Description("Pulses/Unit")] PulsePerUnit,//...MF
|
||||
[Description("Pulses/kWh")] PulsePerKWh,
|
||||
[Description("Conductivity")] Conductivity,
|
||||
|
||||
@@ -227,6 +264,8 @@ namespace Common
|
||||
[Description("Boolean")] Boolean,
|
||||
[Description("Date and time")] DateTime,
|
||||
[Description("Enumerated")] Enumerated,
|
||||
[Description("Current")] Current,
|
||||
[Description("Voltage")] Voltage,
|
||||
#endif
|
||||
Count,
|
||||
}
|
||||
@@ -242,59 +281,103 @@ namespace Common
|
||||
{
|
||||
return unit == Unit.l || unit == Unit.m3ph || unit == Unit.kg || unit == Unit.s || unit == Unit.C ||
|
||||
unit == Unit.bar || unit == Unit.RPct || unit == Unit.Pct || unit == Unit.mm || unit == Unit.kgpm3 ||
|
||||
unit == Unit.J || unit == Unit.uSpcm || unit == Unit.ppl || unit == Unit.ppkWh;
|
||||
unit == Unit.J || unit == Unit.uSpcm || unit == Unit.ppl || unit == Unit.ppkWh || unit == Unit.A ||
|
||||
unit == Unit.V;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
case Unit.pulse:
|
||||
case Unit.degree:
|
||||
return Quantity.Pulses;
|
||||
return new[] { Quantity.Pulses };
|
||||
|
||||
case Unit.ml:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.l:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.dm3:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.USgal:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.UKgal:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.cf:
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
case Unit.m3:
|
||||
return Quantity.Volume;
|
||||
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
|
||||
|
||||
case Unit.lph:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.cfph:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.lpm:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.USgalpm:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.m3ph:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.lps:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.USgalps:
|
||||
return new[] { Quantity.Flow };
|
||||
case Unit.m3pm:
|
||||
return new[] { Quantity.Flow };
|
||||
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:
|
||||
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||
case Unit.oz:
|
||||
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||
case Unit.lb:
|
||||
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||
case Unit.kg:
|
||||
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||
case Unit.t:
|
||||
return Quantity.Mass;
|
||||
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
|
||||
|
||||
case Unit.ms:
|
||||
case Unit.s:
|
||||
case Unit.min:
|
||||
case Unit.hour:
|
||||
return Quantity.Time;
|
||||
return new[] { Quantity.Time };
|
||||
|
||||
case Unit.C:
|
||||
case Unit.F:
|
||||
case Unit.K:
|
||||
return Quantity.Temperature;
|
||||
return new[] { Quantity.Temperature };
|
||||
|
||||
case Unit.Pa:
|
||||
case Unit.hPa:
|
||||
@@ -304,14 +387,14 @@ namespace Common
|
||||
case Unit.psi:
|
||||
case Unit.bar:
|
||||
case Unit.MPa:
|
||||
return Quantity.Pressure;
|
||||
return new[] { Quantity.Pressure };
|
||||
|
||||
case Unit.RPct:
|
||||
return Quantity.Humidity;
|
||||
return new[] { Quantity.Humidity };
|
||||
|
||||
case Unit.Promile:
|
||||
case Unit.Pct:
|
||||
return Quantity.Error;
|
||||
return new[] { Quantity.Error };
|
||||
|
||||
case Unit.mm:
|
||||
case Unit.cm:
|
||||
@@ -320,23 +403,23 @@ namespace Common
|
||||
case Unit.foot:
|
||||
case Unit.yard:
|
||||
case Unit.m:
|
||||
return Quantity.Length;
|
||||
return new[] { Quantity.Length };
|
||||
|
||||
case Unit.kgpm3:
|
||||
case Unit.kgpm3:
|
||||
case Unit.kgpl:
|
||||
return Quantity.Density;
|
||||
return new[] { Quantity.Density };
|
||||
|
||||
case Unit.J:
|
||||
case Unit.J:
|
||||
case Unit.kJ:
|
||||
case Unit.MJ:
|
||||
case Unit.Wh:
|
||||
case Unit.kWh:
|
||||
case Unit.MWh:
|
||||
return Quantity.Energy;
|
||||
return new[] { Quantity.Energy };
|
||||
|
||||
case Unit.uSpcm:
|
||||
case Unit.mSpm:
|
||||
return Quantity.Conductivity;
|
||||
return new[] { Quantity.Conductivity };
|
||||
|
||||
case Unit.ppl:
|
||||
case Unit.ppdm3:
|
||||
@@ -346,19 +429,29 @@ namespace Common
|
||||
case Unit.dm3pp:
|
||||
case Unit.lpdeg:
|
||||
case Unit.dm3pdeg:
|
||||
return Quantity.PulsePerLtr;
|
||||
return new[] { Quantity.PulsePerLtr };
|
||||
|
||||
case Unit.ppkWh:
|
||||
case Unit.kWhpp:
|
||||
return Quantity.PulsePerKWh;
|
||||
return new[] { Quantity.PulsePerKWh };
|
||||
|
||||
case Unit.A:
|
||||
case Unit.mA:
|
||||
return new[] { Quantity.Current };
|
||||
|
||||
case Unit.V:
|
||||
case Unit.mV:
|
||||
return new[] { Quantity.Voltage };
|
||||
|
||||
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 IsMassFlow(Unit unit) { return IsQuantity(unit, Quantity.MassFlow); }
|
||||
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 IsTemperature(Unit unit) { return IsQuantity(unit, Quantity.Temperature); }
|
||||
@@ -372,6 +465,8 @@ namespace Common
|
||||
public static bool IsPulsePerLtr(Unit unit) { return IsQuantity(unit, Quantity.PulsePerLtr); }
|
||||
public static bool IsPulsePerKWh(Unit unit) { return IsQuantity(unit, Quantity.PulsePerKWh); }
|
||||
public static bool IsConductivity(Unit unit) { return IsQuantity(unit, Quantity.Conductivity); }
|
||||
public static bool IsCurrent(Unit unit) { return IsQuantity(unit, Quantity.Current); }
|
||||
public static bool IsVoltage(Unit unit) { return IsQuantity(unit, Quantity.Voltage); }
|
||||
|
||||
|
||||
|
||||
@@ -455,6 +550,13 @@ namespace Common
|
||||
case Unit.dm3pp:
|
||||
case Unit.lpdeg:
|
||||
case Unit.dm3pdeg: return (v <= float.Epsilon) ? 0 : 1/v;
|
||||
|
||||
/// Current: internal representation in A
|
||||
case Unit.mA: return 1000 * v; /// 1000 mA = 1 A
|
||||
|
||||
/// Voltage: internal representation in V
|
||||
case Unit.mV: return 1000 * v; /// 1000 mV = 1 V
|
||||
|
||||
|
||||
default: return v; /// Do not convert
|
||||
}
|
||||
@@ -534,6 +636,12 @@ namespace Common
|
||||
|
||||
/// Electrical conductivity
|
||||
case Unit.mSpm: return 10 * v;
|
||||
|
||||
/// Current: internal representation in A
|
||||
case Unit.mA: return 0.001 * v; /// 1 mA = 0.001 A
|
||||
|
||||
/// Voltage: internal representation in V
|
||||
case Unit.mV: return 0.001 * v; /// 1 mV = 0.001 V
|
||||
|
||||
/// Invert
|
||||
case Unit.kWhpp:
|
||||
|
||||
+23
-6
@@ -20,15 +20,26 @@ namespace Common
|
||||
return (format == "F0") ? value.ToString("F0") : value.ToString(format).TrimEnd(new char[] { '0' });
|
||||
}
|
||||
|
||||
public static FlowNames ProfileType2FlowNames(ProfileType pt)
|
||||
public static FlowNames ProfileType2FlowNames(ProfileType pt, bool isAux = false)
|
||||
{
|
||||
switch (pt)
|
||||
{
|
||||
case ProfileType.Q3R: return FlowNames.Q3Q2Q1;
|
||||
case ProfileType.QnClassColdWater: return FlowNames.QnQtQmin;
|
||||
case ProfileType.QnClassHotWater: return FlowNames.QnQtQmin;
|
||||
case ProfileType.QpQi: return FlowNames.QpQi;
|
||||
default: return FlowNames.Q3Q2Q1;
|
||||
case ProfileType.Q3R:
|
||||
case ProfileType.CompoundQ3RQ3R:
|
||||
return FlowNames.Q3Q2Q1;
|
||||
case ProfileType.CompoundQ3RQnClass:
|
||||
return isAux ? FlowNames.QnQtQmin : FlowNames.Q3Q2Q1;
|
||||
case ProfileType.QnClassColdWater:
|
||||
case ProfileType.QnClassHotWater:
|
||||
case ProfileType.CompoundQnClassQnClass:
|
||||
return FlowNames.QnQtQmin;
|
||||
case ProfileType.CompoundQnClassQ3R:
|
||||
return isAux ? FlowNames.Q3Q2Q1 : FlowNames.QnQtQmin;
|
||||
case ProfileType.QpQi:
|
||||
case ProfileType.CompoundQpQiQpQi:
|
||||
return FlowNames.QpQi;
|
||||
default:
|
||||
return FlowNames.Q3Q2Q1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +302,7 @@ namespace Common
|
||||
string passwordOfDay = Convert.ToString(number, 8);
|
||||
|
||||
return (userName.Equals("milan") && password.Equals("kraken")) ||
|
||||
(userName.Equals("bumi") && password.Equals("70630")) ||
|
||||
(userName.Equals("igor") && password.Equals("mojronko8")) ||
|
||||
(userName.Equals("lubo1212") && password.Equals("Tatry52")) ||
|
||||
(userName.Equals("Michal") && password.Equals("Plok789456123")) ||
|
||||
@@ -299,6 +311,11 @@ namespace Common
|
||||
(userName.Equals("jan") && password.Equals("jaugust")) ||
|
||||
(userName.Equals("JCermak") && password.Equals("Zt6911")) ||
|
||||
(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));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.15" targetFramework="net472" />
|
||||
<package id="NLog" version="5.2.2" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -19,7 +19,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
@@ -30,7 +30,7 @@
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW</DefineConstants>
|
||||
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
@@ -43,7 +43,7 @@
|
||||
<HintPath>..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net">
|
||||
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
|
||||
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MySql.Data">
|
||||
<HintPath>..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll</HintPath>
|
||||
@@ -68,6 +68,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CalendarEvent\ICalendarEvent.cs" />
|
||||
<Compile Include="Data.cs" />
|
||||
<Compile Include="Entities\BenchPath.cs" />
|
||||
<Compile Include="Entities\CustomEvent.cs" />
|
||||
<Compile Include="Entities\Component.cs" />
|
||||
@@ -95,6 +96,7 @@
|
||||
<Compile Include="Entities\VirtualBenchStep.cs" />
|
||||
<Compile Include="CalendarEvent\Utils.cs" />
|
||||
<Compile Include="FluentCommon.cs" />
|
||||
<Compile Include="Formulas.cs" />
|
||||
<Compile Include="Mappings\BenchPathMap.cs" />
|
||||
<Compile Include="Mappings\ComponentMap.cs" />
|
||||
<Compile Include="Mappings\ComponentProcedureMap.cs" />
|
||||
@@ -115,6 +117,7 @@
|
||||
<Compile Include="Mappings\UncertaintyMap.cs" />
|
||||
<Compile Include="Mappings\UserMap.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PurchaseResources.cs" />
|
||||
<Compile Include="Resources\Strings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<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;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ namespace Config.Entities
|
||||
public virtual string PressMtrUp { get; set; }
|
||||
public virtual string PressMtrDown { get; set; }
|
||||
public virtual string PressMtrDelta { get; set; }
|
||||
public virtual string ElectricMtrUp { get; set; }
|
||||
public virtual string ElectricMtrDown { get; set; }
|
||||
public virtual string ElectricMtrDelta { get; set; }
|
||||
public virtual string StopBFValve { get; set; }
|
||||
|
||||
/// Valves
|
||||
@@ -59,6 +62,9 @@ namespace Config.Entities
|
||||
result.PressMtrUp = PressMtrUp;
|
||||
result.PressMtrDown = PressMtrDown;
|
||||
result.PressMtrDelta = PressMtrDelta;
|
||||
result.ElectricMtrUp = ElectricMtrUp;
|
||||
result.ElectricMtrDown = ElectricMtrDown;
|
||||
result.ElectricMtrDelta = ElectricMtrDelta;
|
||||
result.StopBFValve = StopBFValve;
|
||||
result.ValvesOpen = ValvesOpen;
|
||||
result.ValvesClose = ValvesClose;
|
||||
|
||||
@@ -18,8 +18,9 @@ namespace Config.Entities
|
||||
public virtual Unit FlowUnit { get; set; } /// Not mapped to database, used in UI
|
||||
public virtual string Selector { get; set; }
|
||||
public virtual string RegValve { get; set; }
|
||||
public virtual string FlowMeter { get; set; }
|
||||
public virtual float PidCoef { get; set; } /// PID coefficient for the regulation path
|
||||
public virtual int RegulMinStep { get; set; }
|
||||
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 Diverter { get; set; }
|
||||
public virtual string TempMtrDiv { get; set; }
|
||||
@@ -39,6 +40,7 @@ namespace Config.Entities
|
||||
{
|
||||
ValvesOpen = string.Empty;
|
||||
ValvesClose = string.Empty;
|
||||
RegulMinStep = 0;
|
||||
}
|
||||
|
||||
public OutputPath(string name, int itemNr)
|
||||
@@ -46,7 +48,9 @@ namespace Config.Entities
|
||||
{
|
||||
Name = name;
|
||||
ItemNr = itemNr;
|
||||
}
|
||||
RegulMinStep = 0;
|
||||
|
||||
}
|
||||
|
||||
public virtual OutputPath Clone(string name, int itemNr)
|
||||
{
|
||||
@@ -58,7 +62,8 @@ namespace Config.Entities
|
||||
result.FlowUnit = FlowUnit;
|
||||
result.Selector = Selector;
|
||||
result.RegValve = RegValve;
|
||||
result.FlowMeter = FlowMeter;
|
||||
result.RegulMinStep = RegulMinStep;
|
||||
result.FlowMeter = FlowMeter;
|
||||
result.PidCoef = PidCoef;
|
||||
result.StartValve = StartValve;
|
||||
result.Diverter = Diverter;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2019-2022 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2019-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@@ -16,7 +16,9 @@ namespace Config.Entities
|
||||
public virtual int Id { get; protected set; }
|
||||
public virtual int ItemNr { get; set; }
|
||||
public virtual string Name { get; set; }
|
||||
public virtual int Part { get; set; } /// Part=0 ... test of all WM-s /// Part>0 ... part of a set of tests with the same Name: subset of WM-s is given by MetersPath
|
||||
public virtual int Part { get; set; } /// Part=0 ... test of all WM-s
|
||||
/// Part>0 ... part of a set of tests with the same Name: subset of WM-s is given by MetersPath
|
||||
public virtual FlowId FlowId { get; set; }
|
||||
public virtual double CoefQfrom { get; set; } /// Water flow low limit in [m3/h]
|
||||
public virtual double CoefQto { get; set; } /// Water flow high limit in [m3/h]
|
||||
public virtual double ErrLimLo { get; set; } /// in [%] usually < 0, in case of heat meters: 1=class1, 2=class2, 3=class3
|
||||
@@ -186,6 +188,7 @@ namespace Config.Entities
|
||||
|
||||
output.WriteLine(Name);
|
||||
output.WriteLine(Part);
|
||||
output.WriteLine(((ushort)FlowId).ToString(ci));
|
||||
output.WriteLine(CoefQfrom.ToString(ci));
|
||||
output.WriteLine(CoefQto.ToString(ci));
|
||||
output.WriteLine(ErrLimLo.ToString(ci));
|
||||
@@ -245,6 +248,7 @@ namespace Config.Entities
|
||||
|
||||
tst.Name = firstLine;
|
||||
tst.Part = int.Parse(input.ReadLine());
|
||||
tst.FlowId = (FlowId)ushort.Parse(input.ReadLine(), ci);
|
||||
tst.CoefQfrom = double.Parse(input.ReadLine(), ci);
|
||||
tst.CoefQto = double.Parse(input.ReadLine(), ci);
|
||||
tst.ErrLimLo = double.Parse(input.ReadLine(), ci);
|
||||
@@ -306,6 +310,7 @@ namespace Config.Entities
|
||||
|
||||
if (Name != firstLine) { diff.AppendFormat(fmt, "Name", Name, firstLine); };
|
||||
ln = inp.ReadLine(); if (ln != Part.ToString()) { diff.AppendFormat(fmt, "Part", Part.ToString(), ln); };
|
||||
ln = inp.ReadLine(); if (ln != ((ushort)FlowId).ToString()){diff.AppendFormat(fmt, "FlowId", ((ushort)FlowId).ToString(), ln); };
|
||||
ln = inp.ReadLine(); if (ln != CoefQfrom.ToString(ci)) { diff.AppendFormat(fmt, "CoefQfrom", CoefQfrom.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != CoefQto.ToString(ci)) { diff.AppendFormat(fmt, "CoefQto", CoefQto.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != ErrLimLo.ToString(ci)) { diff.AppendFormat(fmt, "ErrLimLo", ErrLimLo.ToString(ci), ln); };
|
||||
|
||||
+38
-17
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2013-2021 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2013-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -19,14 +19,15 @@ namespace Config.Entities
|
||||
public virtual string Name { get; set; }
|
||||
public virtual int Part { get; set; } /// Part=0 ... test of all WM-s
|
||||
/// Part>0 ... part of a set of tests with the same Name: subset of WM-s is given by MetersPath
|
||||
public virtual TestProfile Profile { get; set; } /// UserDefined, Protected, UserDefinedHeatMeter, ProtectedHeatMeter
|
||||
public virtual TestProfile Profile { get; set; } /// UserDefined, UserDefinedCompound, UserDefinedHeatMeter, Protected, ProtectedCompound, ProtectedHeatMeter
|
||||
public virtual sbyte Publish { get; set; } /// 0=no, 1=in all protocols, 2=on screen, 3=internal
|
||||
public virtual bool DoEvaluate { get; set; }
|
||||
public virtual double Qtg { get; set; } /// Target water flow [m3/h]
|
||||
public virtual double Qfrom { get; set; } /// Water flow low limit in [m3/h]
|
||||
public virtual double Qto { get; set; } /// Water flow high limit in [m3/h]
|
||||
public virtual double Qtg { get; set; } /// When == -1 => Qtg is determined by the test method, otherwise Qtg is in [m3/h],
|
||||
public virtual double Qfrom { get; set; } /// Water flow low limit in [m3/h] or in [%] of Qtg
|
||||
public virtual double Qto { get; set; } /// Water flow high limit in [m3/h] or in [%] of Qtg
|
||||
public virtual bool IsFromToInPct { get; set; } /// true: Qfrom and Qto are in % of Qtg, false: Qfrom and Qto are in [m3/h]
|
||||
public virtual double Volume { get; set; } /// Test volume (target) in [l]
|
||||
public virtual double TestTime { get; set; } /// Test time (estimate) in [s]
|
||||
public virtual double TestTime { get; set; } /// Test time (estimate) in [s]
|
||||
public virtual string Method { get; set; }
|
||||
public virtual float ErrLimLo { get; set; } /// in [%] usually < 0, in case of heat meters: 1=class1, 2=class2, 3=class3
|
||||
public virtual float ErrLimHi { get; set; } /// in [%] usually > 0, in case of heat meters: -Qn in m3/h
|
||||
@@ -57,6 +58,11 @@ namespace Config.Entities
|
||||
public virtual string TransBetween { get; set; }
|
||||
public virtual string TransAfter { get; set; }
|
||||
|
||||
public virtual IList<ComponentTest> MoreParams { get; set; }
|
||||
|
||||
public virtual Procedure Procedure { get; set; }
|
||||
|
||||
/// Additional stuff not mapped into the database
|
||||
public virtual bool IsOuterLoopStart { get; set; } /// Not mapped to database
|
||||
public virtual bool IsOuterLoopEnd { get; set; } /// Not mapped to database
|
||||
|
||||
@@ -72,12 +78,17 @@ namespace Config.Entities
|
||||
public virtual Unit TempUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
|
||||
public virtual Unit PressUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
|
||||
public virtual Unit LengthUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
|
||||
public virtual Unit ElectricUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
|
||||
|
||||
public virtual IList<ComponentTest> MoreParams { get; set; }
|
||||
|
||||
public virtual Procedure Procedure { get; set; }
|
||||
|
||||
/// ------------- Additional stuff not mapped into the database -------------
|
||||
/// Wrappers
|
||||
public virtual double QfromM3ph()
|
||||
{
|
||||
return !IsFromToInPct ? Qfrom : Qtg >= 0 ? Qtg * Qfrom / 100 : 0;
|
||||
}
|
||||
public virtual double QtoM3ph()
|
||||
{
|
||||
return !IsFromToInPct ? Qto : Qtg >= 0 ? Qtg * Qto / 100 : 0;
|
||||
}
|
||||
|
||||
public Test()
|
||||
{
|
||||
@@ -90,6 +101,7 @@ namespace Config.Entities
|
||||
Profile = TestProfile.UserDefined;
|
||||
Publish = (sbyte)Common.Publish.Always;
|
||||
DoEvaluate = true;
|
||||
IsFromToInPct = false;
|
||||
ErrLimLo = -2.0f; /// [%] lower error limit
|
||||
ErrLimHi = 2.0f; /// [%] upper error limit
|
||||
Uncertainty = 0;
|
||||
@@ -159,7 +171,8 @@ namespace Config.Entities
|
||||
result.Qtg = Qtg;
|
||||
result.Qfrom = Qfrom;
|
||||
result.Qto = Qto;
|
||||
result.Volume = Volume;
|
||||
result.IsFromToInPct = IsFromToInPct;
|
||||
result.Volume = Volume;
|
||||
result.TestTime = TestTime;
|
||||
result.Method = Method;
|
||||
result.ErrLimLo = ErrLimLo;
|
||||
@@ -192,10 +205,12 @@ namespace Config.Entities
|
||||
result.TransAfter = TransAfter;
|
||||
result.VolumeUnit = VolumeUnit;
|
||||
result.FlowUnit = FlowUnit;
|
||||
result.FlowUnit = FlowUnit;
|
||||
result.MassUnit = MassUnit;
|
||||
result.TempUnit = TempUnit;
|
||||
result.PressUnit = PressUnit;
|
||||
result.LengthUnit = LengthUnit;
|
||||
result.ElectricUnit = ElectricUnit;
|
||||
foreach (var prms in MoreParams) { result.MoreParams.Add(prms.Clone()); }
|
||||
|
||||
return result;
|
||||
@@ -212,6 +227,7 @@ namespace Config.Entities
|
||||
output.WriteLine(Qtg.ToString(ci));
|
||||
output.WriteLine(Qfrom.ToString(ci));
|
||||
output.WriteLine(Qto.ToString(ci));
|
||||
output.WriteLine(IsFromToInPct.ToString());
|
||||
output.WriteLine(Volume.ToString(ci));
|
||||
output.WriteLine(TestTime.ToString(ci));
|
||||
output.WriteLine(Method);
|
||||
@@ -264,6 +280,7 @@ namespace Config.Entities
|
||||
tst.Qtg = double.Parse(input.ReadLine(), ci);
|
||||
tst.Qfrom = double.Parse(input.ReadLine(), ci);
|
||||
tst.Qto = double.Parse(input.ReadLine(), ci);
|
||||
tst.IsFromToInPct = bool.Parse(input.ReadLine());
|
||||
tst.Volume = double.Parse(input.ReadLine(), ci);
|
||||
tst.TestTime = double.Parse(input.ReadLine(), ci);
|
||||
tst.Method = input.ReadLine();
|
||||
@@ -293,8 +310,11 @@ namespace Config.Entities
|
||||
tst.TransBetween = input.ReadLine();
|
||||
string line = input.ReadLine();
|
||||
tst.Profile = line.Equals(TestProfile.ProtectedHeatMeter.ToString()) ? TestProfile.ProtectedHeatMeter
|
||||
: line.Equals(TestProfile.UserDefinedHeatMeter.ToString()) ? TestProfile.UserDefinedHeatMeter
|
||||
: line.Equals(TestProfile.Protected.ToString()) ? TestProfile.Protected : TestProfile.UserDefined; /// UserDefined is the default
|
||||
: line.Equals(TestProfile.ProtectedCompound.ToString()) ? TestProfile.ProtectedCompound
|
||||
: line.Equals(TestProfile.Protected.ToString()) ? TestProfile.Protected
|
||||
: line.Equals(TestProfile.UserDefinedHeatMeter.ToString()) ? TestProfile.UserDefinedHeatMeter
|
||||
: line.Equals(TestProfile.UserDefinedCompound.ToString()) ? TestProfile.UserDefinedCompound
|
||||
: TestProfile.UserDefined; /// UserDefined is the default
|
||||
tst.TransAfter = input.ReadLine();
|
||||
|
||||
while (true)
|
||||
@@ -327,6 +347,7 @@ namespace Config.Entities
|
||||
ln = inp.ReadLine(); if (ln != Qtg.ToString(ci)) { diff.AppendFormat(fmt, "Qtg", Qtg.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Qfrom.ToString(ci)) { diff.AppendFormat(fmt, "Qfrom", Qfrom.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Qto.ToString(ci)) { diff.AppendFormat(fmt, "Qto", Qto.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != IsFromToInPct.ToString()) { diff.AppendFormat(fmt, "IsFromToInPct", IsFromToInPct.ToString(), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Volume.ToString(ci)) { diff.AppendFormat(fmt, "Volume", Volume.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != TestTime.ToString(ci)) { diff.AppendFormat(fmt, "TstTime", TestTime.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Method) { diff.AppendFormat(fmt, "Method", Method, ln); };
|
||||
@@ -334,11 +355,11 @@ namespace Config.Entities
|
||||
ln = inp.ReadLine(); if (ln != ErrLimHi.ToString(ci)) { diff.AppendFormat(fmt, "ErrLimHi", ErrLimHi.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Uncertainty.ToString(ci)) { diff.AppendFormat(fmt, "Uncertainty", Uncertainty.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != Repeats.ToString(ci)) { diff.AppendFormat(fmt, "Repeats", Repeats.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != DoDraining.ToString(ci)) { diff.AppendFormat(fmt, "DoDraining", DoDraining.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != DoDrainingAfter.ToString(ci)) { diff.AppendFormat(fmt, "DoDrainingAfter", DoDrainingAfter.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != DoDraining.ToString()) { diff.AppendFormat(fmt, "DoDraining", DoDraining.ToString(), ln); };
|
||||
ln = inp.ReadLine(); if (ln != DoDrainingAfter.ToString()) { diff.AppendFormat(fmt, "DoDrainingAfter", DoDrainingAfter.ToString(), ln); };
|
||||
ln = inp.ReadLine(); if (ln != TempLimLo.ToString(ci)) { diff.AppendFormat(fmt, "TempLimLo", TempLimLo.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != TempLimHi.ToString(ci)) { diff.AppendFormat(fmt, "TempLimHi", TempLimHi.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != TempControl) { diff.AppendFormat(fmt, "TempController", TempControl, ln); };
|
||||
ln = inp.ReadLine(); if (ln != TempControl) { diff.AppendFormat(fmt, "TempController", TempControl, ln); };
|
||||
ln = inp.ReadLine(); if (ln != PumpPower.ToString(ci)) { diff.AppendFormat(fmt, "PumpPower", PumpPower.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != MassRepeats.ToString(ci)) { diff.AppendFormat(fmt, "MassRepeats", MassRepeats.ToString(ci), ln); };
|
||||
ln = inp.ReadLine(); if (ln != MassSpread.ToString(ci)) { diff.AppendFormat(fmt, "MassSpread", MassSpread.ToString(ci), ln); };
|
||||
|
||||
@@ -128,10 +128,11 @@ namespace Config
|
||||
case GID.Administrators:
|
||||
#if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL
|
||||
case GID.TraceabilityManagement:
|
||||
#elif LANG_PL
|
||||
//#elif LANG_PL
|
||||
#endif
|
||||
case GID.MetrologicalAuthority:
|
||||
case GID.WaterMeterAuthority:
|
||||
#endif
|
||||
|
||||
admin.AddGroup(group);
|
||||
session.SaveOrUpdate(group); /// Save this group
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,9 @@ namespace Config.Mappings
|
||||
Map(x => x.PressMtrUp);
|
||||
Map(x => x.PressMtrDown);
|
||||
Map(x => x.PressMtrDelta);
|
||||
//Map(x => x.ElectricMtrUp);
|
||||
//Map(x => x.ElectricMtrDown);
|
||||
//Map(x => x.ElectricMtrDelta);
|
||||
Map(x => x.StopBFValve);
|
||||
Map(x => x.ValvesOpen)
|
||||
.CustomType("StringClob")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2013-2019 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2013-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using FluentNHibernate.Mapping;
|
||||
using Config.Entities;
|
||||
@@ -19,7 +19,7 @@ namespace Config.Mappings
|
||||
Map(x => x.LogLevel);
|
||||
Map(x => x.Parameters)
|
||||
.CustomType("StringClob")
|
||||
.CustomSqlType("varchar(8000)");
|
||||
.CustomSqlType("varchar(12000)");
|
||||
HasMany(x => x.Corrections)
|
||||
.OrderBy("Measurement")
|
||||
.Cascade.All();
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Config.Mappings
|
||||
Map(x => x.Qto);
|
||||
Map(x => x.Selector);
|
||||
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.StartValve);
|
||||
Map(x => x.Diverter);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2019 Sensus Slovensko a.s.
|
||||
/// Copyright (c) 2019-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using FluentNHibernate.Mapping;
|
||||
using Config.Entities;
|
||||
@@ -14,6 +14,7 @@ namespace Config.Mappings
|
||||
Map(x => x.ItemNr);
|
||||
Map(x => x.Name);
|
||||
Map(x => x.Part);
|
||||
Map(x => x.FlowId);
|
||||
Map(x => x.CoefQfrom);
|
||||
Map(x => x.CoefQto);
|
||||
Map(x => x.ErrLimLo);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///
|
||||
/// Copyright (c) 2013-2017 Sensus Metering Systems
|
||||
/// Copyright (c) 2013-2023 Sensus Slovensko a.s.
|
||||
///
|
||||
using FluentNHibernate.Mapping;
|
||||
using Config.Entities;
|
||||
@@ -20,7 +20,8 @@ namespace Config.Mappings
|
||||
Map(x => x.Qtg);
|
||||
Map(x => x.Qfrom);
|
||||
Map(x => x.Qto);
|
||||
Map(x => x.Volume);
|
||||
Map(x => x.IsFromToInPct);
|
||||
Map(x => x.Volume);
|
||||
Map(x => x.TestTime);
|
||||
Map(x => x.Repeats);
|
||||
Map(x => x.DoDraining);
|
||||
|
||||
@@ -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]))
|
||||
{
|
||||
Console.WriteLine("Usage: Decode <file>");
|
||||
Console.WriteLine(@"Usage: Decode <file>");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,9 +40,8 @@
|
||||
<StartupObject>DeviceTest.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="log4net, Version=1.2.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
|
||||
<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>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
+27
-2
@@ -1,3 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.15" targetFramework="net472" />
|
||||
</packages>
|
||||
+1
-1
@@ -18,7 +18,7 @@ namespace Encrypt
|
||||
{
|
||||
if ((args.Length < 1) || !File.Exists(args[0]))
|
||||
{
|
||||
Console.WriteLine("Usage: Encode <file>");
|
||||
Console.WriteLine(@"Usage: Encode <file>");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -49,7 +49,7 @@
|
||||
<HintPath>..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net">
|
||||
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
|
||||
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MySql.Data">
|
||||
<HintPath>..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll</HintPath>
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
@@ -65,7 +64,8 @@ namespace GenericTest
|
||||
const int ActivityMsgsCount = 7;
|
||||
ActivityEventArgs[] activityEvents; /// Activity messages displayed in the main window
|
||||
|
||||
|
||||
Common.Forms.ModelessForm modelessForm;
|
||||
|
||||
NHibernate.ISession dbSession; /// MySQL database session
|
||||
static OracleConnection oracleConn; /// Oracle database connection (server is in Stara Tura)
|
||||
#if RF_TEST_400_900
|
||||
@@ -115,7 +115,8 @@ namespace GenericTest
|
||||
UpdateTitle();
|
||||
|
||||
/// Open a modeless form that is closed after initialization and loading the main window
|
||||
new Thread(() => Application.Run(new Common.Forms.ModelessForm("Loading workflows from a database"))).Start();
|
||||
modelessForm = new Common.Forms.ModelessForm("Loading workflows from a database");
|
||||
new Thread(() => Application.Run(modelessForm)).Start();
|
||||
|
||||
|
||||
activityEvents = new ActivityEventArgs[ActivityMsgsCount];
|
||||
@@ -159,7 +160,7 @@ namespace GenericTest
|
||||
StarSensorReading();
|
||||
}
|
||||
|
||||
Common.Forms.ModelessForm.CloseForm(); /// Close the modeless information form
|
||||
if (modelessForm != null) modelessForm.CloseForm(); /// Close the modeless information form
|
||||
}
|
||||
|
||||
private void startButton_Click(object sender, EventArgs e)
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace GenericTest
|
||||
{
|
||||
if (exc is QuitAppException)
|
||||
{
|
||||
Common.Forms.ModelessForm.CloseForm();
|
||||
//Common.Forms.ModelessForm.CloseForm();
|
||||
log.FatalFormat(Strings.Program_was_not_started_0, exc.Message);
|
||||
MessageBox.Show(exc.Message,
|
||||
Strings.Warning,
|
||||
|
||||
+27
-2
@@ -1,3 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user