From e83dca6e861b622b54d3392ca0d3f1f1eb69f7c9 Mon Sep 17 00:00:00 2001 From: ningshuxia <ningshuxia0927@outlook.com> Date: 星期二, 18 六月 2024 09:38:11 +0800 Subject: [PATCH] 新增模型验证 --- IStation.Application/01-ch/01-analysis/Schedule_Controller.cs | 2 IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelRecord.cs | 34 IStation.Epanet/dll/EpanetMethods.cs | 1185 +++ Test/IStation.Test/DayValueHelper.cs | 70 Test/IStation.Test/ModelVerification.cs | 26 IStation.Epanet/network/structures/QualSource.cs | 27 IStation.Epanet/network/structures/Pump.cs | 155 IStation.Service/05-service/03-hydraulic/HydraulicModelScada.cs | 54 IStation.Epanet/network/structures/Demand.cs | 27 Test/IStation.Test/Program _mode.cs | 350 + IStation.Epanet/schedule/input/InputNode.cs | 37 IStation.Server.Validation/GlobalUsings.cs | 3 Test/IStation.Test/Program_bak.cs | 231 IStation.Epanet/schedule/ScheduleHelper.cs | 505 + IStation.Service/02-model/03-hydraulic/HydraulicModelValidation.cs | 59 IStation.Service/03-settings/helper/ParasHelper.cs | 8 IStation.Epanet/schedule/input/Input.cs | 20 IStation.Server.Validation/IStation.Server.Validation.csproj | 23 IStation.Service/05-service/03-hydraulic/HydraulicModelValidation_Instance.cs | 63 IStation.Service/07-algorithm/02-schedule/ScheduleHelper.cs | 6 IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml | 12 IStation.Service/IStation.Service.csproj | 6 Test/IStation.Test/EpanetMethods.cs | 1184 +++ IStation.Service/08-hydraulic/01-/DayValueHelper.cs | 45 IStation.Epanet/network/structures/Element.cs | 62 Test/IStation.Test/Program - 复制.cs | 175 IStation.Epanet/network/Network.cs | 378 + IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user | 10 IStation.Server.Validation/00-core/Log.cs | 55 IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelRecord.cs | 75 IStation.Application/01-ch/03-schedule-config/ScheduleConfig_Controller.cs | 2 Test/IStation.Test/DayValue.cs | 11 IStation.Epanet/network/ElementCollection.cs | 77 IStation.Epanet/schedule/time-value/DayValueHelper.cs | 47 IStation.Service/05-service/03-hydraulic/HydraulicModelRecord.cs | 54 IStation.Service/03-settings/paras/Paras_Task.cs | 14 IStation.Service/01-entity/03-hydraulic/HydraulicModelValidation.cs | 50 IStation.Service/03-settings/paras/Paras_LocalFile.cs | 13 Test/IStation.Test/IStation.Test.csproj | 10 IStation.Epanet/network/structures/Rule.cs | 18 IStation.Epanet/network/structures/Node.cs | 67 IStation.Service/00-core/DbInitHelper.cs | 4 IStation.Service/08-hydraulic/ModeVerifyHelper.cs | 343 + IStation.Epanet/network/structures/Valve.cs | 66 IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelScada.cs | 75 IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj.user | 6 IStation.TopShelf.Validation/Program.cs | 17 IStation.Epanet/network/io/input/NetParser.cs | 2167 ++++++ IStation.Service/08-hydraulic/01-/DayValue.cs | 35 IStation.Service/05-service/01-analysis/AnalysisConclusion.cs | 15 IStation.Service/05-service/03-hydraulic/HydraulicModelRecord_Instance.cs | 63 IStation.Epanet/util/TraceExtensions.cs | 167 IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelScada.cs | 34 IStation.Service/08-hydraulic/01-/TimeValue.cs | 38 IStation.Service/02-model/00-basic/03-verification/HydraulicModelValidationConfig.cs | 29 IStation.Epanet/network/structures/NUConvert.cs | 167 IStation.Epanet/schedule/out/OutNode.cs | 125 IStation.Epanet/schedule/time-value/DayValue.cs | 11 IStation.Epanet/network/structures/Reservoir.cs | 32 IStation.Service/05-service/00-basic/HydraulicModelValidationConfig.cs | 43 IStation.Epanet/enum/Keywords.cs | 347 + IStation.Epanet/schedule/input/InputLink.cs | 53 IStation.Epanet/network/io/output/InpComposer.cs | 1055 +++ IStation.Epanet/network/io/input/XMLParser.cs | 827 ++ IStation.Service/02-model/00-basic/00-station/Station.cs | 12 IStation.Epanet/network/io/input/InpParser.cs | 1996 ++++++ IStation.Service/05-service/03-hydraulic/HydraulicModelValidation.cs | 219 IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelValidation.cs | 17 IStation.Epanet/exception/ENException.cs | 75 IStation.Epanet/network/structures/Pattern.cs | 38 IStation.Epanet/network/io/input/InputParser.cs | 627 + IStation.Server.Validation/00-core/ConfigHelper.cs | 19 IStation.Server.Validation/01-validation/HydraulicModelValidationHelperJob.cs | 205 IStation.Service/paras_schedule_settings.json | 11 IStation.Epanet/network/structures/Control.cs | 63 IStation.Epanet/network/Constants.cs | 103 IStation.Service/02-model/03-hydraulic/HydraulicModelRecord.cs | 100 IStation.Epanet/network/helpers/NetworkHelper.cs | 101 IStation.Epanet/network/structures/Field.cs | 41 IStation.Service/08-hydraulic/01-/Pattern.cs | 38 IStation.Epanet/enum/Error.cs | 23 IStation.Service/03-settings/paras/Paras_DataBase_SQLite.cs | 5 IStation.Server.Validation/00-core/IJobHelper.cs | 18 IStation.Service/03-settings/paras/Paras_ZyDocking.cs | 7 IStation.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user | 2 IStation.Epanet/network/structures/Label.cs | 34 IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user | 10 IStation.Epanet/network/structures/Pipe.cs | 52 IStation.Epanet/epanet2.2.dll | 0 IStation.Service/02-model/03-hydraulic/HydraulicModelScada.cs | 60 IStation.Epanet/enum/Enumerations.cs | 765 ++ IStation.Epanet/network/structures/Curve.cs | 85 IStation.Epanet/IStation.Epanet.csproj | 31 IStation.Epanet/network/io/output/OutputComposer.cs | 34 IStation.Schedule.sln | 62 IStation.Service/00-core/ConfigHelper.cs | 70 IStation.Epanet/enum/KeywordAttribute.cs | 12 IStation.Service/05-service/03-hydraulic/HydraulicModelScada_Instance.cs | 63 IStation.Epanet/network/io/input/NullParser.cs | 28 IStation.Server.Validation/01-validation/HydraulicModelValidationJobHelper.cs | 63 IStation.Epanet/util/Utilities.cs | 234 IStation.Epanet/network/structures/Link.cs | 128 IStation.Service/01-entity/03-hydraulic/HydraulicModelRecord.cs | 84 IStation.Service/01-entity/03-hydraulic/HydraulicModelScada.cs | 60 Test/IStation.Test/HydraulicModelValidationHelperJob.cs | 179 IStation.Server.Validation/00-core/JobHelper.cs | 30 IStation.TopShelf.Validation/Service.cs | 38 IStation.Epanet/schedule/time-value/TimeValue.cs | 27 IStation.Epanet/network/structures/Tank.cs | 148 Test/IStation.Test/TimeValue.cs | 27 IStation.Epanet/enum/ErrorCode.cs | 248 IStation.Epanet/network/structures/Junction.cs | 40 IStation.Service/03-settings/paras/Paras.cs | 5 IStation.Epanet/network/structures/EnPoint.cs | 66 IStation.Epanet/enum/ErrorAttribute.cs | 12 IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelValidation.cs | 11 IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml | 12 IStation.Epanet/network/FieldsMap.cs | 241 IStation.Epanet/schedule/out/Output.cs | 20 IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelScada.cs | 76 IStation.Epanet/schedule/StationScheduleHelper.cs | 96 IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelValidation.cs | 42 IStation.Application/01-ch/02-schedule/Schedule_Controller.cs | 19 IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelRecord.cs | 76 IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj | 21 IStation.Service/08-hydraulic/00-core/Log.cs | 45 IStation.Server.Validation/Program.cs | 17 IStation.Service/00-core/eValueType.cs | 26 IStation.Epanet/enum/EnumsTxt.cs | 456 + IStation.Service/00-core/eModelStatus.cs | 11 Test/IStation.Test/Program.cs | 254 IStation.Epanet/exception/InputException.cs | 39 IStation.Epanet/schedule/out/OutLink.cs | 86 133 files changed, 18,673 insertions(+), 259 deletions(-) diff --git a/IStation.Application/01-ch/01-analysis/Schedule_Controller.cs b/IStation.Application/01-ch/01-analysis/Schedule_Controller.cs index 8d1e63e..710fac9 100644 --- a/IStation.Application/01-ch/01-analysis/Schedule_Controller.cs +++ b/IStation.Application/01-ch/01-analysis/Schedule_Controller.cs @@ -19,7 +19,7 @@ public bool InitDataBase() { - IStation.Algorithm.DbInitHelper.Init(); + IStation.Service.DbInitHelper.Init(); return true; } diff --git a/IStation.Application/01-ch/02-schedule/Schedule_Controller.cs b/IStation.Application/01-ch/02-schedule/Schedule_Controller.cs index b0f4929..f320920 100644 --- a/IStation.Application/01-ch/02-schedule/Schedule_Controller.cs +++ b/IStation.Application/01-ch/02-schedule/Schedule_Controller.cs @@ -81,7 +81,7 @@ var scada = new Model.ScheduleScada(); scada.RequestID = request_id; scada.Tag = key; - if (DateTime.TryParse(vals, out DateTime t)) + if (DateTime.TryParse(time, out DateTime t)) scada.Time = t; if (double.TryParse(vals, out double v)) scada.Value = v; @@ -147,12 +147,13 @@ water_level2 = water_level_valid_value_list2.Average(x => x); } } - + log_title = "褰撳墠姘翠綅"; var target_head1 = Curve.PumpCalculateHelper.Mpa2M(target_pressure1) - water_level1; var target_head2 = Curve.PumpCalculateHelper.Mpa2M(target_pressure2) - water_level2; - var helper = new Algorithm.ScheduleHelper(); + Log.Info(request_id, log_title, $"water_level1:{water_level1},target_head1:{target_head1},water_level2:{water_level2},target_head2:{target_head2}"); + var helper = new Algorithm.ScheduleHelper(); helper.Initial(current_open_pump_flags1,schedule_config1); var optimal_combine1 = helper.Calc(station_info.S1, station_info.S1FlagsPart1, station_info.S1FlagsPart2, target_flow1, target_head1); @@ -285,7 +286,7 @@ } else { - msg += $"1杈撴按璋冨害璁$畻澶辫触锛屾棤娉曟弧瓒崇洰鏍囨祦閲�:{target_flow1},鐩爣鍘嬪姏:{target_pressure1}!\r\n"; + msg += $"\r\n1杈撴按璋冨害璁$畻澶辫触锛屾棤娉曟弧瓒崇洰鏍囨祦閲�:{target_flow1},鐩爣鍘嬪姏:{target_pressure1}!"; } if (optimal_combine2 != null) @@ -379,7 +380,7 @@ } else { - msg += $"2杈撴按璋冨害璁$畻澶辫触锛屾棤娉曟弧瓒崇洰鏍囨祦閲�:{target_flow2},鐩爣鍘嬪姏:{target_pressure2}!\r\n"; + msg += $"\r\n2杈撴按璋冨害璁$畻澶辫触锛屾棤娉曟弧瓒崇洰鏍囨祦閲�:{target_flow2},鐩爣鍘嬪姏:{target_pressure2}!"; } output.flag = 1; } @@ -406,10 +407,10 @@ schedule_rule1.RequestID = request_id; schedule_rule1.Station = eDockingStation.Ch1s; schedule_rule1.Config=JsonHelper.Object2Json(schedule_config1); - bol = _service_schedule_rule.Insert(schedule_rule1)<1; + bol = _service_schedule_rule.Insert(schedule_rule1)>0; if (!bol) { - Log.Info(request_id, log_title, "schedule_rule1 鎻掑叆寮傚父"); + Log.Info(request_id, log_title, "schedule_config1 鎻掑叆寮傚父"); Log.Debug(request_id, log_title, JsonHelper.Object2Json(schedule_rule1)); } } @@ -420,10 +421,10 @@ schedule_rule2.RequestID = request_id; schedule_rule2.Station = eDockingStation.Ch2s; schedule_rule2.Config = JsonHelper.Object2Json(schedule_config2); - bol = _service_schedule_rule.Insert(schedule_rule2) < 1; + bol = _service_schedule_rule.Insert(schedule_rule2) > 0; if (!bol) { - Log.Info(request_id, log_title, "schedule_rule2 鎻掑叆寮傚父"); + Log.Info(request_id, log_title, "schedule_config2 鎻掑叆寮傚父"); Log.Debug(request_id, log_title, JsonHelper.Object2Json(schedule_rule2)); } } diff --git a/IStation.Application/01-ch/03-schedule-config/ScheduleConfig_Controller.cs b/IStation.Application/01-ch/03-schedule-config/ScheduleConfig_Controller.cs index f674d84..7ac032e 100644 --- a/IStation.Application/01-ch/03-schedule-config/ScheduleConfig_Controller.cs +++ b/IStation.Application/01-ch/03-schedule-config/ScheduleConfig_Controller.cs @@ -9,7 +9,7 @@ public class ScheduleConfig_Controller : IDynamicApiController, ITransient { private readonly Service.ScheduleConfig _service_schedule_config = new(); - + /// <summary> /// 鑾峰彇闄堣璋冨害閰嶇疆 /// </summary> diff --git a/IStation.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user b/IStation.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user index 9ead787..6d75690 100644 --- a/IStation.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/IStation.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -5,7 +5,7 @@ <Project> <PropertyGroup> <_PublishTargetUrl>D:\WorkCode\IStation\Service.Ch.V1.1\IStation.Entry\bin\Release\net6.0\publish\</_PublishTargetUrl> - <History>True|2024-05-30T06:57:39.3412922Z||;True|2024-05-30T11:59:44.7664433+08:00||;True|2024-05-30T11:53:59.9799057+08:00||;True|2024-05-30T11:21:35.4761141+08:00||;True|2024-05-30T11:16:55.6157765+08:00||;True|2024-05-30T11:07:45.8336241+08:00||;True|2024-05-30T10:59:58.8877118+08:00||;True|2024-05-30T10:36:09.0290592+08:00||;True|2024-05-30T10:35:17.5448779+08:00||;True|2024-05-27T17:06:29.6833574+08:00||;False|2024-05-27T17:06:07.6398727+08:00||;False|2024-05-27T17:05:20.1523960+08:00||;False|2024-05-27T17:05:04.1140399+08:00||;False|2024-05-27T17:04:44.5872009+08:00||;True|2024-05-11T15:21:00.4779174+08:00||;True|2024-04-23T10:14:25.7074885+08:00||;True|2024-04-22T16:37:12.5852855+08:00||;True|2024-04-22T16:34:52.0894541+08:00||;True|2024-04-19T10:53:01.1630050+08:00||;True|2024-04-19T10:52:02.7021209+08:00||;True|2024-03-30T11:56:28.7775554+08:00||;True|2024-03-13T14:48:09.8998210+08:00||;True|2024-03-13T14:47:27.2433264+08:00||;True|2023-12-25T14:14:16.4083227+08:00||;True|2023-11-16T13:48:01.0622937+08:00||;True|2023-10-18T11:32:08.8356991+08:00||;False|2023-10-18T11:29:58.5783437+08:00||;True|2023-10-16T10:41:58.8456599+08:00||;False|2023-10-16T10:30:14.3272745+08:00||;True|2023-09-11T14:56:16.8635396+08:00||;True|2023-08-31T14:48:56.6882268+08:00||;True|2023-08-31T14:42:46.1818009+08:00||;True|2023-08-31T14:35:48.5604752+08:00||;True|2023-08-08T11:38:41.3309044+08:00||;True|2023-07-28T11:00:59.6697086+08:00||;True|2023-07-18T14:39:48.3496234+08:00||;True|2023-07-17T17:38:55.8911210+08:00||;True|2023-07-17T11:57:31.1164907+08:00||;True|2023-07-17T10:00:17.1961455+08:00||;True|2023-07-13T16:04:03.6854523+08:00||;True|2023-07-11T09:58:12.2295644+08:00||;True|2023-06-26T11:38:29.6648988+08:00||;True|2023-06-21T16:17:40.6226554+08:00||;True|2023-06-21T13:30:18.8163322+08:00||;True|2023-06-21T13:19:43.9290154+08:00||;True|2023-06-21T11:35:28.3376504+08:00||;</History> + <History>True|2024-06-17T07:22:17.6153518Z||;False|2024-06-17T15:22:01.3404718+08:00||;True|2024-06-13T11:15:55.4873247+08:00||;True|2024-06-13T11:14:52.9075916+08:00||;True|2024-06-13T10:09:13.5966875+08:00||;True|2024-06-13T09:58:26.1880685+08:00||;True|2024-06-13T09:49:29.6928004+08:00||;True|2024-06-13T09:46:16.2707476+08:00||;True|2024-05-30T14:57:39.3412922+08:00||;True|2024-05-30T11:59:44.7664433+08:00||;True|2024-05-30T11:53:59.9799057+08:00||;True|2024-05-30T11:21:35.4761141+08:00||;True|2024-05-30T11:16:55.6157765+08:00||;True|2024-05-30T11:07:45.8336241+08:00||;True|2024-05-30T10:59:58.8877118+08:00||;True|2024-05-30T10:36:09.0290592+08:00||;True|2024-05-30T10:35:17.5448779+08:00||;True|2024-05-27T17:06:29.6833574+08:00||;False|2024-05-27T17:06:07.6398727+08:00||;False|2024-05-27T17:05:20.1523960+08:00||;False|2024-05-27T17:05:04.1140399+08:00||;False|2024-05-27T17:04:44.5872009+08:00||;True|2024-05-11T15:21:00.4779174+08:00||;True|2024-04-23T10:14:25.7074885+08:00||;True|2024-04-22T16:37:12.5852855+08:00||;True|2024-04-22T16:34:52.0894541+08:00||;True|2024-04-19T10:53:01.1630050+08:00||;True|2024-04-19T10:52:02.7021209+08:00||;True|2024-03-30T11:56:28.7775554+08:00||;True|2024-03-13T14:48:09.8998210+08:00||;True|2024-03-13T14:47:27.2433264+08:00||;True|2023-12-25T14:14:16.4083227+08:00||;True|2023-11-16T13:48:01.0622937+08:00||;True|2023-10-18T11:32:08.8356991+08:00||;False|2023-10-18T11:29:58.5783437+08:00||;True|2023-10-16T10:41:58.8456599+08:00||;False|2023-10-16T10:30:14.3272745+08:00||;True|2023-09-11T14:56:16.8635396+08:00||;True|2023-08-31T14:48:56.6882268+08:00||;True|2023-08-31T14:42:46.1818009+08:00||;True|2023-08-31T14:35:48.5604752+08:00||;True|2023-08-08T11:38:41.3309044+08:00||;True|2023-07-28T11:00:59.6697086+08:00||;True|2023-07-18T14:39:48.3496234+08:00||;True|2023-07-17T17:38:55.8911210+08:00||;True|2023-07-17T11:57:31.1164907+08:00||;True|2023-07-17T10:00:17.1961455+08:00||;True|2023-07-13T16:04:03.6854523+08:00||;True|2023-07-11T09:58:12.2295644+08:00||;True|2023-06-26T11:38:29.6648988+08:00||;True|2023-06-21T16:17:40.6226554+08:00||;True|2023-06-21T13:30:18.8163322+08:00||;True|2023-06-21T13:19:43.9290154+08:00||;True|2023-06-21T11:35:28.3376504+08:00||;</History> <LastFailureDetails /> </PropertyGroup> </Project> \ No newline at end of file diff --git a/IStation.Epanet/IStation.Epanet.csproj b/IStation.Epanet/IStation.Epanet.csproj new file mode 100644 index 0000000..7d348a6 --- /dev/null +++ b/IStation.Epanet/IStation.Epanet.csproj @@ -0,0 +1,31 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net6.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>disable</Nullable> + <PlatformTarget>AnyCPU</PlatformTarget> + </PropertyGroup> + + <ItemGroup> + <Compile Remove="analysis\**" /> + <Compile Remove="Calculate\**" /> + <Compile Remove="network\**" /> + <Compile Remove="schedule\**" /> + <EmbeddedResource Remove="analysis\**" /> + <EmbeddedResource Remove="Calculate\**" /> + <EmbeddedResource Remove="network\**" /> + <EmbeddedResource Remove="schedule\**" /> + <None Remove="analysis\**" /> + <None Remove="Calculate\**" /> + <None Remove="network\**" /> + <None Remove="schedule\**" /> + </ItemGroup> + + <ItemGroup> + <None Update="epanet2.2.dll"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + </ItemGroup> + +</Project> diff --git a/IStation.Epanet/dll/EpanetMethods.cs b/IStation.Epanet/dll/EpanetMethods.cs new file mode 100644 index 0000000..a756815 --- /dev/null +++ b/IStation.Epanet/dll/EpanetMethods.cs @@ -0,0 +1,1185 @@ +锘縰sing IStation.Epanet.Enums; +using System.Runtime.InteropServices; +using System.Text; + +namespace IStation.Epanet +{ + /// <summary> + /// IStation.Epanet 绋嬪簭鍛樺伐鍏峰寘(EPANET2.2.DLL)涓殑鍑芥暟澹版槑銆� + /// 杩欎簺鏄瀯鎴� DLL 鐨勫閮ㄥ嚱鏁般�� + /// </summary> + public class EpanetMethods + { + /// <summary>鏂囦欢鍚嶇О ("epanet2.2.dll").</summary> + private const string EPANETDLL = "epanet2.2.dll"; + + /// <summary>Epanet dll 璋冪敤绾﹀畾</summary> + private const CallingConvention CONVENTION = CallingConvention.StdCall; + + /// <summary>Epanet dll 鏂规硶鐨勫瓧绗︿覆瀛楃闆�</summary> + private const CharSet CHARSET = CharSet.Ansi; + + /// <summary>Epanet dll 鐨勫瓧绗︿覆绫诲瀷</summary> + private const UnmanagedType STRING = UnmanagedType.LPStr; + + // private const int MAXFNAME = 259; + + /// <summary>Epanet dll id 瀛楃涓�(Node.Id銆丩ink.Id 绛�)鐨勬渶澶у瓧绗︽暟</summary> + public const int MAXID = 31; + + /// <summary>Epanet dll 閿欒瀛楃涓茬殑鏈�澶у瓧绗︽暟锛�*.inp 鏂囦欢涓殑琛屾暟銆�</summary> + public const int MAXMSG = 79; + + /// <summary>Delegate for <see cref="ENepanet" /> progress callback.</summary> + /// <param name="message">浠� IStation.Epanet dll 浼犻�掔殑鐘舵�佷俊鎭��</param> + [UnmanagedFunctionPointer(CONVENTION)] + public delegate void ViewProgCallback([MarshalAs(STRING)] string message); + + #region 鎵撳紑鍜屽叧闂� IStation.Epanet 绯荤粺鐨勫姛鑳� + + /// <summary>鎵ц瀹屾暣鐨凟PANETH妯℃嫙</summary> + /// <param name="f1">杈撳叆鏂囦欢鍚�</param> + /// <param name="f2">杈撳嚭鎶ュ憡鏂囦欢鍚�</param> + /// <param name="f3">鍙�変簩杩涘埗杈撳嚭鏂囦欢鍚�</param> + /// <param name="pviewprog"> + /// Pointer to a user-supplied function (<see cref="ViewProgCallback" />) which accepts + /// a character string as its argument; see note below. + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// The <paramref name="pviewprog" /> argument is a pointer to a callback + /// function that takes a character string (char *) as its only parameter. + /// The function would reside in and be used by the calling + /// program to display the progress messages that IStation.Epanet generates + /// as it carries out its computations. If this feature is not + /// needed then the argument should be <c>null</c>. + /// </para> + /// <para> + /// ENepanet is a stand-alone function and does not interact with + /// any of the other functions in the toolkit. + /// </para> + /// <para> + /// If there is no need to save IStation.Epanet's binary output file + /// then <paramref name="f3" /> can be an empty string (""). + /// </para> + /// <para> + /// The vfunc function pointer allows the calling program + /// to display a progress message generated by IStation.Epanet during its + /// computations. + /// </para> + /// <example> + /// A typical function for a console application might look as follows: + /// <code> + /// static void WriteConsole(string s) { + /// System.Console.WriteLine(s); + /// } + /// </code> + /// and somewhere in the calling program the following declarations would appear: + /// <code> + /// Pviewprog vfunc = WriteConsole; + /// ENepanet(f1, f2, f3, vfunc); + /// </code> + /// If such a function is not desired then this argument should be null. + /// </example> + /// <para> + /// <see cref="ENepanet" /> is used mainly to link the IStation.Epanet engine to third-party + /// user interfaces that build network input files and display the results of a + /// network analysis. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENepanet( + [MarshalAs(STRING)] string f1, + [MarshalAs(STRING)] string f2, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string f3, + [Optional, DefaultParameterValue(null)] ViewProgCallback pviewprog); + + /// <summary>鎵撳紑宸ュ叿绠憋紝涓轰簡鍒嗘瀽鐗瑰畾閰嶆按绯荤粺</summary> + /// <param name="f1">杈撳叆鏂囦欢鍚� </param> + /// <param name="f2">杈撳嚭鎶ュ憡鏂囦欢鍚�</param> + /// <param name="f3">鍙�変簩杩涘埗杈撳嚭鏂囦欢鍚�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// If there is no need to save IStation.Epanet's binary Output file + /// then <paramref name="f3" /> can be an empty string (""). + /// <see cref="ENopen" /> must be called before any of the other + /// toolkit functions (except <see cref="ENepanet" />) are used. + /// </remarks> + /// <seealso cref="ENclose" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true), PreserveSig] + public static extern ErrorCode ENopen( + [MarshalAs(STRING)] string f1, + [MarshalAs(STRING)] string f2, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string f3); + + /// <summary>灏嗘墍鏈夊綋鍓嶇缃戣緭鍏ユ暟鎹啓鍏ュ埌鏂囦欢锛屽埄鐢‥PANETH杈撳叆鏂囦欢鐨勬牸寮�</summary> + /// <param name="filename">鏁版嵁淇濆瓨鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// The data saved reflect any changes made by calls to + /// the <c>ENsetxxx</c> family of functions since IStation.Epanet + /// data was first loaded using <see cref="ENopen" />. + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsaveinpfile([MarshalAs(STRING)] string filename); + + /// <summary>鍏抽棴宸ュ叿绠辩郴缁�(鍖呮嫭鎵�鏈夋鍦ㄥ鐞嗙殑鏂囦欢)</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <see cref="ENclose" /> must be called when all processing has been completed, + /// even if an error condition was encountered. + /// </remarks> + /// <seealso cref="ENopen" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENclose(); + + #endregion + + #region 杩愯姘村姏鍒嗘瀽鐨勫姛鑳� + + /// <summary> + /// 鎵ц涓�娆″畬鏁寸殑姘村姏妯℃嫙锛屾墍鏈夋椂娈电殑缁撴灉鍐欏叆浜岃繘鍒舵按鍔涙枃浠� + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍒╃敤<see cref="ENsolveH" />浜х敓鐙珛瀹屾暣鐨勬按鍔涚粨鏋滐紝鎴栬�呬綔涓烘按璐ㄥ垎鏋愮殑杈撳叆銆備负浜嗗皢姘村姏缁撴灉鍐欏叆 + /// 鎶ュ憡鏂囦欢锛屽叾鍚庝篃璋冨叆鍒�<see cref="ENsaveH" />鍜�<see cref="ENreport" />銆� + /// 涓嶈浣跨敤 <see cref="ENopenH" />, <see cref="ENinitH" />, <see cref="ENrunH" />, <see cref="ENnextH" />, 鍜� <see cref="ENcloseH" /> + /// 杩炴帴鍒�<see cref="ENsolveH" />銆� + /// </remarks> + /// <example> + /// <code> + /// ENopen("net1.inp", "net1.rpt", ""); + /// ENsolveH(); + /// ENsolveQ(); + /// ENreport(); + /// ENclose(); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsolveH(); + + /// <summary> + /// 灏嗘按鍔涙ā鎷熺粨鏋滀粠浜岃繘鍒舵按鍔涙枃浠惰浆鎹㈠埌浜岃繘鍒惰緭鍑烘枃浠讹紝鍏朵腑缁撴灉浠呬粎浠ュ潎鍖�鏃堕棿闂撮殧杩涜鎶ュ憡 + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// <see cref="ENsaveH" />浠呭湪姘村姏鍒嗘瀽鍚庢墽琛岋紝骞跺湪鍧囧寑鎶ュ憡闂撮殧鐨勭粨鏋滈渶瑕佽浆鎹负EPANETH鐨勪簩杩涘埗 + /// 杈撳嚭鏂囦欢鏃朵娇鐢ㄣ�傝繖绉嶆儏鍐碉紝鍒╃敤<see cref="ENreport" />灏嗚緭鍑烘姤鍛婂啓鍏PANETH鎶ュ憡鏂囦欢銆� + /// </para> + /// <para> + /// 鍦‥PANETH杈撳叆鏂囦欢([TIMES]鑺�)鎴栬�呴�氳繃鍒╃敤<see cref="ENsettimeparam" />鍑芥暟锛屽彲浠ヨ缃姤鍛婃椂闂淬�� + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENsettimeparam" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsaveH(); + + /// <summary>鎵撳紑姘村姏鍒嗘瀽绯荤粺</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦ㄧ涓�娆℃墽琛屾按鍔涘垎鏋愪箣鍓嶈皟鐢�<see cref="ENopenH" />锛� + /// 鍒╃敤<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />绯诲垪銆� + /// 鍦ㄨ皟鐢�<see cref="ENcloseH" />锛屼互鍏抽棴姘村姏鍒嗘瀽绯荤粺涔嬪墠锛屽彲浠ヨ繘琛屽娆″垎鏋愩�� + /// </para> + /// <para> + /// 濡傛灉<see cref="ENsolveH" />鐢ㄤ簬杩愯瀹屾暣鐨勬按鍔涘垎鏋愶紝涓嶈璋冪敤璇ュ嚱鏁般�� + /// </para> + /// </remarks> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENopenH(); + + /// <summary>鍦ㄦ墽琛屾按鍔涘垎鏋愪箣鍓嶏紝鍒濆鍖栬搫姘存睜姘翠綅銆佺娈电姸鎬佸拰璁剧疆锛屼互鍙婃ā鎷熼挓琛ㄦ椂闂�</summary> + /// <param name="saveflag"> + /// 0-1鏍囧織锛岃鏄庢按鍔涚粨鏋滄槸鍚︿繚瀛樺埌姘村姏鏂囦欢 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍒╃敤<see cref="ENrunH" />鍜�<see cref="ENnextH" />.鎵ц姘村姏鍒嗘瀽涔嬪墠锛屽簲璋冪敤<see cref="ENinitH"/>銆� + /// 璋冪敤<see cref="ENinitH" />涔嬪墠锛屽繀椤诲凡缁忚皟鐢�<see cref="ENopenH" />銆� + /// 濡傛灉鏁翠釜姘村姏鍒嗘瀽璋冪敤浜�<see cref="ENsolveH" />锛屽皢涓嶅啀璋冪敤<see cref="ENinitH" />銆� + /// </para> + /// <para> + /// 濡傛灉灏嗚繘琛岄殢鍚庣殑姘磋川杩愯锛宻aveflag 璁剧疆涓�1锛屽埄鐢�<see cref="ENreport" />浜х敓鎶ュ憡锛� + /// 鎴栬�呭埄鐢�<see cref="ENsavehydfile" />淇濆瓨浜岃繘鍒舵按鍔涙枃浠躲�� + /// </para> + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENinitH(int saveflag); + + /// <summary>鎵ц绠�鍗曟椂娈垫按鍔涘垎鏋愶紝妫�绱㈠綋鍓嶆ā鎷熼挓琛ㄦ椂闂�<paramref name="t" /></summary> + /// <param name="t"> + /// 褰撳墠妯℃嫙閽熻〃鏃堕棿锛屼互绉掕 (no need to supply a value for <paramref name="t" />). + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍒╃敤<see cref="ENrunH" />缁撳悎<see cref="ENnextH" />鍦╠o ..while寰幆涓紝涓轰簡鍒嗘瀽寤舵椂妯℃嫙涓瘡涓�鏃舵鐨勬按鍔涚壒鎬с�� + /// 璇ヨ繃绋嬭嚜鍔ㄦ洿鏂版ā鎷熼挓琛ㄦ椂闂达紝鍥犳灏唗澶勭悊涓哄彧璇诲彉閲忋�� + /// </para> + /// <para> + /// 鍦ㄦ墽琛�<see cref="ENrunH" /> 鈥� <see cref="ENnextH" />寰幆浠ュ墠锛屽繀椤昏皟鐢�<see cref="ENinitH" />銆� + /// 瀵逛簬璇ュ嚱鏁颁娇鐢ㄧ殑渚嬪瓙锛屽弬瑙�<see cref="ENnextH" />銆� + /// </para> + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENrunH(out int t); + + /// <summary> + /// 纭畾寤舵椂妯℃嫙涓嬩竴娆℃按鍔涙椂闂村彂鐢熷墠鐨勬椂闂撮暱搴� + /// </summary> + /// <param name="tstep"> + /// 鏃堕棿(浠ョ璁�)锛岀洿鍒颁笅涓�娆℃按鍔涙椂闂村彂鐢燂紱鎴栬�呬负0锛屽鏋滃湪妯℃嫙鏃舵鐨勬湯绔�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁颁笌<see cref="ENrunH" />涓�璧蜂娇鐢紝鎵ц寤舵椂姘村姏鍒嗘瀽(瑙佷互涓嬩緥瀛�) + /// </para> + /// <para> + /// tstep鐨勬暟鍊硷紝搴斿鐞嗕负鍙鍙橀噺銆備綔涓轰互涓嬭緝灏忓�艰嚜鍔ㄨ绠�: + /// <list type="bullet"> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�姘村姏鏃堕棿姝ラ暱寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�鎶ュ憡鏃堕棿姝ラ暱寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�闇�姘撮噺鍙戠敓鏀瑰彉寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒版按姹犳敞婊℃垨鑰呮帓绌�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒拌鍒欐帶鍒跺仠姝�;</item> + /// </list> + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tstep; + /// + /// ENopenH(); + /// ENinitH(0); + /// + /// do { + /// ENrunH(out t); + /// /* Retrieve hydraulic results for time t */ + /// ENnextH(out tstep); + /// } while (tstep > 0); + /// + /// ENcloseH(); + /// </code> + /// </example> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENcloseH" /> + /// <seealso cref="ENsettimeparam" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENnextH(out int tstep); + + /// <summary>鍏抽棴姘村姏鍒嗘瀽绯荤粺锛岄噴鏀炬墍鏈夊垎閰嶅唴瀛�</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄥ埄鐢�<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />鎵ц鎵�鏈夋按鍔涘垎鏋愪箣鍚庯紝璋冪敤<see cref="ENcloseH" />銆� + /// 濡傛灉浣跨敤 <see cref="ENsolveH" />锛屼笉瑕佽皟鐢ㄨ鍑芥暟銆� + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENcloseH(); + + /// <summary>灏嗗綋鍓嶄簩杩涘埗姘村姏鏂囦欢鐨勫唴瀹逛繚瀛樺埌涓�涓枃浠�</summary> + /// <param name="fileName">姘村姏缁撴灉搴旇淇濆瓨鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 灏嗚鍑芥暟鐢ㄤ簬灏嗗綋鍓嶆按鍔涚粨鏋滈泦淇濆瓨鍒颁竴涓枃浠讹紝渚夸簬鍚庡鐞嗘垨鑰呬互鍚庢椂闂翠娇鐢紝閫氳繃璋冪敤<see cref="ENusehydfile" />鍑芥暟 + /// </para> + /// <para> + /// 姘村姏鏂囦欢鍖呭惈浜嗚妭鐐归渶姘撮噺鍜屾按澶达紝浠ュ強绠℃娴侀噺锛岀姸鎬佸拰璁剧疆锛屽浜庢墍鏈夋按鍔涙椂闂存闀匡紝鐢氳嚦涓棿鏁板�笺�� + /// </para> + /// <para> + /// 璋冪敤璇ュ嚱鏁颁箣鍓嶏紝姘村姏缁撴灉蹇呴』浜х敓鍜屼繚瀛橈紝閫氳繃璋冪敤<see cref="ENsolveH" /> + /// 鎴栬��<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />搴忓垪锛� + /// 鍏锋湁<see cref="ENinitH" />鐨勪繚瀛樻爣蹇楀弬鏁拌缃负1(true)銆� + /// </para> + /// </remarks> + /// <seealso cref="ENusehydfile" /> + /// <seealso cref="ENsolveH" /> + /// <seealso cref="ENinitH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsavehydfile([MarshalAs(STRING)] string fileName); + + /// <summary>灏嗘寚瀹氭枃浠剁殑鍐呭浣滀负褰撳墠浜岃繘鍒舵按鍔涙枃浠�</summary> + /// <param name="fileName">鍚簡褰撳墠绠$綉姘村姏鍒嗘瀽缁撴灉鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤璇ュ嚱鏁帮紝鎷掔粷鍘熷厛淇濆瓨鐨勬按鍔涘垎鏋愮粨鏋滈泦銆� + /// 杩欎簺缁撴灉琚鏌ワ紝涓轰簡鏌ョ湅鏄惁瀹冧滑鍖归厤涓�涓嬪弬鏁帮紝鐩稿叧浜庡綋鍓嶈鍒嗘瀽鐨勭缃戯細鑺傜偣鎬绘暟銆佹按姹犲拰姘村簱鎬绘暟銆佺娈垫�绘暟銆佹按娉垫�绘暟銆侀榾闂ㄦ�绘暟鍜屾ā鎷熷巻鏃躲�� + /// </para> + /// <para> + /// 褰撴按鍔涘垎鏋愮郴缁熶粛鏃у紑鍚椂锛屼笉鑳藉璋冪敤璇ュ嚱鏁�(鍗�<see cref="ENopenH" />宸茬粡琚皟鐢紝浣嗘槸 <see cref="ENcloseH" /> 杩樻病鏈�)銆� + /// </para> + /// </remarks> + /// <seealso cref="ENsavehydfile" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENusehydfile([MarshalAs(STRING)] string fileName); + + #endregion + + #region 杩愯姘磋川鍒嗘瀽鐨勫姛鑳� + + /// <summary> + /// 鎵ц瀹屾暣鐨勬按璐ㄦā鎷燂紝缁撴灉鍏锋湁鍧囧寑鎶ュ憡闂撮殧锛屽啓鍏PANETH浜岃繘鍒惰緭鍑烘枃浠� + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄨ皟鐢�<see cref="ENsolveQ">涔嬪墠锛屽繀椤诲凡缁忔墽琛屾按鍔涘垎鏋愶紝骞朵繚瀛樺埌浜岃繘鍒舵按鍔涙枃浠躲�� + /// 涓轰簡灏嗘按鍔涘拰姘磋川缁撴灉鍐欏叆鎶ュ憡鏂囦欢锛屽悗闈㈣窡鐫�璋冪敤<see cref="ENreport" />銆� + /// 涓嶈兘澶熷皢<see cref="ENopenQ" />, <see cref="ENinitQ" />, <see cref="ENrunQ" />, <see cref="ENnextQ" />鍜�<see cref="ENcloseQ" />锛屼笌<see cref="ENsolveQ" />涓�璧蜂娇鐢ㄣ�� + /// </remarks> + /// <example> + /// <code> + /// ENopen("net1.inp", "net1.rpt", ""); + /// ENsolveH(); + /// ENsolveQ(); + /// ENreport(); + /// ENclose(); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsolveQ(); + + /// <summary>鎵撳紑姘磋川鍒嗘瀽绯荤粺</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENopenQ" />涔嬪墠锛岄鍏堟墽琛屾按璐ㄥ垎鏋愶紝渚濇鍒╃敤<see cref="ENinitQ" /> - <see cref="ENrunQ" /> - <see cref="ENnextQ" />(or <see cref="ENstepQ" />)銆� + /// 涓轰簡鍏抽棴姘磋川鍒嗘瀽绯荤粺璋冪敤<see cref="ENcloseQ" />涔嬪墠锛屽彲浠ヨ繘琛屽閲嶆按璐ㄥ垎鏋愩�� + /// <para> + /// 濡傛灉鍒╃敤<see cref="ENsolveQ" />杩涜瀹屾暣姘磋川鍒嗘瀽锛屼笉瑕佽皟鐢ㄨ鍑芥暟銆� + /// </para> + /// </remarks> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENopenQ(); + + /// <summary>鍦ㄦ墽琛屾按璐ㄥ垎鏋愪箣鍓嶏紝鍒濆鍖栨按璐ㄥ拰妯℃嫙閽熻〃鏃堕棿</summary> + /// <param name="saveFlag"> + /// 0-1鏍囧織锛岃鏄庡垎鏋愮粨鏋滄槸鍚︿互鍧囧寑鎶ュ憡鏃舵淇濆瓨鍒癊PANETH浜岃繘鍒惰緭鍑烘枃浠躲�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦ㄥ埄鐢�<see cref="ENrunQ" />鑱斿悎<see cref="ENnextQ" />鎴栬��<see cref="ENstepQ" />鎵ц姘磋川鍒嗘瀽涔嬪墠锛岃皟鐢�<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 蹇呴』鍦ㄨ皟鐢�<see cref="ENinitQ" />涔嬪墠璋冪敤<see cref="ENopenQ" />銆� + /// 濡傛灉璋冪敤<see cref="ENsolveQ" />鎵ц瀹屾暣姘磋川鍒嗘瀽锛屼笉闇�璋冪敤<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 濡傛灉甯屾湜鍒╃敤<see cref="ENreport" />浜х敓鎶ュ憡锛屾垨鑰呭笇鏈涘皢璁$畻缁撴灉淇濆瓨涓轰簩杩涘埗杈撳嚭鏂囦欢锛岃缃� saveflag涓�1銆� + /// </para> + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENinitQ(int saveFlag); + + /// <summary> + /// 鍦ㄤ笅涓�鏃舵姘磋川鍒嗘瀽鐨勫紑濮嬶紝浣挎按鍔涘拰姘磋川鍒嗘瀽缁撴灉鍙敤锛屽叾涓椂娈电殑寮�濮嬭繑鍥炰负<paramref name="t" />. + /// </summary> + /// <param name="t"> + /// 褰撳墠妯℃嫙閽熻〃鏃堕棿锛屼互绉掕 (no need to supply a value for <paramref name="t" />). + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦╠o...while寰幆涓埄鐢�<see cref="ENrunQ" />鍜�<see cref="ENnextQ" />锛屼负浜嗚闂欢鏃舵ā鎷熸瘡涓�姘村姏鏃舵寮�濮嬫椂鐨勬按璐ㄧ粨鏋溿�� + /// 鎴栬�呭湪do...while寰幆涓皢瀹冧笌<see cref="ENstepQ" />涓�璧蜂娇鐢紝涓轰簡璁块棶姣忎竴姘磋川鏃堕棿姝ラ暱寮�濮嬫椂鐨勭粨鏋溿�� + /// 鎬庢牱缂栧埗杩欑寰幆鐨勪緥瀛愶紝鍙傝姣忎竴鍑芥暟璇存槑銆� + /// </para> + /// <para> + /// 鍦ㄦ墽琛�<see cref="ENrunQ" /> - <see cref="ENnextQ" /> (or <see cref="ENstepQ" />)寰幆涔嬪墠锛屽繀椤昏皟鐢�<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 妯℃嫙鐨勫綋鍓嶆椂鍒�<paramref name="t" />鐢变繚瀛樺湪姘村姏鍒嗘瀽涓殑淇℃伅纭畾锛岀劧鍚庤繘琛屾按璐ㄥ垎鏋愩�傚畠涓哄彧璇诲彉閲忋�� + /// </para> + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENrunQ(out int t); + + /// <summary>姘磋川妯℃嫙鍓嶈繘鍒颁笅涓�姘村姏鏃舵鐨勫紑濮�</summary> + /// <param name="tstep"> + /// 鏃跺埢(浠ョ璁�)锛岀洿鍒颁笅涓�姘村姏浜嬩欢鍙戠敓锛涙垨鑰呬负闆讹紝濡傛灉鍦ㄦā鎷熸椂娈电殑鏈 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁扮敤鍦�<c>do...while</c>寰幆涓紝鍒╃敤<see cref="ENrunQ" />鎵ц寤舵椂姘磋川鍒嗘瀽銆� + /// 鍏佽鍦ㄦā鎷熺殑姣忎竴姘村姏鏃舵璁块棶姘磋川缁撴灉銆� + /// 姘磋川婕旂畻鍜屽弽搴斾互鏇村皬鐨勬椂闂存闀匡紝鍦ㄥ唴閮ㄦ墽琛屻�� + /// 鍒╃敤<see cref="ENstepQ" />浠f浛璇ュ嚱鏁帮紝濡傛灉闇�瑕佽闂瘡涓�姘磋川浜嬩欢姝ラ暱鍚庣殑缁撴灉銆� + /// </para> + /// <para> + /// <paramref name="tstep" />鐨勫�兼槸鏍规嵁姘磋川鍒嗘瀽涔嬪墠鐨勬按鍔涘垎鏋愭墍淇濆瓨鐨勪俊鎭‘瀹氱殑銆傚皢鍏惰涓哄彧璇诲彉閲忋�� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tstep; + /// ENsolveH(); /* Generate and save hydraulics */ + /// ENopenQ(); + /// ENinitQ(false); + /// + /// do { + /// ENrunQ(out t); + /// /* Monitor results at time t, which */ + /// /* begins a new hydraulic time period */ + /// ENnextQ(out tstep); + /// } while (tstep > 0); + /// + /// ENcloseQ(); + /// </code> + /// </example> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENnextQ(out int tstep); + + /// <summary> + /// 姘磋川妯℃嫙鍓嶈繘涓�涓按璐ㄦ椂闂存闀裤�備繚鐣欐暣涓ā鎷熶腑鐨勬椂闂磋寖鍥村湪<paramref name="tleft" />涓� + /// </summary> + /// <param name="tleft">淇濈暀鍦ㄦ暣涓ā鎷熷巻鏃朵腑鐨勭銆� 鏈虹炕锛氭�绘ā鎷熸椂闂寸殑鍓╀綑绉掓暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁扮敤鍦�<see cref="ENrunQ" />鐨�<c>do...while</c>寰幆涓紝鎵ц寤舵椂姘磋川妯℃嫙銆� + /// 鍏佽璁块棶妯℃嫙姣忎竴姘磋川鏃堕棿姝ラ暱鐨勬按璐ㄧ粨鏋滐紝鑰屼笉鏄儚<see cref="ENnextQ" />鐨勬瘡涓�姘村姏鏃舵鐨勫紑濮嬨�� + /// </para> + /// <para> + /// 浣跨敤鍙傛暟<paramref name="tleft" />锛岀‘瀹氫綍鏃朵笉鍐嶉渶瑕佽皟鐢�<see cref="ENrunQ" />锛屽洜涓鸿揪鍒版ā鎷熸椂娈电殑鏈熬(鍗冲綋tleft = 0 鏃�)銆� + /// </para> + /// <para> + /// 灏� <paramref name="tleft" />澶勭悊涓哄彧璇诲彉閲�(涓嶉渶瑕佽祴鍊�)銆� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tleft; + /// + /// ENsolveH(); /* Generate & save hydraulics */ + /// ENopenQ(); + /// ENinitQ(false); + /// + /// do { + /// ENrunQ(out t); + /// /* Monitor results at time t */ + /// ENstepQ(out tleft); + /// } while (tleft > 0); + /// + /// ENcloseQ(); + /// </code> + /// </example> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENstepQ(out int tleft); + + /// <summary>鍏抽棴姘磋川鍒嗘瀽绯荤粺锛岄噴鏀炬墍鏈夊垎閰嶇殑鍐呭瓨</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄥ埄鐢�<see cref="ENinitQ" /> - <see cref="ENrunQ" /> - <see cref="ENnextQ" /> + /// (or <see cref="ENstepQ" />) 绯诲垪鍑芥暟璋冪敤鎵ц鎵�鏈夋按璐ㄥ垎鏋愪箣鍚庯紝璋冪敤<see cref="ENcloseQ" />銆� + /// 濡傛灉浣跨敤浜�<see cref="ENsolveQ" />锛屽皢涓嶉渶瑕佽皟鐢ㄨ鍑芥暟銆� + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENnextQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENcloseQ(); + + #endregion + + #region 鐢熸垚杈撳嚭鎶ュ憡鐨勫姛鑳� + + /// <summary>灏嗕竴琛屾枃鏈啓鍏ユ姤鍛婃枃浠�</summary> + /// <param name="line">鏂囨湰</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENwriteline([MarshalAs(STRING)] string line); + + /// <summary>灏嗘ā鎷熺粨鏋滅殑鏍煎紡鍖栨枃鏈姤鍛婂啓鍏ュ埌鎶ュ憡鏂囦欢</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENreport" />涔嬪墠锛屽繀椤诲凡缁忚繘琛屼簡瀹屾暣鐨勬按鍔涘垎鏋愭垨鑰呭畬鏁寸殑姘村姏姘磋川鍒嗘瀽锛岀粨鏋滀繚瀛樺湪鏂囦欢涓�� + /// 鍦ㄥ墠鑰呮儏鍐典腑锛屼篃蹇呴』棣栧厛璋冪敤<see cref="ENsaveH" />锛屼负浜嗗皢缁撴灉浠庢按鍔涙枃浠惰浆鎹㈠埌杈撳嚭鏂囦欢銆� + /// </para> + /// <para> + /// 鎶ュ憡鐨勬牸寮忓彈鍒癊PANETH杈撳叆鏂囦欢[REPORT]鑺傜殑鍛戒护鎵�鎺у埗锛屾垨鑰呴�氳繃涓�<see cref="ENsetreport" /> 鍑芥暟涓�璧峰叕甯冪殑绫讳技鍛戒护銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENreport(); + + /// <summary>灏嗘姤鍛婇�夐」閲嶇疆涓洪粯璁ゅ��</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 娓呴櫎浠讳綍鍑虹幇鍦‥PANETH杈撳叆鏂囦欢[REPORT]鑺備腑鐨勬姤鍛婃牸寮忓懡浠わ紝鎴栬�呭埄鐢�<see cref="ENsetreport" />鍑芥暟鎵�鍏竷鐨� + /// </para> + /// <para>璋冪敤璇ュ嚱鏁板悗锛岄粯璁ゆ姤鍛婇�夐」鐢熸晥銆�</para> + /// <para> + /// 杩欎簺涓�: + /// <list type="bullet"> + /// <item>娌℃湁鐘舵�佹姤鍛�</item> + /// <item>娌℃湁鑳介噺鎶ュ憡</item> + /// <item>娌℃湁鑺傜偣鎶ュ憡</item> + /// <item>娌℃湁绠℃鎶ュ憡</item> + /// <item>鑺傜偣鍙橀噺鎶ュ憡涓�2浣嶅皬鏁颁綅</item> + /// <item>绠℃鍙橀噺鎶ュ憡涓轰袱浣嶅皬鏁颁綅 (鎽╂摝鍥犲瓙涓�3浣�)</item> + /// <item>鎶ュ憡鐨勮妭鐐瑰彉閲忎负鏍囬珮銆佹按澶淬�佸帇寮哄拰姘磋川</item> + /// <item>鎶ュ憡鐨勭娈靛彉閲忎负娴侀噺銆佹祦閫熷拰姘村ご鎹熷け</item> + /// </list> + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENsetreport" /> + /// <seealso cref="ENsetstatusreport" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENresetreport(); + + /// <summary>鍏竷鎶ュ憡鏍煎紡鍛戒护,鏍煎紡鍛戒护涓嶦PANETH杈撳叆鏂囦欢鐨刐REPORT]鑺備腑浣跨敤鐨勭浉鍚�</summary> + /// <param name="command">鎶ュ憡鏍煎紡鍛戒护鏂囨湰</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENresetreport" />锛屾槑纭師鏉ユ姤鍛婃牸寮忓懡浠わ紝鍑虹幇鍦ㄨ緭鍏ユ枃浠朵腑锛屾垨鑰呴�氳繃璋冪敤 <see cref="ENsetreport" /> 鎴栬�� <see cref="ENsetstatusreport" />鍏竷 + /// </para> + /// <para> + /// 妯℃嫙鐨勬牸寮忕粨鏋滃彲浠ュ啓鍏ュ埌鎶ュ憡鏂囦欢锛屽埄鐢�<see cref="ENreport" />鍑芥暟 + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENresetreport" /> + /// <seealso cref="ENsetstatusreport" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsetreport([MarshalAs(STRING)] string command); + + #endregion + + #region 妫�绱etwork淇℃伅鐨勫姛鑳� + + /// <summary> + /// 璇诲彇鍒嗛厤缁欐渶杩戜竴娆� 鏇存柊婧愪唬鐮佺殑缂栧彿銆� + /// 璇ョ紪鍙风敱甯搁噺<c> CODEVERSION</c> 璁剧疆鐨勶紝浠� 20001 寮�濮嬶紝 姣忔洿鏂颁竴娆″鍔� 1. + /// </summary> + /// <param name="v">Version number of the source code.</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /> (should always be 0).</returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetversion(out int v); + + /// <summary> + /// 妫�绱㈢畝鍗曟帶鍒惰鍙ョ殑鍙傛暟銆� + /// 鎺у埗鐨勭储寮曞湪浠ュ強鍓╀綑鐨勫弬鏁拌繑鍥炴帶鍒跺弬鏁般�� + /// </summary> + /// <param name="cindex"> + /// 鎺у埗璇彞绱㈠紩 (鎺у埗鏄粠1寮�濮嬬殑绱㈠紩锛屾搴忎负杈撳叆鍒癊PENETH杈撳叆鏂囦欢鐨刐CONTROLS]鑺備腑) + /// </param> + /// <param name="ctype">鎺у埗绫诲瀷浠g爜<see cref="ControlType" /></param> + /// <param name="lindex">鎺у埗绠℃鐨勭储寮�</param> + /// <param name="setting">鎺у埗璁剧疆鏁板��</param> + /// <param name="nindex">鎺у埗鑺傜偣鐨勭储寮� + /// Index of controlling node (0 for <see cref="ControlType.Timer" /> + /// or <see cref="ControlType.TimeOfDay" /> control). + /// </param> + /// <param name="level"> + /// 瀵逛簬鍩轰簬鏃堕棿鎺у埗鐨勬帶鍒惰涓�(浠ョ璁�)鐨勬帶鍒舵按浣嶆垨鑰呮按浣嶆帶鍒剁殑鍘嬪姏鎴栬�呮帶鍒舵椂闂寸殑鏁板�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// Controls are indexed starting from 1 in the order in which they were + /// entered into the [CONTROLS] section of the IStation.Epanet input file. + /// </para> + /// <para> + /// 瀵逛簬绠¢亾锛岃缃负0锛屾剰鍛崇潃绠¢亾琚叧闂紱涓�1鎰忓懗鐫�瀹冩槸寮�鍚殑銆� + /// 瀵逛簬姘存车锛岃缃寘鍚簡姘存车杞�燂紝涓�0锛屾剰鍛崇潃姘存车琚叧闂紝涓�1鎰忓懗鐫�澶勪簬甯歌杞�熴�� + /// 瀵逛簬闃�闂紝璁剧疆鏄寚闃�闂ㄧ殑鍘嬪姏銆佹祦閲忔垨鑰呮崯澶辩郴鏁板�硷紝鍙栧喅浜庨榾闂ㄧ被鍨� + /// </para> + /// <para> + /// 瀵逛簬璁℃椂鍣ㄦ垨鑰呴挓琛ㄦ椂闂存帶鍒讹紝nindex鍙傛暟绛変簬0 + /// </para> + /// <para> + /// See <see cref="ENsetcontrol" /> for an example of using this function. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetcontrol( + int cindex, + out ControlType ctype, + out int lindex, + out float setting, + out int nindex, + out float level); + + /// <summary>妫�绱㈡寚瀹氱被鍨嬬殑绠$綉缁勪欢鏁伴噺</summary> + /// <param name="code">缁勪欢缂栧彿<see cref="CountType" /></param> + /// <param name="count">绠$綉涓璫ountcode缁勪欢鐨勬暟閲�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 绠$綉涓繛鎺ヨ妭鐐圭殑鏁伴噺绛変簬鑺傜偣鎬绘暟鍑忓幓姘存睜鍜屾按搴撴�绘暟銆� + /// </para> + /// <para> + /// 瀵逛簬鍦ㄨ緭鍏ユ枃浠舵弿杩扮殑缁勪欢锛屽伐鍏风鍐呮病鏈夎鏂芥坊鍔犳垨鑰呭垹闄ゅ姛鑳姐�� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetcount(CountType code, out int count); + + /// <summary>妫�绱㈢壒瀹氬垎鏋愰�夐」鐨勬暟鍊�</summary> + /// <param name="code">閫夐」缂栧彿<see cref="MiscOption" /></param> + /// <param name="value">閫夐」鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetoption(MiscOption code, out float value); + + /// <summary>妫�绱㈡寚瀹氬垎鏋愭椂闂村弬鏁扮殑鏁板��</summary> + /// <param name="code">鏃堕棿鍙傛暟缂栧彿 <see cref="TimeParameter" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgettimeparam(TimeParameter code, out int value); + + /// <summary>妫�绱㈣〃鏄庝娇鐢ㄥ崟浣嶇殑浠g爜鏁帮紝涓轰簡琛ㄨ揪鎵�鏈夋祦閲�</summary> + /// <param name="code">娴侀噺鑼冨洿浠g爜鍙风殑鏁板��<see cref="FlowUnitsType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦‥PANETH杈撳叆鏂囦欢鐨刐OPTIONS]鑺傛寚瀹氭祦閲忓崟浣嶃�� + /// </para> + /// <para> + /// 娴侀噺鍗曚綅鍙栧崌鎴栬�呯珛鏂圭背锛岃鏄庢墍鏈夊叾瀹冮噺灏嗛噰鐢ㄥ叕鍒跺崟浣嶏紱鍚﹀垯浣跨敤缇庡埗鍗曚綅銆�(瑙佽閲忓崟浣�)銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetflowunits(out FlowUnitsType code); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忕殑绱㈠紩</summary> + /// <param name="id">妯″紡ID</param> + /// <param name="index">妯″紡绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetpatternindex([MarshalAs(STRING)] string id, out int index); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忕殑ID鏍囩</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="id">妯″紡ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternid(int index, [MarshalAs(STRING)] StringBuilder id); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忎腑鐨勬椂娈垫�绘暟</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="len">妯″紡涓椂娈垫�绘暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternlen(int index, out int len); + + /// <summary>妫�绱㈡椂闂存ā寮忎腑鐗瑰畾鏃舵鐨勪箻瀛�</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="period">鏃堕棿妯″紡鑼冨洿鍐呯殑鏃舵</param> + /// <param name="value">鏃舵鐨勪箻瀛�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鍜屾椂娈垫槸浠�1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENsetpatternvalue" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternvalue(int index, int period, out float value); + + /// <summary>妫�绱㈣皟鐢ㄦ按璐ㄥ垎鏋愮殑绫诲瀷</summary> + /// <param name="qualcode">姘磋川鍒嗘瀽浠g爜<see cref="QualType" /></param> + /// <param name="tracenode">婧愬ご璺熻釜鍒嗗瓙涓窡韪殑鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 褰搎ualcode涓嶄负EN_TRACE鏃讹紝tracenode鐨勬暟鍊煎皢涓�0 + /// The <paramref name="tracenode" /> value will be 0 when <paramref name="qualcode" /> + /// is not <see cref="QualType.Trace" />. + /// </remarks> + /// <seealso cref="ENsetqualtype" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetqualtype(out QualType qualcode, out int tracenode); + + /// <summary>妫�绱笌鐗瑰畾閿欒鎴栬�呰鍛婁唬鐮佺浉鍏崇殑淇℃伅鏂囨湰</summary> + /// <param name="err">閿欒鎴栬�呰鍛婁唬鐮�</param> + /// <param name="errmsg">瀵瑰簲浜巈rrcode鐨勯敊璇垨鑰呰鍛婁俊鎭殑鏂囨湰</param> + /// <param name="n">errmsg 鐨勬渶澶ч暱搴� (should be <see cref="MAXMSG" /> characters).</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>閿欒淇℃伅瀛楃涓茬殑闀垮害鑷冲皯搴斾负 <see cref="MAXMSG" /> 涓瓧绗�.</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgeterror(ErrorCode err, [MarshalAs(STRING), Out] StringBuilder errmsg, int n); + + #endregion + + #region 妫�绱ode鏁版嵁鐨勫姛鑳� + + /// <summary>妫�绱㈠叿鏈夋寚瀹欼D鐨勮妭鐐圭储寮�</summary> + /// <param name="id">鑺傜偣ID</param> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetnodeid" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetnodeindex([MarshalAs(STRING)] string id, out int index); + + /// <summary> + /// 妫�绱㈠叿鏈夋寚瀹氱储寮曠殑ID鏍囩 + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="id">鑺傜偣ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + /// <seealso cref="ENgetnodeindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetnodeid(int index, [MarshalAs(STRING), Out] StringBuilder id); + + /// <summary> + /// 妫�绱㈡寚瀹氳妭鐐圭殑鑺傜偣绫诲瀷缂栧彿 + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣绫诲瀷缂栧彿 <see cref="NodeType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetnodetype(int index, out NodeType code); + + /// <summary> + /// 妫�绱㈢壒瀹氱娈靛弬鏁板�� + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠e彿 <see cref="NodeValue" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟 + /// </para> + /// <para> + /// 杩斿洖鍊肩殑鍗曚綅鍙栧喅浜庡湪 IStation.Epanet 杈撳叆鏂囦欢涓敤浜庢祦閲忕殑鍗曚綅銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetnodevalue(int index, NodeValue code, out float value); + + #endregion + + #region 妫�绱ink鏁版嵁鐨勫姛鑳� + + /// <summary> + /// 妫�绱㈠叿鏈夌壒瀹欼D鐨勭娈电储寮� + /// </summary> + /// <param name="id">绠℃ID</param> + /// <param name="index">绠℃绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetlinkid" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetlinkindex([MarshalAs(STRING)] string id, out int index); + + /// <summary>妫�绱㈠叿鏈夌壒瀹氭寚鏍囩殑绠℃鐨処D鏍囩</summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="id">绠℃ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetlinkid(int index, [MarshalAs(STRING), Out] StringBuilder id); + + /// <summary>妫�绱㈢壒瀹氱娈电殑绫诲瀷缂栧彿</summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">绠℃绫诲瀷浠e彿<see cref="LinkType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinktype(int index, out LinkType code); + + /// <summary> + /// 妫�绱㈡寚瀹氱娈电鐐硅妭鐐圭殑绱㈠紩 + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="fnode">绠℃璧峰鑺傜偣绱㈠紩</param> + /// <param name="tnode">绠℃缁堟鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>鑺傜偣鍜岀娈电储寮曟槸浠�1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 绠℃璧峰鍜岀粓姝㈣妭鐐瑰畾涔夊湪EPANETH杈撳叆鏂囦欢涓�� + /// 娌℃湁鑰冭檻绠℃鐨勫疄闄呮祦鍚戙�� + /// </para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinknodes(int index, out int fnode, out int tnode); + + /// <summary> + /// 妫�绱㈡寚瀹氱娈靛弬鏁板�� + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">绠℃鍙傛暟浠e彿<see cref="LinkValue" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// EN_DIAMETER 0 鐩村緞 + /// EN_LENGTH 1 闀垮害 + /// EN_ROUGHNESS 2 绮楃硻绯绘暟 + /// EN_MINORLOSS 3 灞�閮ㄦ崯澶辩郴鏁� + /// EN_INITSTATUS 4 鍒濆绠℃鐘舵�� (0 = 鍏抽棴, 1 = 寮�鍚�) + /// EN_INITSETTING 5 鍒濆绠¢亾绮楃硻搴﹀垵濮嬫按娉佃浆閫熷垵濮嬮榾闂ㄨ缃� + /// EN_KBULK 6 涓绘祦鍙嶅簲绯绘暟 + /// EN_KWALL 7 绠″鍙嶅簲绯绘暟 + /// EN_FLOW 8 娴侀噺 + /// EN_VELOCITY 9 娴侀�� + /// EN_HEADLOSS 10 姘村ご鎹熷け + /// EN_STATUS 11 瀹為檯绠℃鐘舵�� (0 = 鍏抽棴, 1 = 寮�鍚�) + /// EN_SETTING 12 绠¢亾绮楃硻绯绘暟瀹為檯姘存车杞�熷疄闄呴榾闂ㄨ缃� + /// EN_ENERGY 13 娑堣�楄兘閲忥紝浠ュ崈鐡﹁ + /// (鍙傛暟8 - 13 (EN_FLOW鍒癊N_ENERGY)涓鸿绠楀�笺�傚叾浠栦负璁捐鍙傛暟) + /// </para> + /// <para> + /// 濡傛灉浠庢寚瀹氱娈佃捣濮嬭妭鐐规祦鍚戞寚瀹氱粓姝㈣妭鐐癸紝娴侀噺涓烘锛涘惁鍒欎负璐熴�� + /// 杩斿洖鐨勬暟鍊煎崟浣嶏紝鍙栧喅浜嶦PANETH杈撳叆鏂囦欢涓祦閲忎娇鐢ㄧ殑鍗曚綅銆� + /// </para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinkvalue(int index, LinkValue code, out float value); + + #endregion + + #region 鏇存敼network鏁版嵁鐨勫姛鑳� + + /// <summary>璁剧疆鐗瑰畾绠�鍗曟帶鍒惰鍙ョ殑鍙傛暟</summary> + /// <param name="cindex"> + /// 鎺у埗绱㈠紩鏄粠1寮�濮嬬殑锛岄『搴忎腑瀹冧滑琚緭鍏ワ紝鍦‥PANETH鐨勮緭鍏ユ枃浠剁殑[CONTROLS]鑺傘�� + /// </param> + /// <param name="ctype">鎺у埗绫诲瀷浠g爜 <see cref="ControlType" /></param> + /// <param name="lindex">琚帶鍒剁娈电殑绱㈠紩</param> + /// <param name="setting">鎺у埗璁剧疆鐨勬暟鍊�</param> + /// <param name="nindex">鎺у埗鑺傜偣鐨勭储寮� + /// index of controlling node (0 for <see cref="ControlType.Timer" /> + /// or <see cref="ControlType.TimeOfDay" /> control) + /// </param> + /// <param name="level"> + /// 瀵逛簬姘翠綅鎺у埗鐨勬帶鍒舵按浣嶆垨鑰呭帇鍔涚殑鏁板�硷紝鎴栬�呮帶鍒惰涓虹殑鏃堕棿(浠ョ璁�)锛屽浜庡熀浜庢椂闂寸殑鎺у埗 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 瀵逛簬绠¢亾, setting涓�0锛屾剰鍛崇潃绠¢亾鍏抽棴锛屼负1鎰忓懗鐫�瀹冩槸寮�鍚殑銆� + /// 瀵逛簬姘存车, setting鍖呭惈浜嗘按娉佃浆閫燂紝涓�0鎰忓懗鐫�姘存车鍏抽棴锛屼负1鎰忓懗鐫�鍦ㄥ父瑙勮浆閫熶笅寮�鍚�� + /// 瀵逛簬闃�闂�, setting鎸囬榾闂ㄥ帇鍔涖�佹祦閲忔垨鑰呮崯澶辩郴鏁帮紝鍙栧喅浜庨榾闂ㄧ被鍨嬨�� + /// </para> + /// <para> + /// 瀵逛簬璁℃椂鍣ㄦ垨鑰呴挓琛ㄦ椂闂存帶鍒讹紝璁剧疆<paramref name="nindex" /> 鍙傛暟涓�0 + /// </para> + /// <para> + /// 瀵逛簬姘翠綅鎺у埗锛屽鏋滄帶鍒惰妭鐐� <paramref name="nindex" />涓烘按姹狅紝閭d箞level鍙傛暟搴斾负楂樹簬姹犲簳鐨勬按浣�(鑰屼笉鏄爣楂�)銆傚惁鍒檒evel搴斾负杩炴帴鑺傜偣鍘嬪姏銆� + /// </para> + /// <para> + /// 涓洪櫎鍘诲叧浜庣壒瀹氱娈电殑鎺у埗锛岃缃�<paramref name="lindex" />鍙傛暟涓�0銆傚嚱鏁颁腑鍏跺畠鍙傛暟鐨勬暟鍊煎皢琚拷鐣ャ�� + /// </para> + /// </remarks> + /// <example> + /// 鏈緥灏咵Ngetcontrol and ENsetcontrol鐢ㄤ簬鏀瑰彉鑺傜偣鐨勪綆姘翠綅璁剧疆锛屾帶鍒剁娈碉紝鍏锋湁绱㈠紩 thelink 鍒版柊鐨勬暟鍊糿ewlevel銆� + /// This example uses <see cref="ENgetcontrol" /> and <see cref="ENsetcontrol" /> + /// to change the low level setting on the node that controls a link with + /// index <c>thelink</c> to a new value <c>newlevel</c>. + /// <code> + /// int numctrls, lindex, nindex, thelink = 1; + /// float setting, level, newlevel = 1f; + /// ControlType ctype; + /// + /// ENgetcount(CountType.Control, out numctrls); + /// + /// for (int i = 1; i <= numctrls; i++) { + /// ENgetcontrol(i, out ctype, out lindex, out setting, out nindex, out level); + /// if (ctype == ControlType.Lowlevel && lindex == thelink) { + /// ENsetcontrol(i, ctype, lindex, setting, nindex, newlevel); + /// break; + /// } + /// } + /// </code> + /// </example> + /// <seealso cref="ENgetcontrol" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetcontrol( + int cindex, + ControlType ctype, + int lindex, + float setting, + int nindex, + float level); + + /// <summary> + /// 璁剧疆鐗瑰畾鑺傜偣鐨勫弬鏁板�� + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠g爜 <see cref="NodeValue" /></param> + /// <param name="v">parameter value</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鎻愪緵鐨勬暟鍊煎崟浣嶅彇鍐充簬鍦� IStation.Epanet 杈撳叆鏂囦欢涓敤浜庢祦閲忕殑鍗曚綅銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetnodevalue(int index, NodeValue code, float v); + + /// <summary> + /// 璁剧疆鎸囧畾绠℃鐨勫弬鏁板�� + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠g爜<see cref="LinkValue" /></param> + /// <param name="v">parameter value</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鏁板�煎崟浣嶅彇鍐充簬鍦‥PANETH杈撳叆鏂囦欢涓娇鐢ㄧ殑娴侀噺鑼冨洿(鍙傝璁¢噺鍗曚綅) + /// </para> + /// <para> + /// + /// ps(pdf):瀵逛簬瀛樺湪鐨勭娈电姸鎬佹垨鑰呰缃紝鍦ㄦā鎷熷紑濮嬩箣鍓嶏紝鍒╃敤EN_INITSTATUS鍜� EN_INITSETTING璁剧疆璁捐鏁板�笺�� + /// 鍦ㄦā鎷熸墽琛岀殑鍚屾椂锛屽埄鐢‥N_STATUS鍜孍N_SETTING 鏀瑰彉杩欎簺鏁板��(鍦‥NrunH - ENnextH寰幆鑼冨洿鍐�)銆� + /// + /// ps(鑻辨枃缈昏瘧):浣跨敤 <see cref="LinkValue.InitStatus" /> 鍜� <see cref="LinkValue.InitSetting" /> + /// 璁剧疆link鐘舵�佹垨璁剧疆鐨勮璁″�硷紝杩欎簺鍊煎湪浠跨湡寮�濮嬪墠灏卞凡瀛樺湪銆� 浣跨敤 <see cref="LinkValue.Status" /> 鍜� <see cref="LinkValue.Setting" /> + /// 鍙互鍦ㄨ繍琛屼豢鐪熸椂锛堝湪 <see cref="ENrunH" /> - <see cref="ENnextH" /> 寰幆鍐咃級鏇存敼杩欎簺鍊笺�� + /// + /// Use <see cref="LinkValue.InitStatus" /> and <see cref="LinkValue.InitSetting" /> + /// to set the design value for a link's status or setting that exists prior to the + /// start of a simulation. Use <see cref="LinkValue.Status" /> and + /// <see cref="LinkValue.Setting" /> to change these values while a simulation + /// is being run (within the <see cref="ENrunH" /> - <see cref="ENnextH" /> loop). + /// + /// </para> + /// <para> + /// 濡傛灉鎺у埗闃�鍏锋湁鏄庣‘鐨勭姸鎬侊紝璁剧疆涓�<see cref="StatusType.Open" />鎴栬��<see cref="StatusType.Closed" />銆� + /// 闇�瑕佸啀娆″惎鐢紝鍦ㄦā鎷熻繃绋嬩腑蹇呴』閲囩敤<see cref="LinkValue.Setting" />鍙傛暟鎻愪緵铏氭嫙鐨勯榾闂ㄨ缃暟鍊笺�� + /// 瀵逛簬绠¢亾<see cref="LinkValue.Roughness" />鎴栬��<see cref="LinkValue.InitSetting" />鐢ㄤ簬鏀瑰彉绮楃硻绯绘暟銆� + /// </para> + /// <para> + /// For pipes, either <see cref="LinkValue.Roughness" /> + /// or <see cref="LinkValue.InitSetting" /> can be used to change roughness. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetlinkvalue(int index, LinkValue code, float v); + + /// <summary> + /// 娣诲姞涓�涓柊鐨勬椂闂存ā寮忥紝闄勫姞鍒扮幇鏈夋ā寮忕殑鏈熬 + /// </summary> + /// <param name="id">鏂版ā寮忕殑 ID 鍚嶇О</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>ID 鏍囩搴斿寘鍚笉瓒呰繃 31 涓瓧绗�</para> + /// <para>鏂版ā寮忓皢鍖呭惈涓�涓椂闂存锛屽叾涔樻暟绯绘暟涓� 1</para> + /// <para> + /// 浣跨敤 <see cref="ENsetpattern" /> 鍑芥暟锛岄鍏堢敤 <see cref="ENgetpatternindex" /> 鍑芥暟鑾峰彇妯″紡鐨勭储寮曪紝鐒跺悗鐢ㄤ竴缁勭壒瀹氱殑涔樻暟濉厖妯″紡銆� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// string patId = "NewPattern"; + /// float[] patFactors = new[] {0.8f, 1.1f, 1.4f, 1.1f, 0.8f, 0.7f}; + /// int patIndex; + /// ENaddpattern(patId); + /// ENgetpatternindex(patId, out patIndex); + /// ENsetpattern(patIndex, patFactors, 6); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENaddpattern([MarshalAs(STRING)] string id); + // public static extern ErrorCode ENsetpattern([MarshalAs(STRING)] string id); + + /// <summary> + /// 璁剧疆鎸囧畾鏃堕棿妯″紡鐨勬墍鏈変箻瀛� + /// </summary> + /// <param name="index">鏃堕棿妯″紡绱㈠紩</param> + /// <param name="f">鏁翠釜妯″紡鐨勪箻瀛�</param> + /// <param name="n">妯″紡涓洜瀛愭�绘暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// <paramref name="f" /> Factors鐐瑰埌鍩轰簬闆剁殑鏁扮粍锛屽寘鍚簡nfactors鐨勫厓绱� + /// </para> + /// <para> + /// 浣跨敤璇ュ嚱鏁伴噸鏂板畾涔�(鍜岄噸鏂拌缃昂瀵�)鏃堕棿妯″紡锛涘埄鐢�<see cref="ENsetpatternvalue" />锛屼慨鏀规ā寮忔寚瀹氭椂娈电殑妯″紡鍥犲瓙銆� + /// </para> + /// </remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENgetpatternvalue" /> + /// <seealso cref="ENsetpatternvalue" /> + /// <seealso cref="ENaddpattern" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetpattern( + int index, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R4, SizeParamIndex = 2), In] float[] f, + int n); + + /// <summary> + /// 璁剧疆鏃堕棿妯″紡鍐呯壒瀹氭椂娈电殑涔樺瓙鍥犲瓙 + /// </summary> + /// <param name="index">鏃堕棿妯″紡绱㈠紩</param> + /// <param name="period">鏃堕棿妯″紡鍐呯殑鏃舵</param> + /// <param name="value">鏃舵鐨勪箻瀛愬洜瀛�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鍒╃敤 <see cref="ENsetpattern" /> 閲嶆柊璁剧疆鏃堕棿妯″紡鍐呯殑鎵�鏈夊洜瀛�. + /// </para> + /// </remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENgetpatternvalue" /> + /// <seealso cref="ENsetpattern" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetpatternvalue(int index, int period, float value); + + /// <summary>璁剧疆鏃堕棿鍙傛暟鍊�</summary> + /// <param name="code">鏃堕棿鍙傛暟浠g爜<see cref="TimeParameter" /></param> + /// <param name="value">鏃堕棿鍙傛暟鍊硷紝浠ョ璁�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// Do not change time parameters after calling <see cref="ENinitH" /> in a + /// hydraulic analysis or <see cref="ENinitQ" /> in a water quality analysis. + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsettimeparam(TimeParameter code, int value); + + /// <summary>璁剧疆鐗瑰畾鍒嗘瀽閫夐」鐨勬暟鍊�</summary> + /// <param name="code">閫夐」缂栧彿<see cref="MiscOption" /></param> + /// <param name="v">閫夐」鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetoption(MiscOption code, float v); + + /// <summary>璁剧疆姘村姏鐘舵�佹姤鍛婄殑姘村钩</summary> + /// <param name="statuslevel">鐘舵�佹姤鍛婄殑姘村钩<see cref="StatusLevel" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 褰撴按鍔涙ā鎷熼�愭笎灞曞紑鏃讹紝鐘舵�佹姤鍛婂皢绠$綉鍏冪礌鐨勬按鍔涚姸鎬佸彉鍖栧啓鍏ュ埌鎶ュ憡鏂囦欢 + /// 鍏锋湁涓夌姘村钩鐨勬姤鍛�<see cref="StatusLevel" />: + /// <list type="number"> + /// <item>鏃犵姸鎬佹姤鍛�</item> + /// <item>甯歌鎶ュ憡</item> + /// <item>瀹屾暣鐘舵�佹姤鍛�</item> + /// </list> + /// </para> + /// <para> + /// 瀹屾暣鐘舵�佹姤鍛婂寘鍚簡姹傝В绯荤粺姘村姏鏂圭▼缁勶紝鍦ㄦā鎷熺殑姣忎竴鏃堕棿姝ラ暱鐨勬敹鏁涗俊鎭�備富瑕佺敤浜庤皟璇曠洰鐨勩�� + /// </para> + /// <para> + /// 濡傛灉灏嗗湪搴旂敤绋嬪簭涓墽琛岃澶氭姘村姏鍒嗘瀽锛屽缓璁姸鎬佹姤鍛婂叧闂�(<paramref name="statuslevel" /> = <see cref="StatusLevel.None" />)銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetstatusreport(StatusLevel statuslevel); + + /// <summary>璁剧疆琚皟鐢ㄦ按璐ㄥ垎鏋愮殑绫诲瀷</summary> + /// <param name="qualcode">姘磋川鍒嗘瀽浠g爜 <see cref="QualType" /></param> + /// <param name="chemname">琚垎鏋愬寲瀛︽垚鍒嗗悕绉�</param> + /// <param name="chemunits">鍖栧鎴愬垎璁¢噺鍗曚綅</param> + /// <param name="tracenode">婧愬ご璺熻釜鍒嗘瀽涓璺熻釜鑺傜偣鐨処D</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 濡傛灉涓嶈繘琛屽寲瀛︽垚鍒嗗垎鏋愶紝鍖栧鎴愬垎鍚嶇О鍜屽崟浣嶅彲浠ヤ负绌哄瓧绗︿覆銆傚鏋滀笉杩涜婧愬ご璺熻釜鍒嗘瀽锛岃窡韪妭鐐逛繚鐣欏悕绉般�� + /// </para> + /// <para> + /// 娉ㄦ剰璺熻釜鑺傜偣閫氳繃ID鎸囧畾鑰屼笉鏄储寮曘�� + /// </para> + /// </remarks> + /// <seealso cref="ENgetqualtype" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsetqualtype( + QualType qualcode, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string chemname, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string chemunits, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string tracenode); + + #endregion + + } + +} diff --git a/IStation.Epanet/enum/Enumerations.cs b/IStation.Epanet/enum/Enumerations.cs new file mode 100644 index 0000000..412c6de --- /dev/null +++ b/IStation.Epanet/enum/Enumerations.cs @@ -0,0 +1,765 @@ +锘縩amespace IStation.Epanet.Enums +{ + + /// <summary>Node鍊煎弬鏁�</summary> + /// <remarks> + /// 鍙傛暟 13 (<see cref="Demand" /> through <see cref="SourceMass" />)涓鸿绠楀�笺�� + /// 鍏朵粬涓鸿緭鍏ヨ璁″弬鏁般�� + /// </remarks> + /// <remarks> + /// 鍙傛暟 14 - 23(<see cref="InitVolume" /> 鑷� <see cref="TankKbulk" />)浠呴�傜敤浜庡偍缃愯妭鐐广�� + /// </remarks> + public enum NodeValue + { + /// <summary>鏍囬珮</summary> + Elevation = 0, + + /// <summary>鍩虹闇�姘撮噺</summary> + BaseDemand = 1, + + /// <summary>鏃堕棿妯″紡绱㈠紩</summary> + Pattern = 2, + + /// <summary>鎵╂暎鍣ㄧ郴鏁�</summary> + Emitter = 3, + + /// <summary>鍒濆姘磋川</summary> + InitQual = 4, + + /// <summary>婧愬ご姘磋川</summary> + SourceQual = 5, + + /// <summary>婧愬ご妯″紡</summary> + SourcePat = 6, + + /// <summary>婧愬ご绫诲瀷(See <see cref="SourceType" /></summary> + SourceType = 7, + + /// <summary>姘存睜鐨勫垵濮嬫按浣�</summary> + TankLevel = 8, + + /// <summary>瀹為檯闇�姘撮噺</summary> + Demand = 9, + + /// <summary>姘村姏姘村ご</summary> + Head = 10, + + /// <summary>鍘嬪己</summary> + Pressure = 11, + + /// <summary>瀹為檯姘磋川</summary> + Quality = 12, + + /// <summary>姣忓垎閽熷寲瀛︽垚鍒嗘簮澶寸殑璐ㄩ噺娴侀噺</summary> + SourceMass = 13, + + /// <summary>鍒濆姘撮噺</summary> + InitVolume = 14, + + /// <summary>娣峰悎妯″瀷缂栫爜 (see <see cref="MixType" />)</summary> + MixModel = 15, + + /// <summary>鍙岄殧灞傛按绠辩殑鍏ュ彛/鍑哄彛鍖哄煙瀹圭Н(Inlet/Outlet zone volume in a 2-compartment tank )</summary> + MixZonevol = 16, + + /// <summary>姘寸鐩村緞</summary> + TankDiam = 17, + + /// <summary>鏈�灏忔按閲�</summary> + MinVolume = 18, + + /// <summary>浣撶Н涓庢繁搴︽洸绾跨殑鎸囨暟(濡傛灉娌℃湁鎸囧畾锛屽垯涓� 0)</summary> + VolCurve = 19, + + /// <summary>鏈�浣庢按浣�</summary> + MinLevel = 20, + + /// <summary>鏈�楂樻按浣�</summary> + MaxLevel = 21, + + /// <summary>鍙屾牸姘存Ы涓叆鍙�/鍑哄彛鍖烘墍鍗犳�诲绉殑姣斾緥 (Fraction of total volume occupied by the inlet/outlet zone in a 2-compartment tank)</summary> + MixFraction = 22, + + /// <summary>浣撶Н鍙嶅簲閫熺巼绯绘暟</summary> + TankKbulk = 23 + } + + /// <summary>Link鍊煎弬鏁�</summary> + /// <remarks> + /// 鍙傛暟 13(<see cref="Flow" /> 鑷� <see cref="Energy" />)鍧囦负涓鸿绠楀�笺�傚叾浠栧弬鏁颁负璁捐鍙傛暟銆� + /// </remarks> + /// <remarks> + /// 濡傛灉娴侀噺鏂瑰悜鏄粠鎸囧畾鐨� 鐨勮捣鐐硅妭鐐规祦鍚戝叾鎸囧畾鐨勭粓鐐硅妭鐐癸紝鍒欐祦閫熶负姝o紱鍙嶄箣锛屾祦閫熶负璐熴�� + /// </remarks> + /// <remarks> + /// 杩斿洖鍊肩殑鍗曚綅鍙栧喅浜� IStation.Epanet 杈撳叆鏂囦欢涓敤浜庢祦閲忕殑鍗曚綅(鍙傝娴嬮噺鍗曚綅) + /// </remarks> + public enum LinkValue + { + /// <summary>鐩村緞</summary> + Diameter = 0, + + /// <summary>闀垮害</summary> + Length = 1, + + /// <summary>绮楃硻绯绘暟</summary> + Roughness = 2, + + /// <summary>灞�閮ㄦ崯澶辩郴鏁�</summary> + MinorLoss = 3, + + /// <summary>鍒濆绠℃鐘舵�� (0 = closed, 1 = open)</summary> + InitStatus = 4, + + /// <summary>绠¢亾绮楃硻搴﹀垵濮嬫按娉佃浆閫熷垵濮嬮榾闂ㄨ缃�</summary> + InitSetting = 5, + + /// <summary>涓绘祦鍙嶅簲绯绘暟</summary> + Kbulk = 6, + + /// <summary>绠″鍙嶅簲绯绘暟</summary> + Kwall = 7, + + /// <summary>娴侀噺</summary> + Flow = 8, + + /// <summary>娴侀��</summary> + Velocity = 9, + + /// <summary>10 - 姘村ご鎹熷け</summary> + HeadLoss = 10, + + /// <summary>11 - 褰撳墠姘存车鎴栬�呴榾闂ㄧ姸鎬� (0 = closed, 1 = open)</summary> + Status = 11, + + /// <summary>12 - 褰撳墠姘存车杞�熸垨鑰呴榾闂ㄨ缃�</summary> + Setting = 12, + + /// <summary>13 - 娑堣�楄兘閲忥紝浠ュ崈鐡﹁</summary> + Energy = 13 + } + + /// <summary>鏃堕棿鍙傛暟</summary> + /// <remarks> + /// 鍦ㄦ按鍔涘垎鏋愪腑璋冪敤 <see cref="EpanetMethods.ENinitH" /> 鎴栧湪姘磋川鍒嗘瀽涓皟鐢� <see cref="EpanetMethods.ENinitQ" /> 鍚庯紝涓嶈鏇存敼鏃堕棿鍙傛暟銆� + /// 鎴栧湪姘磋川鍒嗘瀽涓皟鐢�<see cref="EpanetMethods.ENinitQ" /> 鍚庯紝璇峰嬁鏇存敼鏃堕棿鍙傛暟銆� + /// </remarks> + public enum TimeParameter + { + /// <summary>妯℃嫙鍘嗘椂</summary> + Duration = 0, + + /// <summary>姘村姏鏃堕棿姝ラ暱</summary> + HydStep = 1, + + /// <summary>姘磋川鏃堕棿姝ラ暱</summary> + QualStep = 2, + + /// <summary>鏃堕棿妯″紡鏃堕棿姝ラ暱</summary> + PatternStep = 3, + + /// <summary>鏃堕棿妯″紡璧峰鏃堕棿</summary> + PatternStart = 4, + + /// <summary>鎶ュ憡鏃堕棿姝ラ暱</summary> + ReportStep = 5, + + /// <summary>鎶ュ憡璧峰鏃堕棿</summary> + ReportStart = 6, + + /// <summary>浼拌鍩轰簬瑙勫垯鎺у埗鐨勬椂闂存闀�</summary> + RuleStep = 7, + + /// <summary>浣跨敤鏃堕棿搴忓垪鍚庡鐞嗙殑绫诲瀷 (see <see cref="TstatType" />)</summary> + Statistic = 8, + + /// <summary>淇濆瓨鍒颁簩杩涘埗杈撳嚭鏂囦欢鐨勬姤鍛婃椂娈垫暟</summary> + Periods = 9 + } + + /// <summary>缁勪欢鏁伴噺</summary> + /// <remarks> + /// 缃戠粶涓殑杩炴帴鐐规暟閲忕瓑浜庤妭鐐规暟閲忓噺鍘绘按绠卞拰钃勬按姹犳暟閲忋�� + /// </remarks> + /// <remarks> + /// 宸ュ叿鍖呬腑娌℃湁娣诲姞鎴栧垹闄よ緭鍏ユ枃浠朵腑鎻忚堪鐨勭粍浠躲�� + /// </remarks> + public enum CountType + { + /// <summary>鑺傜偣</summary> + Node = 0, + + /// <summary>姘村簱鍜屾按姹犺妭鐐�</summary> + Tank = 1, + + /// <summary>绠℃</summary> + Link = 2, + + /// <summary>鏃堕棿妯″紡</summary> + Pattern = 3, + + /// <summary>鏇茬嚎</summary> + Curve = 4, + + /// <summary>绠�鍗曟帶鍒�</summary> + Control = 5 + } + + /// <summary> + /// 鑺傜偣绫诲瀷 + /// </summary> + public enum NodeType + { + /// <summary>杩炴帴鐐�</summary> + Junction = 0, + + /// <summary>姘村簱</summary> + Reservoir = 1, + + /// <summary>姘寸鑺傜偣</summary> + Tank = 2 + } + + /// <summary> + /// 绠$嚎绫诲瀷锛堜笌闃�闂ㄧ被鍨嬫湁閲嶅锛岄榾闂ㄧ被鍨嬬敤浜巌np鏂囦欢锛� + /// </summary> + public enum LinkType + { + /// <summary>鍏锋湁姝㈠洖闃�鐨勭閬�</summary> + CVPipe = 0, + + /// <summary>绠¢亾</summary> + Pipe = 1, + + /// <summary>姘存车</summary> + Pump = 2, + + /// <summary>鍑忓帇闃�</summary> + PRV = 3, + + /// <summary>绋冲帇闃�</summary> + PSV = 4, + + /// <summary>鍘嬪姏鍒跺姩闃�</summary> + PBV = 5, + + /// <summary>娴侀噺鎺у埗闃�</summary> + FCV = 6, + + /// <summary>鑺傛祦鎺у埗闃�</summary> + TCV = 7, + + /// <summary>甯歌闃�闂�</summary> + GPV = 8, + + /// <summary> + /// 闇�瑕佷慨鏀� + /// </summary> + VALVE = 999 + } + + /// <summary> + /// 姘磋川鍒嗘瀽绫诲瀷 + /// </summary> + public enum QualType + { + /// <summary>涓嶈繘琛屾按璐ㄥ垎鏋�</summary> + None = 0, + + /// <summary>鍖栧鎴愬垎鍒嗘瀽</summary> + Chem = 1, + + /// <summary>鍖栧鎴愬垎鍒嗘瀽</summary> + Age = 2, + + /// <summary>婧愬ご璺熻釜</summary> + Trace = 3 + } + + /// <summary>杈撳叆婧愯川閲忕被鍨�</summary> + /// <remarks>鏈夊叧杩欎簺婧愮被鍨嬬殑鎻忚堪锛岃鍙傞槄 <c>[SOURCES]</c>銆�</remarks> + /// <remarks> + /// Used in functions <see cref="EpanetMethods.ENgetnodevalue" /> and + /// <see cref="EpanetMethods.ENsetnodevalue" /> with <see cref="NodeValue.SourceType" /> + /// parameter. + /// + /// 1. MASS绫诲瀷婧愬ご鐨勫己搴﹁閲忎负璐ㄩ噺娴侀噺姣忓垎銆傛墍鏈夊叾瀹冪被鍨嬫祴璇曟簮澶村己搴︼紝浠ユ祿搴﹀崟浣嶈銆� + /// 2. 閫氳繃鎸囧畾鏃堕棿妯″紡锛屾簮澶村己搴﹀彲浠ヨ涓洪殢鏃堕棿鑰屽彉鍖栥�� + /// 3. CONCEN婧愬ご锛� + /// *琛ㄧず浠讳綍杩涘叆鑺傜偣鐨勫閮ㄦ簮澶磋繘娴佹祿搴� + /// *浠呭綋鑺傜偣鍏锋湁璐熼渶姘撮噺鏃朵娇鐢�(姘村湪璇ヨ妭鐐硅繘鍏ョ缃�) + /// *濡傛灉鑺傜偣涓鸿繛鎺ヨ妭鐐癸紝鎶ュ憡鐨勬祿搴︿负娣峰悎浜嗘簮澶存祦閲忓拰浠庣缃戝叾瀹冪偣鏉ョ殑杩涙祦缁撴灉 + /// *濡傛灉鑺傜偣涓烘按搴擄紝鎶ュ憡鐨勬祿搴︿负婧愬ご娴撳害 + /// *濡傛灉鑺傜偣涓烘按姹狅紝鎶ュ憡鐨勬祿搴︿负姘存睜鐨勫唴閮ㄦ祿搴� + /// *閫傚悎浜庤〃绀烘簮澶翠緵姘存垨鑰呭鐞嗗巶鐨勮妭鐐�(渚嬪璧嬩互璐熼渶姘撮噺鐨勬按搴撴垨鑰呰妭鐐�) + /// *涓嶉�傜敤浜庡悓鏃跺叿鏈夎繘娴�/鍑烘祦鐨勮搫姘存睜銆� + /// 4. MASS, FLOWPACED鎴栬�匰ETPOINT婧愬ご锛� + /// *琛ㄧず浜嗘敞灏勬簮澶达紝杩欓噷鐗╄川琚洿鎺ユ敞灏勫埌绠$綉锛屽拷鐣ヤ簡鑺傜偣鐨勯渶姘撮噺澶氬皯 + /// *浠ヤ笅鏂瑰紡褰卞搷浜嗙寮�鑺傜偣鍒扮缃戝叾瀹冧綅缃殑姘达細 + /// -MASS娉ㄥ叆锛屽皢璐ㄩ噺娴侀噺娣诲姞鍒拌妭鐐硅繘娴� + /// -FLOWPACED娉ㄥ叆锛屽皢鍥哄畾娴撳害娣诲姞鍒拌妭鐐硅繘娴佹祿搴︿腑 + /// -SETPOINT娉ㄥ叆锛屽浐瀹氫簡浠讳綍绂诲紑鑺傜偣鐨勬按娴佹祿搴�(鍙杩涙祦鏉ョ殑娴撳害浣庝簬璁剧疆鍊�) + /// *鍦ㄨ繛鎺ヨ妭鐐规垨鑰呮按搴撴敞灏勬簮澶寸殑鎶ュ憡娴撳害锛屾槸鍦ㄥ簲鐢ㄦ敞灏勪箣鍚庣殑缁撴灉娴撳害锛涘叿鏈夋敞灏勬簮澶寸殑姘存睜鎶ュ憡娴撳害涓烘按姹犵殑鍐呴儴娴撳害 + /// *閫傚悎浜庢ā鎷熺ず韪墏鐨勭洿鎺ユ敞鍏ワ紝鎴栬�呰繘鍏ョ缃戠殑娑堟瘨鍓傦紝鎴栬�呮ā鎷熸薄鏌撶墿鍏ヤ镜銆� + /// 5. 瀵逛簬妯℃嫙姘撮緞鎴栬�呮簮澶磋窡韪紝[SOURCES] 鑺傛槸涓嶉渶瑕佺殑 + /// </remarks> + public enum SourceType + { + /// <summary>杩涙祦娴撳害</summary> + Concen = 0, + + /// <summary>缇ゆ祦澧炲帇鍣�</summary> + Mass = 1, + + /// <summary>璁惧畾鐐瑰鍘嬪櫒</summary> + SetPoint = 2, + + /// <summary>娴侀噺姝ヨ繘鍔╂帹鍣�</summary> + FlowPaced = 3 + + } + + /// <summary>娴侀噺鍗曚綅缂栫爜</summary> + /// <remarks> + /// 娴侀噺鍗曚綅鍦� IStation.Epanet 杈撳叆鏂囦欢鐨� <c>[OPTIONS]</c> 閮ㄥ垎涓寚瀹� + /// </remarks> + /// <remarks> + /// 浠ュ崌鎴栫珛鏂圭背涓烘祦閲忓崟浣嶆剰鍛崇潃闄ゆ祦閲忓锛屾墍鏈夊叾浠栭噺閮戒娇鐢ㄥ叕鍒跺崟浣嶃�傚惁鍒欏皢浣跨敤缇庡浗鍗曚綅銆�(鍙傝璁¢噺鍗曚綅)銆� + /// </remarks> + public enum FlowUnitsType + { + /// <summary>绔嬫柟鑻卞昂姣忕</summary> + Cfs = 0, + + /// <summary>鍔犱粦姣忓垎</summary> + Gpm = 1, + + /// <summary>鐧句竾鍔犱粦姣忔棩</summary> + Mgd = 2, + + /// <summary>鑻卞埗mgd</summary> + Imgd = 3, + + /// <summary>鑻变憨鈥旇嫳灏烘瘡鏃�</summary> + Afd = 4, + + /// <summary>鍗囨瘡绉�</summary> + Lps = 5, + + /// <summary>鍗囨瘡鍒�</summary> + Lpm = 6, + + /// <summary>鐧句竾鍗囨瘡鏃�</summary> + Mld = 7, + + /// <summary>绔嬫柟绫虫瘡鏃�</summary> + Cmh = 8, + + /// <summary>绔嬫柟绫虫瘡鏃�</summary> + Cmd = 9 + } + + /// <summary>鎺у埗绫诲瀷</summary> + /// <remarks> + /// 瀵逛簬绠¢亾, setting涓�0锛屾剰鍛崇潃绠¢亾鍏抽棴锛屼负1鎰忓懗鐫�瀹冩槸寮�鍚殑銆� + /// 瀵逛簬姘存车, setting鍖呭惈浜嗘按娉佃浆閫燂紝涓�0鎰忓懗鐫�姘存车鍏抽棴锛屼负1鎰忓懗鐫�鍦ㄥ父瑙勮浆閫熶笅寮�鍚�� + /// 瀵逛簬闃�闂�, setting鎸囬榾闂ㄥ帇鍔涖�佹祦閲忔垨鑰呮崯澶辩郴鏁帮紝鍙栧喅浜庨榾闂ㄧ被鍨嬨�� + /// </remarks> + /// <remarks>瀵逛簬璁℃椂鍣ㄦ垨鑰呴挓琛ㄦ椂闂存帶鍒讹紝璁剧疆 nindex 鍙傛暟涓�0</remarks> + /// <remarks> + /// 鏈夊叧浣跨敤姝ゅ嚱鏁扮殑绀轰緥,璇峰弬瑙�<see cref="EpanetMethods.ENsetcontrol" /> + /// </remarks> + public enum ControlType + { + /// <summary>浣庢按浣嶆帶鍒�</summary> + /// <remarks>褰撴按姹犳按浣嶆垨鑰呰妭鐐瑰帇鍔涢檷钀藉埌鎸囧畾姘翠綅浠ヤ笅鏃朵娇鐢�</remarks> + LowLevel = 0, + + /// <summary>楂樻按浣嶆帶鍒�</summary> + /// <remarks>褰撴按姹犳按浣嶆垨鑰呰妭鐐瑰帇鍔涗笂鍗囬珮浜庢寚瀹氭按浣嶆椂浣跨敤</remarks> + HiLevel = 1, + + /// <summary>璁℃椂鍣ㄦ帶鍒�</summary> + /// <remarks>鍦ㄦ寚瀹氭椂闂寸敤浜庢ā鎷�</remarks> + Timer = 2, + + /// <summary>閽熻〃鏃堕棿鎺у埗</summary> + /// <remarks>浣跨敤鎸囧畾鏃ュ巻鏃堕棿</remarks> + TimeOfDay = 3 + } + + /// <summary>鏃堕棿搴忓垪缁熻绫诲瀷</summary> + /// <remarks> + /// Used as value in <see cref="EpanetMethods.ENgettimeparam" /> and + /// <see cref="EpanetMethods.ENsettimeparam" /> functions + /// with <see cref="TimeParameter.Statistic" /> code. + /// </remarks> + public enum TstatType + { + /// <summary>鏃�</summary> + None = 0, + + /// <summary>鍧囧��</summary> + Average = 1, + + /// <summary>灏忓��</summary> + Minimum = 2, + + /// <summary>澶у��</summary> + Maximum = 3, + + /// <summary>鑼冨洿</summary> + Range = 4 + } + + /// <summary> + /// 钃勬按姹犳贩鍚堟ā鍨� + /// </summary> + public enum MixType + { + /// <summary>瀹屽叏娣峰悎</summary> + Mix1 = 0, + + /// <summary>鍙屽娣峰悎</summary> + Mix2 = 1, + + /// <summary>鏌卞娴�</summary> + Fifo = 2, + + /// <summary>鍫嗘爤寮忔煴濉炴祦</summary> + Lifo = 3 + } + + /// <summary> + /// 姘村姏鐘舵�佹姤鍛婃按骞� + /// </summary> + public enum StatusLevel + { + /// <summary>鏃�</summary> + None = 0, + /// <summary>甯歌鎶ュ憡</summary> + Normal = 1, + /// <summary>瀹屾暣鐘舵�佹姤鍛�</summary> + FULL = 2 + } + + /// <summary> + /// 鍏朵粬閫夐」 + /// </summary> + public enum MiscOption + { + /// <summary>Trials 鏄湪姣忎竴妯℃嫙姘村姏鏃堕棿姝ラ暱涓紝姹傝В绠$綉姘村姏鐗规�т娇鐢ㄧ殑鏈�澶ц瘯绠楁鏁般�傜己鐪佷负40銆�</summary> + MaxTrials = 0, + + /// <summary>Accuracy 鎸囧畾浜嗙‘瀹氫綍鏃惰揪鍒版按鍔涚粨鏋滅殑鏀舵暃鍑嗗垯銆傚綋鎵�鏈夋祦閲忔�诲拰鏀瑰彉锛屾潵鑷師鍏堟眰瑙i櫎浠ユ墍鏈夌娈电殑鎬绘祦閲忎綆浜庤鏁板�兼椂锛岃瘯绠椾腑姝€�傜己鐪佷负0.001銆�</summary> + Accuracy = 1, + + /// <summary>Tolerance 鏄按璐ㄦ按骞崇簿搴︺�傚浜庢墍鏈夋按璐ㄥ垎鏋�(鍖栧悎鐗┿�佹按榫�(浠ュ皬鏃惰)锛屾垨鑰呮簮澶磋窡韪�(浠ョ櫨鍒嗘瘮璁�))锛岀己鐪佸�间负0.01</summary> + Tolerance = 2, + + /// <summary>Emitter exponent 鎸囧畾浜嗗綋璁$畻鎵╂暎鍣ㄧ殑娴侀噺鏃讹紝鑺傜偣鍘嬪姏涓婂崌鐨勫箓鎸囨暟銆傜己鐪佷负0.5銆�</summary> + EmitExpon = 3, + + /// <summary>Demandmult 鐢ㄤ簬璋冩暣鎵�鏈夎繛鎺ヨ妭鐐圭殑鍩烘湰闇�姘撮噺鏁板�硷紝浠ュ強鎵�鏈夐渶姘撮噺鐨勭被鍨嬨�備緥濡傦紝鏁板��2涓轰袱鍊嶇殑鍩哄噯闇�姘撮噺锛岃�屾暟鍊�0.5灏嗕负瀹冧滑鐨勪竴鍗娿�傜己鐪佸�间负1.0銆�</summary> + DemandMult = 4 + } + + + + + + + + + + + + ///<summary> + /// Rule 鎿嶄綔 + /// </summary> + public enum Operators { ABOVE, BELOW, EQ, GE, GT, IS, LE, LT, NE, NOT } + + ///<summary> + /// Rule 瀵硅薄绫诲瀷 + /// </summary> + public enum Objects { JUNC, LINK, NODE, PIPE, PUMP, RESERV, SYSTEM, TANK, VALVE } + + ///<summary> + /// Rule statements. + /// </summary> + public enum Rulewords { AND, ELSE, ERROR, IF, OR, PRIORITY, RULE, THEN } + + /// <summary> + /// Reporting flag. + /// </summary> + public enum ReportFlag { FALSE = 0, SOME = 2, TRUE = 1 } + + /// <summary> + /// 绠$綉鍙橀噺 + /// </summary> + public enum FieldType + { + ///<summary>nodal elevation</summary> + ELEV = 0, + ///<summary>nodal demand flow</summary> + DEMAND = 1, + ///<summary>nodal hydraulic head</summary> + HEAD = 2, + ///<summary>nodal pressure</summary> + PRESSURE = 3, + ///<summary>nodal water quality</summary> + QUALITY = 4, + + ///<summary>link length</summary> + LENGTH = 5, + ///<summary>link diameter</summary> + DIAM = 6, + ///<summary>link flow rate</summary> + FLOW = 7, + ///<summary>link flow velocity</summary> + VELOCITY = 8, + ///<summary>link head loss</summary> + HEADLOSS = 9, + ///<summary>avg. water quality in link</summary> + LINKQUAL = 10, + ///<summary>link status</summary> + STATUS = 11, + ///<summary>pump/valve setting</summary> + SETTING = 12, + ///<summary>avg. reaction rate in link</summary> + REACTRATE = 13, + ///<summary>link friction factor</summary> + FRICTION = 14, + + ///<summary>pump power output</summary> + POWER = 15, + ///<summary>simulation time</summary> + TIME = 16, + ///<summary>tank volume</summary> + VOLUME = 17, + ///<summary>simulation time of day</summary> + CLOCKTIME = 18, + ///<summary>time to fill a tank</summary> + FILLTIME = 19, + ///<summary>time to drain a tank</summary> + DRAINTIME = 20 + } + + ///<summary> + /// 鍙敤鏂囦欢绫诲瀷 + /// </summary> + public enum FileType + { + NET_FILE, + INP_FILE, + NULL_FILE, + } + + /// <summary> + /// 鏇茬嚎绫诲瀷 + /// </summary> + public enum CurveType + { + /// <summary>瀹圭Н鏇茬嚎</summary> + Volume = 0, + + /// <summary>娉垫洸绾�</summary> + Pump = 1, + + /// <summary>鏁堢巼鏇茬嚎</summary> + Efficiency = 2, + + /// <summary>姘村ご鎹熷け鏇茬嚎</summary> + HeadLoss = 3 + } + + ///<summary> + /// 娉垫洸绾跨被鍨� + ///</summary> + public enum PumpType + { + ///<summary>Constant horsepower.</summary> + CONST_HP = 0, + ///<summary>Power function.</summary> + POWER_FUNC = 1, + ///<summary>User-defined custom curve.</summary> + CUSTOM = 2, + /// <summary> </summary> + NOCURVE = 3 + } + + /// <summary> + /// 姘村ご鎹熷け鍏紡绫诲瀷 + /// </summary> + public enum HeadLossFormulaType + { + /// <summary>Hazen-Williams</summary> + HW, + /// <summary>Darcy-Weisbach</summary> + DW, + /// <summary>Chezy-Manning</summary> + CM + } + + /// <summary> + /// 鍗曚綅绯荤粺绫诲瀷 + /// </summary> + public enum UnitsType + { + /// <summary>SI (metric)</summary> + SI, + /// <summary>US</summary> + US + } + + /// <summary> + /// 闃�闂ㄧ被鍨� + /// </summary> + public enum ValveType + { + ///<summary> pressure reducing valve</summary> + [Keyword("PRV")] + PRV = 3, + ///<summary> pressure sustaining valve</summary> + [Keyword("PSV")] + PSV = 4, + ///<summary> pressure breaker valve</summary> + [Keyword("PBV")] + PBV = 5, + ///<summary> flow control valve</summary> + [Keyword("FCV")] + FCV = 6, + ///<summary> throttle control valve</summary> + [Keyword("TCV")] + TCV = 7, + ///<summary> general purpose valve</summary> + [Keyword("GPV")] + GPV = 8 + } + + ///<summary> + /// 绠¤矾/姘寸/娉� 鐘舵�� + /// </summary> + public enum StatusType + { + /// <summary>娉垫棤娉曡緭鍑烘壃绋�(鍏抽棴)</summary> + XHEAD = 0, + /// <summary>鏆傛椂鍏抽棴</summary> + TEMPCLOSED = 1, + /// <summary>鍏抽棴</summary> + CLOSED = 2, + /// <summary>寮�鍚�</summary> + OPEN = 3, + /// <summary>闃�闂ㄥ惎鐢�(閮ㄥ垎鎵撳紑)</summary> + ACTIVE = 4, + /// <summary>娉佃秴杩囨渶澶ф祦閲�</summary> + XFLOW = 5, + /// <summary>FCV 鏃犳硶鎻愪緵娴侀噺</summary> + XFCV = 6, + /// <summary>闃�闂ㄦ棤娉曟彁渚涘帇鍔�</summary> + XPRESSURE = 7, + /// <summary>姘寸鍔犳按</summary> + FILLING = 8, + /// <summary>娓呯┖姘寸</summary> + EMPTYING = 9 + } + + ///<summary> + ///鍙敤閮ㄥ垎绫诲瀷 + /// </summary> + public enum SectType + { + TITLE = 0, + JUNCTIONS = 1, + RESERVOIRS = 2, + TANKS = 3, + PIPES = 4, + PUMPS = 5, + VALVES = 6, + CONTROLS = 7, + RULES = 8, + DEMANDS = 9, + SOURCES = 10, + EMITTERS = 11, + PATTERNS = 12, + CURVES = 13, + QUALITY = 14, + STATUS = 15, + ROUGHNESS = 16, + ENERGY = 17, + REACTIONS = 18, + MIXING = 19, + REPORT = 20, + TIMES = 21, + OPTIONS = 22, + COORDINATES = 23, + VERTICES = 24, + LABELS = 25, + BACKDROP = 26, + TAGS = 27, + END = 28 + } + + ///<summary> + /// 鑼冨洿闄愬埗 + ///</summary> + public enum RangeType + { + ///<summary>涓嬮檺</summary> + LOW = 0, + ///<summary>涓婇檺</summary> + HI = 1, + ///<summary>绮剧‘搴�</summary> + PREC = 2 + } + + /// <summary> + /// 鍘嬪姏鍗曚綅 + /// </summary> + public enum PressUnitsType + { + /// <summary>pounds per square inch</summary> + PSI, + /// <summary>kiloPascals</summary> + KPA, + /// <summary>meters</summary> + METERS + } + + /// <summary> + /// 姘村姏瑙e喅鏂规閫夐」 + /// </summary> + public enum HydType + { + /// <summary>浣跨敤涓婃杩愯鐨勬暟鎹�</summary> + USE, + /// <summary>褰撳墠杩愯鍚庝繚瀛�</summary> + SAVE, + /// <summary>浣跨敤涓存椂鏂囦欢</summary> + SCRATCH + } + + #region TODO: move it to SimulationRule + ///<summary>Rule variables.</summary> + public enum Varwords + { + CLOCKTIME, + DEMAND, + DRAINTIME, + FILLTIME, + FLOW, + GRADE, + HEAD, + LEVEL, + POWER, + PRESSURE, + SETTING, + STATUS, + TIME + } + + ///<summary>Rule values types.</summary> + public enum Values { IS_NUMBER, IS_OPEN, IS_CLOSED, IS_ACTIVE } + + #endregion +} diff --git a/IStation.Epanet/enum/EnumsTxt.cs b/IStation.Epanet/enum/EnumsTxt.cs new file mode 100644 index 0000000..2b7c53e --- /dev/null +++ b/IStation.Epanet/enum/EnumsTxt.cs @@ -0,0 +1,456 @@ +using IStation.Epanet.Util; + +namespace IStation.Epanet.Enums +{ + + public static class EnumsTxt + { + + public static bool TryParse(string text, out Varwords result) + { + if (text.Match(Keywords.wr_DEMAND)) result = Varwords.DEMAND; + else if (text.Match(Keywords.wr_HEAD)) result = Varwords.HEAD; + else if (text.Match(Keywords.wr_GRADE)) result = Varwords.GRADE; + else if (text.Match(Keywords.wr_LEVEL)) result = Varwords.LEVEL; + else if (text.Match(Keywords.wr_PRESSURE)) result = Varwords.PRESSURE; + else if (text.Match(Keywords.wr_FLOW)) result = Varwords.FLOW; + else if (text.Match(Keywords.wr_STATUS)) result = Varwords.STATUS; + else if (text.Match(Keywords.wr_SETTING)) result = Varwords.SETTING; + else if (text.Match(Keywords.wr_POWER)) result = Varwords.POWER; + else if (text.Match(Keywords.wr_TIME)) result = Varwords.CLOCKTIME; + else if (text.Match(Keywords.wr_CLOCKTIME)) result = Varwords.CLOCKTIME; + else if (text.Match(Keywords.wr_FILLTIME)) result = Varwords.FILLTIME; + else if (text.Match(Keywords.wr_DRAINTIME)) result = Varwords.DRAINTIME; + else + { + result = (Varwords)(-1); + return false; + } + + return true; + } + + + public static bool TryParse(string text, out Values result) + { + if (text.Match(Keywords.wr_ACTIVE)) result = Values.IS_ACTIVE; + else if (text.Match(Keywords.wr_CLOSED)) result = Values.IS_CLOSED; + // else if (text.Match("XXXX")) result = Rule.Values.IS_NUMBER; + else if (text.Match(Keywords.wr_OPEN)) result = Values.IS_OPEN; + else + { + result = (Values)(-1); + return false; + } + + return true; + } + + + public static bool TryParse(this string text, out Rulewords result) + { + int index = text.FindMatch( + text, + Keywords.wr_AND, + Keywords.wr_ELSE, + string.Empty, + Keywords.wr_IF, + Keywords.wr_OR, + Keywords.wr_PRIORITY, + Keywords.wr_RULE, + Keywords.wr_THEN); + + return (int)(result = (Rulewords)index) != -1; + } + + public static bool TryParse(this string text, out Operators result) + { + text = text.Trim(); + + if (text.Equals(Keywords.wr_ABOVE, StringComparison.OrdinalIgnoreCase)) + result = Operators.ABOVE; + else if (text.Equals(Keywords.wr_BELOW, StringComparison.OrdinalIgnoreCase)) + result = Operators.BELOW; + else if (text == "=") + result = Operators.EQ; + else if (text == ">=") + result = Operators.GE; + else if (text == ">") + result = Operators.GT; + else if (text.Equals(Keywords.wr_IS, StringComparison.OrdinalIgnoreCase)) + result = Operators.IS; + else if (text == "<=") + result = Operators.LE; + else if (text == "<") + result = Operators.LT; + else if (text == "<>") + result = Operators.NE; + else if (text.Equals(Keywords.wr_NOT, StringComparison.OrdinalIgnoreCase)) + result = Operators.NOT; + else + { + result = (Operators)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out Objects result) + { + if (text.Match(Keywords.wr_JUNC)) result = Objects.JUNC; + else if (text.Match(Keywords.w_RESERV)) result = Objects.RESERV; + else if (text.Match(Keywords.w_TANK)) result = Objects.TANK; + else if (text.Match(Keywords.w_PIPE)) result = Objects.PIPE; + else if (text.Match(Keywords.w_PUMP)) result = Objects.PUMP; + else if (text.Match(Keywords.w_VALVE)) result = Objects.VALVE; + else if (text.Match(Keywords.w_NODE)) result = Objects.NODE; + else if (text.Match(Keywords.w_LINK)) result = Objects.LINK; + else if (text.Match(Keywords.w_SYSTEM)) result = Objects.SYSTEM; + else + { + result = (Objects)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out MixType result) + { + if (text.Match(Keywords.w_MIXED)) result = MixType.Mix1; + else if (text.Match(Keywords.w_2COMP)) result = MixType.Mix2; + else if (text.Match(Keywords.w_FIFO)) result = MixType.Fifo; + else if (text.Match(Keywords.w_LIFO)) result = MixType.Lifo; + else + { + result = (MixType)(-1); + return false; + } + + return true; + } + + public static string ParseStr(this MixType value) + { + switch (value) + { + case MixType.Fifo: return Keywords.w_FIFO; + case MixType.Lifo: return Keywords.w_LIFO; + case MixType.Mix1: return Keywords.w_MIXED; + case MixType.Mix2: return Keywords.w_2COMP; + default: return null; + } + } + + public static bool TryParse(this string text, out FieldType result) + { + if (text.Match(Keywords.t_ELEV)) result = FieldType.ELEV; + else if (text.Match(Keywords.t_DEMAND)) result = FieldType.DEMAND; + else if (text.Match(Keywords.t_HEAD)) result = FieldType.HEAD; + else if (text.Match(Keywords.t_PRESSURE)) result = FieldType.PRESSURE; + else if (text.Match(Keywords.t_QUALITY)) result = FieldType.QUALITY; + else if (text.Match(Keywords.t_LENGTH)) result = FieldType.LENGTH; + else if (text.Match(Keywords.t_DIAM)) result = FieldType.DIAM; + else if (text.Match(Keywords.t_FLOW)) result = FieldType.FLOW; + else if (text.Match(Keywords.t_VELOCITY)) result = FieldType.VELOCITY; + else if (text.Match(Keywords.t_HEADLOSS)) result = FieldType.HEADLOSS; + else if (text.Match(Keywords.t_LINKQUAL)) result = FieldType.LINKQUAL; + else if (text.Match(Keywords.t_STATUS)) result = FieldType.STATUS; + else if (text.Match(Keywords.t_SETTING)) result = FieldType.SETTING; + else if (text.Match(Keywords.t_REACTRATE)) result = FieldType.REACTRATE; + else if (text.Match(Keywords.t_FRICTION)) result = FieldType.FRICTION; + else + { + result = (FieldType)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out SourceType result) + { + if (text.Match(Keywords.w_CONCEN)) result = SourceType.Concen; + else if (text.Match(Keywords.w_FLOWPACED)) result = SourceType.FlowPaced; + else if (text.Match(Keywords.w_MASS)) result = SourceType.Mass; + else if (text.Match(Keywords.w_SETPOINT)) result = SourceType.SetPoint; + else + { + result = (SourceType)(-1); + return false; + } + + return true; + } + + public static string ReportStr(this StatusType value) + { + switch (value) + { + case StatusType.XHEAD: return Keywords.t_XHEAD; + case StatusType.TEMPCLOSED: return Keywords.t_TEMPCLOSED; + case StatusType.CLOSED: return Keywords.t_CLOSED; + case StatusType.OPEN: return Keywords.t_OPEN; + case StatusType.ACTIVE: return Keywords.t_ACTIVE; + case StatusType.XFLOW: return Keywords.t_XFLOW; + case StatusType.XFCV: return Keywords.t_XFCV; + case StatusType.XPRESSURE: return Keywords.t_XPRESSURE; + case StatusType.FILLING: return Keywords.t_FILLING; + case StatusType.EMPTYING: return Keywords.t_EMPTYING; + default: return null; + } + } + + public static string ParseStr(this ControlType value) + { + switch (value) + { + case ControlType.HiLevel: return Keywords.w_ABOVE; + case ControlType.LowLevel: return Keywords.w_BELOW; + case ControlType.TimeOfDay: return Keywords.w_CLOCKTIME; + case ControlType.Timer: return Keywords.w_TIME; + default: return null; + } + } + + public static string ParseStr(this FieldType value) + { + switch (value) + { + case FieldType.ELEV: return Keywords.t_ELEV; + case FieldType.DEMAND: return Keywords.t_DEMAND; + case FieldType.HEAD: return Keywords.t_HEAD; + case FieldType.PRESSURE: return Keywords.t_PRESSURE; + case FieldType.QUALITY: return Keywords.t_QUALITY; + case FieldType.LENGTH: return Keywords.t_LENGTH; + case FieldType.DIAM: return Keywords.t_DIAM; + case FieldType.FLOW: return Keywords.t_FLOW; + case FieldType.VELOCITY: return Keywords.t_VELOCITY; + case FieldType.HEADLOSS: return Keywords.t_HEADLOSS; + case FieldType.LINKQUAL: return Keywords.t_LINKQUAL; + case FieldType.STATUS: return Keywords.t_STATUS; + case FieldType.SETTING: return Keywords.t_SETTING; + case FieldType.REACTRATE: return Keywords.t_REACTRATE; + case FieldType.FRICTION: return Keywords.t_FRICTION; + default: return null; + } + } + + public static bool TryParse(this string text, out FlowUnitsType result) + { + if (text.Match(Keywords.w_CFS)) result = FlowUnitsType.Cfs; + else if (text.Match(Keywords.w_GPM)) result = FlowUnitsType.Gpm; + else if (text.Match(Keywords.w_MGD)) result = FlowUnitsType.Mgd; + else if (text.Match(Keywords.w_IMGD)) result = FlowUnitsType.Imgd; + else if (text.Match(Keywords.w_AFD)) result = FlowUnitsType.Afd; + else if (text.Match(Keywords.w_LPS)) result = FlowUnitsType.Lps; + else if (text.Match(Keywords.w_LPM)) result = FlowUnitsType.Lpm; + else if (text.Match(Keywords.w_MLD)) result = FlowUnitsType.Mld; + else if (text.Match(Keywords.w_CMH)) result = FlowUnitsType.Cmh; + else if (text.Match(Keywords.w_CMD)) result = FlowUnitsType.Cmd; + else + { + result = (FlowUnitsType)(-1); + return false; + } + + return true; + } + + public static string ParseStr(this FlowUnitsType value) + { + switch (value) + { + case FlowUnitsType.Afd: return Keywords.w_AFD; + case FlowUnitsType.Cfs: return Keywords.w_CFS; + case FlowUnitsType.Cmd: return Keywords.w_CMD; + case FlowUnitsType.Cmh: return Keywords.w_CMH; + case FlowUnitsType.Gpm: return Keywords.w_GPM; + case FlowUnitsType.Imgd: return Keywords.w_IMGD; + case FlowUnitsType.Lpm: return Keywords.w_LPM; + case FlowUnitsType.Lps: return Keywords.w_LPS; + case FlowUnitsType.Mgd: return Keywords.w_MGD; + case FlowUnitsType.Mld: return Keywords.w_MLD; + default: return null; + } + } + + /// <summary>Parse string id.</summary> + public static string ParseStr(this HeadLossFormulaType value) + { + switch (value) + { + case HeadLossFormulaType.CM: return Keywords.w_CM; + case HeadLossFormulaType.DW: return Keywords.w_DW; + case HeadLossFormulaType.HW: return Keywords.w_HW; + default: return null; + } + } + + public static bool TryParse(this string text, out QualType result) + { + if (text.Match(Keywords.w_NONE)) result = QualType.None; + else if (text.Match(Keywords.w_CHEM)) result = QualType.Chem; + else if (text.Match(Keywords.w_AGE)) result = QualType.Age; + else if (text.Match(Keywords.w_TRACE)) result = QualType.Trace; + else + { + result = (QualType)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out StatusLevel result) + { + if (text.Match(Keywords.w_NO)) result = StatusLevel.None; + else if (text.Match(Keywords.w_FULL)) result = StatusLevel.FULL; + else if (text.Match(Keywords.w_YES)) result = StatusLevel.Normal; + else + { + result = (StatusLevel)(-1); + return false; + } + + return true; + } + + public static string ParseStr(this StatusLevel value) + { + switch (value) + { + case StatusLevel.None: return Keywords.w_NO; + case StatusLevel.FULL: return Keywords.w_FULL; + case StatusLevel.Normal: return Keywords.w_YES; + default: return null; + } + } + + public static string ParseStr(this TstatType value) + { + switch (value) + { + case TstatType.Average: return Keywords.w_AVG; + case TstatType.Maximum: return Keywords.w_MAX; + case TstatType.Minimum: return Keywords.w_MIN; + case TstatType.Range: return Keywords.w_RANGE; + case TstatType.None: return Keywords.w_NONE; + default: return null; + } + } + public static bool TryParse(this string text, out TstatType result) + { + if (text.Match(Keywords.w_NONE)) result = TstatType.None; + else if (text.Match(Keywords.w_NO)) result = TstatType.None; + else if (text.Match(Keywords.w_AVG)) result = TstatType.Average; + else if (text.Match(Keywords.w_MIN)) result = TstatType.Minimum; + else if (text.Match(Keywords.w_MAX)) result = TstatType.Maximum; + else if (text.Match(Keywords.w_RANGE)) result = TstatType.Range; + else + { + result = (TstatType)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out RangeType result) + { + if (text.Match(Keywords.w_BELOW)) result = RangeType.LOW; + else if (text.Match(Keywords.w_ABOVE)) result = RangeType.HI; + else if (text.Match(Keywords.w_PRECISION)) result = RangeType.PREC; + else + { + result = (RangeType)(-1); + return false; + } + + return true; + } + + public static string ParseStr(this SectType value) + { + if (value < SectType.TITLE || value > SectType.END) + { + // throw new System.ArgumentOutOfRangeException("value"); + return null; + } + + return "[" + value + "]"; + } + + public static string ParseStr(this PressUnitsType value) + { + switch (value) + { + case PressUnitsType.KPA: return Keywords.w_KPA; + case PressUnitsType.METERS: return Keywords.w_METERS; + case PressUnitsType.PSI: return Keywords.w_PSI; + default: return null; + } + } + + public static bool TryParse(this string text, out PressUnitsType result) + { + if (text.Match(Keywords.w_PSI)) result = PressUnitsType.PSI; + else if (text.Match(Keywords.w_KPA)) result = PressUnitsType.KPA; + else if (text.Match(Keywords.w_METERS)) result = PressUnitsType.METERS; + else + { + result = (PressUnitsType)(-1); + return false; + } + + return true; + + } + + public static bool TryParse(this string text, out HeadLossFormulaType result) + { + if (text.Match(Keywords.w_HW)) result = HeadLossFormulaType.HW; + else if (text.Match(Keywords.w_DW)) result = HeadLossFormulaType.DW; + else if (text.Match(Keywords.w_CM)) result = HeadLossFormulaType.CM; + else + { + result = (HeadLossFormulaType)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out HydType result) + { + if (text.Match(Keywords.w_USE)) result = HydType.USE; + else if (text.Match(Keywords.w_SAVE)) result = HydType.SAVE; + else + { + result = (HydType)(-1); + return false; + } + + return true; + } + + public static bool TryParse(this string text, out ValveType result) + { + if (text.Match(Keywords.w_PRV)) result = ValveType.PRV; + else if (text.Match(Keywords.w_PSV)) result = ValveType.PSV; + else if (text.Match(Keywords.w_FCV)) result = ValveType.FCV; + else if (text.Match(Keywords.w_TCV)) result = ValveType.TCV; + else if (text.Match(Keywords.w_PBV)) result = ValveType.PBV; + else if (text.Match(Keywords.w_GPV)) result = ValveType.GPV; + else + { + result = (ValveType)(-1); + return false; + } + + return true; + } + } + +} diff --git a/IStation.Epanet/enum/Error.cs b/IStation.Epanet/enum/Error.cs new file mode 100644 index 0000000..c8bde6a --- /dev/null +++ b/IStation.Epanet/enum/Error.cs @@ -0,0 +1,23 @@ +using System.Reflection; + +namespace IStation.Epanet.Enums +{ + public static class Error + { + public static string GetMsg(this ErrorCode errorCode) + { + FieldInfo field; + Type t = typeof(ErrorCode); + if (t.IsEnum + && (field = t.GetField(errorCode.ToString())) != null + && Attribute.GetCustomAttribute(field, typeof(ErrorAttribute)) is ErrorAttribute att + && att.Msg != null) + { + return att.Msg; + } + + return string.Empty; + } + + } +} \ No newline at end of file diff --git a/IStation.Epanet/enum/ErrorAttribute.cs b/IStation.Epanet/enum/ErrorAttribute.cs new file mode 100644 index 0000000..2152c0b --- /dev/null +++ b/IStation.Epanet/enum/ErrorAttribute.cs @@ -0,0 +1,12 @@ +锘縩amespace IStation.Epanet.Enums +{ + internal sealed class ErrorAttribute : Attribute + { + public ErrorAttribute(string msg) { Msg = msg; } + + public string Msg { get; } + } + + + +} diff --git a/IStation.Epanet/enum/ErrorCode.cs b/IStation.Epanet/enum/ErrorCode.cs new file mode 100644 index 0000000..291b0b3 --- /dev/null +++ b/IStation.Epanet/enum/ErrorCode.cs @@ -0,0 +1,248 @@ +锘縩amespace IStation.Epanet.Enums +{ + /// <summary> + /// 閿欒浠g爜 + /// </summary> + public enum ErrorCode + { + /// <summary>No error</summary> + Ok = 0, + + /// <summary> WARNING:System hydraulically unbalanced.</summary> + [Error("璀﹀憡:绯荤粺娑插帇澶辫 ")] + Warn1 = 1, + + /// <summary> WARNING:System may be hydraulically unstable.</summary> + [Error("璀﹀憡:绯荤粺娑插帇鍙兘涓嶇ǔ瀹�")] + Warn2 = 2, + + /// <summary> WARNING:System disconnected.</summary> + [Error("璀﹀憡:绯荤粺鏂紑杩炴帴")] + Warn3 = 3, + + /// <summary> WARNING:Pumps cannot deliver enough flow or head.</summary> + [Error("璀﹀憡:娉垫棤娉曟彁渚涜冻澶熺殑娴侀噺鎴栨壃绋�")] + Warn4 = 4, + + /// <summary> WARNING:Valves cannot deliver enough flow.</summary> + [Error("璀﹀憡:闃�闂ㄦ棤娉曟彁渚涜冻澶熺殑娴侀噺")] + Warn5 = 5, + + /// <summary> WARNING:System has negative pressures.</summary> + [Error("璀﹀憡:绯荤粺鏈夎礋鍘�")] + Warn6 = 6, + + /// <summary>System Error 101:insufficient memory available.</summary> + [Error("绯荤粺閿欒:鍙敤鍐呭瓨涓嶈冻")] + Err101 = 101, + + /// <summary>System Error 102:no network data available.</summary> + [Error("绯荤粺閿欒:娌℃湁闇�瑕佸鐞嗙殑绠$綉鏁版嵁")] + Err102 = 102, + + /// <summary>System Error 103:hydraulics not initialized.</summary> + [Error("绯荤粺閿欒:姘村姏姹傝В鍣ㄦ病鏈夊垵濮嬪寲")] + Err103 = 103, + + /// <summary>System Error 104:no hydraulics for water quality analysis.</summary> + [Error("绯荤粺閿欒:娌℃湁鍙敤鐨勬按鍔涚粨鏋�")] + Err104 = 104, + + /// <summary>System Error 105:water quality not initialized.</summary> + [Error("绯荤粺閿欒:姘磋川姹傝В鍣ㄦ病鏈夊垵濮嬪寲")] + Err105 = 105, + + /// <summary>System Error 106:no results saved to report on.</summary> + [Error("绯荤粺閿欒:娌℃湁鍙互鎶ュ憡鐨勭粨鏋�")] + Err106 = 106, + + /// <summary>System Error 107:hydraulics supplied from external file.</summary> + [Error("绯荤粺閿欒:娑插帇绯荤粺鐢卞閮ㄦ枃浠舵彁渚�")] + Err107 = 107, + + /// <summary>System Error 108:cannot use external file while hydraulics solver is active.</summary> + [Error("绯荤粺閿欒:娑插帇姹傝В鍣ㄦ縺娲绘椂鏃犳硶浣跨敤澶栭儴鏂囦欢")] + Err108 = 108, + + /// <summary>System Error 109:cannot change time parameter when solver is active.</summary> + [Error("绯荤粺閿欒:姹傝В鍣ㄦ縺娲绘椂鏃犳硶鏇存敼鏃堕棿鍙傛暟")] + Err109 = 109, + + /// <summary>System Error 110:cannot solve network hydraulic equations.</summary> + [Error("绯荤粺閿欒:涓嶈兘澶熸眰瑙f按鍔涙柟绋嬬粍")] + Err110 = 110, + + /// <summary>System Error 120:cannot solve water quality transport equations.</summary> + [Error("绯荤粺閿欒:涓嶈兘澶熸眰瑙Q杩佺Щ鏂圭▼缁�")] + Err120 = 120, + + /// <summary>Input Error 200:one or more errors in input file.</summary> + [Error("杈撳叆閿欒:杈撳叆鏂囦欢涓湁涓�澶勬垨鑰呭澶勯敊璇�")] + Err200 = 200, + + /// <summary>Input Error 201:syntax error in following line of [%s] section:</summary> + [Error("杈撳叆閿欒:syntax error in following line of [%s] section")] + Err201 = 201, + + /// <summary>Input Error 202:%s %s contains illegal numeric value.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夐潪娉曟暟鍊�")] + Err202 = 202, + + /// <summary>Input Error 203:%s %s refers to undefined node.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夋湭瀹氫箟鐨勮妭鐐�")] + Err203 = 203, + + /// <summary>Input Error 204:%s %s refers to undefined link.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夋湭瀹氫箟鐨勭娈�")] + Err204 = 204, + + /// <summary>Input Error 205:%s %s refers to undefined time pattern.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夋湭瀹氫箟鐨勬椂闂存ā寮�")] + Err205 = 205, + + /// <summary>Input Error 206:%s %s refers to undefined curve.</summary> + [Error("杈撳叆閿欒:%s %s refers to undefined curve")] + Err206 = 206, + + /// <summary>Input Error 207:%s %s attempts to control a CV.</summary> + [Error("杈撳叆閿欒:璇曞浘鎺у埗姝㈠洖闃�")] + Err207 = 207, + + /// <summary>Input Error 208:%s specified for undefined Node %s.</summary> + [Error("杈撳叆閿欒:%s specified for undefined Node %s")] + Err208 = 208, + + /// <summary>Input Error 209:illegal %s value for Node %s.</summary> + [Error("杈撳叆閿欒:illegal %s value for Node %s")] + Err209 = 209, + + /// <summary>Input Error 210:%s specified for undefined Link %s.</summary> + [Error("杈撳叆閿欒:%s specified for undefined Link %s")] + Err210 = 210, + + /// <summary>Input Error 211:illegal %s value for Link %s.</summary> + [Error("杈撳叆閿欒:illegal %s value for Link %s")] + Err211 = 211, + + /// <summary>Input Error 212:trace node %.0s %s is undefined.</summary> + [Error("杈撳叆閿欒:trace node %.0s %s is undefined")] + Err212 = 212, + + /// <summary>Input Error 213:illegal option value in [%s] section:</summary> + [Error("杈撳叆閿欒:illegal option value in [%s] section")] + Err213 = 213, + + /// <summary>Input Error 214:following line of [%s] section contains too many characters:</summary> + [Error("杈撳叆閿欒:following line of [%s] section contains too many characters")] + Err214 = 214, + + /// <summary>Input Error 215:%s %s is a duplicate ID.</summary> + [Error("杈撳叆閿欒:%s %s is a duplicate ID")] + Err215 = 215, + + /// <summary>Input Error 216:%s data specified for undefined Pump %s.</summary> + [Error("杈撳叆閿欒:%s data specified for undefined Pump %s")] + Err216 = 216, + + /// <summary>Input Error 217:invalid %s data for Pump %s.</summary> + [Error("杈撳叆閿欒:invalid %s data for Pump %s")] + Err217 = 217, + + /// <summary>Input Error 219:%s %s illegally connected to a tank.</summary> + [Error("杈撳叆閿欒:%s %s illegally connected to a tank")] + Err219 = 219, + + /// <summary>Input Error 220:%s %s illegally connected to another valve.</summary> + [Error("杈撳叆閿欒:%s %s illegally connected to another valve")] + Err220 = 220, + + /*** Updated on 10/25/00 ***/ + + /// <summary>Input Error 222:%s %s has same start and end nodes.</summary> + [Error("杈撳叆閿欒:%s %s has same start and end nodes")] + Err222 = 222, + + /// <summary>Input Error 223:not enough nodes in network</summary> + [Error("杈撳叆閿欒:绠$綉涓病鏈夊厖鍒嗙殑鑺傜偣")] + Err223 = 223, + + /// <summary>Input Error 224:no tanks or reservoirs in network.</summary> + [Error("杈撳叆閿欒:绠$綉涓笉瀛樺湪姘存睜鎴栬�呮按搴�")] + Err224 = 224, + + /// <summary>Input Error 225:invalid lower/upper levels for Tank %s.</summary> + [Error("杈撳叆閿欒:invalid lower/upper levels for Tank %s")] + Err225 = 225, + + /// <summary>Input Error 226:no head curve supplied for Pump %s.</summary> + [Error("杈撳叆閿欒:no head curve supplied for Pump %s")] + Err226 = 226, + + /// <summary>Input Error 227:invalid head curve for Pump %s.</summary> + [Error("杈撳叆閿欒:invalid head curve for Pump %s")] + Err227 = 227, + + /// <summary>Input Error 230:Curve %s has nonincreasing x-values.</summary> + [Error("杈撳叆閿欒:Curve %s has nonincreasing x-values")] + Err230 = 230, + + /// <summary>Input Error 233:Node %s is unconnected.</summary> + [Error("杈撳叆閿欒:Node %s is unconnected")] + Err233 = 233, + + /// <summary>Input Error 240:%s %s refers to undefined source.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夋湭瀹氫箟鐨勬按婧�")] + Err240 = 240, + + /// <summary>Input Error 241:%s %s refers to undefined control.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夋湭瀹氫箟鐨勬帶鍒惰鍙�")] + Err241 = 241, + + /// <summary>Input Error 250:function call contains invalid format.</summary> + [Error("杈撳叆閿欒:鍑芥暟鍙傛暟鏍煎紡闈炴硶")] + Err250 = 250, + + /// <summary>Input Error 251:function call contains invalid parameter code.</summary> + [Error("杈撳叆閿欒:鍑芥暟璋冪敤涓叿鏈夐潪娉曞弬鏁颁唬鍙�")] + Err251 = 251, + + /// <summary>File Error 301:identical file names.</summary> + [Error("鏂囦欢閿欒:鐩稿悓鐨勬枃浠跺悕")] + Err301 = 301, + + /// <summary>File Error 302:cannot open input file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熸墦寮�杈撳叆鏂囦欢")] + Err302 = 302, + + /// <summary>File Error 303:cannot open report file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熸墦寮�鎶ュ憡鏂囦欢")] + Err303 = 303, + + /// <summary>File Error 304:cannot open binary output file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熸墦寮�浜岃繘鍒惰緭鍑烘枃浠�")] + Err304 = 304, + + /// <summary>File Error 305:cannot open hydraulics file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熸墦寮�姘村姏鏂囦欢")] + Err305 = 305, + + /// <summary>File Error 306:hydraulics file does not match network data.</summary> + [Error("鏂囦欢閿欒:闈炴硶姘村姏鏂囦欢")] + Err306 = 306, + + /// <summary>File Error 307:cannot read hydraulics file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熼槄璇绘按鍔涙枃浠�")] + Err307 = 307, + + /// <summary>File Error 308:cannot save results to file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熷皢缁撴灉淇濆瓨鍒版枃浠�")] + Err308 = 308, + + /// <summary>File Error 309:cannot save results to report file.</summary> + [Error("鏂囦欢閿欒:涓嶈兘澶熷皢鎶ュ憡鍐欏叆鏂囦欢")] + Err309 = 309, + } + + + +} diff --git a/IStation.Epanet/enum/KeywordAttribute.cs b/IStation.Epanet/enum/KeywordAttribute.cs new file mode 100644 index 0000000..acc275f --- /dev/null +++ b/IStation.Epanet/enum/KeywordAttribute.cs @@ -0,0 +1,12 @@ +锘縩amespace IStation.Epanet.Enums +{ + internal sealed class KeywordAttribute : Attribute + { + public KeywordAttribute(string keyword) { Keyword = keyword; } + + public string Keyword { get; } + } + + + +} diff --git a/IStation.Epanet/enum/Keywords.cs b/IStation.Epanet/enum/Keywords.cs new file mode 100644 index 0000000..339955f --- /dev/null +++ b/IStation.Epanet/enum/Keywords.cs @@ -0,0 +1,347 @@ +using System.Reflection; + +namespace IStation.Epanet.Enums +{ + + ///<summary>Parse and report keywords.</summary> + internal static class Keywords + { + + /// <summary>Retrives <see cref="KeywordAttribute"/> value from enum member.</summary> + /// <returns>Keyword strng on success, <see cref="string.Empty"/> otherwise.</returns> + public static string Keyword2<T>(this T value) where T : struct + { + FieldInfo field; + Type t = typeof(T); + + if (t.IsEnum + && (field = t.GetField(value.ToString())) != null + && Attribute.GetCustomAttribute(field, typeof(KeywordAttribute)) is KeywordAttribute att + && att.Keyword != null) + { + return att.Keyword; + } + + return string.Empty; + + } + + /// <summary>Searches for Enum member, marked with <see cref="KeywordAttribute"/> with value <paramref name="keyword"/>.</summary> + /// <typeparam name="T">Enum type.</typeparam> + /// <param name="keyword">Value of <see cref="KeywordAttribute"/> to search.</param> + /// <param name="value">Recives value of Enum member found.</param> + /// <returns>true, if Enum member witn <paramref name="keyword"/> was found, false if not.</returns> + public static bool ToEnum<T>(this string keyword, out T value) where T : struct + { + Type t = typeof(T); + + if (t.IsEnum) + foreach (FieldInfo fi in t.GetFields()) + if (Attribute.GetCustomAttribute(fi, typeof(KeywordAttribute)) is KeywordAttribute at) + if (string.Equals(at.Keyword, keyword, StringComparison.OrdinalIgnoreCase)) + { + value = (T)fi.GetRawConstantValue(); + return true; + } + + value = default(T); + return false; + } + + public const string t_ACTIVE = "active"; + public const string t_CHEMICAL = "Chemical"; + public const string t_CLOSED = "closed"; + public const string t_CM = "Chezy-Manning"; + public const string t_CONTINUED = " (continued)"; + + public const string t_CONTROL = "Control"; + public const string t_COORD = "Coordinate"; + public const string t_CURVE = "Curve"; + public const string t_DEMAND = "Demand"; + public const string t_DEMANDFOR = "Demand for Node"; + public const string t_DIAM = "Diameter"; + public const string t_DIFFER = "DIFFERENTIAL"; + public const string t_DW = "Darcy-Weisbach"; + public const string t_ELEV = "Elevation"; + public const string t_EMITTER = "Emitter"; + public const string t_EMPTYING = "emptying"; + + public const string t_END = "End"; + public const string t_ENERGY = "Energy"; + public const string t_FILLING = "filling"; + + public const string t_FLOW = "Flow"; + public const string t_FRICTION = "F-Factor"; + public const string t_FUNCCALL = "function call"; + public const string t_HALTED = " EXECUTION HALTED."; + public const string t_HEAD = "Head"; + public const string t_HEADLOSS = "Headloss"; + public const string t_HW = "Hazen-Williams"; + public const string t_JUNCTION = "Junction"; + public const string t_LABEL = "Label"; + public const string t_LENGTH = "Length"; + public const string t_LINKID = "Link"; + + public const string t_LINKQUAL = "Quality"; + public const string t_LINKSTATUS = "State"; + public const string t_MIXING = "Mixing"; + + public const string t_NODEID = "Node"; + public const string t_OPEN = "open"; + public const string t_OPTION = "Options"; + public const string t_PATTERN = "Pattern"; + + public const string t_PERDAY = "/day"; + public const string t_perM3 = " /m3"; + public const string t_perMGAL = "/Mgal"; + public const string t_PIPE = "Pipe"; + public const string t_PRESSURE = "Pressure"; + public const string t_PUMP = "Pump"; + public const string t_QUALITY = "Quality"; + public const string t_REACTION = "Reaction"; + public const string t_REACTRATE = "Reaction"; + public const string t_REPORT = "Report"; + public const string t_RESERVOIR = "Reservoir"; + public const string t_ROUGHNESS = "Roughness"; + + public const string t_RULE = "Rule"; + public const string t_RULES_SECT = "[RULES] section"; + public const string t_SETTING = "Setting"; + public const string t_SOURCE = "Source"; + public const string t_STATUS = "Status"; + + public const string t_TAG = "Tag"; + public const string t_TANK = "Tank"; + public const string t_TEMPCLOSED = "temporarily closed"; + public const string t_TIME = "Times"; + public const string t_TITLE = "Title"; + public const string t_VALVE = "Valve"; + public const string t_VELOCITY = "Velocity"; + public const string t_VERTICE = "Vertice"; + public const string t_XFCV = "open but cannot deliver flow"; + public const string t_XFLOW = "open but exceeds maximum flow"; + public const string t_XHEAD = "closed because cannot deliver head"; + + public const string t_XPRESSURE = "open but cannot deliver pressure"; + public const string u_AFD = "a-f/d"; + + // Units + public const string u_CFS = "cfs"; + public const string u_CMD = "m3/d"; + public const string u_CMH = "m3/h"; + public const string u_FEET = "ft"; + public const string u_FTperSEC = "fps"; + public const string u_GPM = "gpm"; + + public const string u_HOURS = "hrs"; + public const string u_HP = "hp"; + public const string u_IMGD = "Imgd"; + public const string u_INCHES = "in"; + public const string u_KPA = "kPa"; + public const string u_KW = "kw"; + + public const string u_LPM = "Lpm"; + public const string u_LPS = "L/s"; + public const string u_METERS = "m"; + public const string u_MGD = "mgd"; + + public const string u_MGperL = "mg/L"; + public const string u_MINUTES = "min"; + + public const string u_MLD = "ML/d"; + public const string u_MMETERS = "mm"; + public const string u_MperSEC = "m/s"; + public const string u_per1000FT = "/kft"; + + public const string u_per1000M = "/km"; + public const string u_PERCENT = "%"; + public const string u_PSI = "psi"; + public const string u_SQFTperSEC = "sq ft/sec"; + + public const string u_SQMperSEC = "sq m/sec"; + public const string u_UGperL = "ug/L"; + public const string w_2COMP = "2COMP"; + public const string w_ABOVE = "ABOVE"; + public const string w_ACCURACY = "ACCU"; + + public const string w_ACTIVE = "ACTIVE"; + public const string w_ADD = "ADD"; + public const string w_AFD = "AFD"; + + public const string w_AGE = "AGE"; + + public const string w_ALL = "ALL"; + public const string w_AM = "AM"; + public const string w_AVG = "AVERAGE"; + public const string w_BELOW = "BELOW"; + public const string w_BULK = "BULK"; + public const string w_CFS = "CFS"; + public const string w_CHECKFREQ = "CHECKFREQ"; + public const string w_CHEM = "CHEM"; + public const string w_CLOCKTIME = "CLOCKTIME"; + public const string w_CLOSED = "CLOSED"; + public const string w_CM = "C-M"; + public const string w_CMD = "CMD"; + public const string w_CMH = "CMH"; + public const string w_CONCEN = "CONCEN"; + public const string w_CONTINUE = "CONT"; + public const string w_CURVE = "CURV"; + public const string w_CV = "CV"; + public const string w_DAMPLIMIT = "DAMPLIMIT"; + public const string w_DAYS = "DAY"; + public const string w_DEMAND = "DEMA"; + public const string w_DIAM = "DIAM"; + public const string w_DIFFUSIVITY = "DIFF"; + public const string w_DMNDCHARGE = "DEMAN"; + public const string w_DRAINTIME = "DRAI"; + public const string w_DURATION = "DURA"; + public const string w_DW = "D-W"; + public const string w_EFFIC = "EFFI"; + public const string w_ELEV = "ELEV"; + public const string w_EMITTER = "EMIT"; + + public const string w_ENERGY = "ENER"; + public const string w_FCV = "FCV"; + public const string w_FIFO = "FIFO"; + + public const string w_FILE = "FILE"; + public const string w_FILLTIME = "FILL"; + public const string w_FLOW = "FLOW"; + public const string w_FLOWPACED = "FLOWPACED"; + public const string w_FULL = "FULL"; + public const string w_GLOBAL = "GLOB"; + public const string w_GPM = "GPM"; + public const string w_GPV = "GPV"; + public const string w_GRADE = "GRADE"; + public const string w_HEAD = "HEAD"; + public const string w_HEADLOSS = "HEADL"; + public const string w_HOURS = "HOU"; + public const string w_HTOL = "HTOL"; + public const string w_HW = "H-W"; + public const string w_HYDRAULIC = "HYDR"; + + public const string w_IMGD = "IMGD"; + public const string w_IS = "IS"; + public const string w_JUNC = "Junc"; + public const string w_KPA = "KPA"; + public const string w_LEVEL = "LEVEL"; + public const string w_LIFO = "LIFO"; + public const string w_LIMITING = "LIMIT"; + public const string w_LINK = "LINK"; + public const string w_LPM = "LPM"; + public const string w_LPS = "LPS"; + public const string w_MAP = "MAP"; + public const string w_MASS = "MASS"; + public const string w_MAX = "MAXIMUM"; + public const string w_MAXCHECK = "MAXCHECK"; + public const string w_MESSAGES = "MESS"; + public const string w_METERS = "METERS"; + public const string w_MGD = "MGD"; + public const string w_MIN = "MINIMUM"; + public const string w_MINIMUM = "MINI"; + public const string w_MINUTES = "MIN"; + public const string w_MIXED = "MIXED"; + public const string w_MLD = "MLD"; + public const string w_MULTIPLY = "MULT"; + public const string w_NO = "NO"; + public const string w_NODE = "NODE"; + public const string w_NONE = "NONE"; + public const string w_NOT = "NOT"; + public const string w_OPEN = "OPEN"; + public const string w_ORDER = "ORDER"; + public const string w_PAGE = "PAGE"; + public const string w_PATTERN = "PATT"; + public const string w_PBV = "PBV"; + public const string w_PIPE = "Pipe"; + public const string w_PM = "PM"; + public const string w_POWER = "POWE"; + + public const string w_PRECISION = "PREC"; + public const string w_PRESSURE = "PRES"; + public const string w_PRICE = "PRICE"; + public const string w_PRV = "PRV"; + public const string w_PSI = "PSI"; + public const string w_PSV = "PSV"; + public const string w_PUMP = "Pump"; + public const string w_QTOL = "QTOL"; + public const string w_QUALITY = "QUAL"; + public const string w_RANGE = "RANGE"; + public const string w_REPORT = "REPO"; + public const string w_RESERV = "Reser"; + public const string w_ROUGHNESS = "ROUG"; + public const string w_RQTOL = "RQTOL"; + public const string w_RULE = "RULE"; + public const string w_SAVE = "SAVE"; + public const string w_SECONDS = "SEC"; + public const string w_SEGMENTS = "SEGM"; + public const string w_SETPOINT = "SETPOINT"; + public const string w_SETTING = "SETT"; + public const string w_SI = "SI"; + public const string w_SPECGRAV = "SPEC"; + public const string w_SPEED = "SPEE"; + public const string w_START = "STAR"; + public const string w_STATISTIC = "STAT"; + public const string w_STATUS = "STATUS"; + public const string w_STOP = "STOP"; + public const string w_SUMMARY = "SUMM"; + public const string w_SYSTEM = "SYST"; + + public const string w_TANK = "Tank"; + public const string w_TCV = "TCV"; + public const string w_TIME = "TIME"; + + public const string w_TOLERANCE = "TOLER"; + public const string w_TRACE = "TRACE"; + public const string w_TRIALS = "TRIAL"; + + public const string w_UNBALANCED = "UNBA"; + public const string w_UNITS = "UNIT"; + public const string w_USE = "USE"; + public const string w_VALVE = "Valve"; + // public const string w_VELOCITY = "VELO"; + // public const string w_VERIFY = "VERI"; + public const string w_VISCOSITY = "VISC"; + // public const string w_VOLUME = "VOLU"; + public const string w_WALL = "WALL"; + public const string w_YES = "YES"; + + public const string wr_ABOVE = "ABOVE"; + public const string wr_ACTIVE = "ACTIVE"; + public const string wr_AND = "AND"; + public const string wr_BELOW = "BELOW"; + public const string wr_CLOCKTIME = "CLOCKTIME"; + public const string wr_CLOSED = "CLOSED"; + public const string wr_DEMAND = "DEMA"; + public const string wr_DRAINTIME = "DRAI"; + public const string wr_ELSE = "ELSE"; + public const string wr_FILLTIME = "FILL"; + public const string wr_FLOW = "FLOW"; + public const string wr_GRADE = "GRADE"; + public const string wr_HEAD = "HEAD"; + public const string wr_IF = "IF"; + public const string wr_IS = "IS"; + public const string wr_JUNC = "Junc"; + public const string wr_LEVEL = "LEVEL"; + + public const string wr_NOT = "NOT"; + public const string wr_OPEN = "OPEN"; + public const string wr_OR = "OR"; + public const string wr_PIPE = "Pipe"; + public const string wr_POWER = "POWE"; + public const string wr_PRESSURE = "PRES"; + public const string wr_PRIORITY = "PRIO"; + public const string wr_PUMP = "Pump"; + public const string wr_RESERV = "Reser"; + public const string wr_RULE = "RULE"; + public const string wr_SETTING = "SETT"; + public const string wr_STATUS = "STATUS"; + public const string wr_SYSTEM = "SYST"; + public const string wr_TANK = "Tank"; + public const string wr_THEN = "THEN"; + public const string wr_TIME = "TIME"; + public const string wr_VALVE = "Valve"; + + } + + // ReSharper restore InconsistentNaming +} \ No newline at end of file diff --git a/IStation.Epanet/epanet2.2.dll b/IStation.Epanet/epanet2.2.dll new file mode 100644 index 0000000..e28cfb1 --- /dev/null +++ b/IStation.Epanet/epanet2.2.dll Binary files differ diff --git a/IStation.Epanet/exception/ENException.cs b/IStation.Epanet/exception/ENException.cs new file mode 100644 index 0000000..f2ce3cb --- /dev/null +++ b/IStation.Epanet/exception/ENException.cs @@ -0,0 +1,75 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet +{ + + ///<summary>Epanet exception codes handler.</summary> + public class EnException : Exception + { + + ///<summary>Array of arguments to be used in the error string creation.</summary> + private readonly object[] _arguments; + + ///<summary>Epanet error code.</summary> + protected readonly ErrorCode _code; + + /// <summary>Get error code.</summary> + /// <value>Code id.</value> + public ErrorCode Code => _code; + + ///<summary>Constructor from error code id.</summary> + ///<param name="code">Error code id.</param> + + public EnException(ErrorCode code) + { + _arguments = null; + _code = code; + } + + /// <summary>Constructor from error code id and multiple arguments.</summary> + /// <param name="code">Error code id.</param> + /// <param name="arg">Extra arguments.</param> + /// + public EnException(ErrorCode code, params object[] arg) + { + _code = code; + _arguments = arg; + } + + /// <summary>Constructor from error code id and inner exception.</summary> + /// <param name="code">Error code id.</param> + /// <param name="innerException"></param> + /// + public EnException(ErrorCode code, Exception innerException) + : base(null, innerException) + { + _code = code; + } + + ///<summary>Handles the exception string conversion.</summary> + /// <returns>Final error string.</returns> + public override string Message + { + get + { + string format; + + try + { + format = _code.GetMsg(); + } + catch (Exception) + { + format = null; + } + + + if (format == null) + return string.Format("Unknown error message ({0})", _code); + + return _arguments != null ? string.Format(format, _arguments) : format; + } + } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/exception/InputException.cs b/IStation.Epanet/exception/InputException.cs new file mode 100644 index 0000000..8cc792e --- /dev/null +++ b/IStation.Epanet/exception/InputException.cs @@ -0,0 +1,39 @@ +锘縰sing IStation.Epanet.Enums; + +namespace IStation.Epanet +{ + class InputException : EnException + { + + private readonly SectType _section; + public string Arg { get; } + + public InputException(ErrorCode code, SectType section, string arg) : base(code) + { + _section = section; + Arg = arg; + } + + public override string Message + { + get + { + string fmt; + + try + { + fmt = _code.GetMsg(); + } + catch (Exception) + { + fmt = null; + } + + return fmt == null + ? string.Format("Unknown error #({0})", (int)_code) + : string.Format(fmt, _section.ToString(), Arg); + } + } + + } +} diff --git a/IStation.Epanet/network/Constants.cs b/IStation.Epanet/network/Constants.cs new file mode 100644 index 0000000..475fed8 --- /dev/null +++ b/IStation.Epanet/network/Constants.cs @@ -0,0 +1,103 @@ +namespace IStation.Epanet +{ + + ///<summary>Epanet constants.</summary> + public static class Constants + { + /// public static double PI = 3.141592654; + + ///<summary>Max. # of disconnected nodes listed</summary> + public const int MAXCOUNT = 10; + ///<summary>Max. # title lines</summary> + public const int MAXTITLE = 3; + /// <summary>Max. # characters read from input line.</summary> + public const int MAXLINE = 255; + /// <summary>Max. items per line of input</summary> + public const int MAXTOKS = 40; + ///<summary>Max. input errors reported</summary> + public const int MAXERRS = 10; + public const int MAXMSG = 79; + public const int MAXID = 31; + //public const int MAXFNAME = 259; + + ///<summary>Epanet binary files code version</summary> + public const int CODEVERSION = 20012; + + ///<summary>Epanet binary files ID</summary> + public const int MAGICNUMBER = 516114521; + + ///<summary>Epanet binary files version</summary> + public const int VERSION = 200; + + ///<summary>Equivalent to zero flow</summary> + public const double QZERO = 1e-6d; + + ///<summary>Big coefficient</summary> + public const double CBIG = 1e8d; + ///<summary>Small coefficient</summary> + public const double CSMALL = 1 - 6d; + + ///<summary>Default max. # hydraulic iterations</summary> + public const int MAXITER = 200; + ///<summary>Default hydraulics convergence ratio</summary> + public const double HACC = 0.001d; + ///<summary>Default hydraulic head tolerance (ft)</summary> + public const double HTOL = 0.0005d; + ///<summary>Default flow rate tolerance (cfs)</summary> + public const double QTOL = 0.0001d; + + ///<summary>Default water age tolerance (hrs)</summary> + public const double AGETOL = 0.01d; + ///<summary>Default concentration tolerance</summary> + public const double CHEMTOL = 0.01d; + ///<summary>Default uses no page breaks</summary> + public const int PAGESIZE = 0; + ///<summary>Default specific gravity</summary> + public const double SPGRAV = 1.0d; + ///<summary>Default pump efficiency</summary> + public const double EPUMP = 75d; + ///<summary>Default demand pattern ID</summary> + public const string DEFPATID = "1"; + + ///<summary>Default low flow resistance tolerance</summary> + public const double RQTOL = 1E-7d; + + ///<summary>Default status check frequency</summary> + public const int CHECKFREQ = 2; + ///<summary>Default # iterations for status checks</summary> + public const int MAXCHECK = 10; + ///<summary>Default damping threshold</summary> + public const double DAMPLIMIT = 0; + + ///<summary>Max. # types of network variables</summary> + public const int MAXVAR = 21; + + public const double BIG = 1E10d; + public const double TINY = 1E-6d; + + // ReSharper disable InconsistentNaming + public const double GPMperCFS = 448.831d; + public const double AFDperCFS = 1.9837d; + public const double MGDperCFS = 0.64632d; + public const double IMGDperCFS = 0.5382d; + public const double LPSperCFS = 28.317d; + public const double LPMperCFS = 1699.0d; + public const double CMHperCFS = 101.94d; + public const double CMDperCFS = 2446.6d; + public const double MLDperCFS = 2.4466d; + public const double M3perFT3 = 0.028317d; + public const double LperFT3 = 28.317d; + public const double MperFT = 0.3048d; + public const double MMperFT = 304.8d; + public const double INperFT = 12.0d; + public const double PSIperFT = 0.4333d; + public const double KPAperPSI = 6.895d; + public const double KWperHP = 0.7457d; + public const int SECperDAY = 86400; + // ReSharper restore InconsistentNaming + + public const double DIFFUS = 1.3E-8d; + public const double VISCOS = 1.1E-5; + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/ElementCollection.cs b/IStation.Epanet/network/ElementCollection.cs new file mode 100644 index 0000000..1140a61 --- /dev/null +++ b/IStation.Epanet/network/ElementCollection.cs @@ -0,0 +1,77 @@ +锘縰sing IStation.Epanet.Network.Structures; +using System.Collections.ObjectModel; + +namespace IStation.Epanet.Network +{ + + internal class ElementCollection<TItem> : KeyedCollection<string, TItem> where TItem : Element + { + public ElementCollection() : base(StringComparer.OrdinalIgnoreCase, 10) { } + + // public new void Add(TItem item) { base.Add(item); } + + public void AddOrReplace(TItem item) + { + string key = GetKeyForItem(item); + base.Remove(key); + base.Add(item); + } + + /// <summary>Get item by Name.</summary> + /// <param name="key">Item <see cref="Element.Name"/></param> + /// <returns>Item with given key, null otherwise.</returns> + public new TItem this[string key] + { + get + { + + if (key == null) + throw new ArgumentNullException(nameof(key)); + + if (Dictionary != null) + { + return Dictionary.TryGetValue(key, out TItem value) + ? value + : default(TItem); + } + + var comparer = base.Comparer ?? StringComparer.OrdinalIgnoreCase; + + foreach (var item in Items) + if (comparer.Equals(item.Name, key)) + return item; + + return default(TItem); + } + } + + + public bool TryGetValue(string key, out TItem value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + if (base.Dictionary != null) + return base.Dictionary.TryGetValue(key, out value); + + var comparer = Comparer ?? StringComparer.OrdinalIgnoreCase; + + foreach (var item in Items) + { + string itemKey = item.Name; + + if (comparer.Equals(itemKey, key)) + { + value = item; + return true; + } + } + + value = default(TItem); + return false; + } + + protected override string GetKeyForItem(TItem item) => item.Name; + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/FieldsMap.cs b/IStation.Epanet/network/FieldsMap.cs new file mode 100644 index 0000000..417a04e --- /dev/null +++ b/IStation.Epanet/network/FieldsMap.cs @@ -0,0 +1,241 @@ +using IStation.Epanet; +using IStation.Epanet.Enums; +using IStation.Epanet.Network.Structures; +using System.Diagnostics; + +namespace IStation.Epanet.Network +{ + + ///<summary>Units report properties & conversion support class</summary> + public class FieldsMap + { + ///<summary>Report fields properties.</summary> + private readonly Dictionary<FieldType, Field> _fields = new Dictionary<FieldType, Field>(); + ///<summary>Fields units values.</summary> + private readonly Dictionary<FieldType, double> _units = new Dictionary<FieldType, double>(); + + ///<summary>Init fields default configuration</summary> + public FieldsMap() + { + try + { + + foreach (FieldType type in Enum.GetValues(typeof(FieldType))) + _fields[type] = new Field(type); + + GetField(FieldType.FRICTION).Precision = 3; + + for (var i = FieldType.DEMAND; i <= FieldType.QUALITY; i++) + GetField(i).Enabled = true; + + for (var i = FieldType.FLOW; i <= FieldType.HEADLOSS; i++) + GetField(i).Enabled = true; + } + catch (EnException e) + { + Debug.Print(e.ToString()); + } + } + + ///<summary>Get report field properties from type.</summary> + /// <param name="fieldType">Field type.</param> + /// <returns>Report field.</returns> + /// <remarks> + /// Throws <see cref="EnException"/> + /// If specified type not found. + /// </remarks> + public Field GetField(FieldType fieldType) + { + if (!_fields.TryGetValue(fieldType, out Field value)) + throw new EnException(ErrorCode.Err201, fieldType.ParseStr()); + + return value; + } + + ///<summary>Get conversion value from field type.</summary> + /// <param name="fieldType">Field type.</param> + /// <returns>Conversion units value (from user units to system units).</returns> + /// <remarks> + /// Throws <see cref="EnException"/> If specified type not found. + /// </remarks> + public double GetUnits(FieldType fieldType) + { + if (!_units.TryGetValue(fieldType, out double value)) + throw new EnException(ErrorCode.Err201, fieldType.ParseStr()); + + return value; + } + + ///<summary>Update fields and units, after loading the INP.</summary> + public void Prepare( + UnitsType targetUnits, + FlowUnitsType flowFlag, + PressUnitsType pressFlag, + QualType qualFlag, + string chemUnits, + double spGrav, + TimeSpan hstep) + { + + double dcf, + ccf, + qcf, + hcf, + pcf, + wcf; + + if (targetUnits == UnitsType.SI) + { + + GetField(FieldType.DEMAND).Units = flowFlag.ToString(); + GetField(FieldType.ELEV).Units = Keywords.u_METERS; + GetField(FieldType.HEAD).Units = Keywords.u_METERS; + + GetField(FieldType.PRESSURE).Units = pressFlag == PressUnitsType.METERS + ? Keywords.u_METERS + : Keywords.u_KPA; + + GetField(FieldType.LENGTH).Units = Keywords.u_METERS; + GetField(FieldType.DIAM).Units = Keywords.u_MMETERS; + GetField(FieldType.FLOW).Units = flowFlag.ToString(); + GetField(FieldType.VELOCITY).Units = Keywords.u_MperSEC; + GetField(FieldType.HEADLOSS).Units = "m" + Keywords.u_per1000M; + GetField(FieldType.FRICTION).Units = ""; + GetField(FieldType.POWER).Units = Keywords.u_KW; + + dcf = 1000.0 * Constants.MperFT; + qcf = Constants.LPSperCFS; + switch (flowFlag) + { + case FlowUnitsType.Lpm: + qcf = Constants.LPMperCFS; + break; + case FlowUnitsType.Mld: + qcf = Constants.MLDperCFS; + break; + case FlowUnitsType.Cmh: + qcf = Constants.CMHperCFS; + break; + case FlowUnitsType.Cmd: + qcf = Constants.CMDperCFS; + break; + } + + hcf = Constants.MperFT; + if (pressFlag == PressUnitsType.METERS) pcf = Constants.MperFT * spGrav; + else pcf = Constants.KPAperPSI * Constants.PSIperFT * spGrav; + wcf = Constants.KWperHP; + } + else + { + GetField(FieldType.DEMAND).Units = flowFlag.ToString(); + GetField(FieldType.ELEV).Units = Keywords.u_FEET; + GetField(FieldType.HEAD).Units = Keywords.u_FEET; + + GetField(FieldType.PRESSURE).Units = Keywords.u_PSI; + GetField(FieldType.LENGTH).Units = Keywords.u_FEET; + GetField(FieldType.DIAM).Units = Keywords.u_INCHES; + GetField(FieldType.FLOW).Units = flowFlag.ToString(); + GetField(FieldType.VELOCITY).Units = Keywords.u_FTperSEC; + GetField(FieldType.HEADLOSS).Units = "ft" + Keywords.u_per1000FT; + GetField(FieldType.FRICTION).Units = ""; + GetField(FieldType.POWER).Units = Keywords.u_HP; + + + dcf = 12.0; + qcf = 1.0; + + switch (flowFlag) + { + case FlowUnitsType.Gpm: + qcf = Constants.GPMperCFS; + break; + case FlowUnitsType.Mgd: + qcf = Constants.MGDperCFS; + break; + case FlowUnitsType.Imgd: + qcf = Constants.IMGDperCFS; + break; + case FlowUnitsType.Afd: + qcf = Constants.AFDperCFS; + break; + } + + hcf = 1.0; + pcf = Constants.PSIperFT * spGrav; + wcf = 1.0; + } + + GetField(FieldType.QUALITY).Units = ""; + ccf = 1.0; + + switch (qualFlag) + { + case QualType.Chem: + ccf = 1.0 / Constants.LperFT3; + GetField(FieldType.QUALITY).Units = chemUnits; + GetField(FieldType.REACTRATE).Units = chemUnits + Keywords.t_PERDAY; + break; + case QualType.Age: + GetField(FieldType.QUALITY).Units = Keywords.u_HOURS; + break; + case QualType.Trace: + GetField(FieldType.QUALITY).Units = Keywords.u_PERCENT; + break; + } + + SetUnits(FieldType.DEMAND, qcf); + SetUnits(FieldType.ELEV, hcf); + SetUnits(FieldType.HEAD, hcf); + SetUnits(FieldType.PRESSURE, pcf); + SetUnits(FieldType.QUALITY, ccf); + SetUnits(FieldType.LENGTH, hcf); + SetUnits(FieldType.DIAM, dcf); + SetUnits(FieldType.FLOW, qcf); + SetUnits(FieldType.VELOCITY, hcf); + SetUnits(FieldType.HEADLOSS, hcf); + SetUnits(FieldType.LINKQUAL, ccf); + SetUnits(FieldType.REACTRATE, ccf); + SetUnits(FieldType.FRICTION, 1.0); + SetUnits(FieldType.POWER, wcf); + SetUnits(FieldType.VOLUME, hcf * hcf * hcf); + + if (hstep.TotalSeconds < 1800) + { + SetUnits(FieldType.TIME, 1.0 / 60.0); + GetField(FieldType.TIME).Units = Keywords.u_MINUTES; + } + else + { + SetUnits(FieldType.TIME, 1.0 / 3600.0); + GetField(FieldType.TIME).Units = Keywords.u_HOURS; + } + } + + /// <summary>Revert system units to user units.</summary> + /// <param name="fieldType">Field type.</param> + /// <param name="value">Value to be converted.</param> + /// <returns>Value in user units.</returns> + public double RevertUnit(FieldType fieldType, double value) + { + return fieldType == (FieldType)(-1) + ? value + : value * GetUnits(fieldType); + } + + public double ConvertUnitToSystem(FieldType fieldType, double value) + { + return value / GetUnits(fieldType); + } + + ///<summary>Set conversion value from field type.</summary> + /// <param name="fieldType">Field type.</param> + /// <param name="value">Field value.</param> + private void SetUnits(FieldType fieldType, double value) + { + _units[fieldType] = value; + } + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/Network.cs b/IStation.Epanet/network/Network.cs new file mode 100644 index 0000000..f0548ab --- /dev/null +++ b/IStation.Epanet/network/Network.cs @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2016 Vyacheslav Shevelyov (slavash at aha dot ru) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +using IStation.Epanet.Enums; +using IStation.Epanet.Network.Structures; + +namespace IStation.Epanet.Network +{ + + + ///<summary>Hydraulic network structure.</summary> + public sealed class Network + { + private readonly List<Control> _controls = new List<Control>(); + private readonly ElementCollection<Curve> _curves = new ElementCollection<Curve>(); + + [NonSerialized] + private readonly FieldsMap _fields = new FieldsMap(); + + private readonly List<Label> _labels = new List<Label>(); + private readonly ElementCollection<Link> _links = new ElementCollection<Link>(); + private readonly ElementCollection<Node> _nodes = new ElementCollection<Node>(); + + private readonly ElementCollection<Pattern> _patterns = new ElementCollection<Pattern>(); + + private readonly ElementCollection<Rule> _rules = new ElementCollection<Rule>(); + + public Network() + { + LoadDefaults(); + } + + public IList<Control> Controls => _controls; + + public Curve GetCurve(string name) { return _curves[name]; } + + public IList<Curve> Curves => _curves; + + ///<summary>Fields map with report variables properties and conversion units.</summary> + public FieldsMap FieldsMap => _fields; + + ///<summary>Transient colleciton of junctions.</summary> + public IEnumerable<Node> Junctions + { + get { return _nodes.Where(x => x.NodeType == NodeType.Junction); } + } + + public IList<Label> Labels => _labels; + + public Link GetLink(string name) => _links[name]; + + public IList<Link> Links => _links; + + public IEnumerable<Pipe> Pipes => _links.Where(x => x.LinkType == LinkType.Pipe).Cast<Pipe>(); + + public Node GetNode(string name) => _nodes[name]; + + public IList<Node> Nodes => _nodes; + + public Pattern GetPattern(string name) => _patterns[name]; + + public IList<Pattern> Patterns => _patterns; + + ///<summary>Transient colleciton of pumps.</summary> + public IEnumerable<Pump> Pumps + { + get { return _links.Where(x => x.LinkType == LinkType.Pump).Cast<Pump>(); } + } + + public Rule GetRule(string name) => _rules[name]; + + public IList<Rule> Rules => _rules; + + public IEnumerable<Tank> Tanks + { + get { return _nodes.Where(x => x.NodeType == NodeType.Tank).Cast<Tank>(); } + } + + public IEnumerable<Reservoir> Reservoirs + { + get { return _nodes.Where(x => x.NodeType == NodeType.Reservoir).Cast<Reservoir>(); } + } + + public List<string> Title { get; } = new List<string>(); + + ///<summary>Transient colleciton of valves.</summary> + public IEnumerable<Valve> Valves => _links.OfType<Valve>(); + + #region properties map + + + private Dictionary<string, string> _extraOptions; + + + #region properties accessors/mutators + + public string AltReport { get; set; } + + /// <summary>Bulk flow reaction order</summary> + public double BulkOrder { get; set; } + + /// <summary>Hydraulics solver parameter.</summary> + public int CheckFreq { get; set; } + + /// <summary>Name of chemical.</summary> + public string ChemName { get; set; } + + /// <summary>Units of chemical.</summary> + public string ChemUnits { get; set; } + + /// <summary>Limiting potential quality.</summary> + public double CLimit { get; set; } + + /// <summary>Water quality tolerance.</summary> + public double Ctol { get; set; } + + /// <summary>Solution damping threshold.</summary> + public double DampLimit { get; set; } + + /// <summary>Energy demand charge/kw/day.</summary> + public double DCost { get; set; } + + /// <summary>Default demand pattern ID.</summary> + public string DefPatId { get; set; } + + /// <summary>Diffusivity (sq ft/sec).</summary> + public double Diffus { get; set; } + + /// <summary>Demand multiplier.</summary> + public double DMult { get; set; } + + /// <summary>Duration of simulation (sec).</summary> + public TimeSpan Duration { get; set; } + + /// <summary>Base energy cost per kwh.</summary> + public double ECost { get; set; } + + /// <summary>Peak energy usage.</summary> + public double EMax { get; set; } + + /// <summary>Energy report flag.</summary> + public bool EnergyFlag { get; set; } + + /// <summary>Energy cost time pattern.</summary> + public string EPatId { get; set; } + + /// <summary>Global pump efficiency.</summary> + public double EPump { get; set; } + + /// <summary>Extra hydraulic trials.</summary> + public int ExtraIter { get; set; } + + /// <summary>Flow units flag.</summary> + public FlowUnitsType FlowFlag { get; set; } + + /// <summary>Hydraulic formula flag.</summary> + public HeadLossFormulaType FormFlag { get; set; } + + /// <summary>Hydraulics solution accuracy.</summary> + public double HAcc { get; set; } + + /// <summary>Exponent in headloss formula.</summary> + public double HExp { get; set; } + + /// <summary>Nominal hyd. time step (sec).</summary> + public TimeSpan HStep { get; set; } + + /// <summary>Hydraulic head tolerance.</summary> + public double HTol { get; set; } + + /// <summary>Hydraulics flag.</summary> + public HydType HydFlag { get; set; } + + /// <summary>Hydraulics file name.</summary> + public string HydFname { get; set; } + + /// <summary>Global bulk reaction coeff.</summary> + public double KBulk { get; set; } + + /// <summary>Global wall reaction coeff.</summary> + public double KWall { get; set; } + + /// <summary>Link report flag.</summary> + public ReportFlag LinkFlag { get; set; } + + /// <summary>Map file name.</summary> + public string MapFname { get; set; } + + /// <summary>Hydraulics solver parameter.</summary> + public int MaxCheck { get; set; } + + /// <summary>Max. hydraulic trials.</summary> + public int MaxIter { get; set; } + + /// <summary>Error/warning message flag.</summary> + public bool MessageFlag { get; set; } + + /// <summary>Node report flag.</summary> + public ReportFlag NodeFlag { get; set; } + + /// <summary>Lines/page in output report.</summary> + public int PageSize { get; set; } + + /// <summary>Pressure units flag.</summary> + public PressUnitsType PressFlag { get; set; } + + /// <summary>Starting pattern time (sec).</summary> + public TimeSpan PStart { get; set; } + + /// <summary>Time pattern time step (sec).</summary> + public TimeSpan PStep { get; set; } + + /// <summary>Exponent in orifice formula.</summary> + public double QExp { get; set; } + + /// <summary>Quality time step (sec).</summary> + public TimeSpan QStep { get; set; } + + /// <summary>Flow rate tolerance.</summary> + public double QTol { get; set; } + + /// <summary>Water quality flag.</summary> + public QualType QualFlag { get; set; } + + /// <summary>Roughness-reaction factor.</summary> + public double RFactor { get; set; } + + /// <summary>Flow resistance tolerance.</summary> + public double RQtol { get; set; } + + /// <summary>Time when reporting starts.</summary> + public TimeSpan RStart { get; set; } + + /// <summary>Reporting time step (sec).</summary> + public TimeSpan RStep { get; set; } + + /// <summary>Rule evaluation time step.</summary> + public TimeSpan RuleStep { get; set; } + + /// <summary>Specific gravity.</summary> + public double SpGrav { get; set; } + + /// <summary>Status report flag.</summary> + public StatusLevel StatFlag { get; set; } + + /// <summary>Report summary flag.</summary> + public bool SummaryFlag { get; set; } + + /// <summary>Tank reaction order.</summary> + public double TankOrder { get; set; } + + /// <summary>Source node for flow tracing.</summary> + public string TraceNode { get; set; } + + /// <summary>Starting time of day (sec).</summary> + public TimeSpan Tstart { get; set; } + + /// <summary>Time statistics flag.</summary> + public TstatType TstatFlag { get; set; } + + /// <summary>Unit system flag.</summary> + public UnitsType UnitsFlag { get; set; } + + /// <summary>Kin. viscosity (sq ft/sec).</summary> + public double Viscos { get; set; } + + /// <summary>Pipe wall reaction order.</summary> + public double WallOrder { get; set; } + + #endregion + + public Dictionary<string, string> ExtraOptions => _extraOptions + ?? (_extraOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)); + public bool AutoLength { get; set; } + + /// <summary>Init properties with default value.</summary> + private void LoadDefaults() + { + BulkOrder = 1.0d; // 1st-order bulk reaction rate + TankOrder = 1.0d; // 1st-order tank reaction rate + WallOrder = 1.0d; // 1st-order wall reaction rate + RFactor = 1.0d; // No roughness-reaction factor + CLimit = 0.0d; // No limiting potential quality + KBulk = 0.0d; // No global bulk reaction + KWall = 0.0d; // No global wall reaction + DCost = 0.0d; // Zero energy demand charge + ECost = 0.0d; // Zero unit energy cost + EPatId = string.Empty; // No energy price pattern + EPump = Constants.EPUMP; // Default pump efficiency + PageSize = Constants.PAGESIZE; + StatFlag = StatusLevel.None; + SummaryFlag = true; + MessageFlag = true; + EnergyFlag = false; + NodeFlag = ReportFlag.FALSE; + LinkFlag = ReportFlag.FALSE; + TstatFlag = TstatType.None; // Generate time series output + HStep = new TimeSpan(TimeSpan.TicksPerHour); // 1 hr hydraulic time step + Duration = TimeSpan.Zero; // 0 sec duration (steady state) + QStep = TimeSpan.Zero; // No pre-set quality time step + RuleStep = TimeSpan.Zero; // No pre-set rule time step + PStep = TimeSpan.FromHours(1); // 1 hr time pattern period + PStart = TimeSpan.Zero; // Starting pattern period + RStep = TimeSpan.FromHours(1); // 1 hr reporting period + RStart = TimeSpan.Zero; // Start reporting at time 0 + Tstart = TimeSpan.Zero; // Starting time of day + FlowFlag = FlowUnitsType.Gpm; // Flow units are gpm + PressFlag = PressUnitsType.PSI; // Pressure units are psi + FormFlag = HeadLossFormulaType.HW; // Use Hazen-Williams formula + HydFlag = HydType.SCRATCH; // No external hydraulics file + QualFlag = QualType.None; // No quality simulation + UnitsFlag = UnitsType.US; // US unit system + HydFname = ""; + ChemName = Keywords.t_CHEMICAL; + ChemUnits = Keywords.u_MGperL; // mg/L + DefPatId = Constants.DEFPATID; // Default demand pattern index + MapFname = ""; + AltReport = ""; + TraceNode = ""; // No source tracing + ExtraIter = -1; // Stop if network unbalanced + Ctol = double.NaN; // No pre-set quality tolerance + Diffus = double.NaN; // Temporary diffusivity + DampLimit = Constants.DAMPLIMIT; + Viscos = double.NaN; // Temporary viscosity + SpGrav = Constants.SPGRAV; // Default specific gravity + MaxIter = Constants.MAXITER; // Default max. hydraulic trials + HAcc = Constants.HACC; // Default hydraulic accuracy + HTol = Constants.HTOL; // Default head tolerance + QTol = Constants.QTOL; // Default flow tolerance + RQtol = Constants.RQTOL; // Default hydraulics parameters + HExp = 0.0d; + QExp = 2.0d; // Flow exponent for emitters + CheckFreq = Constants.CHECKFREQ; + MaxCheck = Constants.MAXCHECK; + DMult = 1.0d; // Demand multiplier + EMax = 0.0d; // Zero peak energy usage + } + + + #endregion + + public override string ToString() + { + + return new System.Text.StringBuilder(0x200) + .AppendLine(" Network") + .Append(" Nodes : ").Append(_nodes.Count).AppendLine() + .Append(" Links : ").Append(_links.Count).AppendLine() + .Append(" Pattern : ").Append(_patterns.Count).AppendLine() + .Append(" Curves : ").Append(_curves.Count).AppendLine() + .Append(" Controls : ").Append(_controls.Count).AppendLine() + .Append(" Labels : ").Append(_labels.Count).AppendLine() + .Append(" Rules : ").Append(_rules.Count).AppendLine() + .Append(" Tanks : ").Append(Tanks.Count()).AppendLine() + .Append(" Reservoirs : ").Append(Reservoirs.Count()).AppendLine() + .Append(" Pumps : ").Append(Pumps.Count()).AppendLine() + .Append(" Valves : ").Append(Valves.Count()).AppendLine() + .ToString(); + + } + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/helpers/NetworkHelper.cs b/IStation.Epanet/network/helpers/NetworkHelper.cs new file mode 100644 index 0000000..b4bfce3 --- /dev/null +++ b/IStation.Epanet/network/helpers/NetworkHelper.cs @@ -0,0 +1,101 @@ +锘縰sing IStation.Epanet; +using IStation.Epanet.Network.IO.Input; +using IStation.Epanet.Network.IO.Output; +using EpanetNetwork = IStation.Epanet.Network.Network; + +namespace IStation.Epanet +{ + public class NetworkHelper + { + public static string Open(string netFile, out EpanetNetwork eapNet) + { + eapNet = null; + string fileExtension = (Path.GetExtension(netFile) ?? string.Empty).ToLowerInvariant(); + + if (fileExtension != ".net" && + fileExtension != ".inp" && + fileExtension != ".xml" && + fileExtension != ".gz") + return $"鏃犳硶璇嗗埆{fileExtension}"; + + InputParser inpParser; + switch (fileExtension.ToLowerInvariant()) + { + case ".inp": + inpParser = new InpParser(); + break; + + case ".net": + inpParser = new NetParser(); + break; + + case ".xml": + inpParser = new XmlParser(false); + break; + + case ".gz": + inpParser = new XmlParser(true); + break; + default: + return $"Not supported file type:{fileExtension}"; + } + + EpanetNetwork net; + + try + { + var network = new EpanetNetwork(); + net = inpParser.Parse(network, netFile); + } + catch (EnException ex) + { + return $"{ex.Message}\nCheck epanet.log for detailed error description"; + } + catch (Exception ex) + { + return $"{ex.Message}\nUnable to parse network configuration file"; + } + eapNet = net; + return string.Empty; + } + + public static void Save(EpanetNetwork eapNet, string filePath) + { + if (eapNet == null) + return; + + OutputComposer composer; + string fileName = Path.GetFullPath(filePath); + string extension = Path.GetExtension(filePath); + + switch (extension) + { + case ".inp": + composer = new InpComposer(eapNet); + break; + + default: + extension = ".inp"; + composer = new InpComposer(eapNet); + break; + } + + fileName = Path.ChangeExtension(fileName, extension); + + try + { + composer.Compose(fileName); + } + catch (EnException ex) + { + throw new Exception(ex.Message + "\nCheck epanet.log for detailed error description"); + } + catch (Exception ex) + { + throw new Exception(ex.Message + "\nCheck epanet.log for detailed error description"); + } + + } + + } +} diff --git a/IStation.Epanet/network/io/input/InpParser.cs b/IStation.Epanet/network/io/input/InpParser.cs new file mode 100644 index 0000000..e08b787 --- /dev/null +++ b/IStation.Epanet/network/io/input/InpParser.cs @@ -0,0 +1,1996 @@ +using IStation.Epanet; +using IStation.Epanet.Enums; +using IStation.Epanet.Log; +using IStation.Epanet.Network.Structures; +using IStation.Epanet.Util; +using System.Text; + +namespace IStation.Epanet.Network.IO.Input +{ + + + ///<summary>INP parser class.</summary> + public class InpParser : InputParser + { + private SectType _sect = (SectType)(-1); + private readonly List<string> _tok = new List<string>(Constants.MAXTOKS); + private string _comment; + private string _line; + + private Rule _currentRule; // Current rule + + private static readonly string[] optionValueKeywords = { + Keywords.w_TOLERANCE, Keywords.w_DIFFUSIVITY, Keywords.w_DAMPLIMIT, Keywords.w_VISCOSITY, + Keywords.w_SPECGRAV, Keywords.w_TRIALS, Keywords.w_ACCURACY, Keywords.w_HTOL, + Keywords.w_QTOL, Keywords.w_RQTOL, Keywords.w_CHECKFREQ, Keywords.w_MAXCHECK, + Keywords.w_EMITTER, Keywords.w_DEMAND + }; + + public InpParser() + { + _currentRule = null; + } + + public override Network Parse(Network nw, string fileName) + { + + if (string.IsNullOrEmpty(fileName)) + throw new ArgumentNullException(nameof(fileName)); + + net = nw ?? new Network(); + + try + { + using (FileStream fs = File.OpenRead(fileName)) + { + //Parse demands and time patterns first + //首先解析需求和时间模式 + ParsePc(fs); + fs.Position = 0; + Parse(fs); + + if (errors.Count > 0) + throw new EnException(ErrorCode.Err200); + + } + } + catch (IOException ex) + { + throw new EnException(ErrorCode.Err302, ex); + } + + AdjustData(net); + + net.FieldsMap.Prepare(net.UnitsFlag, net.FlowFlag, net.PressFlag, net.QualFlag, net.ChemUnits, net.SpGrav, net.HStep); + + Convert(); + + return net; + } + + /// <summary>Parse demands and time patterns first.</summary> + /// <param name="stream"></param> + private void ParsePc(Stream stream) + { + var buffReader = new StreamReader(stream, Encoding.Default); + + while ((_line = buffReader.ReadLine()) != null) + { + + _line = _line.Trim(); + + if (string.IsNullOrEmpty(_line)) + continue; + + if (_line[0] == '[') + { + if (_line.StartsWith("[PATTERNS]", StringComparison.OrdinalIgnoreCase)) + { + _sect = SectType.PATTERNS; + } + else if (_line.StartsWith("[CURVES]", StringComparison.OrdinalIgnoreCase)) + _sect = SectType.CURVES; + else + _sect = (SectType)(-1); + + continue; + } + + if (_sect != (SectType)(-1)) + { + if (_line.IndexOf(';') >= 0) + _line = _line.Substring(0, _line.IndexOf(';')); + + if (string.IsNullOrEmpty(_line)) + continue; + + if (GetTokens(_line, _tok) == 0) + continue; + + try + { + switch (_sect) + { + case SectType.PATTERNS: + ParsePattern(); + break; + case SectType.CURVES: + ParseCurve(); + break; + } + } + catch (InputException ex) + { + LogException(ex); + } + } + } + + } + + // Parse INP file + private void Parse(Stream stream) + { + + _sect = (SectType)(-1); + TextReader reader = new StreamReader(stream, Encoding.Default); // "ISO-8859-1"; + + while ((_line = reader.ReadLine()) != null) + { + + if (_line.Length > Constants.MAXLINE) + LogException(new EnException(ErrorCode.Err214, _sect, _line)); + + _comment = null; + + int index = _line.IndexOf(';'); + + if (index >= 0) + { + if (index > 0) + _comment = _line.Substring(index + 1).Trim(); + + _line = _line.Substring(0, index); + } + + //lineCount++; + _line = _line.Trim(); + if (string.IsNullOrEmpty(_line)) + continue; + + if (GetTokens(_line, _tok) == 0) + continue; + + if (_tok[0][0] == '[') + { + _sect = FindSectionType(_tok[0]); + + if (_sect < 0) + { + log.Warning("Unknown section type: " + _tok[0]); + } + + if (_sect == SectType.END) + break; + + continue; + } + + if (_sect < 0) continue; + + + + try + { + NewLine(); + } + catch (InputException ex) + { + LogException(ex); + } + } + + } + + + private void NewLine() + { + switch (_sect) + { + case SectType.TITLE: + ParseTitle(); + break; + + case SectType.JUNCTIONS: + ParseJunction(); + break; + + case SectType.RESERVOIRS: + ParseReservoir(); + break; + case SectType.TANKS: + ParseTank(); + break; + + case SectType.PIPES: + ParsePipe(); + break; + + case SectType.PUMPS: + ParsePump(); + break; + + case SectType.VALVES: + ParseValve(); + break; + + case SectType.PATTERNS: + //ParsePattern(); + break; + + case SectType.CURVES: + //ParseCurve(); + break; + + case SectType.DEMANDS: + ParseDemand(); + break; + + case SectType.CONTROLS: + ParseControl(); + break; + + case SectType.RULES: + ParseRule(); /* See RULES.C */ + break; + + case SectType.SOURCES: + ParseSource(); + break; + + case SectType.EMITTERS: + ParseEmitter(); + break; + + case SectType.QUALITY: + ParseQuality(); + break; + + case SectType.STATUS: + ParseStatus(); + break; + + case SectType.ROUGHNESS: + break; + + case SectType.ENERGY: + ParseEnergy(); + break; + + case SectType.REACTIONS: + ParseReaction(); + break; + + case SectType.MIXING: + ParseMixing(); + break; + + case SectType.REPORT: + ParseReport(); + break; + + case SectType.TIMES: + ParseTime(); + break; + + case SectType.OPTIONS: + ParseOption(); + break; + + /* Data in these sections are not used for any computations */ + case SectType.COORDINATES: + ParseCoordinate(); + break; + + case SectType.LABELS: + ParseLabel(); + break; + + case SectType.TAGS: + ParseTag(); + break; + + case SectType.VERTICES: + ParseVertice(); + break; + + case SectType.BACKDROP: + break; + + default: + throw new InputException(ErrorCode.Err201, _sect, _line); + } + } + + /// <summary>Mimics C atol function behavior</summary> + private static bool Atol(string s, out int value) + { + if (string.IsNullOrEmpty(s)) + { + value = 0; + return false; + } + + int i = 0; + + foreach (char c in s) + { + if (c != '-' && c != '+' && !char.IsWhiteSpace(c)) break; + i++; + } + + while (i < s.Length) + { + + if (!char.IsNumber(s[i])) + break; + + i++; + } + + return int.TryParse(s.Substring(0, i), out value); + } + + private void ParseTitle() + { + if (net.Title.Count >= 3) return; + + string s = _line.Length > Constants.MAXMSG + ? _line.Substring(0, Constants.MAXMSG) + : _line; + + net.Title.Add(s); + } + + private void ParseJunction() + { + int n = _tok.Count; + double y = 0.0d; + Pattern p = null; + + if (net.GetNode(_tok[0]) != null) + throw new InputException(ErrorCode.Err215, SectType.JUNCTIONS, _tok[0]); + + Node node = new Junction(_tok[0]); + + net.Nodes.Add(node); + + if (n < 2) + throw new InputException(ErrorCode.Err201, SectType.JUNCTIONS, _line); + + if (!_tok[1].ToDouble(out double el)) + throw new InputException(ErrorCode.Err202, SectType.JUNCTIONS, _tok[0]); + + if (n >= 3 && !_tok[2].ToDouble(out y)) + throw new InputException(ErrorCode.Err202, SectType.JUNCTIONS, _tok[0]); + + if (n >= 4) + { + p = net.GetPattern(_tok[3]); + if (p == null) + { + throw new InputException(ErrorCode.Err205, SectType.JUNCTIONS, _tok[0]); + } + } + + node.Elevation = el; + node.C0 = 0.0; + node.QualSource = null; + node.Ke = 0.0; + node.RptFlag = false; + + if (!string.IsNullOrEmpty(_comment)) + node.Comment = _comment; + + if (n >= 3) + { + node.PrimaryDemand.Base = y; + node.PrimaryDemand.Pattern = p; + } + + } + + private void ParseReservoir() + { + int n = _tok.Count; + Pattern p = null; + + if (net.GetNode(_tok[0]) != null) + throw new InputException(ErrorCode.Err215, SectType.TANKS, _tok[0]); + + Reservoir reservoir = new Reservoir(_tok[0]); + + if (!string.IsNullOrEmpty(_comment)) + reservoir.Comment = _comment; + + net.Nodes.Add(reservoir); + + if (n < 2) + throw new InputException(ErrorCode.Err201, SectType.REACTIONS, _line); + + if (!_tok[1].ToDouble(out double head)) + throw new InputException(ErrorCode.Err202, SectType.REACTIONS, _tok[0]); + + if (n == 3) + { + p = net.GetPattern(_tok[2]); + if (p == null) + { + throw new InputException(ErrorCode.Err205, SectType.REACTIONS, _tok[0]); + } + } + + + reservoir.RptFlag = false; + reservoir.Elevation = 0.0; + reservoir.C0 = 0.0; + reservoir.QualSource = null; + reservoir.Ke = 0.0; + + reservoir.Head = head; + reservoir.Pattern = p; + } + + private void ParseTank() + { + int n = _tok.Count; + Pattern p = null; + Curve vcurve = null; + double initlevel = 0.0d, + minlevel = 0.0d, + maxlevel = 0.0d, + minvol = 0.0d, + diam = 0.0d; + + if (net.GetNode(_tok[0]) != null) + throw new InputException(ErrorCode.Err215, SectType.TANKS, _tok[0]); + + Tank tank = new Tank(_tok[0]); + + if (!string.IsNullOrEmpty(_comment)) + tank.Comment = _comment; + + net.Nodes.Add(tank); + + if (n < 2) + throw new InputException(ErrorCode.Err201, SectType.TANKS, _line); + + if (!_tok[1].ToDouble(out double el)) + throw new InputException(ErrorCode.Err202, SectType.TANKS, _tok[0]); + + if (n <= 3) + { + /* Tank is reservoir.*/ + if (n == 3) + { + p = net.GetPattern(_tok[2]); + if (p == null) + { + throw new InputException(ErrorCode.Err205, SectType.TANKS, _tok[0]); + } + } + } + else if (n < 6) + { + throw new InputException(ErrorCode.Err201, SectType.TANKS, _line); + } + else + { + + /* Check for valid input data */ + if (!_tok[2].ToDouble(out initlevel) || + !_tok[3].ToDouble(out minlevel) || + !_tok[4].ToDouble(out maxlevel) || + !_tok[5].ToDouble(out diam) || + diam < 0.0) + throw new InputException(ErrorCode.Err202, SectType.TANKS, _tok[0]); + + if (n >= 7 && !_tok[6].ToDouble(out minvol)) + throw new InputException(ErrorCode.Err202, SectType.TANKS, _tok[0]); + + if (n == 8) + { + vcurve = net.GetCurve(_tok[7]); + + if (vcurve == null) + throw new InputException(ErrorCode.Err206, SectType.TANKS, _tok[0]); + } + } + + tank.RptFlag = false; + tank.Elevation = el; + tank.C0 = 0.0; + tank.QualSource = null; + tank.Ke = 0.0; + + tank.H0 = initlevel; + tank.Hmin = minlevel; + tank.Hmax = maxlevel; + tank.Area = diam; + tank.Pattern = p; + tank.Kb = double.NaN; + + /* + ******************************************************************* + NOTE: The min, max, & initial volumes set here are based on a + nominal tank diameter. They will be modified in INPUT1.C if + a volume curve is supplied for this tank. + ******************************************************************* + */ + + double area = Math.PI * diam * diam / 4.0d; + tank.Vmin = area * minlevel; + if (minvol > 0.0) tank.Vmin = minvol; + tank.V0 = tank.Vmin + area * (initlevel - minlevel); + tank.Vmax = tank.Vmin + area * (maxlevel - minlevel); + + tank.Vcurve = vcurve; + tank.MixModel = MixType.Mix1; + tank.V1Max = 1.0; + + } + + private void ParsePipe() + { + StatusType status = StatusType.OPEN; + double lcoeff = 0.0d; + + if (net.GetLink(_tok[0]) != null) + throw new InputException(ErrorCode.Err215, SectType.PIPES, _tok[0]); + + Pipe pipe = new Pipe(_tok[0]); + net.Links.Add(pipe); + + if (_tok.Count < 6) + throw new InputException(ErrorCode.Err201, SectType.PIPES, _line); + + + Node j1 = net.GetNode(_tok[1]), + j2 = net.GetNode(_tok[2]); + + if (j1 == null || j2 == null) + throw new InputException(ErrorCode.Err203, SectType.PIPES, _tok[0]); + + + if (j1.Equals(j2)) + throw new InputException(ErrorCode.Err222, SectType.PIPES, _tok[0]); + + if (!_tok[3].ToDouble(out double length) || length <= 0.0 || + !_tok[4].ToDouble(out double diam) || diam <= 0.0 || + !_tok[5].ToDouble(out double rcoeff) || rcoeff <= 0.0) + throw new InputException(ErrorCode.Err202, SectType.PIPES, _tok[0]); + + + if (_tok.Count == 7) + { + if (_tok[6].Match(Keywords.w_CV)) pipe.HasCheckValve = true; + else if (_tok[6].Match(Keywords.w_CLOSED)) status = StatusType.CLOSED; + else if (_tok[6].Match(Keywords.w_OPEN)) status = StatusType.OPEN; + else if (!_tok[6].ToDouble(out lcoeff) || lcoeff < 0) + throw new InputException(ErrorCode.Err202, SectType.PIPES, _tok[0]); + } + + if (_tok.Count == 8) + { + if (!_tok[6].ToDouble(out lcoeff) || lcoeff < 0) + throw new InputException(ErrorCode.Err202, SectType.PIPES, _tok[0]); + + if (_tok[7].Match(Keywords.w_CV)) pipe.HasCheckValve = true; + else if (_tok[7].Match(Keywords.w_CLOSED)) status = StatusType.CLOSED; + else if (_tok[7].Match(Keywords.w_OPEN)) status = StatusType.OPEN; + else + throw new InputException(ErrorCode.Err202, SectType.PIPES, _tok[0]); + } + + pipe.FirstNode = j1; + pipe.SecondNode = j2; + pipe.Lenght = length; + pipe.Diameter = diam; + pipe.Kc = rcoeff; + pipe.Km = lcoeff; + pipe.Kb = double.NaN; + pipe.Kw = double.NaN; + pipe.Status = status; + pipe.RptFlag = false; + + if (!string.IsNullOrEmpty(_comment)) + pipe.Comment = _comment; + } + + private void ParsePump() + { + int m; + double[] x = new double[6]; + + if (net.GetLink(_tok[0]) != null) + throw new InputException(ErrorCode.Err215, SectType.PUMPS, _tok[0]); + + if (_tok.Count < 4) + throw new InputException(ErrorCode.Err201, SectType.PUMPS, _line); + + Node j1 = net.GetNode(_tok[1]), + j2 = net.GetNode(_tok[2]); + + if (j1 == null || j2 == null) + throw new InputException(ErrorCode.Err203, SectType.PUMPS, _tok[0]); + + if (j1.Equals(j2)) + throw new InputException(ErrorCode.Err222, SectType.PUMPS, _tok[0]); + + Pump pump = new Pump(_tok[0]) + { + FirstNode = j1, + SecondNode = j2, + }; + + net.Links.Add(pump); + + if (!string.IsNullOrEmpty(_comment)) pump.Comment = _comment; + + // If 4-th token is a number then input follows Version 1.x format + // so retrieve pump curve parameters + if (_tok[3].ToDouble(out x[0])) + { + + m = 1; + for (int j = 4; j < _tok.Count; j++) + { + if (!_tok[j].ToDouble(out x[m])) + throw new InputException(ErrorCode.Err202, SectType.PUMPS, _tok[0]); + m++; + } + + GetPumpCurve(pump, m, x); /* Get pump curve params */ + + return; + } + + /* Otherwise input follows Version 2 format */ + /* so retrieve keyword/value pairs. */ + m = 4; + while (m < _tok.Count) + { + if (_tok[m - 1].Match(Keywords.w_POWER)) + { /* Const. HP curve */ + if (!_tok[m].ToDouble(out double y) || y <= 0.0) + throw new InputException(ErrorCode.Err202, SectType.PUMPS, _tok[0]); + + pump.Ptype = PumpType.CONST_HP; + pump.Km = y; + } + else if (_tok[m - 1].Match(Keywords.w_HEAD)) + { /* Custom pump curve */ + Curve t = net.GetCurve(_tok[m]); + if (t == null) + throw new InputException(ErrorCode.Err206, SectType.PUMPS, _tok[0]); + + pump.HCurve = t; + } + else if (_tok[m - 1].Match(Keywords.w_PATTERN)) + { /* Speed/status pattern */ + Pattern p = net.GetPattern(_tok[m]); + if (p == null) + throw new InputException(ErrorCode.Err205, SectType.PUMPS, _tok[0]); + + pump.UPat = p; + } + else if (_tok[m - 1].Match(Keywords.w_SPEED)) + { /* Speed setting */ + if (!_tok[m].ToDouble(out double y) || y < 0.0) + throw new InputException(ErrorCode.Err202, SectType.PUMPS, _tok[0]); + + pump.Kc = y; + } + else + { + throw new InputException(ErrorCode.Err201, SectType.PUMPS, _line); + } + + m += 2; + } + } + + private void ParseValve() + { + int n = _tok.Count; + StatusType status = StatusType.ACTIVE; + string key = _tok[0]; + + double setting, lcoeff = 0.0; + + if (net.GetLink(key) != null) + throw new InputException(ErrorCode.Err215, SectType.VALVES, _tok[0]); + + if (n < 6) + throw new InputException(ErrorCode.Err201, SectType.VALVES, _line); + + Node j1 = net.GetNode(_tok[1]), + j2 = net.GetNode(_tok[2]); + + if (j1 == null || j2 == null) + throw new InputException(ErrorCode.Err203, SectType.VALVES, _tok[0]); + + if (j1.Equals(j2)) + throw new InputException(ErrorCode.Err222, SectType.VALVES, _tok[0]); + + if (!_tok[4].TryParse(out ValveType type)) + throw new InputException(ErrorCode.Err201, SectType.VALVES, _line); + + if (!_tok[3].ToDouble(out double diam) || diam <= 0.0) + throw new InputException(ErrorCode.Err202, SectType.VALVES, _tok[0]); + + Valve valve = new Valve(key, type); + net.Links.Add(valve); + + if (type == ValveType.GPV) + { + Curve t = net.GetCurve(_tok[5]); + + if (t == null) + throw new InputException(ErrorCode.Err206, SectType.VALVES, _tok[0]); + + setting = net.Curves.IndexOf(t); + log.Warning("GPV Valve, index as roughness !"); + valve.Curve = t; + status = StatusType.OPEN; + } + else if (!_tok[5].ToDouble(out setting)) + { + throw new InputException(ErrorCode.Err202, SectType.VALVES, _tok[0]); + } + + if (n >= 7) + { + if (!_tok[6].ToDouble(out lcoeff)) + { + throw new InputException(ErrorCode.Err202, SectType.VALVES, _tok[0]); + } + } + + if (j1.NodeType > NodeType.Junction || j2.NodeType > NodeType.Junction) + { + if (type == ValveType.PRV || type == ValveType.PSV || type == ValveType.FCV) + throw new InputException(ErrorCode.Err219, SectType.VALVES, _tok[0]); + } + + if (!Valvecheck(net, type, j1, j2)) + throw new InputException(ErrorCode.Err220, SectType.VALVES, _tok[0]); + + + valve.FirstNode = j1; + valve.SecondNode = j2; + valve.Diameter = diam; + valve.Lenght = 0.0d; + valve.Kc = setting; + valve.Km = lcoeff; + valve.Kb = 0.0d; + valve.Kw = 0.0d; + valve.Status = status; + valve.RptFlag = false; + + if (!string.IsNullOrEmpty(_comment)) + valve.Comment = _comment; + } + + private void ParsePattern() + { + Pattern pat = net.GetPattern(_tok[0]); + + if (pat == null) + { + pat = new Pattern(_tok[0]); + net.Patterns.Add(pat); + } + + for (int i = 1; i < _tok.Count; i++) + { + if (!_tok[i].ToDouble(out double x)) + throw new InputException(ErrorCode.Err202, SectType.PATTERNS, _tok[0]); + + pat.Add(x); + } + } + + private void ParseCurve() + { + Curve cur = net.GetCurve(_tok[0]); + + if (cur == null) + { + cur = new Curve(_tok[0]); + net.Curves.Add(cur); + } + + if (!_tok[1].ToDouble(out double x) || !_tok[2].ToDouble(out double y)) + throw new InputException(ErrorCode.Err202, SectType.CURVES, _tok[0]); + + cur.Add(x, y); + } + + private void ParseCoordinate() + { + if (_tok.Count < 3) + throw new InputException(ErrorCode.Err201, SectType.COORDINATES, _line); + + Node node = net.GetNode(_tok[0]); + + if (node == null) + throw new InputException(ErrorCode.Err203, SectType.COORDINATES, _tok[0]); + + if (!_tok[1].ToDouble(out double x) || !_tok[2].ToDouble(out double y)) + throw new InputException(ErrorCode.Err202, SectType.COORDINATES, _tok[0]); + + node.Coordinate = new EnPoint(x, y); + } + + private void ParseLabel() + { + if (_tok.Count < 3) + throw new InputException(ErrorCode.Err201, SectType.LABELS, _line); + + if (!_tok[0].ToDouble(out double x) || !_tok[1].ToDouble(out double y)) + throw new InputException(ErrorCode.Err201, SectType.LABELS, _line); + + string text = _tok[2].Replace("\"", ""); + + Label label = new Label(text) + { + Position = new EnPoint(x, y) + }; + + net.Labels.Add(label); + } + + private void ParseVertice() + { + if (_tok.Count < 3) + throw new InputException(ErrorCode.Err201, SectType.VERTICES, _line); + + Link link = net.GetLink(_tok[0]); + + if (link == null) + throw new InputException(ErrorCode.Err204, SectType.VERTICES, _tok[0]); + + if (!_tok[1].ToDouble(out double x) || !_tok[2].ToDouble(out double y)) + throw new InputException(ErrorCode.Err202, SectType.VERTICES, _tok[0]); + + link.Vertices.Add(new EnPoint(x, y)); + } + + private void ParseControl() + { + StatusType status = StatusType.ACTIVE; + + double setting = double.NaN, level = 0.0; + TimeSpan time = TimeSpan.Zero; + + if (_tok.Count < 6) + throw new InputException(ErrorCode.Err201, SectType.CONTROLS, _line); + + Node node = null; + Link link = net.GetLink(_tok[1]); + + if (link == null) + throw new InputException(ErrorCode.Err204, SectType.CONTROLS, _line); + + LinkType ltype = link.LinkType; + + if (link.LinkType == LinkType.Pipe && ((Pipe)link).HasCheckValve) + throw new InputException(ErrorCode.Err207, SectType.CONTROLS, _line); + + if (_tok[2].Match(StatusType.OPEN.Keyword2())) + { + status = StatusType.OPEN; + switch (link.LinkType) + { + case LinkType.Pump: + setting = 1.0; + break; + case LinkType.VALVE: + if (((Valve)link).ValveType == ValveType.GPV) + setting = link.Kc; + break; + } + } + else if (_tok[2].Match(StatusType.CLOSED.Keyword2())) + { + status = StatusType.CLOSED; + switch (link.LinkType) + { + case LinkType.Pump: + setting = 0.0; + break; + case LinkType.VALVE: + if (((Valve)link).ValveType == ValveType.GPV) + setting = link.Kc; + break; + } + } + else if (ltype == LinkType.VALVE && ((Valve)link).ValveType == ValveType.GPV) + { + throw new InputException(ErrorCode.Err206, SectType.CONTROLS, _line); + } + else if (!_tok[2].ToDouble(out setting)) + { + throw new InputException(ErrorCode.Err202, SectType.CONTROLS, _line); + } + + if (ltype == LinkType.Pump || ltype == LinkType.Pipe) + { + if (!double.IsNaN(setting)) + { + if (setting < 0.0) + throw new InputException(ErrorCode.Err202, SectType.CONTROLS, _line); + + status = setting.IsZero() ? StatusType.CLOSED : StatusType.OPEN; + } + } + + ControlType ctype; + + if (_tok[4].Match(Keywords.w_TIME)) + ctype = ControlType.Timer; + else if (_tok[4].Match(Keywords.w_CLOCKTIME)) + ctype = ControlType.TimeOfDay; + else + { + if (_tok.Count < 8) + throw new InputException(ErrorCode.Err201, SectType.CONTROLS, _line); + + if ((node = net.GetNode(_tok[5])) == null) + throw new InputException(ErrorCode.Err203, SectType.CONTROLS, _line); + + if (_tok[6].Match(Keywords.w_BELOW)) ctype = ControlType.LowLevel; + else if (_tok[6].Match(Keywords.w_ABOVE)) ctype = ControlType.HiLevel; + else + throw new InputException(ErrorCode.Err201, SectType.CONTROLS, _line); + } + + switch (ctype) + { + case ControlType.Timer: + case ControlType.TimeOfDay: + time = _tok.Count > 6 + ? Utilities.ToTimeSpan(_tok[5], _tok[6]) + : Utilities.ToTimeSpan(_tok[5]); + + if (time == TimeSpan.MinValue) + throw new InputException(ErrorCode.Err201, SectType.CONTROLS, _line); + + break; + + case ControlType.LowLevel: + case ControlType.HiLevel: + if (!_tok[7].ToDouble(out level)) + throw new InputException(ErrorCode.Err202, SectType.CONTROLS, _line); + + break; + } + + Control cntr = new Control + { + Link = link, + Node = node, + Type = ctype, + Status = status, + Setting = setting, + Time = ctype == ControlType.TimeOfDay ? time.TimeOfDay() : time, + Grade = level + }; + + net.Controls.Add(cntr); + } + + private void ParseSource() + { + int n = _tok.Count; + + Pattern pat = null; + + + if (n < 2) + throw new InputException(ErrorCode.Err201, SectType.SOURCES, _line); + + Node node = net.GetNode(_tok[0]); + + if (node == null) + throw new InputException(ErrorCode.Err203, SectType.SOURCES, _tok[0]); + + /* NOTE: Under old format, SourceType not supplied so let */ + /* i = index of token that contains quality value. */ + int i = 2; /* Token with quality value */ + + if (!_tok[1].TryParse(out SourceType type)) + { + i = 1; + type = SourceType.Concen; + } + + if (!_tok[i].ToDouble(out double c0)) + { + /* Illegal WQ value */ + throw new InputException(ErrorCode.Err202, SectType.SOURCES, _tok[0]); + } + + if (n > i + 1 && !string.IsNullOrEmpty(_tok[i + 1]) && _tok[i + 1] != "*") + { + if ((pat = net.GetPattern(_tok[i + 1])) == null) + throw new InputException(ErrorCode.Err205, SectType.SOURCES, _tok[0]); + } + + node.QualSource = new QualSource(type, c0, pat); + } + + private void ParseEmitter() + { + int n = _tok.Count; + Node node; + + if (n < 2) throw + new InputException(ErrorCode.Err201, SectType.EMITTERS, _line); + + if ((node = net.GetNode(_tok[0])) == null) + throw new InputException(ErrorCode.Err203, SectType.EMITTERS, _tok[0]); + + if (node.NodeType != NodeType.Junction) + throw new InputException(ErrorCode.Err209, SectType.EMITTERS, _tok[0]); + + if (!_tok[1].ToDouble(out double k) || k < 0.0) + throw new InputException(ErrorCode.Err202, SectType.EMITTERS, _tok[0]); + + node.Ke = k; + + } + + private void ParseQuality() + { + int n = _tok.Count; + double c0; + + if (n < 2) return; + + /* Single node entered */ + if (n == 2) + { + Node node = net.GetNode(_tok[0]); + + if (node == null) return; + + if (!_tok[1].ToDouble(out c0)) + throw new InputException(ErrorCode.Err209, SectType.QUALITY, _tok[0]); + + node.C0 = c0; + } + else + { + + /* Node range entered */ + if (!_tok[2].ToDouble(out c0)) + throw new InputException(ErrorCode.Err209, SectType.QUALITY, _tok[0]); + + /* If numerical range supplied, then use numerical comparison */ + if (Atol(_tok[0], out int i0) && Atol(_tok[1], out int i1)) + { + foreach (Node node in net.Nodes) + { + if (Atol(node.Name, out int i) && i >= i0 && i <= i1) + node.C0 = c0; + } + } + else + { + foreach (Node node in net.Nodes) + { + if (string.Compare(_tok[0], node.Name, StringComparison.Ordinal) <= 0 && + string.Compare(_tok[1], node.Name, StringComparison.Ordinal) >= 0) + node.C0 = c0; + } + } + } + } + + private void ParseReaction() + { + int item, n = _tok.Count; + double y; + + if (n < 3) return; + + + if (_tok[0].Match(Keywords.w_ORDER)) + { /* Reaction order */ + + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + + if (_tok[1].Match(Keywords.w_BULK)) net.BulkOrder = y; + else if (_tok[1].Match(Keywords.w_TANK)) net.TankOrder = y; + else if (_tok[1].Match(Keywords.w_WALL)) + { + if (y.IsZero()) net.WallOrder = 0.0; + else if (y.EqualsTo(1.0)) net.WallOrder = 1.0; + else throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + } + else throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + + return; + } + + if (_tok[0].Match(Keywords.w_ROUGHNESS)) + { /* Roughness factor */ + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + + net.RFactor = y; + return; + } + + if (_tok[0].Match(Keywords.w_LIMITING)) + { /* Limiting potential */ + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + + net.CLimit = y; + return; + } + + if (_tok[0].Match(Keywords.w_GLOBAL)) + { /* Global rates */ + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.REACTIONS, _line); + + if (_tok[1].Match(Keywords.w_BULK)) net.KBulk = y; + else if (_tok[1].Match(Keywords.w_WALL)) net.KWall = y; + else throw new InputException(ErrorCode.Err201, SectType.REACTIONS, _line); + + return; + } + + /* Individual rates */ + if (_tok[0].Match(Keywords.w_BULK)) item = 1; + else if (_tok[0].Match(Keywords.w_WALL)) item = 2; + else if (_tok[0].Match(Keywords.w_TANK)) item = 3; + else throw new InputException(ErrorCode.Err201, SectType.REACTIONS, _line); + + _tok[0] = _tok[1]; + + if (item == 3) + { + /* Tank rates */ + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err209, SectType.REACTIONS, _tok[1]); + + if (n == 3) + { + Node node; + if ((node = net.GetNode(_tok[1])) == null) + throw new InputException(ErrorCode.Err208, SectType.REACTIONS, _tok[1]); + + Tank tank = node as Tank; + + if (tank == null) return; + tank.Kb = y; + } + else + { + /* If numerical range supplied, then use numerical comparison */ + if (Atol(_tok[1], out int i1) && Atol(_tok[2], out int i2)) + { + foreach (Tank tank in net.Tanks) + { + if (Atol(tank.Name, out int i) && i >= i1 && i <= i2) + tank.Kb = y; + } + } + else + { + foreach (Tank tank in net.Tanks) + { + if (string.Compare(_tok[1], tank.Name, StringComparison.Ordinal) <= 0 && + string.Compare(_tok[2], tank.Name, StringComparison.Ordinal) >= 0) + tank.Kb = y; + } + } + } + } + else + { /* Link rates */ + if (!_tok[n - 1].ToDouble(out y)) + throw new InputException(ErrorCode.Err211, SectType.REACTIONS, _tok[1]); + + if (net.Links.Count == 0) return; + + if (n == 3) + { /* Single link */ + Link link; + if ((link = net.GetLink(_tok[1])) == null) return; + if (item == 1) link.Kb = y; + else link.Kw = y; + } + else + { + /* Range of links */ + + /* If numerical range supplied, then use numerical comparison */ + if (Atol(_tok[1], out int i1) && Atol(_tok[2], out int i2)) + { + foreach (Link link in net.Links) + { + if (Atol(link.Name, out int i) && i >= i1 && i <= i2) + { + if (item == 1) link.Kb = y; + else link.Kw = y; + } + } + } + else + foreach (Link link in net.Links) + { + if (string.Compare(_tok[1], link.Name, StringComparison.Ordinal) <= 0 && + string.Compare(_tok[2], link.Name, StringComparison.Ordinal) >= 0) + { + if (item == 1) link.Kb = y; + else link.Kw = y; + } + } + } + } + } + + private void ParseMixing() + { + int n = _tok.Count; + + if (net.Nodes.Count == 0) + throw new InputException(ErrorCode.Err208, SectType.MIXING, _tok[0]); + + if (n < 2) return; + + Node node = net.GetNode(_tok[0]); + + if (node == null) + throw new InputException(ErrorCode.Err208, SectType.MIXING, _tok[0]); + + if (node.NodeType != NodeType.Junction) return; + + Tank tank = (Tank)node; + + if (!_tok[1].TryParse(out MixType type)) + throw new InputException(ErrorCode.Err201, SectType.MIXING, _line); + + var v = 1.0; + if (type == MixType.Mix2 && n == 3) + { + if (!_tok[2].ToDouble(out v)) + { + throw new InputException(ErrorCode.Err209, SectType.MIXING, _tok[0]); + } + } + + if (v.IsZero()) + v = 1.0; + + if (tank.NodeType == NodeType.Reservoir) return; + + tank.MixModel = type; + tank.V1Max = v; + } + + private void ParseStatus() + { + int n = _tok.Count - 1; + double y = 0.0; + StatusType status = StatusType.ACTIVE; + + if (net.Links.Count == 0) + throw new InputException(ErrorCode.Err210, SectType.STATUS, _tok[0]); + + if (n < 1) + throw new InputException(ErrorCode.Err201, SectType.STATUS, _line); + + if (_tok[n].Match(Keywords.w_OPEN)) status = StatusType.OPEN; + else if (_tok[n].Match(Keywords.w_CLOSED)) status = StatusType.CLOSED; + else if (!_tok[n].ToDouble(out y) || y < 0.0) + throw new InputException(ErrorCode.Err211, SectType.STATUS, _tok[0]); + + if (n == 1) + { + Link link = net.GetLink(_tok[0]); + if (link == null) return; + + switch (link.LinkType) + { + case LinkType.Pipe: + if (((Pipe)link).HasCheckValve) + throw new InputException(ErrorCode.Err211, SectType.STATUS, _tok[0]); + + break; + + case LinkType.VALVE: + if (((Valve)link).ValveType == ValveType.GPV && status == StatusType.ACTIVE) + throw new InputException(ErrorCode.Err211, SectType.STATUS, _tok[0]); + + break; + } + + ChangeStatus(link, status, y); + } + else + { + /* If numerical range supplied, then use numerical comparison */ + if (Atol(_tok[0], out int i0) && Atol(_tok[1], out int i1)) + { + foreach (Link link in net.Links) + { + if (Atol(link.Name, out int i) && i >= i0 && i <= i1) + ChangeStatus(link, status, y); + + } + } + else + foreach (Link j in net.Links) + if (string.Compare(_tok[0], j.Name, StringComparison.Ordinal) <= 0 && + string.Compare(_tok[1], j.Name, StringComparison.Ordinal) >= 0) + ChangeStatus(j, status, y); + } + } + + + private void ParseEnergy() + { + int n = _tok.Count; + double y; + + if (n < 3) + throw new InputException(ErrorCode.Err201, SectType.ENERGY, _line); + + if (_tok[0].Match(Keywords.w_DMNDCHARGE)) + { + if (!_tok[2].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.ENERGY, _line); + + net.DCost = y; + return; + } + + Pump pump; + + if (_tok[0].Match(Keywords.w_GLOBAL)) + { + pump = null; + } + else if (_tok[0].Match(Keywords.w_PUMP)) + { + if (n < 4) + throw new InputException(ErrorCode.Err201, SectType.ENERGY, _line); + + Link link = net.GetLink(_tok[1]); + + if (link == null || link.LinkType != LinkType.Pump) + throw new InputException(ErrorCode.Err216, SectType.ENERGY, _tok[0]); + + pump = (Pump)link; + } + else + throw new InputException(ErrorCode.Err201, SectType.ENERGY, _line); + + + if (_tok[n - 2].Match(Keywords.w_PRICE)) + { + if (!_tok[n - 1].ToDouble(out y)) + { + throw pump == null + ? new InputException(ErrorCode.Err213, SectType.ENERGY, _line) + : new InputException(ErrorCode.Err217, SectType.ENERGY, _tok[0]); + } + + if (pump == null) + net.ECost = y; + else + pump.ECost = y; + + return; + } + + if (_tok[n - 2].Match(Keywords.w_PATTERN)) + { + Pattern t = net.GetPattern(_tok[n - 1]); + if (t == null) + { + throw pump == null + ? new InputException(ErrorCode.Err213, SectType.ENERGY, _line) + : new InputException(ErrorCode.Err217, SectType.ENERGY, _tok[0]); + } + + if (pump == null) + net.EPatId = t.Name; + else + pump.EPat = t; + + return; + } + + if (_tok[n - 2].Match(Keywords.w_EFFIC)) + { + if (pump == null) + { + if (!_tok[n - 1].ToDouble(out y) || y <= 0.0) + throw new InputException(ErrorCode.Err213, SectType.ENERGY, _line); + + net.EPump = y; + } + else + { + Curve t = net.GetCurve(_tok[n - 1]); + if (t == null) + throw new InputException(ErrorCode.Err217, SectType.ENERGY, _tok[0]); + + pump.ECurve = t; + } + return; + } + + throw new InputException(ErrorCode.Err201, SectType.ENERGY, _line); + } + + private void ParseReport() + { + int n = _tok.Count - 1; + //FieldType i; + double y; + + if (n < 1) + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + if (_tok[0].Match(Keywords.w_PAGE)) + { + if (!_tok[n].ToDouble(out y)) + throw new InputException(ErrorCode.Err213, SectType.REPORT, _line); + + if (y < 0.0 || y > 255.0) + throw new InputException(ErrorCode.Err213, SectType.REPORT, _line); + + net.PageSize = (int)y; + return; + } + + + if (_tok[0].Match(Keywords.w_STATUS)) + { + if (_tok[n].TryParse(out StatusLevel flag)) + { + net.StatFlag = flag; + } + + return; + } + + if (_tok[0].Match(Keywords.w_SUMMARY)) + { + if (_tok[n].Match(Keywords.w_NO)) net.SummaryFlag = false; + if (_tok[n].Match(Keywords.w_YES)) net.SummaryFlag = true; + return; + } + + if (_tok[0].Match(Keywords.w_MESSAGES)) + { + if (_tok[n].Match(Keywords.w_NO)) net.MessageFlag = false; + if (_tok[n].Match(Keywords.w_YES)) net.MessageFlag = true; + return; + } + + if (_tok[0].Match(Keywords.w_ENERGY)) + { + if (_tok[n].Match(Keywords.w_NO)) net.EnergyFlag = false; + if (_tok[n].Match(Keywords.w_YES)) net.EnergyFlag = true; + return; + } + + if (_tok[0].Match(Keywords.w_NODE)) + { + + if (_tok[n].Match(Keywords.w_NONE)) + { + net.NodeFlag = ReportFlag.FALSE; + } + else if (_tok[n].Match(Keywords.w_ALL)) + { + net.NodeFlag = ReportFlag.TRUE; + } + else + { + + if (net.Nodes.Count == 0) + throw new InputException(ErrorCode.Err208, SectType.REPORT, _tok[1]); + + for (int i = 1; i <= n; i++) + { + Node node = net.GetNode(_tok[i]); + + if (node == null) + throw new InputException(ErrorCode.Err208, SectType.REPORT, _tok[i]); + + node.RptFlag = true; + } + + net.NodeFlag = ReportFlag.SOME; + } + return; + } + + if (_tok[0].Match(Keywords.w_LINK)) + { + if (_tok[n].Match(Keywords.w_NONE)) + net.LinkFlag = ReportFlag.FALSE; + else if (_tok[n].Match(Keywords.w_ALL)) + net.LinkFlag = ReportFlag.TRUE; + else + { + + if (net.Links.Count == 0) + throw new InputException(ErrorCode.Err210, SectType.REPORT, _tok[1]); + + for (int i = 1; i <= n; i++) + { + Link link = net.GetLink(_tok[i]); + + if (link == null) + throw new InputException(ErrorCode.Err210, SectType.REPORT, _tok[i]); + + link.RptFlag = true; + } + + net.LinkFlag = ReportFlag.SOME; + } + return; + } + + FieldsMap fMap = net.FieldsMap; + + if (_tok[0].TryParse(out FieldType iFieldId)) + { + if (iFieldId > FieldType.FRICTION) + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + if (_tok.Count == 1 || _tok[1].Match(Keywords.w_YES)) + { + fMap.GetField(iFieldId).Enabled = true; + return; + } + + if (_tok[1].Match(Keywords.w_NO)) + { + fMap.GetField(iFieldId).Enabled = false; + return; + } + + if (_tok.Count < 3) + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + if (!_tok[1].TryParse(out RangeType rj)) + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + if (!_tok[2].ToDouble(out y)) + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + if (rj == RangeType.PREC) + { + fMap.GetField(iFieldId).Enabled = true; + fMap.GetField(iFieldId).Precision = (int)Math.Round(y); //roundOff(y)); + } + else + fMap.GetField(iFieldId).SetRptLim(rj, y); + + return; + } + + if (_tok[0].Match(Keywords.w_FILE)) + { + net.AltReport = _tok[1]; + return; + } + + throw new InputException(ErrorCode.Err201, SectType.REPORT, _line); + + } + + private void ParseOption() + { + int n = _tok.Count - 1; + bool notHandled = OptionChoice(n); + if (notHandled) + notHandled = OptionValue(n); + if (notHandled) + { + net.ExtraOptions[_tok[0]] = _tok[1]; + } + } + + ///<summary>Handles options that are choice values, such as quality type, for example.</summary> + /// <param name="n">number of tokens</param> + /// <returns><c>true</c> is it didn't handle the option.</returns> + private bool OptionChoice(int n) + { + + if (n < 0) + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + + if (_tok[0].Match(Keywords.w_UNITS)) + { + if (n < 1) + return false; + + if (!_tok[1].TryParse(out FlowUnitsType type)) + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + + net.FlowFlag = type; + } + else if (_tok[0].Match(Keywords.w_PRESSURE)) + { + if (n < 1) return false; + + if (!_tok[1].TryParse(out PressUnitsType value)) + { + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + } + + net.PressFlag = value; + } + else if (_tok[0].Match(Keywords.w_HEADLOSS)) + { + if (n < 1) return false; + + if (!_tok[1].TryParse(out HeadLossFormulaType value)) + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + + net.FormFlag = value; + } + else if (_tok[0].Match(Keywords.w_HYDRAULIC)) + { + if (n < 2) return false; + + if (!_tok[1].TryParse(out HydType value)) + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + + net.HydFlag = value; + net.HydFname = _tok[2]; + } + else if (_tok[0].Match(Keywords.w_QUALITY)) + { + if (n < 1) + return false; + + net.QualFlag = _tok[1].TryParse(out QualType type) + ? type + : QualType.Chem; + + switch (net.QualFlag) + { + case QualType.Chem: + net.ChemName = _tok[1]; + if (n >= 2) + net.ChemUnits = _tok[2]; + break; + + case QualType.Trace: + + _tok[0] = ""; + if (n < 2) + throw new InputException(ErrorCode.Err212, SectType.OPTIONS, _line); + + _tok[0] = _tok[2]; + Node node = net.GetNode(_tok[2]); + + if (node == null) + throw new InputException(ErrorCode.Err212, SectType.OPTIONS, _line); + + net.TraceNode = node.Name; + net.ChemName = Keywords.u_PERCENT; + net.ChemUnits = _tok[2]; + break; + + case QualType.Age: + net.ChemName = Keywords.w_AGE; + net.ChemUnits = Keywords.u_HOURS; + break; + } + + } + else if (_tok[0].Match(Keywords.w_MAP)) + { + if (n < 1) + return false; + + net.MapFname = _tok[1]; + } + else if (_tok[0].Match(Keywords.w_UNBALANCED)) + { + if (n < 1) + return false; + + if (_tok[1].Match(Keywords.w_STOP)) + { + net.ExtraIter = -1; + } + else if (_tok[1].Match(Keywords.w_CONTINUE)) + { + if (n >= 2) + { + if (!_tok[2].ToDouble(out double d)) + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + + net.ExtraIter = (int)d; + } + else + net.ExtraIter = 0; + } + else + { + throw new InputException(ErrorCode.Err201, SectType.OPTIONS, _line); + } + } + else if (_tok[0].Match(Keywords.w_PATTERN)) + { + if (n < 1) + return false; + + net.DefPatId = _tok[1]; + } + else + return true; + return false; + } + + private bool OptionValue(int n) + { + string name = _tok[0]; + + /* Check for obsolete SEGMENTS keyword */ + if (name.Match(Keywords.w_SEGMENTS)) + return false; + + /* Check for missing value (which is permissible) */ + int nvalue = name.Match(Keywords.w_SPECGRAV) || + name.Match(Keywords.w_EMITTER) || + name.Match(Keywords.w_DEMAND) + ? 2 + : 1; + + string keyword = null; + foreach (string k in optionValueKeywords) + { + if (name.Match(k)) + { + keyword = k; + break; + } + } + + if (keyword == null) return true; + name = keyword; + + if (!_tok[nvalue].ToDouble(out double y)) + throw new InputException(ErrorCode.Err213, SectType.OPTIONS, _line); + + if (name.Match(Keywords.w_TOLERANCE)) + { + if (y < 0.0) + throw new InputException(ErrorCode.Err213, SectType.OPTIONS, _line); + + net.Ctol = y; + return false; + } + + if (name.Match(Keywords.w_DIFFUSIVITY)) + { + if (y < 0.0) + throw new InputException(ErrorCode.Err213, SectType.OPTIONS, _line); + + net.Diffus = y; + return false; + } + + if (name.Match(Keywords.w_DAMPLIMIT)) + { + net.DampLimit = y; + return false; + } + + if (y <= 0.0) + throw new InputException(ErrorCode.Err213, SectType.OPTIONS, _line); + + if (name.Match(Keywords.w_VISCOSITY)) net.Viscos = y; + else if (name.Match(Keywords.w_SPECGRAV)) net.SpGrav = y; + else if (name.Match(Keywords.w_TRIALS)) net.MaxIter = (int)y; + else if (name.Match(Keywords.w_ACCURACY)) + { + y = Math.Max(y, 1e-5); + y = Math.Min(y, 1e-1); + net.HAcc = y; + } + else if (name.Match(Keywords.w_HTOL)) net.HTol = y; + else if (name.Match(Keywords.w_QTOL)) net.QTol = y; + else if (name.Match(Keywords.w_RQTOL)) + { + + if (y >= 1.0) + throw new InputException(ErrorCode.Err213, SectType.OPTIONS, _line); + + net.RQtol = y; + } + else if (name.Match(Keywords.w_CHECKFREQ)) net.CheckFreq = (int)y; + else if (name.Match(Keywords.w_MAXCHECK)) net.MaxCheck = (int)y; + else if (name.Match(Keywords.w_EMITTER)) net.QExp = 1.0 / y; + else if (name.Match(Keywords.w_DEMAND)) net.DMult = y; + + return false; + } + + private void ParseTime() + { + int n = _tok.Count - 1; + + if (n < 1) + throw new InputException(ErrorCode.Err201, SectType.TIMES, _line); + + if (_tok[0].Match(Keywords.w_STATISTIC)) + { + if (!_tok[n].TryParse(out TstatType type)) + throw new InputException(ErrorCode.Err201, SectType.TIMES, _line); + + net.TstatFlag = type; + + return; + } + + TimeSpan t; + + if ((t = Utilities.ToTimeSpan(_tok[n])) == TimeSpan.MinValue && + (t = Utilities.ToTimeSpan(_tok[n - 1])) == TimeSpan.MinValue && + (t = Utilities.ToTimeSpan(_tok[n - 1], _tok[n])) == TimeSpan.MinValue) + throw new InputException(ErrorCode.Err213, SectType.TIMES, _line); + + if (_tok[0].Match(Keywords.w_DURATION)) + net.Duration = t; + else if (_tok[0].Match(Keywords.w_HYDRAULIC)) + net.HStep = t; + else if (_tok[0].Match(Keywords.w_QUALITY)) + net.QStep = t; + else if (_tok[0].Match(Keywords.w_RULE)) + net.RuleStep = t; + else if (_tok[0].Match(Keywords.w_MINIMUM)) { } + else if (_tok[0].Match(Keywords.w_PATTERN)) + { + if (_tok[1].Match(Keywords.w_TIME)) + net.PStep = t; + else if (_tok[1].Match(Keywords.w_START)) + net.PStart = t; + else + throw new InputException(ErrorCode.Err201, SectType.TIMES, _line); + } + else if (_tok[0].Match(Keywords.w_REPORT)) + { + if (_tok[1].Match(Keywords.w_TIME)) + net.RStep = t; + else if (_tok[1].Match(Keywords.w_START)) + net.RStart = t; + else + throw new InputException(ErrorCode.Err201, SectType.TIMES, _line); + } + else if (_tok[0].Match(Keywords.w_START)) + net.Tstart = t.TimeOfDay(); + + else + throw new InputException(ErrorCode.Err201, SectType.TIMES, _line); + } + + private static SectType FindSectionType(string line) + { + if (string.IsNullOrEmpty(line)) return (SectType)(-1); + + for (var type = SectType.TITLE; type <= SectType.END; type++) + { + string sectName = '[' + type.ToString() + ']'; + + // if(line.Contains(type.parseStr())) return type; + // if (line.IndexOf(type.parseStr(), StringComparison.OrdinalIgnoreCase) >= 0) { + if (line.StartsWith(sectName, StringComparison.OrdinalIgnoreCase)) + { + return type; + } + } + + return (SectType)(-1); + } + + private void ParseTag() + { + int n = _tok.Count; + + if (n < 3) + throw new InputException(ErrorCode.Err201, SectType.DEMANDS, _line); + + Element el; + + if (_tok[0].Match(Keywords.w_NODE)) + { + if ((el = net.GetNode(_tok[1])) == null) + throw new InputException(ErrorCode.Err203, SectType.TAGS, _tok[1]); + } + else if (_tok[0].Match(Keywords.w_LINK)) + { + if ((el = net.GetLink(_tok[2])) == null) + throw new InputException(ErrorCode.Err204, SectType.TAGS, _tok[1]); + } + else + { + throw new InputException(ErrorCode.Err201, SectType.TAGS, _line); + } + + el.Tag = _tok[3]; + + } + + private void ParseDemand() + { + int n = _tok.Count; + Pattern pat = null; + + if (n < 2) + throw new InputException(ErrorCode.Err201, SectType.DEMANDS, _line); + + if (!_tok[1].ToDouble(out double y)) + throw new InputException(ErrorCode.Err202, SectType.DEMANDS, _tok[0]); + + if (_tok[0].Match(Keywords.w_MULTIPLY)) + { + if (y <= 0.0) + throw new InputException(ErrorCode.Err202, SectType.DEMANDS, _tok[0]); + + net.DMult = y; + return; + } + + Node node = net.GetNode(_tok[0]); + + if (node == null || node.NodeType != NodeType.Junction) + throw new InputException(ErrorCode.Err208, SectType.DEMANDS, _tok[0]); + + if (n >= 3) + { + pat = net.GetPattern(_tok[2]); + if (pat == null) + throw new InputException(ErrorCode.Err205, SectType.DEMANDS, _line); + } + + node.Demands.Add(new Demand(y, pat)); + + } + + private void ParseRule() + { + _tok[0].TryParse(out Rulewords key); + if (key == Rulewords.RULE) + { + _currentRule = new Rule(_tok[1]); + net.Rules.Add(_currentRule); + } + else + { + _currentRule?.Code.Add(_line); + } + } + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/io/input/InputParser.cs b/IStation.Epanet/network/io/input/InputParser.cs new file mode 100644 index 0000000..9fcecc7 --- /dev/null +++ b/IStation.Epanet/network/io/input/InputParser.cs @@ -0,0 +1,627 @@ +using IStation.Epanet; +using IStation.Epanet.Enums; +using IStation.Epanet.Log; +using IStation.Epanet.Network.Structures; +using IStation.Epanet.Util; +using System.Diagnostics; +using System.Text; + +namespace IStation.Epanet.Network.IO.Input +{ + + + ///<summary>Abstract input file parser.</summary> + public abstract class InputParser + { + protected Network net; + + ///<summary>Reference to the error logger.</summary> + protected readonly TraceSource log = new TraceSource("epanet", SourceLevels.All); + protected readonly List<EnException> errors = new List<EnException>(); + + protected void LogException(EnException ex) + { + errors.Add(ex); + + log.Error(ex); + + if (errors.Count > Constants.MAXERRS) + throw new EnException(ErrorCode.Err200); + } + + public static InputParser Create(FileType type) + { + switch (type) + { + case FileType.INP_FILE: + return new InpParser(); + case FileType.NET_FILE: + return new NetParser(); + case FileType.NULL_FILE: + return new NullParser(); + } + return null; + } + + public abstract Network Parse(Network net, string fileName); + + /// <summary> + /// Parses string S into N tokens stored in T. + /// Words between " " are stored as a single token. + /// Characters to right of ';' are ignored. + /// </summary> + protected static int GetTokens(string stringToSplit, List<string> results) + { + // const string SEPARATORS = " \t,\r\n"; + // char[] SEPARATORS = { ' ', '\t', ',', '\r', '\n' }; + + if (results == null) + throw new ArgumentNullException(nameof(results)); + + bool inQuote = false; + var tok = new StringBuilder(Constants.MAXLINE); + + results.Clear(); + + foreach (char c in stringToSplit) + { + if (c == ';') + break; + + if (c == '"') + { + // When we see a ", we need to decide whether we are + // at the start or send of a quoted section... + inQuote = !inQuote; + } + else if (!inQuote && char.IsWhiteSpace(c)) + { + // We've come to the end of a token, so we find the token, + // trim it and add it to the collection of results... + if (tok.Length > 0) + { + string result = tok.ToString().Trim(); + if (!string.IsNullOrEmpty(result)) + results.Add(result); + } + + // We start a new token... + tok.Length = 0; + } + else + { + // We've got a 'normal' character, so we add it to the curent token... + tok.Append(c); + } + } + + // We've come to the end of the string, so we add the last token... + if (tok.Length > 0) + { + string lastResult = tok.ToString().Trim(); + if (!string.IsNullOrEmpty(lastResult)) + results.Add(lastResult); + } + + return results.Count; + } + + ///<summary>Prepare the hydraulic network for simulation.</summary> + protected void Convert() + { + InitTanks(net); + InitPumps(net); + InitPatterns(net); + CheckUnlinked(); + ConvertUnits(net); + } + + ///<summary>Adjust simulation configurations.</summary> + protected static void AdjustData(Network net) + { + + if (net.PStep.Ticks <= 0) net.PStep = TimeSpan.FromHours(1); + if (net.RStep.IsZero()) net.RStep = net.PStep; + if (net.HStep.Ticks <= 0) net.HStep = TimeSpan.FromHours(1); + if (net.HStep > net.PStep) net.HStep = net.PStep; + if (net.HStep > net.RStep) net.HStep = net.RStep; + if (net.RStart > net.Duration) net.RStart = TimeSpan.Zero; + if (net.Duration.IsZero()) net.QualFlag = QualType.None; + if (net.QStep.IsZero()) net.QStep = new TimeSpan(net.HStep.Ticks / 10); + if (net.RuleStep.IsZero()) net.RuleStep = new TimeSpan(net.HStep.Ticks / 10); + + net.RuleStep = new TimeSpan(Math.Min(net.RuleStep.Ticks, net.HStep.Ticks)); + net.QStep = new TimeSpan(Math.Min(net.QStep.Ticks, net.HStep.Ticks)); + + if (double.IsNaN(net.Ctol)) + { + net.Ctol = net.QualFlag == QualType.Age ? Constants.AGETOL : Constants.CHEMTOL; + } + + switch (net.FlowFlag) + { + case FlowUnitsType.Lps: + case FlowUnitsType.Lpm: + case FlowUnitsType.Mld: + case FlowUnitsType.Cmh: + case FlowUnitsType.Cmd: + net.UnitsFlag = UnitsType.SI; + break; + default: + net.UnitsFlag = UnitsType.US; + break; + } + + + if (net.UnitsFlag != UnitsType.SI) + net.PressFlag = PressUnitsType.PSI; + else if (net.PressFlag == PressUnitsType.PSI) + net.PressFlag = PressUnitsType.METERS; + + var ucf = 1.0; + if (net.UnitsFlag == UnitsType.SI) + ucf = Math.Pow(Constants.MperFT, 2); + + if (double.IsNaN(net.Viscos)) + net.Viscos = Constants.VISCOS; + else if (net.Viscos > 1e-3) + net.Viscos *= Constants.VISCOS; + else + net.Viscos /= ucf; + + if (double.IsNaN(net.Diffus)) + net.Diffus = Constants.DIFFUS; + else if (net.Diffus > 1e-4) + net.Diffus *= Constants.DIFFUS; + else + net.Diffus /= ucf; + + net.HExp = net.FormFlag == HeadLossFormulaType.HW ? 1.852 : 2.0; + + double rfactor = net.RFactor; + HeadLossFormulaType formFlag = net.FormFlag; + double kbulk = net.KBulk; + + foreach (Link link in net.Links) + { + if (link.LinkType > LinkType.Pipe) + continue; + + if (double.IsNaN(link.Kb)) + link.Kb = kbulk; + + if (!double.IsNaN(link.Kw)) continue; + + if (rfactor.IsZero()) + link.Kw = net.KWall; + else if (link.Kc > 0.0 && link.Diameter > 0.0) + { + switch (formFlag) + { + case HeadLossFormulaType.HW: + link.Kw = rfactor / link.Kc; + break; + case HeadLossFormulaType.DW: + link.Kw = rfactor / Math.Abs(Math.Log(link.Kc / link.Diameter)); + break; + case HeadLossFormulaType.CM: + link.Kw = rfactor * link.Kc; + break; + } + } + else + link.Kw = 0.0; + } + + foreach (Tank tank in net.Tanks) + if (double.IsNaN(tank.Kb)) + tank.Kb = kbulk; + + Pattern defpat = net.GetPattern(net.DefPatId) ?? net.GetPattern(""); + + foreach (Node node in net.Nodes) + { + if (node.Demands.Count == 0) + node.Demands.Add(node.PrimaryDemand); + + foreach (Demand d in node.Demands) + { + if (d.Pattern == null) + d.Pattern = defpat; + } + } + + if (net.QualFlag == QualType.None) + net.FieldsMap.GetField(FieldType.QUALITY).Enabled = false; + + } + + ///<summary>Initialize tank properties.</summary> + /// <param name="net">Hydraulic network reference.</param> + private static void InitTanks(Network net) + { + int n = 0; + + foreach (Tank tank in net.Tanks) + { + int levelerr = 0; + + if (tank.H0 > tank.Hmax || tank.Hmin > tank.Hmax || tank.H0 < tank.Hmin) + levelerr = 1; + + Curve curv = tank.Vcurve; + + if (curv != null) + { + n = curv.Count - 1; + if (tank.Hmin < curv[0].X || + tank.Hmax > curv[n].X) + + levelerr = 1; + } + + if (levelerr != 0) + { + throw new EnException(ErrorCode.Err225, tank.Name); + } + + if (curv != null) + { + + tank.Vmin = curv.Interpolate(tank.Hmin); + tank.Vmax = curv.Interpolate(tank.Hmax); + tank.V0 = curv.Interpolate(tank.H0); + + double a = (curv[n].Y - curv[0].Y) / (curv[n].X - curv[0].X); + + tank.Area = Math.Sqrt(4.0 * a / Math.PI); + } + } + } + + ///<summary>Convert hydraulic structures values from user units to simulation system units.</summary> + /// <param name="nw">Hydraulic network reference.</param> + private static void ConvertUnits(Network nw) + { + FieldsMap fMap = nw.FieldsMap; + + foreach (Node node in nw.Nodes) + { + node.ConvertUnits(nw); + } + + nw.CLimit /= fMap.GetUnits(FieldType.QUALITY); + nw.Ctol /= fMap.GetUnits(FieldType.QUALITY); + nw.KBulk /= Constants.SECperDAY; + nw.KWall /= Constants.SECperDAY; + + foreach (Link link in nw.Links) + { + link.ConvertUnits(nw); + link.InitResistance(nw.FormFlag, nw.HExp); + } + + foreach (Control ctl in nw.Controls) + { + ctl.ConvertUnits(nw); + } + } + + ///<summary>Initialize pump properties.</summary> + /// <param name="net">Hydraulic network reference.</param> + private static void InitPumps(Network net) + { + double h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; + + foreach (Pump pump in net.Pumps) + { + + switch (pump.Ptype) + { + case PumpType.CONST_HP: + // Constant Hp pump + pump.H0 = 0.0; + pump.FlowCoefficient = -8.814 * pump.Km; + pump.N = -1.0; + pump.Hmax = Constants.BIG; + pump.Qmax = Constants.BIG; + pump.Q0 = 1.0; + continue; + case PumpType.NOCURVE: + // Set parameters for pump curves + Curve curve = pump.HCurve; + if (curve == null) + throw new EnException(ErrorCode.Err226, pump.Name); + + int n = curve.Count; + + if (n == 1) + { + pump.Ptype = PumpType.POWER_FUNC; + var pt = curve[0]; + q1 = pt.X; + h1 = pt.Y; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + else if (n == 3 && curve[0].X.IsZero()) + { + pump.Ptype = PumpType.POWER_FUNC; + h0 = curve[0].Y; + q1 = curve[1].X; + h1 = curve[1].Y; + q2 = curve[2].X; + h2 = curve[2].Y; + } + else + pump.Ptype = PumpType.CUSTOM; + + // Compute shape factors & limits of power function pump curves + if (pump.Ptype == PumpType.POWER_FUNC) + { + if (!GetPowerCurve(h0, h1, h2, q1, q2, out double a, out double b, out double c)) + throw new EnException(ErrorCode.Err227, pump.Name); + + + pump.H0 = -a; + pump.FlowCoefficient = -b; + pump.N = c; + pump.Q0 = q1; + pump.Qmax = Math.Pow(-a / b, 1.0 / c); + pump.Hmax = h0; + } + + break; + } + + // Assign limits to custom pump curves + if (pump.Ptype == PumpType.CUSTOM) + { + Curve curve = pump.HCurve; + + for (int i = 1; i < curve.Count; i++) + { + // Check for invalid curve + if (curve[i].Y >= curve[i - 1].Y) + { + throw new EnException(ErrorCode.Err227, pump.Name); + } + } + + pump.Qmax = curve[curve.Count - 1].X; + pump.Q0 = (curve[0].X + pump.Qmax) / 2.0; + pump.Hmax = curve[0].Y; + } + } + + } + + ///<summary>Initialize patterns.</summary> + /// <param name="net">Hydraulic network reference.</param> + private static void InitPatterns(Network net) + { + foreach (Pattern pat in net.Patterns) + if (pat.Count == 0) + pat.Add(1.0); + } + + ///<summary>Check for unlinked nodes.</summary> + private void CheckUnlinked() + { + //int[] marked = new int[net.Nodes.Count + 1]; + var nodes = net.Nodes; + var marked = new Dictionary<Node, bool>(nodes.Count + 1); + + foreach (Link link in net.Links) + { + marked[link.FirstNode] = true; + marked[link.SecondNode] = true; + } + + foreach (Node node in nodes) + { + if (!marked[node]) + { + LogException(new EnException(ErrorCode.Err233, node.Name)); + } + } + + } + + /// <summary>Computes coeffs. for pump curve.</summary> + /// <param name="h0">shutoff head</param> + /// <param name="h1">design head</param> + /// <param name="h2">head at max. flow</param> + /// <param name="q1">design flow</param> + /// <param name="q2">max. flow</param> + /// <param name="a">pump curve coeffs. (H = a-bQ^c)</param> + /// <param name="b">pump curve coeffs. (H = a-bQ^c)</param> + /// <param name="c">pump curve coeffs. (H = a-bQ^c)</param> + /// <returns>Returns true if sucessful, false otherwise.</returns> + private static bool GetPowerCurve(double h0, double h1, double h2, double q1, double q2, out double a, out double b, out double c) + { + a = b = c = 0; + + if ( + h0 < Constants.TINY || + h0 - h1 < Constants.TINY || + h1 - h2 < Constants.TINY || + q1 < Constants.TINY || + q2 - q1 < Constants.TINY + ) return false; + + a = h0; + double h4 = h0 - h1; + double h5 = h0 - h2; + c = Math.Log(h5 / h4) / Math.Log(q2 / q1); + if (c <= 0.0 || c > 20.0) return false; + b = -h4 / Math.Pow(q1, c); + + return !(b >= 0.0); + } + + /// <summary>checks for legal connections between PRVs & PSVs</summary> + /// <param name="net"></param> + /// <param name="type">valve type</param> + /// <param name="j1">upstream node</param> + /// <param name="j2">downstream node</param> + /// <returns>returns true for legal connection, false otherwise</returns> + protected static bool Valvecheck(Network net, ValveType type, Node j1, Node j2) + { + // Examine each existing valve + foreach (Valve valve in net.Valves) + { + Node fNode = valve.FirstNode; + Node tNode = valve.SecondNode; + ValveType type2 = valve.ValveType; + + /* Cannot have two PRVs sharing downstream nodes or in series */ + if (type2 == ValveType.PRV && type == ValveType.PRV) + { + if (Equals(tNode, j2) || + Equals(tNode, j1) || + Equals(fNode, j2)) + return false; + } + + /* Cannot have two PSVs sharing upstream nodes or in series */ + if (type2 == ValveType.PSV && type == ValveType.PSV) + { + if (Equals(fNode, j1) || + Equals(fNode, j2) || + Equals(tNode, j1)) + return false; + } + + /* Cannot have PSV connected to downstream node of PRV */ + if (type2 == ValveType.PSV && type == ValveType.PRV && fNode == j2) + return false; + if (type2 == ValveType.PRV && type == ValveType.PSV && tNode == j1) + return false; + + /* Cannot have PSV connected to downstream node of FCV */ + /* nor have PRV connected to upstream node of FCV */ + if (type2 == ValveType.FCV && type == ValveType.PSV && tNode == j1) + return false; + if (type2 == ValveType.FCV && type == ValveType.PRV && fNode == j2) + return false; + + if (type2 == ValveType.PSV && type == ValveType.FCV && fNode == j2) + return false; + if (type2 == ValveType.PRV && type == ValveType.FCV && tNode == j1) + return false; + } + + return true; + } + + protected static void GetPumpCurve(Pump pump, int n, double[] x) + { + + if (n == 1) + { + if (x[0] <= 0.0) + throw new EnException(ErrorCode.Err202); + pump.Ptype = PumpType.CONST_HP; + pump.Km = x[0]; + } + else + { + double h0, h1, h2, q1, q2; + + if (n == 2) + { + q1 = x[1]; + h1 = x[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + else if (n >= 5) + { + h0 = x[0]; + h1 = x[1]; + q1 = x[2]; + h2 = x[3]; + q2 = x[4]; + } + else + throw new EnException(ErrorCode.Err202); + + pump.Ptype = PumpType.POWER_FUNC; + + if (!GetPowerCurve(h0, h1, h2, q1, q2, out double a, out double b, out double c)) + throw new EnException(ErrorCode.Err206); + + pump.H0 = -a; + pump.FlowCoefficient = -b; + pump.N = c; + pump.Q0 = q1; + pump.Qmax = Math.Pow(-a / b, 1.0 / c); + pump.Hmax = h0; + } + } + + // TODO: move to link class? + protected static void ChangeStatus(Link link, StatusType status, double y) + { + + switch (link.LinkType) + { + case LinkType.Pipe: + if (!((Pipe)link).HasCheckValve && status != StatusType.ACTIVE) + link.Status = status; + + break; + + case LinkType.Pump: + + switch (status) + { + case StatusType.ACTIVE: + link.Kc = y; + link.Status = y.IsZero() ? StatusType.CLOSED : StatusType.OPEN; + break; + case StatusType.OPEN: + link.Kc = 1.0; + break; + } + + link.Status = status; + break; + + case LinkType.VALVE: + switch (((Valve)link).ValveType) + { + case ValveType.GPV: + if (status != StatusType.ACTIVE) + link.Status = status; + + break; + + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + case ValveType.FCV: + case ValveType.TCV: + link.Status = status; + link.Kc = status == StatusType.ACTIVE ? y : double.NaN; + break; + + } + + break; + } + } + + } + + + + + +} + + + diff --git a/IStation.Epanet/network/io/input/NetParser.cs b/IStation.Epanet/network/io/input/NetParser.cs new file mode 100644 index 0000000..316861b --- /dev/null +++ b/IStation.Epanet/network/io/input/NetParser.cs @@ -0,0 +1,2167 @@ +锘縰sing IStation.Epanet; +using IStation.Epanet.Enums; +using IStation.Epanet.Log; +using IStation.Epanet.Network.Structures; +using IStation.Epanet.Util; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace IStation.Epanet.Network.IO.Input +{ + + public class NetParser : InputParser + { + + #region Data indexes + //------------------------- + // Generic property indexes + //------------------------- + private const int COMMENT_INDEX = 0; //Comment index + private const int TAG_INDEX = 1; //Tag index + + //-------------------------- + // Junction property indexes + //-------------------------- + private const int JUNC_ELEV_INDEX = 2; //Elevation + private const int JUNC_DEMAND_INDEX = 3; //Demand + private const int JUNC_PATTERN_INDEX = 4; //Demand pattern + private const int JUNC_DMNDCAT_INDEX = 5; //Demand categories + private const int JUNC_EMITTER_INDEX = 6; //Emitter coeff. + private const int JUNC_INITQUAL_INDEX = 7; //Init. quality + private const int JUNC_SRCQUAL_INDEX = 8; //Source quality + private const int JUNC_SRCPAT_INDEX = 9; //Source pattern + private const int JUNC_SRCTYPE_INDEX = 10; //Source type + + //--------------------------- + // Reservoir property indexes + //--------------------------- + private const int RES_HEAD_INDEX = 2; //Head + private const int RES_PATTERN_INDEX = 3; //Head pattern + private const int RES_INITQUAL_INDEX = 4; //Init. quality + private const int RES_SRCQUAL_INDEX = 5; //Source quality + private const int RES_SRCPAT_INDEX = 6; //Source pattern + private const int RES_SRCTYPE_INDEX = 7; //Source type + + //---------------------- + // Tank property indexes + //---------------------- + private const int TANK_ELEV_INDEX = 2; //Elevation + private const int TANK_INITLVL_INDEX = 3; //Init. level + private const int TANK_MINLVL_INDEX = 4; //Min. level + private const int TANK_MAXLVL_INDEX = 5; //Max. level + private const int TANK_DIAM_INDEX = 6; //Diameter + private const int TANK_MINVOL_INDEX = 7; //Min. volume + private const int TANK_VCURVE_INDEX = 8; //Volume curve + private const int TANK_MIXMODEL_INDEX = 9; //Mixing model + private const int TANK_MIXFRAC_INDEX = 10; //Mixing fraction + private const int TANK_KBULK_INDEX = 11; //Bulk coeff. + private const int TANK_INITQUAL_INDEX = 12; //Init. quality + private const int TANK_SRCQUAL_INDEX = 13; //Source quality + private const int TANK_SRCPAT_INDEX = 14; //Source pattern + private const int TANK_SRCTYPE_INDEX = 15; //Source type + + //---------------------- + // Pipe property indexes + //---------------------- + private const int PIPE_LEN_INDEX = 2; //Length + private const int PIPE_DIAM_INDEX = 3; //Diameter + private const int PIPE_ROUGH_INDEX = 4; //Roughness coeff. + private const int PIPE_MLOSS_INDEX = 5; //Minor loss coeff. + private const int PIPE_STATUS_INDEX = 6; //Status + private const int PIPE_KBULK_INDEX = 7; //Bulk coeff. + private const int PIPE_KWALL_INDEX = 8; //Wall coeff. + + //---------------------- + // Pump property indexes + //---------------------- + private const int PUMP_HCURVE_INDEX = 2; //Head curve + private const int PUMP_HP_INDEX = 3; //Horsepower + private const int PUMP_SPEED_INDEX = 4; //Speed + private const int PUMP_PATTERN_INDEX = 5; //Speed pattern + private const int PUMP_STATUS_INDEX = 6; //Status + private const int PUMP_ECURVE_INDEX = 7; //Efficiency curve + private const int PUMP_EPRICE_INDEX = 8; //Energy price + private const int PUMP_PRICEPAT_INDEX = 9; //Price pattern + + //----------------------- + // Valve property indexes + //----------------------- + private const int VALVE_DIAM_INDEX = 2; //Diameter + private const int VALVE_TYPE_INDEX = 3; //Type + private const int VALVE_SETTING_INDEX = 4; //Setting + private const int VALVE_MLOSS_INDEX = 5; //Minor loss coeff. + private const int VALVE_STATUS_INDEX = 6; //Status + + //--------------------------- + // Map Label property indexes + //--------------------------- + private const int LABEL_TEXT_INDEX = 0; + private const int ANCHOR_NODE_INDEX = 3; + private const int METER_TYPE_INDEX = 4; + private const int METER_ID_INDEX = 5; + + //------------------------ + // Analysis option indexes + //------------------------ + private const int FLOW_UNITS_INDEX = 0; //Flow units + private const int HLOSS_FORM_INDEX = 1; //Headloss formula + private const int SPEC_GRAV_INDEX = 2; //Specific gravity + private const int VISCOS_INDEX = 3; //Relative viscosity + private const int TRIALS_INDEX = 4; //Max. trials + private const int ACCURACY_INDEX = 5; //Hydraul. accuracy + private const int UNBALANCED_INDEX = 6; //If unbalanced option + private const int GLOBAL_PAT_INDEX = 7; //Default demand pattern + private const int DEMAND_MULT_INDEX = 8; //Demand multiplier + private const int EMITTER_EXP_INDEX = 9; //Emitter exponent + private const int STATUS_RPT_INDEX = 10; //Status report option + + private const int QUAL_PARAM_INDEX = 11; //Quality parameter + private const int QUAL_UNITS_INDEX = 12; //Concen. units + private const int DIFFUS_INDEX = 13; //Diffusivity + private const int TRACE_NODE_INDEX = 14; //Trace node index + private const int QUAL_TOL_INDEX = 15; //Quality tolerance + private const int MAX_SEGS_INDEX = 16; //Max. pipe segments + + private const int BULK_ORDER_INDEX = 17; //Bulk reaction order + private const int WALL_ORDER_INDEX = 18; //Wall reaction order + private const int GLOBAL_KBULK_INDEX = 19; //Default bulk react. coeff. + private const int GLOBAL_KWALL_INDEX = 20; //Default wall react. coeff. + private const int LIMIT_QUAL_INDEX = 21; //Limiting potential concen. + private const int ROUGH_CORREL_INDEX = 22; //Relation between Kwall & roughness + + private const int DURATION_INDEX = 23; //Simulation duration + private const int HYD_TSTEP_INDEX = 24; //Hydraulic time step + private const int QUAL_TSTEP_INDEX = 25; //Quality time step + private const int PAT_TSTEP_INDEX = 26; //Pattern time step + private const int PAT_START_INDEX = 27; //Pattern start time + private const int RPT_TSTEP_INDEX = 28; //Reporting time step + private const int RPT_START_INDEX = 29; //Report start time + private const int START_TIME_INDEX = 30; //Starting time of day + private const int TIME_STAT_INDEX = 31; //Time statistic option + + private const int EFFIC_INDEX = 32; //Default pump effic. + private const int EPRICE_INDEX = 33; //Default energy price + private const int PRICE_PAT_INDEX = 34; //Default price pattern + private const int DMND_CHARGE_INDEX = 35; //Energy demand charge + private const int CHECK_FREQ_INDEX = 36; + private const int MAX_CHECK_INDEX = 37; + private const int DAMP_LIMIT_INDEX = 38; + + #endregion + + /// <summary>Object categories</summary> + public enum ObjectCategories + { + JUNCS = 0, + RESERVS = 1, + TANKS = 2, + PIPES = 3, + PUMPS = 4, + VALVES = 5, + LABELS = 6, + PATTERNS = 7, + CURVES = 8, + CNTRLS = 9, + OPTS = 10, + VERTICES = 11 + } + + /// <summary>EPANET2 *.net project file signature.</summary> + private const string NETFILE_SIGNATURE = "<EPANET2>"; + + /// <summary>EPANET2 *.net project file version ID.</summary> + private const int VERSIONID1 = 20005; + /// <summary>EPANET2 *.net project file version ID.</summary> + private const int VERSIONID2 = 20008; + + /// <summary>Max. index for network options array.</summary> + private const int MAXOPTIONS = 38; + /// <summary>Max. index for node property array.</summary> + private const int MAXNODEPROPS = 25; + /// <summary>Max. index for link property array</summary> + private const int MAXLINKPROPS = 25; + + private Reader _reader; + + public override Network Parse(Network nw, string fileName) + { + net = nw ?? new Network(); + + FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + + using (fs) + { + _reader = new Reader(fs, Encoding.ASCII); + + string signature = _reader.ReadString(); + + //Check for <EPANET2> marker at start of input file + if (signature != NETFILE_SIGNATURE) throw new EnException(ErrorCode.Err200); + + //Read version ID + int ver = _reader.ReadInteger(); + + if (ver < VERSIONID1 || ver > VERSIONID2) throw new EnException(ErrorCode.Err200); + + //Total up number of network components + int ncomp = 0; + + for (ObjectCategories i = ObjectCategories.JUNCS; i <= ObjectCategories.CNTRLS; i++) + ncomp += _reader.ReadInteger(); + + Debug.Print("Ncomp=" + ncomp); + + net.Title.Add(_reader.ReadString()); + net.Title.AddRange(_reader.ReadList()); + + // TODO: move to end + ReadOptions(); + + //Read in time patterns. + ReadPatterns(); + + //Read in curves. + ReadCurves(); + + //Read in junctions. + ReadJunctions(); + + //Read in reservoirs + ReadReservoirs(); + + //Read in tanks + ReadTanks(); + + //Read in pipes, pumps, & valves + ReadPipes(); + ReadPumps(); + ReadValves(); + + //Read in control rules. + ReadControls(); + ReadRules(); + + //Read in map labels. + ReadLabels(); + + } + + AdjustData(net); + + net.FieldsMap.Prepare(net.UnitsFlag, net.FlowFlag, net.PressFlag, net.QualFlag, net.ChemUnits, net.SpGrav, net.HStep); + + Convert(); + + return net; + } + + private void ReadLabels() + { + int n = _reader.ReadInteger(); + + if (n <= 0) return; + + for (int i = 0; i < n; i++) + { + Label mlabel = new Label(_reader.ReadString()) + { + Position = _reader.ReadPoint(), + Anchor = net.GetNode(_reader.ReadString()), + FontName = _reader.ReadString(), + FontSize = _reader.ReadInteger(), + FontBold = _reader.ReadBoolean(), + FontItalic = _reader.ReadBoolean(), + MeterType = (Label.MeterTypes)_reader.ReadInteger(), + MeterId = _reader.ReadString() + }; + + net.Labels.Add(mlabel); + } + } + + private void ReadRules() + { + var lines = _reader.ReadList(); + List<string> tok = new List<string>(Constants.MAXTOKS); + Rule currentRule = null; + + + foreach (string line in lines) + { + if (string.IsNullOrEmpty(line)) + continue; + + if (GetTokens(line, tok) == 0) + continue; + + tok[0].TryParse(out Rulewords key); + if (key == Rulewords.RULE) + { + currentRule = new Rule(tok[1]); + net.Rules.Add(currentRule); + } + else + { + currentRule?.Code.Add(line); + } + } + + } + + private void ReadControls() + { + var lines = _reader.ReadList(); + List<string> tok = new List<string>(Constants.MAXTOKS); + + foreach (string line in lines) + { + if (string.IsNullOrEmpty(line)) + continue; + + if (GetTokens(line, tok) == 0) + continue; + + try + { + + int n = tok.Count; + StatusType status = StatusType.ACTIVE; + + double setting = double.NaN, level = 0.0; + TimeSpan time = TimeSpan.Zero; + + if (n < 6) + throw new EnException(ErrorCode.Err201, SectType.CONTROLS, line); + + Node node = null; + Link link = net.GetLink(tok[1]); + + if (link == null) + throw new EnException(ErrorCode.Err204, SectType.CONTROLS, tok[0]); + + LinkType ltype = link.LinkType; + + if (ltype == LinkType.Pipe && ((Pipe)link).HasCheckValve) + throw new EnException(ErrorCode.Err207, SectType.CONTROLS, tok[0]); + + if (tok[2].Match(StatusType.OPEN.ToString())) + { + status = StatusType.OPEN; + switch (ltype) + { + case LinkType.Pump: + setting = 1.0; + break; + case LinkType.VALVE: + if (((Valve)link).ValveType == ValveType.GPV) + setting = link.Kc; + break; + } + } + else if (tok[2].Match(StatusType.CLOSED.ToString())) + { + status = StatusType.CLOSED; + switch (ltype) + { + case LinkType.Pump: + setting = 0.0; + break; + case LinkType.VALVE: + if (((Valve)link).ValveType == ValveType.GPV) + setting = link.Kc; + break; + } + } + else if (ltype == LinkType.VALVE && ((Valve)link).ValveType == ValveType.GPV) + { + throw new EnException(ErrorCode.Err206, SectType.CONTROLS, tok[0]); + } + else if (!tok[2].ToDouble(out setting)) + { + throw new EnException(ErrorCode.Err202, SectType.CONTROLS, tok[0]); + } + + if (ltype == LinkType.Pump || ltype == LinkType.Pipe) + { + if (!double.IsNaN(setting)) + { + if (setting < 0.0) + throw new EnException(ErrorCode.Err202, SectType.CONTROLS, tok[0]); + + status = setting.IsZero() ? StatusType.CLOSED : StatusType.OPEN; + } + } + + ControlType ctype; + + if (tok[4].Match(Keywords.w_TIME)) + ctype = ControlType.Timer; + else if (tok[4].Match(Keywords.w_CLOCKTIME)) + ctype = ControlType.TimeOfDay; + else + { + if (n < 8) + throw new EnException(ErrorCode.Err201, SectType.CONTROLS, line); + + if ((node = net.GetNode(tok[5])) == null) + throw new EnException(ErrorCode.Err203, SectType.CONTROLS, tok[0]); + + if (tok[6].Match(Keywords.w_BELOW)) + ctype = ControlType.LowLevel; + else if (tok[6].Match(Keywords.w_ABOVE)) + ctype = ControlType.HiLevel; + else + throw new EnException(ErrorCode.Err201, SectType.CONTROLS, line); + } + + switch (ctype) + { + case ControlType.Timer: + case ControlType.TimeOfDay: + + switch (n) + { + case 6: + time = Utilities.ToTimeSpan(tok[5]); + break; + case 7: + time = Utilities.ToTimeSpan(tok[5], tok[6]); + break; + } + + if (time == TimeSpan.MinValue) + throw new EnException(ErrorCode.Err201, SectType.CONTROLS, line); + + break; + + case ControlType.LowLevel: + case ControlType.HiLevel: + if (!tok[7].ToDouble(out level)) + throw new EnException(ErrorCode.Err202, SectType.CONTROLS, tok[0]); + + break; + } + + Control cntr = new Control + { + Link = link, + Node = node, + Type = ctype, + Status = status, + Setting = setting, + Time = ctype == ControlType.TimeOfDay ? time.TimeOfDay() : time, + Grade = level + }; + + net.Controls.Add(cntr); + } + catch (EnException ex) + { + LogException(ex); + } + } + } + + private void ReadPumps() + { + int n = _reader.ReadInteger(); + + if (n <= 0) + return; + + for (int i = 0; i < n; i++) + { + string name = _reader.ReadString(); + Node j1 = net.GetNode(_reader.ReadString()); + Node j2 = net.GetNode(_reader.ReadString()); + var points = _reader.ReadVertices(); + var data = _reader.ReadArray(MAXLINKPROPS); + + if (net.GetLink(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.PUMPS, name)); + continue; + } + + if (j1 == null || j2 == null) + { + LogException(new EnException(ErrorCode.Err203, SectType.PUMPS, name)); + continue; + } + + if (j1.Equals(j2)) + { + LogException(new EnException(ErrorCode.Err222, SectType.PUMPS, name)); + continue; + } + + Pump pump = new Pump(name) + { + FirstNode = j1, + SecondNode = j2, + }; + + if (points.Length > 0) + pump.Vertices.AddRange(points); + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + pump.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + pump.Tag = data[TAG_INDEX]; + + //---------------------------------- + + if (!string.IsNullOrEmpty(data[PUMP_HP_INDEX])) + { + if (!data[PUMP_HP_INDEX].ToDouble(out double y) || y <= 0.0) + { + LogException(new EnException(ErrorCode.Err202, SectType.PUMPS, name)); + } + else + { + pump.Ptype = PumpType.CONST_HP; + pump.Km = y; + } + } + + if (!string.IsNullOrEmpty(data[PUMP_HCURVE_INDEX])) + { + Curve curve = net.GetCurve(data[PUMP_HCURVE_INDEX]); + if (curve == null) + { + LogException(new EnException(ErrorCode.Err206, SectType.PUMPS, name)); + } + else + { + pump.HCurve = curve; + } + } + + if (!string.IsNullOrEmpty(data[PUMP_PATTERN_INDEX])) + { + Pattern p = net.GetPattern(data[PUMP_PATTERN_INDEX]); + + if (p == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.PUMPS, name)); + } + else + { + pump.UPat = p; + } + } + + + if (!string.IsNullOrEmpty(data[PUMP_SPEED_INDEX])) + { + if (!data[PUMP_SPEED_INDEX].ToDouble(out double y) || y < 0.0) + { + LogException(new EnException(ErrorCode.Err202, SectType.PUMPS, name)); + } + else + { + pump.Kc = y; + } + } + + //------------------------------ + + if (!string.IsNullOrEmpty(data[PUMP_ECURVE_INDEX])) + { + Curve curve = net.GetCurve(data[PUMP_ECURVE_INDEX]); + + if (curve == null) + { + LogException(new EnException(ErrorCode.Err217, SectType.ENERGY, name)); + } + else + { + pump.ECurve = curve; + } + } + + if (!string.IsNullOrEmpty(data[PUMP_EPRICE_INDEX])) + { + if (!data[PUMP_EPRICE_INDEX].ToDouble(out double cost)) + { + LogException(new EnException(ErrorCode.Err217, SectType.ENERGY, name)); + } + else + { + pump.ECost = cost; + } + } + + if (!string.IsNullOrEmpty(data[PUMP_PRICEPAT_INDEX])) + { + Pattern pattern = net.GetPattern(data[PUMP_PRICEPAT_INDEX]); + + if (pattern == null) + { + LogException(new EnException(ErrorCode.Err217, SectType.ENERGY, name)); + } + else + { + pump.EPat = pattern; + } + } + + + // FIXME: status changes + if (!string.IsNullOrEmpty(data[PUMP_STATUS_INDEX])) + { + StatusType status = (StatusType)(-1); + if (data[PUMP_STATUS_INDEX].Match(Keywords.w_OPEN)) + { + status = StatusType.OPEN; + } + else if (data[PUMP_STATUS_INDEX].Match(Keywords.w_CLOSED)) + { + status = StatusType.CLOSED; + } + else + { + LogException(new EnException(ErrorCode.Err211, SectType.STATUS, name)); + } + + if (status >= 0) + ChangeStatus(pump, status, 0.0); + } + + net.Links.Add(pump); + } + } + + private void ReadValves() + { + int n = _reader.ReadInteger(); + + if (n <= 0) + return; + + for (int i = 0; i < n; i++) + { + StatusType status = StatusType.OPEN; + + string name = _reader.ReadString(); + Node j1 = net.GetNode(_reader.ReadString()); + Node j2 = net.GetNode(_reader.ReadString()); + var points = _reader.ReadVertices(); + var data = _reader.ReadArray(MAXLINKPROPS); + + + if (net.GetLink(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.VALVES, name)); + continue; + } + + if (j1 == null || j2 == null) + { + LogException(new EnException(ErrorCode.Err203, SectType.VALVES, name)); + continue; + } + + if (j1.Equals(j2)) + { + LogException(new EnException(ErrorCode.Err222, SectType.VALVES, name)); + continue; + } + + if (!data[VALVE_TYPE_INDEX].TryParse(out ValveType type)) + { + LogException(new EnException(ErrorCode.Err201, SectType.VALVES, data[VALVE_TYPE_INDEX])); + continue; + } + + if (!data[VALVE_DIAM_INDEX].ToDouble(out double diam) || diam <= 0.0) + { + LogException(new EnException(ErrorCode.Err202, SectType.VALVES, name)); + continue; + } + + Valve valve = new Valve(name, type) + { + FirstNode = j1, + SecondNode = j2 + }; + + if (type == ValveType.GPV) + { /* Headloss curve for GPV */ + Curve t = net.GetCurve(data[VALVE_SETTING_INDEX]); + if (t == null) + { + LogException(new EnException(ErrorCode.Err206, SectType.VALVES, name)); + continue; + } + + // setting = net.Curves.IndexOf(t); + log.Warning("GPV Valve, index as roughness !"); + valve.Curve = t; + status = StatusType.OPEN; + } + else if (!data[VALVE_SETTING_INDEX].ToDouble(out double _)) + { + LogException(new EnException(ErrorCode.Err202)); + continue; + } + + if (!data[VALVE_MLOSS_INDEX].ToDouble(out double lcoeff) || lcoeff < 0.0) + { + LogException(new EnException(ErrorCode.Err202, SectType.VALVES, name)); + continue; + } + + /* Check that PRV, PSV, or FCV not connected to a tank & */ + /* check for illegal connections between pairs of valves.*/ + if (j1.NodeType > NodeType.Junction || j2.NodeType > NodeType.Junction) + { + if (type == ValveType.PRV || type == ValveType.PSV || type == ValveType.FCV) + { + LogException(new EnException(ErrorCode.Err219, SectType.VALVES, name)); + continue; + } + } + + if (!Valvecheck(net, type, j1, j2)) + throw new EnException(ErrorCode.Err220); + + valve.FirstNode = j1; + valve.SecondNode = j2; + valve.Lenght = 0.0; + valve.Diameter = diam; + valve.Kc = 0.0; + valve.Km = lcoeff; + valve.Kb = double.NaN; + valve.Kw = double.NaN; + valve.Status = status; + + if (points.Length > 0) + valve.Vertices.AddRange(points); + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + valve.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + valve.Tag = data[TAG_INDEX]; + + net.Links.Add(valve); + } + } + + private void ReadPipes() + { + int n = _reader.ReadInteger(); + + if (n <= 0) return; + + for (int i = 0; i < n; i++) + { + string name = _reader.ReadString(); + Node j1 = net.GetNode(_reader.ReadString()); + Node j2 = net.GetNode(_reader.ReadString()); + var points = _reader.ReadVertices(); + var data = _reader.ReadArray(MAXLINKPROPS); + + if (net.GetLink(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.JUNCTIONS, name)); + continue; + } + + if (j1 == null || j2 == null) + { + LogException(new EnException(ErrorCode.Err203, SectType.PIPES, name)); + continue; + } + + if (j1.Equals(j2)) + { + LogException(new EnException(ErrorCode.Err222, SectType.PIPES, name)); + continue; + } + + + + if (!data[PIPE_LEN_INDEX].ToDouble(out double length) || length <= 0.0 || + !data[PIPE_DIAM_INDEX].ToDouble(out double diam) || diam <= 0.0 || + !data[PIPE_ROUGH_INDEX].ToDouble(out double rcoeff) || rcoeff <= 0.0 || + !data[PIPE_MLOSS_INDEX].ToDouble(out double lcoeff) || lcoeff < 0.0 + ) + { + LogException(new EnException(ErrorCode.Err202, SectType.PIPES, name)); + continue; + } + + StatusType status = StatusType.OPEN; + bool isCv = false; + + if (data[PIPE_STATUS_INDEX].Match(Keywords.w_CV)) + { + isCv = true; + } + else if (data[PIPE_STATUS_INDEX].Match(Keywords.w_CLOSED)) + { + status = StatusType.CLOSED; + } + else if (data[PIPE_STATUS_INDEX].Match(Keywords.w_OPEN)) + { + status = StatusType.OPEN; + } + else + { + LogException(new EnException(ErrorCode.Err202, SectType.PIPES, name)); + continue; + } + + Pipe link = new Pipe(name) + { + FirstNode = j1, + SecondNode = j2, + Lenght = length, + Diameter = diam, + Kc = rcoeff, + Km = lcoeff, + Kb = double.NaN, + Kw = double.NaN, + HasCheckValve = isCv, + Status = status + }; + + if (points.Length > 0) + link.Vertices.AddRange(points); + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + link.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + link.Tag = data[TAG_INDEX]; + + if (!string.IsNullOrEmpty(data[PIPE_KBULK_INDEX])) + { + if (!data[PIPE_KBULK_INDEX].ToDouble(out double kb)) + { + LogException(new EnException(ErrorCode.Err202, SectType.PIPES, name)); + } + else + { + link.Kb = kb; + } + } + + if (!string.IsNullOrEmpty(data[PIPE_KWALL_INDEX])) + { + if (!data[PIPE_KWALL_INDEX].ToDouble(out double kw)) + { + LogException(new EnException(ErrorCode.Err202, SectType.PIPES, name)); + } + else + { + link.Kw = kw; + } + } + + net.Links.Add(link); + } + } + + private void ReadTanks() + { + int n = _reader.ReadInteger(); + if (n <= 0) return; + + for (int i = 0; i < n; i++) + { + Curve vcurve = null; + + string name = _reader.ReadString(); + var point = _reader.ReadPoint(); + var data = _reader.ReadArray(MAXNODEPROPS); + + if (net.GetNode(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.TANKS, name)); + continue; + } + + if (!data[TANK_ELEV_INDEX].ToDouble(out double el)) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_ELEV_INDEX])); + continue; + } + + if (!data[TANK_INITLVL_INDEX].ToDouble(out double initlevel)) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_INITLVL_INDEX])); + continue; + } + + if (!data[TANK_MINLVL_INDEX].ToDouble(out double minlevel)) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_MINLVL_INDEX])); + continue; + } + + if (!data[TANK_MAXLVL_INDEX].ToDouble(out double maxlevel)) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_MAXLVL_INDEX])); + continue; + } + + if (!data[TANK_DIAM_INDEX].ToDouble(out double diam) || diam < 0) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_DIAM_INDEX])); + continue; + } + + if (!data[TANK_MINVOL_INDEX].ToDouble(out double minvol) || minvol < 0) + { + LogException(new EnException(ErrorCode.Err202, SectType.TANKS, data[TANK_MINVOL_INDEX])); + continue; + } + + if (!string.IsNullOrEmpty(data[TANK_VCURVE_INDEX])) + { + if ((vcurve = net.GetCurve(data[TANK_VCURVE_INDEX])) == null) + { + LogException(new EnException(ErrorCode.Err206, SectType.TANKS, data[TANK_VCURVE_INDEX])); + } + } + + var tank = new Tank(name) { Coordinate = point }; + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + tank.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + tank.Tag = data[TAG_INDEX]; + + tank.RptFlag = false; + tank.Elevation = el; + tank.C0 = 0.0; + tank.QualSource = null; + tank.Ke = 0.0; + + tank.H0 = initlevel; + tank.Hmin = minlevel; + tank.Hmax = maxlevel; + tank.Area = diam; + tank.Pattern = null; + tank.Kb = double.NaN; + + double area = Math.PI * diam * diam / 4.0d; + + tank.Vmin = area * minlevel; + if (minvol > 0.0) + tank.Vmin = minvol; + + tank.V0 = tank.Vmin + area * (initlevel - minlevel); + tank.Vmax = tank.Vmin + area * (maxlevel - minlevel); + + tank.Vcurve = vcurve; + tank.MixModel = MixType.Mix1; + tank.V1Max = 1.0; + + if (!string.IsNullOrEmpty(data[TANK_MIXMODEL_INDEX])) + { + if (!data[TANK_MIXMODEL_INDEX].TryParse(out MixType type)) + { + LogException(new EnException(ErrorCode.Err201, SectType.MIXING, data[TANK_MIXMODEL_INDEX])); + } + else + { + tank.MixModel = type; + + if (type == MixType.Mix2 && !string.IsNullOrEmpty(data[TANK_MIXFRAC_INDEX])) + { + if (!data[TANK_MIXFRAC_INDEX].ToDouble(out double v)) + { + LogException(new EnException(ErrorCode.Err209, SectType.MIXING, name)); + } + else + { + if (v.IsZero()) + v = 1.0; + + tank.V1Max = v; + } + } + + } + } + + if (!string.IsNullOrEmpty(data[TANK_KBULK_INDEX])) + { + if (!data[TANK_KBULK_INDEX].ToDouble(out double kb)) + { + LogException(new EnException(ErrorCode.Err209, SectType.REACTIONS, name)); + } + else + { + tank.Kb = kb; + } + } + + if (!string.IsNullOrEmpty(data[TANK_INITQUAL_INDEX])) + { + if (!data[TANK_INITQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202, SectType.QUALITY, data[TANK_INITQUAL_INDEX])); + } + else + { + tank.C0 = c0; + } + } + + if (!string.IsNullOrEmpty(data[TANK_SRCQUAL_INDEX])) + { + Pattern pat = null; + + if (!data[TANK_SRCQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202, SectType.SOURCES, data[TANK_SRCQUAL_INDEX])); + } + else if (!data[TANK_SRCTYPE_INDEX].TryParse(out SourceType type)) + { + LogException(new EnException(ErrorCode.Err201, SectType.SOURCES, data[TANK_SRCTYPE_INDEX])); + } + else if (!string.IsNullOrEmpty(data[TANK_SRCPAT_INDEX]) && (pat = net.GetPattern(data[TANK_SRCPAT_INDEX])) == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.SOURCES, data[TANK_SRCPAT_INDEX])); + } + else + { + tank.QualSource = new QualSource(type, c0, pat); + } + } + + net.Nodes.Add(tank); + } + } + + private void ReadReservoirs() + { + + int n = _reader.ReadInteger(); + + if (n <= 0) return; + + for (int i = 0; i < n; i++) + { + string name = _reader.ReadString(); + var point = _reader.ReadPoint(); + var data = _reader.ReadArray(MAXNODEPROPS); + + if (net.GetNode(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.RESERVOIRS, name)); + continue; + } + + var tank = new Tank(name) { Coordinate = point }; + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + tank.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + tank.Tag = data[TAG_INDEX]; + + if (!data[RES_HEAD_INDEX].ToDouble(out double head)) + { + LogException(new EnException(ErrorCode.Err202, SectType.RESERVOIRS, data[RES_HEAD_INDEX])); + continue; + } + + tank.Elevation = head; + + if (!string.IsNullOrEmpty(data[RES_PATTERN_INDEX])) + { + Pattern pat = net.GetPattern(data[RES_PATTERN_INDEX]); + + if (pat == null) + { + LogException(new EnException(ErrorCode.Err205, tank.Name, data[RES_PATTERN_INDEX])); + } + + tank.Pattern = pat; + + } + + if (!string.IsNullOrEmpty(data[RES_INITQUAL_INDEX])) + { + if (!data[RES_INITQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202, SectType.RESERVOIRS, data[RES_INITQUAL_INDEX])); + } + else + { + tank.C0 = c0; + } + } + + if (!string.IsNullOrEmpty(data[RES_SRCQUAL_INDEX])) + { + Pattern pat = null; + + if (!data[RES_SRCQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202, SectType.SOURCES, data[RES_SRCQUAL_INDEX])); + } + else if (!data[RES_SRCTYPE_INDEX].TryParse(out SourceType type)) + { + LogException(new EnException(ErrorCode.Err201, SectType.SOURCES, data[RES_SRCTYPE_INDEX])); + } + else if (!string.IsNullOrEmpty(data[RES_SRCPAT_INDEX]) && (pat = net.GetPattern(data[RES_SRCPAT_INDEX])) == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.SOURCES, data[RES_SRCPAT_INDEX])); + } + else + { + tank.QualSource = new QualSource(type, c0, pat); + } + } + + net.Nodes.Add(tank); + } + } + + private void ReadJunctions() + { + + + int n = _reader.ReadInteger(); + + if (n <= 0) return; + + for (int i = 0; i < n; i++) + { + string name = _reader.ReadString(); + var position = _reader.ReadPoint(); + var data = _reader.ReadArray(MAXNODEPROPS); + var demands = _reader.ReadList(); + + if (net.GetNode(name) != null) + { + LogException(new EnException(ErrorCode.Err215, SectType.JUNCTIONS, name)); + continue; + } + + var node = new Junction(name) { Coordinate = position }; + + + if (!string.IsNullOrEmpty(data[COMMENT_INDEX])) + node.Comment = data[COMMENT_INDEX]; + + if (!string.IsNullOrEmpty(data[TAG_INDEX])) + node.Tag = data[TAG_INDEX]; + + if (!data[JUNC_ELEV_INDEX].ToDouble(out double el)) + { + LogException(new EnException(ErrorCode.Err202, SectType.JUNCTIONS, name)); + continue; + } + + node.Elevation = el; + + if (!string.IsNullOrEmpty(data[JUNC_DEMAND_INDEX])) + { + if (!data[JUNC_DEMAND_INDEX].ToDouble(out double demand)) + { + LogException(new EnException(ErrorCode.Err202, SectType.JUNCTIONS, name)); + continue; + } + + node.PrimaryDemand.Base = demand; + } + else + { + // node.PrimaryDemand.Base = 0; + } + + if (!string.IsNullOrEmpty(data[JUNC_PATTERN_INDEX])) + { + Pattern pattern = net.GetPattern(data[JUNC_PATTERN_INDEX]); + if (pattern == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.JUNCTIONS, data[JUNC_PATTERN_INDEX])); + } + else + { + node.PrimaryDemand.Pattern = pattern; + } + } + + if (int.TryParse(data[JUNC_DMNDCAT_INDEX], out int demandCount) && demandCount > 1) + { + foreach (string s in demands) + { + Pattern pat; + + string[] demandData = s.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); + + if (demandData.Length < 2) + { + // TODO:ERROR? + } + else if (!demandData[0].ToDouble(out double @base)) + { + LogException(new EnException(ErrorCode.Err202, SectType.JUNCTIONS, name)); + } + else if ((pat = net.GetPattern(demandData[1])) == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.JUNCTIONS, demandData[1])); + } + else + { + node.Demands.Add(new Demand(@base, pat)); + } + + } + } + + if (!string.IsNullOrEmpty(data[JUNC_EMITTER_INDEX])) + { + if (node.NodeType == NodeType.Tank) + { + LogException(new EnException(ErrorCode.Err209, data[JUNC_EMITTER_INDEX], name)); + } + else if (!data[JUNC_EMITTER_INDEX].ToDouble(out double k) || k < 0.0) + { + LogException(new EnException(ErrorCode.Err202)); + } + else + { + node.Ke = k; + } + } + + if (!string.IsNullOrEmpty(data[JUNC_INITQUAL_INDEX])) + { + if (!data[JUNC_INITQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202)); + } + else + { + node.C0 = c0; + } + } + + if (!string.IsNullOrEmpty(data[JUNC_SRCQUAL_INDEX])) + { + Pattern pat = null; + + if (!data[JUNC_SRCQUAL_INDEX].ToDouble(out double c0)) + { + LogException(new EnException(ErrorCode.Err202, SectType.SOURCES, data[JUNC_SRCQUAL_INDEX])); + } + else if (!data[JUNC_SRCTYPE_INDEX].TryParse(out SourceType type)) + { + LogException(new EnException(ErrorCode.Err201, SectType.SOURCES, data[JUNC_SRCTYPE_INDEX])); + } + else if (!string.IsNullOrEmpty(data[JUNC_SRCPAT_INDEX]) && (pat = net.GetPattern(data[JUNC_SRCPAT_INDEX])) == null) + { + LogException(new EnException(ErrorCode.Err205, SectType.SOURCES, data[JUNC_SRCPAT_INDEX])); + } + else + { + node.QualSource = new QualSource(type, c0, pat); + } + } + + net.Nodes.Add(node); + } + } + + private void ReadCurves() + { + int n = _reader.ReadInteger(); + + if (n < 0) return; + + for (int i = 0; i < n; i++) + { + var curve = new Curve(_reader.ReadString()) + { + Comment = _reader.ReadString() + }; + + string sCurveType = _reader.ReadString(); + + try + { + curve.Type = (CurveType)Enum.Parse(typeof(CurveType), sCurveType, true); + } + catch + { + + } + + var xa = _reader.ReadList(); + var ya = _reader.ReadList(); + + for (int j = 0; j < xa.Count; j++) + curve.Add( + double.Parse(xa[j], NumberFormatInfo.InvariantInfo), + double.Parse(ya[j], NumberFormatInfo.InvariantInfo)); + + net.Curves.Add(curve); + } + } + + private void ReadPatterns() + { + int n = _reader.ReadInteger(); + + if (n < 0) return; + + for (int i = 0; i < n; i++) + { + var pat = new Pattern(_reader.ReadString()) + { + Comment = _reader.ReadString() + }; + + var multipliers = _reader.ReadList(); + + foreach (string s in multipliers) + { + if (s.ToDouble(out double factor)) + { + pat.Add(factor); + } + else + { + LogException(new EnException(ErrorCode.Err202, "PATTERNS", s)); + // FIXME: what to do here? + break; + } + } + + net.Patterns.Add(pat); + } + } + + private static TimeSpan GetSeconds(string value) + { + value = (value ?? string.Empty).Trim(); + if (string.IsNullOrEmpty(value)) return TimeSpan.MinValue; + + int index = value.LastIndexOfAny(new[] { ' ', '\t' }); + + return index == -1 + ? Utilities.ToTimeSpan(value) + : Utilities.ToTimeSpan(value.Substring(0, index), value.Substring(index + 1)); + } + + private void ReadOptions() + { + var options = _reader.ReadArray(MAXOPTIONS); + + net.AutoLength = _reader.ReadBoolean(); + + //----------------------------------------------------------------------------------- + + string line = options[FLOW_UNITS_INDEX]; + + { + if (line.TryParse(out FlowUnitsType flag)) + { + net.FlowFlag = flag; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.OPTIONS, "UNITS " + line)); + } + } + + //----------------------------------------------------------------------------------- + + line = options[HLOSS_FORM_INDEX]; + { + if (line.TryParse(out HeadLossFormulaType flag)) + { + net.FormFlag = flag; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.OPTIONS, "HEADLOSS " + line)); + } + } + + //----------------------------------------------------------------------------------- + + line = options[SPEC_GRAV_INDEX]; + + if (line.ToDouble(out double y) && y > 0) + { + net.SpGrav = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "SPECIFIC GRAVITY " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[VISCOS_INDEX]; + + if (line.ToDouble(out y)) + { + net.Viscos = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "VISCOSITY " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[TRIALS_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.MaxIter = (int)y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "TRIALS " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[ACCURACY_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + y = Math.Max(y, 1e-5); + y = Math.Min(y, 1e-1); + net.HAcc = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "ACCURACY " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[UNBALANCED_INDEX]; + + if (line.Match(Keywords.w_STOP)) + { + net.ExtraIter = -1; + } + else if (line.Match(Keywords.w_CONTINUE)) + { + net.ExtraIter = 0; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.OPTIONS, "UNBALANCED " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[GLOBAL_PAT_INDEX]; + + if (!string.IsNullOrEmpty(line)) + { + net.DefPatId = line; + } + + //----------------------------------------------------------------------------------- + + line = options[DEMAND_MULT_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.DMult = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "DEMAND MULTIPLIER " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[EMITTER_EXP_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.QExp = 1 / y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "EMITTER EXPONENT " + line)); + } + + //----------------------------------------------------------------------------------- + + { + line = options[STATUS_RPT_INDEX]; + + if (line.TryParse(out StatusLevel flag) && y > 0) + { + net.StatFlag = flag; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.REPORT, "STATUS " + line)); + } + } + + //----------------------------------------------------------------------------------- + + line = options[QUAL_PARAM_INDEX]; + + { + net.QualFlag = line.TryParse(out QualType flag) ? flag : QualType.Chem; + + string units = options[QUAL_UNITS_INDEX]; + + switch (net.QualFlag) + { + case QualType.Chem: + net.ChemName = line; + if (!string.IsNullOrEmpty(units)) net.ChemUnits = units; + break; + + case QualType.Trace: + // FIXME: parse nodes first! + Node node = net.GetNode(options[TRACE_NODE_INDEX]); + + if (node == null) + { + LogException(new EnException(ErrorCode.Err212, SectType.OPTIONS, "QUALITY TRACE " + line)); + } + else + { + net.TraceNode = node.Name; + net.ChemName = Keywords.u_PERCENT; + net.ChemUnits = units; + } + + break; + + case QualType.Age: + net.ChemName = Keywords.w_AGE; + net.ChemUnits = Keywords.u_HOURS; + break; + } + } + + //----------------------------------------------------------------------------------- + + line = options[DIFFUS_INDEX]; + + if (line.ToDouble(out y) && y >= 0) + { + net.Diffus = y; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.OPTIONS, "DIFFUSIVITY " + line)); + } + + + //----------------------------------------------------------------------------------- + + line = options[QUAL_TOL_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.QTol = y; + } + else + { + LogException(new EnException(ErrorCode.Err201, SectType.OPTIONS, "QTOL " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[BULK_ORDER_INDEX]; + + if (line.ToDouble(out y)) + { + net.BulkOrder = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "ORDER BULK " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[WALL_ORDER_INDEX]; + + if (string.Equals(line, "Zero", StringComparison.OrdinalIgnoreCase)) + { + net.WallOrder = 0; + } + else if (string.Equals(line, "First", StringComparison.OrdinalIgnoreCase)) + { + net.WallOrder = 1; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "ORDER WALL " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[GLOBAL_KBULK_INDEX]; + + if (line.ToDouble(out y)) + { + net.KBulk = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "GLOBAL BULK " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[GLOBAL_KWALL_INDEX]; + + if (line.ToDouble(out y)) + { + net.KWall = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "GLOBAL WALL " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[LIMIT_QUAL_INDEX]; + + if (line.ToDouble(out y)) + { + net.CLimit = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "LIMITING POTENTIAL " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[ROUGH_CORREL_INDEX]; + + if (line.ToDouble(out y)) + { + net.RFactor = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.REACTIONS, "ROUGHNESS CORRELATION " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[DURATION_INDEX]; + TimeSpan t; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.Duration = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "DURATION " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[HYD_TSTEP_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.HStep = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "HYDRAULIC TIMESTEP " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[QUAL_TSTEP_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + //Check if Quality Time Step is in hours instead of minutes + net.QStep = t > TimeSpan.FromHours(1) ? new TimeSpan(t.Ticks / 60) : t; //? BUG? + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "QUALITY TIMESTEP " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[PAT_TSTEP_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.PStep = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "PATTERN TIMESTEP " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[PAT_START_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.PStart = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "PATTERN START " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[RPT_TSTEP_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.RStep = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "REPORT TIMESTEP " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[RPT_START_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.RStart = t; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "REPORT START " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[START_TIME_INDEX]; + + if ((t = GetSeconds(line)) != TimeSpan.MinValue) + { + net.Tstart = t.TimeOfDay(); + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "START CLOCKTIME " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[TIME_STAT_INDEX]; + { + if (line.TryParse(out TstatType flag)) + { + net.TstatFlag = flag; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.TIMES, "STATISTIC " + line)); + } + } + + //----------------------------------------------------------------------------------- + + line = options[EFFIC_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.EPump = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.ENERGY, "GLOBAL EFFIC " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[EPRICE_INDEX]; + + if (line.ToDouble(out y)) + { + net.ECost = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.ENERGY, "GLOBAL PRICE " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[EPRICE_INDEX]; + + if (line.ToDouble(out y)) + { + net.ECost = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.ENERGY, "GLOBAL PRICE " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[PRICE_PAT_INDEX]; + + if (!string.IsNullOrEmpty(line)) + { + Pattern pat = net.GetPattern(line); + + if (pat != null) + { + net.EPatId = pat.Name; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.ENERGY, "GLOBAL PATTERN " + line)); + } + } + + //----------------------------------------------------------------------------------- + + line = options[DMND_CHARGE_INDEX]; + + if (line.ToDouble(out y)) + { + net.DCost = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.ENERGY, "DEMAND CHARGE " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[CHECK_FREQ_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.CheckFreq = (int)y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "CHECKFREQ " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[MAX_CHECK_INDEX]; + + if (line.ToDouble(out y) && y > 0) + { + net.MaxCheck = (int)y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "MAXCHECK " + line)); + } + + //----------------------------------------------------------------------------------- + + line = options[DAMP_LIMIT_INDEX]; + + if (line.ToDouble(out y)) + { + net.DampLimit = y; + } + else + { + LogException(new EnException(ErrorCode.Err213, SectType.OPTIONS, "DAMPLIMIT " + line)); + } + + } + + /// <summary>Delphi TReader partial implementation.</summary> + /// <remarks>TReader is a specialized filer that reads component data from an associated stream.</remarks> + private class Reader + { + private readonly BinaryReader _br; + + public Reader(Stream stream, Encoding enc) { _br = new BinaryReader(stream, enc); } + + /// <summary> + /// Reads the type of the next item on the reader object's stream and returns + /// with the stream positioned after the value-type indicator. + /// </summary> + private TValueType ReadValue() { return (TValueType)_br.ReadByte(); } + + /// <summary> + /// Returns the type of the next item in the reader object's stream without + /// moving the position of the stream. + /// </summary> + /// <returns></returns> + private TValueType NextValue() { return (TValueType)unchecked((byte)_br.PeekChar()); } + + /// <summary>Reads a tagged string value written by WriteString from the reader object's stream and returns its contents.</summary> + public string ReadString() + { + int len; + + switch (ReadValue()) + { + case TValueType.vaString: + // pascal short string + len = _br.ReadByte(); + return Encoding.ASCII.GetString(_br.ReadBytes(len)); + + case TValueType.vaLString: + len = _br.ReadInt32(); + return Encoding.ASCII.GetString(_br.ReadBytes(len)); + + case TValueType.vaWString: + len = _br.ReadInt32(); + return Encoding.Unicode.GetString(_br.ReadBytes(len << 1)); + + case TValueType.vaUTF8String: + len = _br.ReadInt32(); + return Encoding.UTF8.GetString(_br.ReadBytes(len)); + + default: + throw new InvalidDataException(); + } + } + + /// <summary> + /// Reads an integer-type number from the reader object's stream and returns its value. + /// </summary> + public int ReadInteger() + { + var vt = ReadValue(); + switch (vt) + { + case TValueType.vaInt8: + return _br.ReadSByte(); + case TValueType.vaInt16: + return _br.ReadInt16(); + case TValueType.vaInt32: + return _br.ReadInt32(); + default: + throw new InvalidDataException(); + } + } + + public List<string> ReadList() + { + var result = new List<string>(); + + var tv = ReadValue(); + if (tv != TValueType.vaList) + throw new InvalidDataException(); + + while (NextValue() != TValueType.vaNull) + result.Add(ReadString()); + + ReadValue(); + + return result; + } + + /// <summary> + /// Reads a boolean from the reader object's stream and returns that boolean value. + /// </summary> + public bool ReadBoolean() { return ReadValue() == TValueType.vaTrue; } + + /// <summary> + /// Reads 10-byte extended value from file. + /// </summary> + /// <returns></returns> + private double ReadExtended() + { + ulong mantissa = _br.ReadUInt64(); + int expon = _br.ReadInt16(); + + int sign = (expon & 0x8000) == 0x00 ? 1 : -1; + expon &= 0x7FFF; + + switch (expon) + { + case 0: + return mantissa == 0 ? 0 : double.NaN; + + case 0x7FFF: + // Infinity or NaN + + // var integral = mantissa >> 63; + mantissa &= 0x7FFFFFFFFFFFFFFF; + + if (mantissa == 0) + return sign / 0.0; // return double.PositiveInfinity * sign; + + return double.NaN; + + default: + expon -= 16383; + + double f = (mantissa >> 32) * Math.Pow(2, expon - 31); + f += (mantissa & 0xFFFFFFFF) * Math.Pow(2, expon - 63); + return sign < 0 ? -f : f; + } + } + + /// <summary> + /// Reads a floating-point number from the reader object's stream and returns its value. + /// </summary> + private double ReadFloat() + { + switch (ReadValue()) + { + case TValueType.vaExtended: + return ReadExtended(); + + default: + _br.BaseStream.Position--; + return ReadInt64(); + } + } + + /// <summary> + /// Reads an 64-bit integer from the reader object's stream and returns its value. + /// </summary> + private long ReadInt64() + { + switch (NextValue()) + { + case TValueType.vaInt64: + ReadValue(); + return _br.ReadInt64(); + + default: + return ReadInteger(); + } + + + } + + public string[] ReadArray(int ubound) + { + ubound++; + + int len = ReadInteger(); + // if (len > maxLen) len = maxLen; + + string[] result = new string[ubound]; + + for (int i = 0; i < len; i++) + if (i < ubound) + result[i] = ReadString(); + else + { + // skip remaining strings + ReadString(); + } + + return result; + } + + public EnPoint[] ReadVertices() + { + int n = ReadInteger(); + EnPoint[] points = new EnPoint[n]; + + for (int i = 0; i < n; i++) + points[i] = ReadPoint(); + + // if (n > 1) Array.Reverse(points); + return points; + } + + public EnPoint ReadPoint() + { + return new EnPoint(ReadFloat(), ReadFloat()); + } + + // ReSharper disable InconsistentNaming + /// <summary>TValueType defines the kinds of values written to and read from filer objects.</summary> + private enum TValueType : byte + { + ///<summary>identifies the type as the const Variant Null.</summary> + vaNull, + + ///<summary>identifies the type as a list of tagged items ending with a NULL pointer.</summary> + + vaList, + + ///<summary>identifies the type as an 8-bit integer.</summary> + vaInt8, + + ///<summary>identifies the type as a 16-bit integer.</summary> + vaInt16, + + ///<summary>identifies the type as a 32-bit integer.</summary> + vaInt32, + + ///<summary>identifies the type as a float or long double.</summary> + vaExtended, + + ///<summary>identifies the type as a short string</summary> + vaString, + + ///<summary>identifies the type as an identifier string.</summary> + vaIdent, + + ///<summary>identifies the type as the boolean false.</summary> + vaFalse, + + ///<summary>identifies the type as the boolean true.</summary> + vaTrue, + + ///<summary>identifies the type as a block of binary preceded by a length count.</summary> + vaBinary, + + ///<summary>identifies the type as a set.</summary> + vaSet, + + ///<summary>identifies the type as an AnsiString (long string).</summary> + vaLString, + + ///<summary>identifies the type as nil (Delphi) or NULL (C++).</summary> + vaNil, + + ///<summary>identifies the type as a TCollection.</summary> + vaCollection, + + ///<summary>identifies the type as a Single (float).</summary> + vaSingle, + + ///<summary>identifies the type as a Currency.</summary> + vaCurrency, + + ///<summary>identifies the type as a Date.</summary> + vaDate, + + ///<summary>identifies the type as a WideString.</summary> + vaWString, + + ///<summary>identifies the type as a 64-bit integer.</summary> + vaInt64, + + ///<summary>identifies the type as a UTF8String.</summary> + vaUTF8String + } + // ReSharper restore InconsistentNaming + } + } + +} diff --git a/IStation.Epanet/network/io/input/NullParser.cs b/IStation.Epanet/network/io/input/NullParser.cs new file mode 100644 index 0000000..0b817fc --- /dev/null +++ b/IStation.Epanet/network/io/input/NullParser.cs @@ -0,0 +1,28 @@ +namespace IStation.Epanet.Network.IO.Input +{ + + ///<summary>Network conversion units only class.</summary> + public class NullParser : InputParser + { + + public override Network Parse(Network nw, string f) + { + net = nw ?? new Network(); + + AdjustData(net); + net.FieldsMap.Prepare( + net.UnitsFlag, + net.FlowFlag, + net.PressFlag, + net.QualFlag, + net.ChemUnits, + net.SpGrav, + net.HStep); + + Convert(); + + return net; + } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/io/input/XMLParser.cs b/IStation.Epanet/network/io/input/XMLParser.cs new file mode 100644 index 0000000..a3c9c89 --- /dev/null +++ b/IStation.Epanet/network/io/input/XMLParser.cs @@ -0,0 +1,827 @@ +using IStation.Epanet; +using IStation.Epanet.Enums; +using IStation.Epanet.Network.Structures; +using System.IO.Compression; +using System.Xml; + +namespace IStation.Epanet.Network.IO.Input +{ + + public class XmlParser : InputParser + { + private SectType _sectionType; + private XmlReader _reader; + private readonly bool _gzipped; + + public XmlParser(bool gzipped) { _gzipped = gzipped; } + + private void LogException(ErrorCode err, string line) + { + if (err == ErrorCode.Ok) + return; + + var ex = new InputException(err, _sectionType, line); + + base.errors.Add(ex); + + base.LogException(ex); + + } + + public override Network Parse(Network nw, string f) + { + net = nw ?? new Network(); + + XmlReaderSettings settings = new XmlReaderSettings + { + CloseInput = false, + ValidationType = ValidationType.None, + CheckCharacters = true, + IgnoreComments = false, + IgnoreWhitespace = true, + + }; + + try + { + + + Stream stream = _gzipped + ? (Stream)new GZipStream(File.OpenRead(f), CompressionMode.Decompress) + : File.OpenRead(f); + + using (stream) + { + + using (_reader = XmlReader.Create(stream, settings)) + { + _reader.ReadToFollowing("network"); + ParsePc(_reader); + } + + stream.Position = 0; + + using (_reader = XmlReader.Create(stream, settings)) + { + _reader.ReadToFollowing("network"); + + while (_reader.Read()) + { + if (_reader.NodeType != XmlNodeType.Element || _reader.IsEmptyElement) + continue; + + try + { + _sectionType = (SectType)Enum.Parse(typeof(SectType), _reader.Name, true); + } + catch (ArgumentException) + { + continue; + } + + + var r = _reader.ReadSubtree(); + + switch (_sectionType) + { + case SectType.TITLE: + ParseTitle(r); + break; + + case SectType.JUNCTIONS: + ParseJunction(r); + break; + + case SectType.RESERVOIRS: + ParseReservoir(r); + break; + + case SectType.TANKS: + ParseTank(r); + break; + + case SectType.PIPES: + ParsePipe(r); + break; + + case SectType.PUMPS: + ParsePump(r); + break; + + case SectType.VALVES: + ParseValve(r); + break; + + case SectType.CONTROLS: + ParseControl(r); + break; + + case SectType.RULES: + ParseRule(r); + break; + + case SectType.DEMANDS: + ParseDemand(r); + break; + + case SectType.SOURCES: + ParseSource(r); + break; + + case SectType.EMITTERS: + ParseEmitter(r); + break; + + case SectType.PATTERNS: + case SectType.CURVES: + break; + + case SectType.QUALITY: + ParseQuality(r); + break; + + case SectType.STATUS: + ParseStatus(r); + break; + + case SectType.ROUGHNESS: + // TODO add ParseRoughness + break; + + case SectType.ENERGY: + ParseEnergy(r); + break; + + case SectType.REACTIONS: + ParseReact(r); + break; + + case SectType.MIXING: + ParseMixing(r); + break; + + case SectType.REPORT: + ParseReport(r); + break; + + case SectType.TIMES: + ParseTime(r); + break; + + case SectType.OPTIONS: + ParseOption(r); + break; + + case SectType.COORDINATES: + ParseCoordinate(r); + break; + + case SectType.VERTICES: + ParseVertice(r); + break; + + case SectType.LABELS: + ParseLabel(r); + break; + + case SectType.BACKDROP: + // TODO add ParseRoughness + break; + + case SectType.TAGS: + + case SectType.END: + break; + // TODO add etc + } + } + } + } + } + catch (IOException) + { + throw new EnException(ErrorCode.Err302); + } + + return net; + } + + private void ParseTitle(XmlReader r) + { + + int i = Constants.MAXTITLE; + + while (r.ReadToFollowing("line") && i-- > 0) + { + string s = r.ReadString(); + net.Title.Add(s); + } + } + + private void ParseJunction(XmlReader r) + { + if (!r.ReadToDescendant("node")) + return; + + // while (r.ReadToFollowing("node")) + + do + { + string name = r.GetAttribute("name"); + + Node node = new Junction(name); + + try + { + net.Nodes.Add(node); + } + catch (ArgumentException) + { + LogException(ErrorCode.Err215, name); + continue; + } + + string el = r.GetAttribute("elevation"); + + if (string.IsNullOrEmpty(el)) + { + LogException(ErrorCode.Err201, r.Value); + continue; + } + + try + { + node.Elevation = XmlConvert.ToDouble(el); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + + string sx = r.GetAttribute("x"); + string sy = r.GetAttribute("y"); + + if (!string.IsNullOrEmpty(sx) && !string.IsNullOrEmpty(sy)) + { + try + { + node.Coordinate = new EnPoint(XmlConvert.ToDouble(sx), XmlConvert.ToDouble(sy)); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, node.Name); + continue; + } + } + + + using (var rr = r.ReadSubtree()) + { + + rr.Read(); + + while (rr.Read()) + { + switch (rr.NodeType) + { + case XmlNodeType.Element: + + if (rr.Name.Equals("demand", StringComparison.Ordinal)) + { + string att = rr.GetAttribute("base"); + + if (!string.IsNullOrEmpty(att)) + { + try + { + node.PrimaryDemand.Base = XmlConvert.ToDouble(att.Trim()); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + } + + att = rr.GetAttribute("pattern"); + + if (!string.IsNullOrEmpty(att)) + { + Pattern p = net.GetPattern(att.Trim()); + if (p == null) + { + LogException(ErrorCode.Err205, name); + continue; + } + + node.PrimaryDemand.Pattern = p; + } + + } + else if (rr.Name.Equals("tag", StringComparison.Ordinal)) + { + string s = rr.ReadString(); + if (!string.IsNullOrEmpty(s)) node.Tag = s.Trim(); + } + + break; + + case XmlNodeType.Comment: + node.Comment = rr.Value; + break; + } + } + } + + } while (r.ReadToNextSibling("node")); + } + + private void ParseReservoir(XmlReader r) + { + + if (!r.ReadToDescendant("node")) + return; + + // while (r.ReadToFollowing("node")) + + do + { + string name = r.GetAttribute("name"); + + Tank node = new Tank(name); + + try + { + net.Nodes.Add(node); + } + catch (ArgumentException) + { + LogException(ErrorCode.Err215, name); + continue; + } + + string el = r.GetAttribute("elevation"); + + if (string.IsNullOrEmpty(el)) + { + LogException(ErrorCode.Err201, r.Value); + continue; + } + + try + { + node.Elevation = XmlConvert.ToDouble(el); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + + + string pattern = r.GetAttribute("pattern"); + + if (!string.IsNullOrEmpty(pattern)) + { + Pattern p = net.GetPattern(pattern.Trim()); + if (p == null) + { + LogException(ErrorCode.Err205, name); + continue; + } + + node.Pattern = p; + } + + string sx = r.GetAttribute("x"); + string sy = r.GetAttribute("y"); + + if (!string.IsNullOrEmpty(sx) && !string.IsNullOrEmpty(sy)) + { + try + { + node.Coordinate = new EnPoint(XmlConvert.ToDouble(sx), XmlConvert.ToDouble(sy)); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, node.Name); + continue; + } + } + + + using (var rr = r.ReadSubtree()) + { + + rr.Read(); + + while (rr.Read()) + { + switch (rr.NodeType) + { + case XmlNodeType.Element: + + if (rr.Name.Equals("demand", StringComparison.Ordinal)) + { + string att = rr.GetAttribute("base"); + + if (!string.IsNullOrEmpty(att)) + { + try + { + node.PrimaryDemand.Base = XmlConvert.ToDouble(att.Trim()); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + } + + att = rr.GetAttribute("pattern"); + + if (!string.IsNullOrEmpty(att)) + { + Pattern p = net.GetPattern(att.Trim()); + if (p == null) + { + LogException(ErrorCode.Err205, name); + continue; + } + + node.PrimaryDemand.Pattern = p; + } + + } + else if (rr.Name.Equals("tag", StringComparison.Ordinal)) + { + string s = rr.ReadString(); + if (!string.IsNullOrEmpty(s)) node.Tag = s.Trim(); + } + + break; + + case XmlNodeType.Comment: + node.Comment = rr.Value; + break; + } + } + } + + } while (r.ReadToNextSibling("node")); + + } + + private void ParseTank(XmlReader r) + { + + if (!r.ReadToDescendant("node")) + return; + + // while (r.ReadToFollowing("node")) + + do + { + string name = r.GetAttribute("name"); + + Tank node = new Tank(name); + + try + { + net.Nodes.Add(node); + } + catch (ArgumentException) + { + LogException(ErrorCode.Err215, name); + continue; + } + + string el = r.GetAttribute("elevation"); + + + + if (string.IsNullOrEmpty(el)) + { + // Reservour + string pattern = r.GetAttribute("pattern"); + + if (!string.IsNullOrEmpty(pattern)) + { + // TODO + } + + + } + else + { + // Tank + // TODO + } + + + try + { + node.Elevation = XmlConvert.ToDouble(el); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + + string sx = r.GetAttribute("x"); + string sy = r.GetAttribute("y"); + + if (!string.IsNullOrEmpty(sx) && !string.IsNullOrEmpty(sy)) + { + try + { + node.Coordinate = new EnPoint(XmlConvert.ToDouble(sx), XmlConvert.ToDouble(sy)); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, node.Name); + continue; + } + } + + + using (var rr = r.ReadSubtree()) + { + + rr.Read(); + + while (rr.Read()) + { + switch (rr.NodeType) + { + case XmlNodeType.Element: + + if (rr.Name.Equals("demand", StringComparison.Ordinal)) + { + string att = rr.GetAttribute("base"); + + if (!string.IsNullOrEmpty(att)) + { + try + { + node.PrimaryDemand.Base = XmlConvert.ToDouble(att.Trim()); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, name); + continue; + } + } + + att = rr.GetAttribute("pattern"); + + if (!string.IsNullOrEmpty(att)) + { + Pattern p = net.GetPattern(att.Trim()); + if (p == null) + { + LogException(ErrorCode.Err205, name); + continue; + } + + node.PrimaryDemand.Pattern = p; + } + + } + else if (rr.Name.Equals("tag", StringComparison.Ordinal)) + { + string s = rr.ReadString(); + if (!string.IsNullOrEmpty(s)) node.Tag = s.Trim(); + } + + break; + + case XmlNodeType.Comment: + node.Comment = rr.Value; + break; + } + } + } + + } while (r.ReadToNextSibling("node")); + + } + + private void ParsePipe(XmlReader r) { throw new NotImplementedException(); } + + private void ParsePump(XmlReader r) { throw new NotImplementedException(); } + + private void ParseValve(XmlReader r) { throw new NotImplementedException(); } + + private void ParseControl(XmlReader r) { throw new NotImplementedException(); } + + private void ParseRule(XmlReader r) { throw new NotImplementedException(); } + + private void ParseDemand(XmlReader r) { throw new NotImplementedException(); } + + private void ParseSource(XmlReader r) { throw new NotImplementedException(); } + + private void ParseEmitter(XmlReader r) { throw new NotImplementedException(); } + + private void ParseQuality(XmlReader r) { throw new NotImplementedException(); } + + private void ParseStatus(XmlReader r) { throw new NotImplementedException(); } + + private void ParseEnergy(XmlReader r) { throw new NotImplementedException(); } + + private void ParseReact(XmlReader r) { throw new NotImplementedException(); } + + private void ParseMixing(XmlReader r) { throw new NotImplementedException(); } + + private void ParseReport(XmlReader r) { throw new NotImplementedException(); } + + private void ParseTime(XmlReader r) { throw new NotImplementedException(); } + + private void ParseOption(XmlReader r) { throw new NotImplementedException(); } + + private void ParseCoordinate(XmlReader r) { throw new NotImplementedException(); } + + private void ParseVertice(XmlReader r) { throw new NotImplementedException(); } + + private void ParseLabel(XmlReader r) { throw new NotImplementedException(); } + + + private void ParsePc(XmlReader r) + { + _reader.ReadStartElement(); + + while (!r.EOF) + { + + try + { + _sectionType = (SectType)Enum.Parse(typeof(SectType), r.Name, true); + } + catch (ArgumentException) + { + _sectionType = (SectType)(-1); + } + + + try + { + switch (_sectionType) + { + case SectType.PATTERNS: + ParsePattern(r.ReadSubtree()); + break; + case SectType.CURVES: + ParseCurve(r.ReadSubtree()); + break; + } + } + catch (InputException ex) + { + base.LogException(ex); + } + + r.Skip(); + + } + + if (errors.Count > 0) + throw new EnException(ErrorCode.Err200); + + } + + private void ParsePattern(XmlReader r) + { + + if (!r.ReadToDescendant("pattern")) + return; + + do + { + if (r.IsEmptyElement) + continue; + + string name = r.GetAttribute("name"); + + if (string.IsNullOrEmpty(name)) + continue; + + var pat = new Pattern(name); + + using (var rr = r.ReadSubtree()) + { + rr.Read(); + while (rr.Read()) + { + switch (rr.NodeType) + { + case XmlNodeType.Element: + if (rr.Name.Equals("factor", StringComparison.Ordinal)) + { + + string s = rr.GetAttribute("value") ?? string.Empty; + + try + { + pat.Add(XmlConvert.ToDouble(s)); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, rr.ReadInnerXml()); + } + } + + break; + + case XmlNodeType.Comment: + pat.Comment = rr.Value; + break; + } + } + } + + + try + { + net.Patterns.Add(pat); + } + catch (ArgumentException) + { + LogException(ErrorCode.Err215, pat.Name); + } + + + } while (r.ReadToNextSibling("pattern")); + + } + + private void ParseCurve(XmlReader r) + { + + if (!r.ReadToDescendant("curve")) + return; + + do + { + if (r.IsEmptyElement) + continue; + + string name = r.GetAttribute("name"); + + if (string.IsNullOrEmpty(name)) + continue; + + var cur = new Curve(name); + + using (var rr = r.ReadSubtree()) + { + rr.Read(); + while (rr.Read()) + { + switch (rr.NodeType) + { + case XmlNodeType.Element: + if (rr.Name.Equals("point", StringComparison.Ordinal)) + { + + string sx = rr.GetAttribute("x") ?? string.Empty; + string sy = rr.GetAttribute("y") ?? string.Empty; + + try + { + cur.Add(XmlConvert.ToDouble(sx), XmlConvert.ToDouble(sy)); + } + catch (FormatException) + { + LogException(ErrorCode.Err202, rr.ReadInnerXml()); + } + } + + break; + + case XmlNodeType.Comment: + cur.Comment = rr.Value; + break; + } + } + } + + try + { + net.Curves.Add(cur); + } + catch (ArgumentException) + { + LogException(ErrorCode.Err215, cur.Name); + } + + } while (r.ReadToNextSibling("curve")); + + r.ReadEndElement(); + + + } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/io/output/InpComposer.cs b/IStation.Epanet/network/io/output/InpComposer.cs new file mode 100644 index 0000000..45f9718 --- /dev/null +++ b/IStation.Epanet/network/io/output/InpComposer.cs @@ -0,0 +1,1055 @@ +using IStation.Epanet.Enums; +using IStation.Epanet.Network.Structures; +using IStation.Epanet.Util; +using System.Text; + +namespace IStation.Epanet.Network.IO.Output +{ + + ///<summary>INP file composer.</summary> + public class InpComposer : OutputComposer + { + + private const string JUNCS_SUBTITLE = ";ID\tElev\tDemand\tPattern"; + private const string RESERVOIRS_SUBTITLE = ";ID\tHead\tPattern"; + private const string TANK_SUBTITLE = ";ID\tElevation\tInitLevel\tMinLevel\tMaxLevel\tDiameter\tMinVol\tVolCurve"; + private const string PUMPS_SUBTITLE = ";ID\tNode1\tNode2\tParameters"; + private const string VALVES_SUBTITLE = ";ID\tNode1\tNode2\tDiameter\tType\tSetting\tMinorLoss"; + private const string DEMANDS_SUBTITLE = ";Junction\tDemand\tPattern\tCategory"; + private const string STATUS_SUBTITLE = ";ID\tStatus/Setting"; + private const string PIPES_SUBTITLE = ";ID\tNode1\tNode2\tLength\tDiameter\tRoughness\tMinorLoss\tStatus"; + private const string PATTERNS_SUBTITLE = ";ID\tMultipliers"; + private const string EMITTERS_SUBTITLE = ";Junction\tCoefficient"; + private const string CURVE_SUBTITLE = ";ID\tX-Value\tY-Value"; + private const string QUALITY_SUBTITLE = ";Node\tInitQual"; + private const string SOURCE_SUBTITLE = ";Node\tType\tQuality\tPattern"; + private const string MIXING_SUBTITLE = ";Tank\tModel"; + private const string REACTIONS_SUBTITLE = ";Type\tPipe/Tank"; + private const string COORDINATES_SUBTITLE = ";Node\tX-Coord\tY-Coord"; + + private TextWriter _writer; + + public InpComposer(Network net) : base(net) { } + + public override void Compose(string fileName) + { + + try + { + using (_writer = new StreamWriter(File.OpenWrite(fileName), Encoding.Default)) + { // "ISO-8859-1" + ComposeHeader(); + ComposeJunctions(); + ComposeReservoirs(); + ComposeTanks(); + ComposePipes(); + ComposePumps(); + ComposeValves(); + ComposeTags(); + ComposeDemands(); + ComposeEmitters(); + ComposeStatus(); + ComposePatterns(); + ComposeCurves(); + ComposeControls(); + ComposeQuality(); + ComposeSource(); + ComposeMixing(); + ComposeReaction(); + ComposeEnergy(); + ComposeTimes(); + ComposeOptions(); + ComposeExtraOptions(); + ComposeReport(); + ComposeLabels(); + ComposeCoordinates(); + ComposeVertices(); + ComposeRules(); + + _writer.WriteLine($"[{SectType.END}]"); + } + } + catch (IOException ex) + { + throw new EnException(ErrorCode.Err308, ex); + } + + } + + private void ComposeHeader() + { + if (_net.Title.Count == 0) + return; + + _writer.WriteLine(SectType.TITLE.ParseStr()); + + foreach (string str in _net.Title) + { + _writer.WriteLine(str); + } + + _writer.WriteLine(); + } + + + private void ComposeJunctions() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Junctions.Any()) + return; + + _writer.WriteLine(SectType.JUNCTIONS.ParseStr()); + _writer.WriteLine(JUNCS_SUBTITLE); + + foreach (Node node in _net.Junctions) + { + _writer.Write(" {0}\t{1}", node.Name, fMap.RevertUnit(FieldType.ELEV, node.Elevation)); + + //if(node.getDemand()!=null && node.getDemand().size()>0 && !node.getDemand()[0].getPattern().getId().equals("")) + // buffer.write("\t"+node.getDemand()[0].getPattern().getId()); + + if (node.Name == "J114") + { + + } + if (node.Demands.Count > 0) + { + Demand demand = node.Demands[0]; + if (demand.Pattern != null) + { + _writer.Write("\t{0}", fMap.RevertUnit(FieldType.DEMAND, demand.Base)); + + if (!string.IsNullOrEmpty(demand.Pattern?.Name) + && !_net.DefPatId.Equals(demand.Pattern.Name, StringComparison.OrdinalIgnoreCase)) + _writer.Write("\t" + demand.Pattern.Name); + } + } + + if (!string.IsNullOrEmpty(node.Comment)) + _writer.Write("\t;" + node.Comment); + + _writer.WriteLine(); + } + + + _writer.WriteLine(); + } + + private void ComposeReservoirs() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Reservoirs.Any()) + return; + + _writer.WriteLine(SectType.RESERVOIRS.ParseStr()); + _writer.WriteLine(RESERVOIRS_SUBTITLE); + + foreach (Reservoir reservoir in _net.Reservoirs) + { + _writer.Write(" {0}\t{1}", reservoir.Name, fMap.RevertUnit(FieldType.HEAD, reservoir.Head)); + + + if (reservoir.Pattern != null) + _writer.Write("\t{0}", reservoir.Pattern.Name); + + + if (!string.IsNullOrEmpty(reservoir.Comment)) + _writer.Write("\t;" + reservoir.Comment); + + _writer.WriteLine(); + } + + _writer.WriteLine(); + } + + private void ComposeTanks() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Tanks.Any()) + return; + + _writer.WriteLine(SectType.TANKS.ParseStr()); + _writer.WriteLine(TANK_SUBTITLE); + + foreach (Tank tank in _net.Tanks) + { + double vmin = tank.Vmin; + if (Math.Round(vmin / tank.Area).EqualsTo(Math.Round(tank.Hmin - tank.Elevation))) + vmin = 0; + + _writer.Write( + " {0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", + tank.Name, + fMap.RevertUnit(FieldType.ELEV, tank.Elevation), + fMap.RevertUnit(FieldType.ELEV, tank.H0 - tank.Elevation), + fMap.RevertUnit(FieldType.ELEV, tank.Hmin - tank.Elevation), + fMap.RevertUnit(FieldType.ELEV, tank.Hmax - tank.Elevation), + fMap.RevertUnit(FieldType.ELEV, 2 * Math.Sqrt(tank.Area / Math.PI)), + fMap.RevertUnit(FieldType.VOLUME, vmin)); + + if (tank.Vcurve != null) + _writer.Write(" " + tank.Vcurve.Name); + + if (!string.IsNullOrEmpty(tank.Comment)) + _writer.Write("\t;" + tank.Comment); + _writer.WriteLine(); + } + + _writer.WriteLine(); + } + + private void ComposePipes() + { + FieldsMap fMap = _net.FieldsMap; + + if (_net.Links.Count == 0) + return; + + _writer.WriteLine(SectType.PIPES.ParseStr()); + _writer.WriteLine(PIPES_SUBTITLE); + + foreach (Pipe pipe in _net.Pipes) + { + double d = pipe.Diameter; + double kc = pipe.Kc; + if (_net.FormFlag == HeadLossFormulaType.DW) + kc = fMap.RevertUnit(FieldType.ELEV, kc * 1000.0); + + double km = pipe.Km * Math.Pow(d, 4.0) / 0.02517; + + _writer.Write( + " {0}\t{1}\t{2}\t{3}\t{4}", + pipe.Name, + pipe.FirstNode.Name, + pipe.SecondNode.Name, + fMap.RevertUnit(FieldType.LENGTH, pipe.Lenght), + fMap.RevertUnit(FieldType.DIAM, d)); + + // if (net.FormFlag == FormType.DW) + _writer.Write(" {0}\t{1}", kc, km); + + if (pipe.HasCheckValve) + _writer.Write(" CV"); + else + { + switch (pipe.Status) + { + case StatusType.CLOSED: + _writer.Write(" CLOSED"); + break; + case StatusType.OPEN: + _writer.Write(" OPEN"); + break; + } + } + + if (!string.IsNullOrEmpty(pipe.Comment)) + _writer.Write("\t;" + pipe.Comment); + + _writer.WriteLine(); + } + + _writer.WriteLine(); + } + + private void ComposePumps() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Pumps.Any()) + return; + + _writer.WriteLine(SectType.PUMPS.ParseStr()); + _writer.WriteLine(PUMPS_SUBTITLE); + + foreach (Pump pump in _net.Pumps) + { + _writer.Write( + " {0}\t{1}\t{2}", + pump.Name, + pump.FirstNode.Name, + pump.SecondNode.Name); + + + // Pump has constant power + if (pump.Ptype == PumpType.CONST_HP) + _writer.Write(" POWER " + pump.Km); + // Pump has a head curve + else if (pump.HCurve != null) + _writer.Write(" HEAD " + pump.HCurve.Name); + // Old format used for pump curve + else + { + _writer.Write( + " {0}\t{1}\t{2}\t0.0\t{3}", + fMap.RevertUnit(FieldType.HEAD, -pump.H0), + fMap.RevertUnit( + FieldType.HEAD, + -pump.H0 - pump.FlowCoefficient * Math.Pow(pump.Q0, pump.N)), + fMap.RevertUnit(FieldType.FLOW, pump.Q0), + fMap.RevertUnit(FieldType.FLOW, pump.Qmax)); + + continue; + } + + if (pump.UPat != null) + _writer.Write(" PATTERN " + pump.UPat.Name); + + + if (!pump.Kc.EqualsTo(1.0)) + _writer.Write(" SPEED {0}", pump.Kc); + + if (!string.IsNullOrEmpty(pump.Comment)) + _writer.Write("\t;" + pump.Comment); + + _writer.WriteLine(); + } + + _writer.WriteLine(); + } + + private void ComposeValves() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Valves.Any()) + return; + + _writer.WriteLine(SectType.VALVES.ParseStr()); + _writer.WriteLine(VALVES_SUBTITLE); + + foreach (Valve valve in _net.Valves) + { + double d = valve.Diameter; + double kc = valve.Kc; + if (double.IsNaN(kc)) + kc = 0.0; + + switch (valve.ValveType) + { + case ValveType.FCV: + kc = fMap.RevertUnit(FieldType.FLOW, kc); + break; + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + kc = fMap.RevertUnit(FieldType.PRESSURE, kc); + break; + } + + double km = valve.Km * Math.Pow(d, 4) / 0.02517; + + _writer.Write( + " {0}\t{1}\t{2}\t{3}\t{4}", + valve.Name, + valve.FirstNode.Name, + valve.SecondNode.Name, + fMap.RevertUnit(FieldType.DIAM, d), + valve.ValveType.Keyword2()); + + if (valve.ValveType == ValveType.GPV && valve.Curve != null) + _writer.Write(" {0}\t{1}", valve.Curve.Name, km); + else + _writer.Write(" {0}\t{1}", kc, km); + + if (!string.IsNullOrEmpty(valve.Comment)) + _writer.Write("\t;" + valve.Comment); + + _writer.WriteLine(); + } + _writer.WriteLine(); + } + + private void ComposeTags() + { + _writer.WriteLine(SectType.TAGS.ParseStr()); + char[] spaces = { ' ', '\t', }; + + foreach (var node in _net.Nodes) + { + string tag = node.Tag.Trim(); + + if (string.IsNullOrEmpty(tag)) continue; + + if (tag.IndexOf('\r') >= 0) tag = tag.Replace('\r', ' '); + if (tag.IndexOf('\n') >= 0) tag = tag.Replace('\n', ' '); + if (tag.IndexOfAny(spaces) > 0) tag = '"' + tag + '"'; + + _writer.WriteLine(" {0} \t{1,-16} {2}", Keywords.w_NODE, node.Name, tag); + } + + foreach (var link in _net.Links) + { + string tag = link.Tag; + + if (string.IsNullOrEmpty(tag)) + continue; + + if (tag.IndexOf('\r') >= 0) tag = tag.Replace('\r', ' '); + if (tag.IndexOf('\n') >= 0) tag = tag.Replace('\n', ' '); + if (tag.IndexOfAny(spaces) > 0) tag = '"' + tag + '"'; + + _writer.WriteLine(" {0} \t{1,-16} {2}", Keywords.w_LINK, link.Name, tag); + } + + } + + + private void ComposeDemands() + { + FieldsMap fMap = _net.FieldsMap; + + if (!_net.Junctions.Any()) + return; + + _writer.WriteLine(SectType.DEMANDS.ParseStr()); + _writer.WriteLine(DEMANDS_SUBTITLE); + + double ucf = fMap.GetUnits(FieldType.DEMAND); + + foreach (Node node in _net.Junctions) + { + foreach (Demand demand in node.Demands) + { + if (demand.Pattern == null) + continue; + _writer.Write("{0}\t{1}", node.Name, ucf * demand.Base); + + if (demand.Pattern != null) + _writer.Write("\t" + demand.Pattern.Name); + + _writer.WriteLine(); + } + } + + _writer.WriteLine(); + } + + private void ComposeEmitters() + { + + if (_net.Nodes.Count == 0) + return; + + _writer.WriteLine(SectType.EMITTERS.ParseStr()); + _writer.WriteLine(EMITTERS_SUBTITLE); + + double uflow = _net.FieldsMap.GetUnits(FieldType.FLOW); + double upressure = _net.FieldsMap.GetUnits(FieldType.PRESSURE); + double qexp = _net.QExp; + + foreach (Node node in _net.Junctions) + { + if (node.Ke.IsZero()) continue; + double ke = uflow / Math.Pow(upressure * node.Ke, 1.0 / qexp); + _writer.WriteLine(" {0}\t{1}", node.Name, ke); + } + + _writer.WriteLine(); + } + + private void ComposeStatus() + { + + if (_net.Links.Count == 0) + return; + + _writer.WriteLine(SectType.STATUS.ParseStr()); + _writer.WriteLine(STATUS_SUBTITLE); + + foreach (Link link in _net.Links) + { + if (link.LinkType <= LinkType.Pump) + { + if (link.Status == StatusType.CLOSED) + _writer.WriteLine(" {0}\t{1}", link.Name, StatusType.CLOSED); + + // Write pump speed here for pumps with old-style pump curve input + else if (link.LinkType == LinkType.Pump) + { + Pump pump = (Pump)link; + if (pump.HCurve == null && + pump.Ptype != PumpType.CONST_HP && + !pump.Kc.EqualsTo(1.0)) + _writer.WriteLine(" {0}\t{1}", link.Name, link.Kc); + } + } + // Write fixed-status PRVs & PSVs (setting = MISSING) + else if (double.IsNaN(link.Kc)) + { + switch (link.Status) + { + case StatusType.OPEN: + case StatusType.CLOSED: + _writer.WriteLine(" {0}\t{1}", link.Name, link.Status); + break; + } + + } + + } + + _writer.WriteLine(); + } + + private void ComposePatterns() + { + + var pats = _net.Patterns; + + if (pats.Count <= 1) + return; + + _writer.WriteLine(SectType.PATTERNS.ParseStr()); + _writer.WriteLine(PATTERNS_SUBTITLE); + + for (int i = 0; i < pats.Count; i++) + { + Pattern pat = pats[i]; + for (int j = 0; j < pats[i].Count; j++) + { + if (j % 6 == 0) + _writer.Write(" {0}", pat.Name); + + _writer.Write(" {0}", pat[j]); + + if (j % 6 == 5) + _writer.WriteLine(); + } + _writer.WriteLine(); + } + + _writer.WriteLine(); + } + + private void ComposeCurves() + { + + var curves = _net.Curves; + + if (curves.Count == 0) + return; + + _writer.WriteLine(SectType.CURVES.ParseStr()); + _writer.WriteLine(CURVE_SUBTITLE); + + foreach (Curve curve in curves) + { + foreach (var pt in curve) + { + _writer.WriteLine(" {0}\t{1}\t{2}", curve.Name, pt.X, pt.Y); + } + } + + _writer.WriteLine(); + } + + private void ComposeControls() + { + var controls = _net.Controls; + FieldsMap fmap = _net.FieldsMap; + + if (controls.Count == 0) + return; + + _writer.WriteLine(SectType.CONTROLS.ParseStr()); + + foreach (Control control in controls) + { + // Check that controlled link exists + if (control.Link == null) continue; + + // Get text of control's link status/setting + if (double.IsNaN(control.Setting)) + { + _writer.Write(" LINK {0} {1} ", control.Link.Name, control.Status); + } + else + { + double kc = control.Setting; + + if (control.Link.LinkType == LinkType.VALVE) + { + switch (((Valve)control.Link).ValveType) + { + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + kc = fmap.RevertUnit(FieldType.PRESSURE, kc); + break; + case ValveType.FCV: + kc = fmap.RevertUnit(FieldType.FLOW, kc); + break; + } + } + + _writer.Write(" LINK {0} {1} ", control.Link.Name, kc); + } + + + switch (control.Type) + { + // Print level control + case ControlType.LowLevel: + case ControlType.HiLevel: + double kc = control.Grade - control.Node.Elevation; + kc = fmap.RevertUnit( + control.Node.NodeType == NodeType.Junction + ? FieldType.PRESSURE + : FieldType.HEAD, + kc); + + _writer.Write( + " IF NODE {0} {1} {2}", + control.Node.Name, + control.Type.ParseStr(), + kc); + + break; + + // Print timer control + case ControlType.Timer: + _writer.Write( + " AT {0} {1} HOURS", + ControlType.Timer.ParseStr(), + control.Time.TotalHours); + + break; + + // Print time-of-day control + case ControlType.TimeOfDay: + _writer.Write( + " AT {0} {1}", + ControlType.TimeOfDay.ParseStr(), + control.Time.GetClockTime()); + + break; + } + _writer.WriteLine(); + } + _writer.WriteLine(); + } + + private void ComposeQuality() + { + FieldsMap fmap = _net.FieldsMap; + + if (_net.Nodes.Count == 0) + return; + + _writer.WriteLine(SectType.QUALITY.ParseStr()); + _writer.WriteLine(QUALITY_SUBTITLE); + + foreach (Node node in _net.Nodes) + { + if (node.C0.IsZero()) continue; + _writer.WriteLine(" {0}\t{1}", node.Name, fmap.RevertUnit(FieldType.QUALITY, node.C0)); + } + + _writer.WriteLine(); + } + + private void ComposeSource() + { + + if (_net.Nodes.Count == 0) + return; + + _writer.WriteLine(SectType.SOURCES.ParseStr()); + _writer.WriteLine(SOURCE_SUBTITLE); + + + foreach (Node node in _net.Nodes) + { + QualSource source = node.QualSource; + if (source == null) + continue; + + _writer.Write( + " {0}\t{1}\t{2}", + node.Name, + source.Type, + source.C0); + + if (source.Pattern != null) + _writer.Write(" " + source.Pattern.Name); + + _writer.WriteLine(); + } + _writer.WriteLine(); + } + + + private void ComposeMixing() + { + + if (!_net.Tanks.Any()) + return; + + _writer.WriteLine(SectType.MIXING.ParseStr()); + _writer.WriteLine(MIXING_SUBTITLE); + + foreach (Tank tank in _net.Tanks) + { + _writer.WriteLine( + " {0}\t{1}\t{2}", + tank.Name, + tank.MixModel.ParseStr(), + tank.V1Max / tank.Vmax); + } + + _writer.WriteLine(); + } + + private void ComposeReaction() + { + + _writer.WriteLine(SectType.REACTIONS.ParseStr()); + _writer.WriteLine(REACTIONS_SUBTITLE); + + _writer.WriteLine("ORDER BULK {0}", _net.BulkOrder); + _writer.WriteLine("ORDER WALL {0}", _net.WallOrder); + _writer.WriteLine("ORDER TANK {0}", _net.TankOrder); + _writer.WriteLine("GLOBAL BULK {0}", _net.KBulk * Constants.SECperDAY); + _writer.WriteLine("GLOBAL WALL {0}", _net.KWall * Constants.SECperDAY); + + // if (net.CLimit > 0.0) + _writer.WriteLine("LIMITING POTENTIAL {0}", _net.CLimit); + + // if (!net.RFactor.IsMissing() && net.RFactor != 0.0) + _writer.WriteLine("ROUGHNESS CORRELATION {0}", _net.RFactor); + + + foreach (Link link in _net.Links) + { + if (link.LinkType > LinkType.Pipe) + continue; + + if (!link.Kb.EqualsTo(_net.KBulk)) + _writer.WriteLine("BULK {0} {1}", link.Name, link.Kb * Constants.SECperDAY); + if (!link.Kw.EqualsTo(_net.KWall)) + _writer.WriteLine("WALL {0} {1}", link.Name, link.Kw * Constants.SECperDAY); + } + + foreach (Tank tank in _net.Tanks) + { + if (!tank.Kb.EqualsTo(_net.KBulk)) + _writer.WriteLine("TANK {0} {1}", tank.Name, tank.Kb * Constants.SECperDAY); + } + + _writer.WriteLine(); + } + + private void ComposeEnergy() + { + + _writer.WriteLine(SectType.ENERGY.ParseStr()); + + if (!_net.ECost.IsZero()) + _writer.WriteLine("GLOBAL PRICE {0}", _net.ECost); + + if (!_net.EPatId.Equals("")) + _writer.WriteLine("GLOBAL PATTERN {0}", _net.EPatId); + + _writer.WriteLine("GLOBAL EFFIC {0}", _net.EPump); + _writer.WriteLine("DEMAND CHARGE {0}", _net.DCost); + + foreach (Pump p in _net.Pumps) + { + if (p.ECost > 0.0) + _writer.WriteLine("PUMP {0} PRICE {1}", p.Name, p.ECost); + + if (p.EPat != null) + _writer.WriteLine("PUMP {0} PATTERN {1}", p.Name, p.EPat.Name); + + if (p.ECurve != null) + _writer.WriteLine("PUMP {0} EFFIC {1}", p.Name, p.ECurve.Name); + } + + _writer.WriteLine(); + + } + + private void ComposeTimes() + { + + _writer.WriteLine(SectType.TIMES.ParseStr()); + _writer.WriteLine("DURATION {0}", _net.Duration.GetClockTime()); + _writer.WriteLine("HYDRAULIC TIMESTEP {0}", _net.HStep.GetClockTime()); + _writer.WriteLine("QUALITY TIMESTEP {0}", _net.QStep.GetClockTime()); + _writer.WriteLine("REPORT TIMESTEP {0}", _net.RStep.GetClockTime()); + _writer.WriteLine("REPORT START {0}", _net.RStart.GetClockTime()); + _writer.WriteLine("PATTERN TIMESTEP {0}", _net.PStep.GetClockTime()); + _writer.WriteLine("PATTERN START {0}", _net.PStart.GetClockTime()); + _writer.WriteLine("RULE TIMESTEP {0}", _net.RuleStep.GetClockTime()); + _writer.WriteLine("START CLOCKTIME {0}", _net.Tstart.GetClockTime()); + _writer.WriteLine("STATISTIC {0}", _net.TstatFlag.ParseStr()); + _writer.WriteLine(); + } + + + private string ComposeHEADLOSS(HeadLossFormulaType type) + { + var flag = "H-W"; + if (type == HeadLossFormulaType.DW) + { + flag = "D-M"; + } + else if (type == HeadLossFormulaType.CM) + { + flag = "C-M"; + } + else + { + flag = "H-W"; + } + return flag; + } + private void ComposeOptions() + { + + FieldsMap fMap = _net.FieldsMap; + + _writer.WriteLine(SectType.OPTIONS.ParseStr()); + _writer.WriteLine("UNITS " + _net.FlowFlag); + _writer.WriteLine("PRESSURE " + _net.PressFlag); + _writer.WriteLine("HEADLOSS " + ComposeHEADLOSS(_net.FormFlag)); + + if (!string.IsNullOrEmpty(_net.DefPatId)) + _writer.WriteLine("PATTERN " + _net.DefPatId); + + switch (_net.HydFlag) + { + case HydType.USE: + _writer.WriteLine("HYDRAULICS USE " + _net.HydFname); + break; + case HydType.SAVE: + _writer.WriteLine("HYDRAULICS SAVE " + _net.HydFname); + break; + } + + if (_net.ExtraIter == -1) + _writer.WriteLine("UNBALANCED STOP"); + + if (_net.ExtraIter >= 0) + _writer.WriteLine("UNBALANCED CONTINUE " + _net.ExtraIter); + + switch (_net.QualFlag) + { + case QualType.Chem: + _writer.WriteLine("QUALITY {0} {1}", _net.ChemName, _net.ChemUnits); + break; + case QualType.Trace: + _writer.WriteLine("QUALITY TRACE " + _net.TraceNode); + break; + case QualType.Age: + _writer.WriteLine("QUALITY AGE"); + break; + case QualType.None: + _writer.WriteLine("QUALITY NONE"); + break; + } + + _writer.WriteLine("DEMAND MULTIPLIER {0}", _net.DMult); + _writer.WriteLine("EMITTER EXPONENT {0}", 1.0 / _net.QExp); + _writer.WriteLine("VISCOSITY {0}", _net.Viscos / Constants.VISCOS); + _writer.WriteLine("DIFFUSIVITY {0}", _net.Diffus / Constants.DIFFUS); + _writer.WriteLine("SPECIFIC GRAVITY {0}", _net.SpGrav); + _writer.WriteLine("TRIALS {0}", _net.MaxIter); + _writer.WriteLine("ACCURACY {0}", _net.HAcc); + _writer.WriteLine("TOLERANCE {0}", fMap.RevertUnit(FieldType.QUALITY, _net.Ctol)); + _writer.WriteLine("CHECKFREQ {0}", _net.CheckFreq); + _writer.WriteLine("MAXCHECK {0}", _net.MaxCheck); + _writer.WriteLine("DAMPLIMIT {0}", _net.DampLimit); + _writer.WriteLine(); + } + + private void ComposeExtraOptions() + { + var extraOptions = _net.ExtraOptions; + + if (extraOptions.Count == 0) + return; + + foreach (var pair in extraOptions) + { + _writer.WriteLine(pair.Key + " " + pair.Value); + } + + _writer.WriteLine(); + } + + private void ComposeReport() + { + + _writer.WriteLine(SectType.REPORT.ParseStr()); + + FieldsMap fMap = _net.FieldsMap; + _writer.WriteLine("PAGESIZE {0}", _net.PageSize); + _writer.WriteLine("STATUS " + _net.StatFlag); + _writer.WriteLine("SUMMARY " + (_net.SummaryFlag ? Keywords.w_YES : Keywords.w_NO)); + _writer.WriteLine("ENERGY " + (_net.EnergyFlag ? Keywords.w_YES : Keywords.w_NO)); + + switch (_net.NodeFlag) + { + case ReportFlag.FALSE: + _writer.WriteLine("NODES NONE"); + break; + case ReportFlag.TRUE: + _writer.WriteLine("NODES ALL"); + break; + case ReportFlag.SOME: + { + int j = 0; + foreach (Node node in _net.Nodes) + { + if (node.RptFlag) + { + // if (j % 5 == 0) buffer.WriteLine("NODES "); // BUG: Baseform bug + + if (j % 5 == 0) + { + _writer.WriteLine(); + _writer.Write("NODES "); + } + + _writer.Write("{0} ", node.Name); + j++; + } + } + break; + } + } + + switch (_net.LinkFlag) + { + case ReportFlag.FALSE: + _writer.WriteLine("LINKS NONE"); + break; + case ReportFlag.TRUE: + _writer.WriteLine("LINKS ALL"); + break; + case ReportFlag.SOME: + { + int j = 0; + foreach (Link link in _net.Links) + { + if (link.RptFlag) + { + // if (j % 5 == 0) buffer.write("LINKS \n"); // BUG: Baseform bug + if (j % 5 == 0) + { + _writer.WriteLine(); + _writer.Write("LINKS "); + } + + _writer.Write("{0} ", link.Name); + j++; + } + } + break; + } + } + + for (FieldType i = 0; i < FieldType.FRICTION; i++) + { + Field f = fMap.GetField(i); + + if (!f.Enabled) + { + _writer.WriteLine("{0,-19} NO", f.Name); + continue; + } + + _writer.WriteLine("{0,-19} PRECISION {1}", f.Name, f.Precision); + + if (f.GetRptLim(RangeType.LOW) < Constants.BIG) + _writer.WriteLine("{0,-19} BELOW {1:0.######}", f.Name, f.GetRptLim(RangeType.LOW)); + + if (f.GetRptLim(RangeType.HI) > -Constants.BIG) + _writer.WriteLine("{0,-19} ABOVE {1:0.######}", f.Name, f.GetRptLim(RangeType.HI)); + } + + _writer.WriteLine(); + } + + private void ComposeCoordinates() + { + _writer.WriteLine(SectType.COORDINATES.ParseStr()); + _writer.WriteLine(COORDINATES_SUBTITLE); + + foreach (Node node in _net.Nodes) + { + if (!node.Coordinate.IsInvalid) + { + _writer.WriteLine( + " {0,-16}\t{1,-12}\t{2,-12}", + node.Name, + node.Coordinate.X, + node.Coordinate.Y); + } + } + _writer.WriteLine(); + } + + + private void ComposeLabels() + { + _writer.WriteLine(SectType.LABELS.ParseStr()); + _writer.WriteLine(";X-Coord\tY-Coord\tLabel & Anchor Node"); + + foreach (Label label in _net.Labels) + { + _writer.WriteLine( + " {0,-16}\t{1,-16}\t\"{2}\" {3,-16}", + label.Position.X, + label.Position.Y, + label.Text, + // label.AnchorNodeId // TODO: add AnchorNodeId property to label + "" + ); + + } + _writer.WriteLine(); + } + + private void ComposeVertices() + { + _writer.WriteLine(SectType.VERTICES.ParseStr()); + _writer.WriteLine(";Link\tX-Coord\tY-Coord"); + + foreach (Link link in _net.Links) + { + if (link.Vertices.Count == 0) + continue; + + foreach (EnPoint p in link.Vertices) + { + _writer.WriteLine(" {0,-16}\t{1,-16}\t{2,-16}", link.Name, p.X, p.Y); + } + } + + _writer.WriteLine(); + } + + private void ComposeRules() + { + _writer.WriteLine(SectType.RULES.ParseStr()); + _writer.WriteLine(); + + foreach (Rule r in _net.Rules) + { + _writer.WriteLine("RULE " + r.Name); + foreach (string s in r.Code) + _writer.WriteLine(s); + _writer.WriteLine(); + } + _writer.WriteLine(); + } + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/io/output/OutputComposer.cs b/IStation.Epanet/network/io/output/OutputComposer.cs new file mode 100644 index 0000000..d79edba --- /dev/null +++ b/IStation.Epanet/network/io/output/OutputComposer.cs @@ -0,0 +1,34 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.IO.Output +{ + + ///<summary>Abstract class with factory for INP and XLSX composers.</summary> + public abstract class OutputComposer + { + + ///<summary>Composer creation method.</summary> + /// <param name="net">Hydraulic network reference.</param> + /// <param name="type">Composer type.</param> + /// <returns>Composer reference.</returns> + public static OutputComposer Create(Network net, FileType type) + { + switch (type) + { + case FileType.INP_FILE: return new InpComposer(net); + } + return null; + } + + protected readonly Network _net; + + /// <param name="net">Hydraulic network reference.</param> + protected OutputComposer(Network net) { _net = net; } + + ///<summary>Abstract method to implement the output file creation.</summary> + /// <param name="fileName">File name reference.</param> + public abstract void Compose(string fileName); + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Control.cs b/IStation.Epanet/network/structures/Control.cs new file mode 100644 index 0000000..be80cfa --- /dev/null +++ b/IStation.Epanet/network/structures/Control.cs @@ -0,0 +1,63 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Control statement</summary> + public class Control + { + + public void ConvertUnits(Network net) + { + FieldsMap fMap = net.FieldsMap; + + if (Link == null) + return; + + if (Node != null) + { + Grade = Node.NodeType == NodeType.Junction + ? Node.Elevation + Grade / fMap.GetUnits(FieldType.PRESSURE) + : Node.Elevation + Grade / fMap.GetUnits(FieldType.ELEV); + } + + if (!double.IsNaN(Setting) && Link.LinkType == LinkType.VALVE) + { + switch (((Valve)Link).ValveType) + { + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + Setting /= fMap.GetUnits(FieldType.PRESSURE); + break; + case ValveType.FCV: + Setting /= fMap.GetUnits(FieldType.FLOW); + break; + } + } + + } + + ///<summary>Control grade.</summary> + public double Grade { get; set; } + + ///<summary>Assigned link reference.</summary> + public Link Link { get; set; } + + ///<summary>Assigned node reference.</summary> + public Node Node { get; set; } + + ///<summary>New link setting.</summary> + public double Setting { get; set; } + + ///<summary>New link status.</summary> + public StatusType Status { get; set; } + + ///<summary>Control time (in seconds).</summary> + public TimeSpan Time { get; set; } + + ///<summary>Control type</summary> + public ControlType Type { get; set; } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Curve.cs b/IStation.Epanet/network/structures/Curve.cs new file mode 100644 index 0000000..c02002e --- /dev/null +++ b/IStation.Epanet/network/structures/Curve.cs @@ -0,0 +1,85 @@ +using IStation.Epanet.Enums; +using System.Collections; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>2D graph used to map volume, pump, efficiency and head loss curves.</summary> + public sealed class Curve : Element, IEnumerable<EnPoint> + { + private readonly List<EnPoint> _points = new List<EnPoint>(); + + public Curve(string name) : base(name) { } + + public override ElementType ElementType => ElementType.CURVE; + + /// <summary>Computes intercept and slope of head v. flow curve at current flow.</summary> + /// <param name="fMap"></param> + /// <param name="q">Flow value.</param> + /// <param name="h0">Head at zero flow (y-intercept).</param> + /// <param name="r">dHead/dFlow (slope).</param> + public void GetCoeff(FieldsMap fMap, double q, out double h0, out double r) + { + q *= fMap.GetUnits(FieldType.FLOW); + + int npts = _points.Count; + + var k2 = 0; + while (k2 < npts && _points[k2].X < q) k2++; + + if (k2 == 0) k2++; + else if (k2 == npts) k2--; + + int k1 = k2 - 1; + + r = (_points[k2].Y - _points[k1].Y) / (_points[k2].X - _points[k1].X); + h0 = _points[k1].Y - r * _points[k1].X; + + h0 /= fMap.GetUnits(FieldType.HEAD); + r *= fMap.GetUnits(FieldType.FLOW) / fMap.GetUnits(FieldType.HEAD); + + } + + ///<summary>Curve type.</summary> + public CurveType Type { get; set; } //TODO: parse it correctly + + /// <summary>Compute the linear interpolation of a 2d cartesian graph.</summary> + /// <param name="x">The abscissa value.</param> + /// <returns>The interpolated value.</returns> + public double Interpolate(double x) + { + var p = _points; + int m = _points.Count - 1; + + if (x <= p[0].X) return p[0].Y; + + for (int i = 1; i <= m; i++) + { + if (p[i].X >= x) + { + double dx = p[i].X - p[i - 1].X; + double dy = p[i].Y - p[i - 1].Y; + if (Math.Abs(dx) < Constants.TINY) return p[i].Y; + + return p[i].Y - (p[i].X - x) * dy / dx; + } + } + + return p[m].Y; + } + + #region partial implementation of IList<EnPoint> + + public void Add(double x, double y) { _points.Add(new EnPoint(x, y)); } + + IEnumerator IEnumerable.GetEnumerator() { return _points.GetEnumerator(); } + public IEnumerator<EnPoint> GetEnumerator() { return _points.GetEnumerator(); } + + public int Count => _points.Count; + public EnPoint this[int index] => _points[index]; + + #endregion + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Demand.cs b/IStation.Epanet/network/structures/Demand.cs new file mode 100644 index 0000000..2fbcee8 --- /dev/null +++ b/IStation.Epanet/network/structures/Demand.cs @@ -0,0 +1,27 @@ +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Node demand category.</summary> + public class Demand + { + public Demand(double @base, Pattern pattern) + { + Base = @base; + Pattern = pattern; + } + + ///<summary>Baseline demand (Feet^3/t)</summary> + public double Base { get; set; } + + ///<summary>Pattern reference.</summary> + public Pattern Pattern { get; set; } + +#if NUCONVERT + public double GetBaseNu(FlowUnitsType units) { return NUConvert.RevertFlow(units, Base); } + + public void SetBaseNu(FlowUnitsType units, double value) { Base = NUConvert.ConvertFlow(units, value); } +#endif + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Element.cs b/IStation.Epanet/network/structures/Element.cs new file mode 100644 index 0000000..9ba59c3 --- /dev/null +++ b/IStation.Epanet/network/structures/Element.cs @@ -0,0 +1,62 @@ +锘縩amespace IStation.Epanet.Network.Structures +{ + + public enum ElementType { NODE, LINK, PATTERN, CURVE, CONTROL, RULE } + + /// <summary>Base class for all IStation.Epanet elements - Links, Nodes etc.</summary> + public abstract class Element : IComparable<Element>, IEquatable<Element> + { + private readonly string _name; + protected Element(string name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (name.Length == 0 || name.Length > Constants.MAXID) + throw new ArgumentException(nameof(name)); + + _name = name; + } + public abstract ElementType ElementType { get; } + + public string Name => _name; + + public string Tag { get; set; } = string.Empty; + + /// <summary>Element comment (parsed from INP or excel file)</summary> + public string Comment { get; set; } + + #region Overrides of Object + + public override string ToString() + { + return string.Format("{0}{{{1}}}", GetType().Name, _name); + } + + #endregion + + #region Implementation of IComparable<Element>, IEquatable<Element> + + public int CompareTo(Element other) + { + return other == null ? 1 : string.Compare(Name, other.Name, StringComparison.OrdinalIgnoreCase); + } + + public bool Equals(Element other) + { + return other != null && string.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() + { + return string.IsNullOrEmpty(_name) + ? 0 + : _name.GetHashCode(); + } + + #endregion + + + } + +} diff --git a/IStation.Epanet/network/structures/EnPoint.cs b/IStation.Epanet/network/structures/EnPoint.cs new file mode 100644 index 0000000..15c56be --- /dev/null +++ b/IStation.Epanet/network/structures/EnPoint.cs @@ -0,0 +1,66 @@ +using IStation.Epanet.Util; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Simple 2d point.</summary> + + public struct EnPoint : IComparable<EnPoint>, IEquatable<EnPoint> + { + public static readonly EnPoint Invalid = new EnPoint(double.NaN, double.NaN); + private readonly double _x; + private readonly double _y; + + public EnPoint(double x, double y) + { + _x = x; + _y = y; + } + + public bool IsInvalid => double.IsNaN(_x) || double.IsNaN(_y); + + ///<summary>Absciss coordinate.</summary> + public double X => _x; + + ///<summary>Ordinate coordinate.</summary> + public double Y => _y; + + public double DistanceTo(EnPoint other) + { + double dx = _x - other._x; + double dy = _y - other._y; + + return Math.Sqrt(dx * dx + dy * dy); + } + + public int CompareTo(EnPoint other) + { + var cmp = _x.CompareTo(other._x); + return cmp == 0 ? _y.CompareTo(other._y) : cmp; + } + + public bool Equals(EnPoint other) + { + bool ex = _x.EqualsTo(other._x) || double.IsNaN(_x) && double.IsNaN(other._x); + bool ey = _y.EqualsTo(other._y) || double.IsNaN(_y) && double.IsNaN(other._y); + + return ex && ey; + } + + public override bool Equals(object obj) + { + return obj is EnPoint point && Equals(point); + } + + public override int GetHashCode() + { + return _x.GetHashCode() ^ _y.GetHashCode(); + } + + public override string ToString() + { + return string.Format(nameof(EnPoint) + "{{x={0}, y={1}}}", _x, _y); + } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Field.cs b/IStation.Epanet/network/structures/Field.cs new file mode 100644 index 0000000..9d5ccac --- /dev/null +++ b/IStation.Epanet/network/structures/Field.cs @@ -0,0 +1,41 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Report field properties.</summary> + public class Field + { + ///<summary>Lower/upper report limits.</summary> + private readonly double[] _rptLim = { 0d, 0d, 0d }; + + ///<summary>Init field name, precision, report limit and state.</summary> + /// <param name="name">Field name.</param> + public Field(FieldType type) + { + Type = type; + Enabled = false; + Precision = 2; + SetRptLim(RangeType.LOW, Constants.BIG * Constants.BIG); + SetRptLim(RangeType.HI, -Constants.BIG * Constants.BIG); + } + + + public FieldType Type { get; } + ///<summary>Name of reported variable.</summary> + public string Name => Type.ParseStr(); + + ///<summary>Number of decimal places.</summary> + public int Precision { get; set; } + + ///<summary>Units of reported variable.</summary> + public string Units { get; set; } + + ///<summary>Enabled if in table.</summary> + public bool Enabled { get; set; } + + public void SetRptLim(RangeType type, double value) { _rptLim[(int)type] = value; } + public double GetRptLim(RangeType type) { return _rptLim[(int)type]; } + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Junction.cs b/IStation.Epanet/network/structures/Junction.cs new file mode 100644 index 0000000..78ba4d7 --- /dev/null +++ b/IStation.Epanet/network/structures/Junction.cs @@ -0,0 +1,40 @@ +锘縰sing IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + public class Junction : Node + { + public Junction(string name) : base(name) { } + + public override void ConvertUnits(Network nw) + { + FieldsMap fMap = nw.FieldsMap; + + // ... convert elevation & initial quality units + Elevation /= fMap.GetUnits(FieldType.ELEV); + C0 /= fMap.GetUnits(FieldType.QUALITY); + + // ... if no demand categories exist, add primary demand to list + if (Demands.Count == 0) { Demands.Add(PrimaryDemand); } + + // ... convert flow units for base demand in each demand category + double qcf = fMap.GetUnits(FieldType.DEMAND); + + foreach (Demand d in Demands) + { + d.Base /= qcf; + } + + // ... convert emitter flow units + if (Ke > 0.0) + { + double ucf = Math.Pow(fMap.GetUnits(FieldType.FLOW), nw.QExp) / fMap.GetUnits(FieldType.PRESSURE); + + Ke = ucf / Math.Pow(Ke, nw.QExp); + } + + } + + } +} diff --git a/IStation.Epanet/network/structures/Label.cs b/IStation.Epanet/network/structures/Label.cs new file mode 100644 index 0000000..6c0bcdb --- /dev/null +++ b/IStation.Epanet/network/structures/Label.cs @@ -0,0 +1,34 @@ +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Text label</summary> + public class Label + { + public enum MeterTypes + { + None, + Node, + Link + } + + public Label(string text) + { + Text = text; + Position = EnPoint.Invalid; + } + + ///<summary>Label position.</summary> + public EnPoint Position { get; set; } + + ///<summary>Label text.</summary> + public string Text { get; set; } + public Node Anchor { get; set; } + public string FontName { get; set; } + public int FontSize { get; set; } + public bool FontBold { get; set; } + public bool FontItalic { get; set; } + public MeterTypes MeterType { get; set; } + public string MeterId { get; set; } + } + +} diff --git a/IStation.Epanet/network/structures/Link.cs b/IStation.Epanet/network/structures/Link.cs new file mode 100644 index 0000000..fc5225e --- /dev/null +++ b/IStation.Epanet/network/structures/Link.cs @@ -0,0 +1,128 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Hydraulic link structure (pipe)</summary> + public abstract class Link : Element + { + + ///<summary>Init links flow resistance values.</summary> + public abstract void InitResistance(HeadLossFormulaType formflag, double hexp); + + public abstract void ConvertUnits(Network nw); + + protected Link(string name) : base(name) + { + Vertices = new List<EnPoint>(); + Status = StatusType.XHEAD; + } + + public override ElementType ElementType => ElementType.LINK; + + ///<summary>Link subtype.</summary> + public abstract LinkType LinkType { get; } + + ///<summary>Initial species concentrations.</summary> + public double C0 { get; set; } + + ///<summary>Link diameter (feet).</summary> + public double Diameter { get; set; } + + ///<summary>First node.</summary> + public Node FirstNode { get; set; } + + ///<summary>Flow resistance.</summary> + public double FlowResistance { get; protected set; } + + ///<summary>Bulk react. coeff.</summary> + public double Kb { get; set; } + + ///<summary>Minor loss coeff.</summary> + public double Km { get; set; } + + ///<summary>Wall react. coeff.</summary> + public double Kw { get; set; } + + ///<summary>Link length (feet).</summary> + public double Lenght { get; set; } + + ///<summary>Kinetic parameter values.</summary> + public double[] Param { get; set; } + + ///<summary>Roughness factor.</summary> + public double Kc { get; set; } + + ///<summary>Second node.</summary> + public Node SecondNode { get; set; } + + ///<summary>Link status.</summary> + public StatusType Status { get; set; } + + ///<summary>List of points for link path rendering.</summary> + public List<EnPoint> Vertices { get; } + + ///<summary>Link report flag.</summary> + public bool RptFlag { get; set; } + + public void SetDiameterAndUpdate(double diameter, Network net) + { + double realkm = Km * Math.Pow(Diameter, 4.0) / 0.02517; + Diameter = diameter; + Km = 0.02517 * realkm / Math.Pow(diameter, 4); + InitResistance(net.FormFlag, net.HExp); + } + + /// <summary>Returns string with length of pipe with given index.</summary> + + public double GetPipeLength(UnitsType type) + { + double length = 0; + + EnPoint pt1 = FirstNode.Coordinate; + + foreach (var pt in Vertices) + { + length += pt1.DistanceTo(pt); + pt1 = pt; + } + + length += pt1.DistanceTo(SecondNode.Coordinate); + + // length = MapDimensions.LengthUCF * length; + if (type == UnitsType.SI) + length *= 1 / Constants.MperFT; + + return Lenght = length; + } + +#if NUCONVERT + public double GetNuDiameter(UnitsType utype) + { + return NUConvert.RevertDiameter(utype, Diameter); + } + + public double GetNuLength(UnitsType utype) + { + return NUConvert.RevertDistance(utype, Lenght); + } + + public void SetNuDiameter(UnitsType utype, double value) + { + Diameter = NUConvert.ConvertDistance(utype, value); + } + + public void SetNuLenght(UnitsType utype, double value) + { + Lenght = NUConvert.ConvertDistance(utype, value); + } + + public virtual double GetNuRoughness(FlowUnitsType fType, PressUnitsType pType, double spGrav) + { + return Kc; + } + +#endif + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/NUConvert.cs b/IStation.Epanet/network/structures/NUConvert.cs new file mode 100644 index 0000000..6a5758d --- /dev/null +++ b/IStation.Epanet/network/structures/NUConvert.cs @@ -0,0 +1,167 @@ +namespace IStation.Epanet.Network.Structures +{ + +#if NUCONVERT + public static class NUConvert + { + public static double ConvertArea(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value / (Constants.MperFT * Constants.MperFT); + + return value; + } + + public static double ConvertDistance(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * (1 / Constants.MperFT); + + return value; + } + + public static double ConvertFlow(FlowUnitsType flow, double value) + { + switch (flow) + { + case FlowUnitsType.CFS: + return value * (1 / Constants.LPSperCFS); + case FlowUnitsType.GPM: + return value * (1 / Constants.GPMperCFS); + case FlowUnitsType.MGD: + return value * (1 / Constants.MGDperCFS); + case FlowUnitsType.IMGD: + return value * (1 / Constants.IMGDperCFS); + case FlowUnitsType.AFD: + return value * (1 / Constants.AFDperCFS); + case FlowUnitsType.LPS: + return value * (1 / Constants.LPSperCFS); + case FlowUnitsType.LPM: + return value * (1 / Constants.LPMperCFS); + case FlowUnitsType.MLD: + return value * (1 / Constants.MLDperCFS); + case FlowUnitsType.CMH: + return value * (1 / Constants.CMHperCFS); + case FlowUnitsType.CMD: + return value * (1 / Constants.CMHperCFS); + } + return value; + } + + public static double ConvertPower(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * (1 / Constants.KWperHP); + + return value; + } + + public static double ConvertPressure(PressUnitsType type, double spGrav, double value) + { + switch (type) + { + case PressUnitsType.PSI: + return value; + case PressUnitsType.KPA: + return value / (Constants.KPAperPSI * Constants.PSIperFT * spGrav); + case PressUnitsType.METERS: + return value / (Constants.MperFT * spGrav); + } + return value; + } + + public static double ConvertVolume(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value / (Constants.M3perFT3); + + return value; + } + + public static double RevertArea(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * (Constants.MperFT * Constants.MperFT); + + return value; + } + + public static double RevertDistance(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * Constants.MperFT; + + return value; + } + + public static double RevertDiameter(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * Constants.MMperFT; + return value * Constants.INperFT; + } + + public static double RevertFlow(FlowUnitsType flow, double value) + { + switch (flow) + { + case FlowUnitsType.CFS: + return value * Constants.LPSperCFS; + case FlowUnitsType.GPM: + return value * Constants.GPMperCFS; + case FlowUnitsType.MGD: + return value * Constants.MGDperCFS; + case FlowUnitsType.IMGD: + return value * Constants.IMGDperCFS; + case FlowUnitsType.AFD: + return value * Constants.AFDperCFS; + case FlowUnitsType.LPS: + return value * Constants.LPSperCFS; + case FlowUnitsType.LPM: + return value * Constants.LPMperCFS; + case FlowUnitsType.MLD: + return value * Constants.MLDperCFS; + case FlowUnitsType.CMH: + return value * Constants.CMHperCFS; + case FlowUnitsType.CMD: + return value * Constants.CMHperCFS; + } + return value; + } + + public static double RevertPower(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * Constants.KWperHP; + + return value; + } + + public static double RevertPressure(PressUnitsType type, double spGrav, double value) + { + switch (type) + { + case PressUnitsType.PSI: + return value; + case PressUnitsType.KPA: + return value * (Constants.KPAperPSI * Constants.PSIperFT * spGrav); + case PressUnitsType.METERS: + return value * (Constants.MperFT * spGrav); + } + return value; + } + + public static double RevertVolume(UnitsType type, double value) + { + if (type == UnitsType.SI) + return value * (Constants.M3perFT3); + + return value; + } + + + } + +#endif + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Node.cs b/IStation.Epanet/network/structures/Node.cs new file mode 100644 index 0000000..822b35c --- /dev/null +++ b/IStation.Epanet/network/structures/Node.cs @@ -0,0 +1,67 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Hydraulic node structure (junction)</summary> + + public abstract class Node : Element + { + protected Node(string name) : base(name) + { + Coordinate = EnPoint.Invalid; + } + + #region Overrides of Element + + public override ElementType ElementType => ElementType.NODE; + + #endregion + + public abstract void ConvertUnits(Network nw); + + public Demand PrimaryDemand { get; } = new Demand(0, null); + + public virtual NodeType NodeType => NodeType.Junction; + + ///<summary>Node position.</summary> + public EnPoint Coordinate { get; set; } + + ///<summary>Node elevation(foot).</summary> + public double Elevation { get; set; } + + ///<summary>Node demand list.</summary> + public List<Demand> Demands { get; } = new List<Demand>(1); + + ///<summary>Water quality source.</summary> + public QualSource QualSource { get; set; } + + ///<summary>Initial species concentrations.</summary> + public double C0 { get; set; } + + ///<summary>Emitter coefficient.</summary> + public double Ke { get; set; } + + ///<summary>Node reporting flag.</summary> + public bool RptFlag { get; set; } + + + + +#if NUCONVERT + + public double GetNuElevation(UnitsType units) + { + return NUConvert.RevertDistance(units, Elevation); + } + + public void SetNuElevation(UnitsType units, double elev) + { + Elevation = NUConvert.ConvertDistance(units, elev); + } + +#endif + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Pattern.cs b/IStation.Epanet/network/structures/Pattern.cs new file mode 100644 index 0000000..55130c0 --- /dev/null +++ b/IStation.Epanet/network/structures/Pattern.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Diagnostics; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Temporal pattern.</summary> + [DebuggerDisplay("{Name}:{_factors}")] + public class Pattern : Element, IEnumerable<double> + { + ///<summary>Pattern factors list.</summary> + private readonly List<double> _factors = new List<double>(); + + public Pattern(string name) : base(name) { } + + #region Overrides of Element + + public override ElementType ElementType => ElementType.PATTERN; + + #endregion + + // public IList<double> Factors => _factors; + + #region partial implementation of IList<double> + + public void Add(double factor) => _factors.Add(factor); + + public int Count => _factors.Count; + + public double this[int index] => _factors[index]; + + public IEnumerator<double> GetEnumerator() => _factors.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _factors.GetEnumerator(); + + #endregion + } + +} diff --git a/IStation.Epanet/network/structures/Pipe.cs b/IStation.Epanet/network/structures/Pipe.cs new file mode 100644 index 0000000..191e926 --- /dev/null +++ b/IStation.Epanet/network/structures/Pipe.cs @@ -0,0 +1,52 @@ +锘縰sing IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + public class Pipe : Link + { + public Pipe(string name) : base(name) { } + + public override LinkType LinkType => LinkType.Pipe; + + public bool HasCheckValve { get; set; } + + public override void InitResistance(HeadLossFormulaType formflag, double hexp) + { + FlowResistance = Constants.CSMALL; + double d = Diameter; + + switch (formflag) + { + case HeadLossFormulaType.HW: + FlowResistance = 4.727 * Lenght / Math.Pow(Kc, hexp) / Math.Pow(d, 4.871); + break; + case HeadLossFormulaType.DW: + FlowResistance = Lenght / 2.0 / 32.2 / d / Math.Pow(Math.PI * Math.Pow(d, 2) / 4.0, 2); + break; + case HeadLossFormulaType.CM: + FlowResistance = Math.Pow(4.0 * Kc / (1.49 * Math.PI * d * d), 2) + * Math.Pow(d / 4.0, -1.333) * Lenght; + break; + } + } + + public override void ConvertUnits(Network nw) + { + FieldsMap fMap = nw.FieldsMap; + + if (nw.FormFlag == HeadLossFormulaType.DW) + Kc /= 1000.0 * fMap.GetUnits(FieldType.ELEV); + + Diameter /= fMap.GetUnits(FieldType.DIAM); + Lenght /= fMap.GetUnits(FieldType.LENGTH); + + Km = 0.02517 * Km / Math.Pow(Diameter, 2) / Math.Pow(Diameter, 2); + + Kb /= Constants.SECperDAY; + Kw /= Constants.SECperDAY; + } + + + } +} diff --git a/IStation.Epanet/network/structures/Pump.cs b/IStation.Epanet/network/structures/Pump.cs new file mode 100644 index 0000000..8fa5afd --- /dev/null +++ b/IStation.Epanet/network/structures/Pump.cs @@ -0,0 +1,155 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Hydraulic pump structure.</summary> + public class Pump : Link + { + public Pump(string name) : base(name) + { + // Link attributes + Kc = 1.0; + Status = StatusType.OPEN; + + // Pump attributes + Ptype = PumpType.NOCURVE; + } + + public override LinkType LinkType => LinkType.Pump; + + public override void InitResistance(HeadLossFormulaType formflag, double hexp) + { + FlowResistance = Constants.CBIG; + } + + public override void ConvertUnits(Network nw) + { + FieldsMap fMap = nw.FieldsMap; + + if (Ptype == PumpType.CONST_HP) + { + if (nw.UnitsFlag == UnitsType.SI) + FlowCoefficient /= fMap.GetUnits(FieldType.POWER); + } + else + { + if (Ptype == PumpType.POWER_FUNC) + { + H0 /= fMap.GetUnits(FieldType.HEAD); + + FlowCoefficient *= + Math.Pow(fMap.GetUnits(FieldType.FLOW), N) / + fMap.GetUnits(FieldType.HEAD); + } + + Q0 /= fMap.GetUnits(FieldType.FLOW); + Qmax /= fMap.GetUnits(FieldType.FLOW); + Hmax /= fMap.GetUnits(FieldType.HEAD); + } + + } + + + ///<summary>Unit energy cost.</summary> + public double ECost { get; set; } + + ///<summary>Effic. v. flow curve reference.</summary> + public Curve ECurve { get; set; } + + ///<summary>Energy usage statistics.</summary> + public double[] Energy { get; } = { 0, 0, 0, 0, 0, 0 }; + + ///<summary>Energy cost pattern.</summary> + public Pattern EPat { get; set; } + + ///<summary>Flow coefficient.</summary> + public double FlowCoefficient { get; set; } + + ///<summary>Shutoff head (feet)</summary> + public double H0 { get; set; } + + ///<summary>Head v. flow curve reference.</summary> + public Curve HCurve { get; set; } + + ///<summary>Maximum head (feet)</summary> + public double Hmax { get; set; } + + ///<summary>Flow exponent.</summary> + public double N { get; set; } + + ///<summary>Pump curve type.</summary> + public PumpType Ptype { get; set; } + + ///<summary>Initial flow (feet^3/s).</summary> + public double Q0 { get; set; } + + ///<summary>Maximum flow (feet^3/s).</summary> + public double Qmax { get; set; } + + ///<summary>Utilization pattern reference.</summary> + public Pattern UPat { get; set; } + +#if NUCONVERT + + public double GetNuFlowCoefficient(UnitsType utype) + { + return NUConvert.RevertPower(utype, FlowCoefficient); + } + + + public double GetNuInitialFlow(FlowUnitsType utype) + { + return NUConvert.RevertFlow(utype, Q0); + } + + public double GetNuMaxFlow(FlowUnitsType utype) + { + return NUConvert.RevertFlow(utype, Qmax); + } + + public double GetNuMaxHead(UnitsType utype) + { + return NUConvert.RevertDistance(utype, Hmax); + } + + public double GetNuShutoffHead(UnitsType utype) + { + return NUConvert.RevertDistance(utype, Hmax); + } + + + public void SetNuFlowCoefficient(UnitsType utype, double value) + { + FlowCoefficient = NUConvert.ConvertPower(utype, value); + } + + public void SetNuInitialFlow(FlowUnitsType utype, double value) + { + Q0 = NUConvert.ConvertFlow(utype, value); + } + + public void SetNuMaxFlow(FlowUnitsType utype, double value) + { + Qmax = NUConvert.ConvertFlow(utype, value); + } + + public void SetNuMaxHead(UnitsType utype, double value) + { + Hmax = NUConvert.ConvertDistance(utype, value); + } + + public void SetNuShutoffHead(UnitsType utype, double value) + { + H0 = NUConvert.ConvertDistance(utype, value); + } + + +#endif + + + + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/QualSource.cs b/IStation.Epanet/network/structures/QualSource.cs new file mode 100644 index 0000000..f140718 --- /dev/null +++ b/IStation.Epanet/network/structures/QualSource.cs @@ -0,0 +1,27 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Water quality object, source quality.</summary> + public class QualSource + { + ///<summary>Base concentration.</summary> + public double C0 { get; set; } + + ///<summary>Time pattern reference.</summary> + public Pattern Pattern { get; set; } + + ///<summary>Source type.</summary> + public SourceType Type { get; set; } + + public QualSource(SourceType type, double c0, Pattern pattern) + { + C0 = c0; + Pattern = pattern; + Type = type; + } + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Reservoir.cs b/IStation.Epanet/network/structures/Reservoir.cs new file mode 100644 index 0000000..821d079 --- /dev/null +++ b/IStation.Epanet/network/structures/Reservoir.cs @@ -0,0 +1,32 @@ +锘縰sing IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + public class Reservoir : Node + { + public Reservoir(string name) : base(name) { } + + public override void ConvertUnits(Network nw) + { + // FIXME:Tanks and reservoirs here? + + FieldsMap fMap = nw.FieldsMap; + + // ... convert from user to internal units + double ucfLength = fMap.GetUnits(FieldType.ELEV); + Elevation /= ucfLength; + Head = Elevation + Head / ucfLength; + } + + public override NodeType NodeType => NodeType.Reservoir; + + + ///<summary>鍩哄噯</summary> + public double Head { get; set; } + + ///<summary>Fixed grade time pattern.</summary> + public Pattern Pattern { get; set; } + } + +} diff --git a/IStation.Epanet/network/structures/Rule.cs b/IStation.Epanet/network/structures/Rule.cs new file mode 100644 index 0000000..50a521f --- /dev/null +++ b/IStation.Epanet/network/structures/Rule.cs @@ -0,0 +1,18 @@ +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Rule source code class.</summary> + public class Rule : Element + { + public Rule(string name) : base(name) { } + + #region Overrides of Element + + public override ElementType ElementType => ElementType.RULE; + + #endregion + + public List<string> Code { get; } = new List<string>(); + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Tank.cs b/IStation.Epanet/network/structures/Tank.cs new file mode 100644 index 0000000..54cb11a --- /dev/null +++ b/IStation.Epanet/network/structures/Tank.cs @@ -0,0 +1,148 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Hydraulic tank structure.</summary> + public class Tank : Node + { + public Tank(string name) : base(name) { } + + public override void ConvertUnits(Network nw) + { + // FIXME:Tanks and reservoirs here? + + FieldsMap fMap = nw.FieldsMap; + + // ... convert from user to internal units + double ucfLength = fMap.GetUnits(FieldType.ELEV); + Elevation /= ucfLength; + H0 = Elevation + H0 / ucfLength; + Hmin = Elevation + Hmin / ucfLength; + Hmax = Elevation + Hmax / ucfLength; + Area = Math.PI * Math.Pow(Area / ucfLength, 2) / 4.0; + V0 /= fMap.GetUnits(FieldType.VOLUME); + Vmin /= fMap.GetUnits(FieldType.VOLUME); + Vmax /= fMap.GetUnits(FieldType.VOLUME); + Kb /= Constants.SECperDAY; + // tk.Volume = tk.V0; + C = C0; + V1Max *= Vmax; + } + + public override NodeType NodeType => NodeType.Tank; + + ///<summary>Tank area (feet^2).</summary> + public double Area { get; set; } + + ///<summary>Species concentration.</summary> + public double C { get; set; } + + ///<summary>Initial water elev.</summary> + public double H0 { get; set; } + + ///<summary>Maximum water elev (feet).</summary> + public double Hmax { get; set; } + + ///<summary>Minimum water elev (feet).</summary> + public double Hmin { get; set; } + + ///<summary>Reaction coeff. (1/days).</summary> + public double Kb { get; set; } + + ///<summary>Type of mixing model</summary> + public MixType MixModel { get; set; } + + ///<summary>Fixed grade time pattern.</summary> + public Pattern Pattern { get; set; } + + ///<summary>Initial volume (feet^3).</summary> + public double V0 { get; set; } + + ///<summary>Mixing compartment size</summary> + public double V1Max { get; set; } + + ///<summary>Fixed grade time pattern</summary> + public Curve Vcurve { get; set; } + + ///<summary>Maximum volume (feet^3).</summary> + public double Vmax { get; set; } + + ///<summary>Minimum volume (feet^3).</summary> + public double Vmin { get; set; } + +#if NUCONVERT + + public double GetNuArea(UnitsType type) { return NUConvert.RevertArea(type, Area); } + + public double GetNuInitHead(UnitsType type) { return NUConvert.RevertDistance(type, H0); } + + public double GetNuInitVolume(UnitsType type) { return NUConvert.RevertVolume(type, V0); } + + public double GetNuMaximumHead(UnitsType type) + { + return NUConvert.RevertDistance(type, Hmax); + } + + public double GetNuMaxVolume(UnitsType type) { return NUConvert.RevertVolume(type, Vmax); } + + public double GetNuMinimumHead(UnitsType type) + { + return NUConvert.RevertDistance(type, Hmin); + } + + public double GetNuMinVolume(UnitsType type) { return NUConvert.RevertVolume(type, Vmin); } + + public void SetNuMinVolume(UnitsType type, double value) + { + Vmin = NUConvert.ConvertVolume(type, value); + } + + + public double GetNuMixCompartimentSize(UnitsType type) + { + return NUConvert.RevertVolume(type, V1Max); + } + + + public void SetNuArea(UnitsType type, double value) + { + Area = NUConvert.ConvertArea(type, value); + } + + public void SetNuInitHead(UnitsType type, double value) + { + H0 = NUConvert.RevertDistance(type, value); + } + + public void SetNuInitVolume(UnitsType type, double value) + { + V0 = NUConvert.ConvertVolume(type, value); + } + + + public void SetNuMaximumHead(UnitsType type, double value) + { + Hmax = NUConvert.RevertDistance(type, value); + } + + public void SetNuMaxVolume(UnitsType type, double value) + { + Vmax = NUConvert.ConvertVolume(type, value); + } + + public void SetNuMinimumHead(UnitsType type, double value) + { + Hmin = NUConvert.ConvertArea(type, value); + } + + public void SetNuMixCompartimentSize(UnitsType type, double value) + { + V1Max = NUConvert.ConvertVolume(type, value); + } + +#endif + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/network/structures/Valve.cs b/IStation.Epanet/network/structures/Valve.cs new file mode 100644 index 0000000..11f4229 --- /dev/null +++ b/IStation.Epanet/network/structures/Valve.cs @@ -0,0 +1,66 @@ +using IStation.Epanet.Enums; + +namespace IStation.Epanet.Network.Structures +{ + + ///<summary>Hydraulic valve structure.</summary> + public class Valve : Link + { + + public Valve(string name, ValveType type) : base(name) => ValveType = type; + + public ValveType ValveType { get; } + + public override LinkType LinkType => LinkType.VALVE; + + public override void InitResistance(HeadLossFormulaType formflag, double hexp) { } + + public override void ConvertUnits(Network nw) + { + FieldsMap fMap = nw.FieldsMap; + + Diameter /= fMap.GetUnits(FieldType.DIAM); + + double diam = Math.Pow(Diameter, 2); + Km = 0.02517 * Km / diam / diam; + + if (!double.IsNaN(Kc)) + switch (ValveType) + { + case ValveType.FCV: + Kc /= fMap.GetUnits(FieldType.FLOW); + break; + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + Kc /= fMap.GetUnits(FieldType.PRESSURE); + break; + } + + } + + ///<summary>Settings curve.</summary> + public Curve Curve { get; set; } + +#if NUCONVERT + public override double GetNuRoughness(FlowUnitsType fType, PressUnitsType pType, double spGrav) + { + + switch (ValveType) + { + case ValveType.FCV: + return NUConvert.RevertFlow(fType, Kc); + case ValveType.PRV: + case ValveType.PSV: + case ValveType.PBV: + return NUConvert.RevertPressure(pType, spGrav, Kc); + } + + return Kc; + } + +#endif + + } + +} \ No newline at end of file diff --git a/IStation.Epanet/schedule/ScheduleHelper.cs b/IStation.Epanet/schedule/ScheduleHelper.cs new file mode 100644 index 0000000..673d6f9 --- /dev/null +++ b/IStation.Epanet/schedule/ScheduleHelper.cs @@ -0,0 +1,505 @@ +锘縰sing IStation.Epanet.Analysis; +using IStation.Epanet.Enums; +using System.Text; +using Yw; + +namespace IStation.Epanet +{ + /// <summary> + /// 姘村姏妯″瀷璋冨害 + /// </summary> + public class ScheduleHelper + { + + + + /// <summary> + /// 寤舵椂姘村埄鍒嗘瀽 + /// </summary> + /// <param name="filePath">inp鏂囦欢</param> + /// <param name="output">杈撳嚭鏁版嵁</param> + /// <returns></returns> + public static string Analysis(string filePath, out Output output) + { + output = new Output(); + if (!File.Exists(filePath)) + { + return $"鏂囦欢涓嶅瓨鍦�!"; + } + + var err = EpanetMethods.ENopen(filePath, "", ""); + if (err != 0) + { + return $"ENopen:{new EnException(err).Message}!"; + } + + + int tstep = 0; + EpanetMethods.ENopenH(); + EpanetMethods.ENinitH(0); + + do + { + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENrunH(out int t); + + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.ID = idBuild.ToString(); + outNode.Time = TimeSpan.FromSeconds(t); + + output.Nodes.Add(outNode); + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.ID = idBuild.ToString(); + outLink.Time = TimeSpan.FromSeconds(t); + output.Links.Add(outLink); + } + + EpanetMethods.ENnextH(out tstep); + + } while (tstep > 0); + EpanetMethods.ENcloseH(); + + return string.Empty; + } + + + + + /// <summary> + /// 鍗曟椂鍒绘按鍒╁垎鏋� + /// </summary> + /// <param name="filePath">inp鏂囦欢</param> + /// <param name="timeSpan">鏃跺埢</param> + /// <param name="output">杈撳嚭鏁版嵁</param> + /// <returns></returns> + public static string Analysis(string filePath, TimeSpan timeSpan, out Output output) + { + output = new Output(); + if (!File.Exists(filePath)) + { + return $"鏂囦欢涓嶅瓨鍦�!"; + } + + var err = EpanetMethods.ENopen(filePath, "", ""); + if (err != 0) + { + return $"ENopen:{new EnException(err).Message}!"; + } + + + int tstep = 0; + EpanetMethods.ENopenH(); + EpanetMethods.ENinitH(0); + + do + { + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENrunH(out int t); + + if (t == timeSpan.TotalSeconds) + { + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.ID = idBuild.ToString(); + outNode.Time = TimeSpan.FromSeconds(t); + + output.Nodes.Add(outNode); + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.ID = idBuild.ToString(); + outLink.Time = TimeSpan.FromSeconds(t); + output.Links.Add(outLink); + } + } + EpanetMethods.ENnextH(out tstep); + + + } while (tstep > 0); + EpanetMethods.ENcloseH(); + + return string.Empty; + } + + /// <summary> + /// 鍗曟椂鍒绘按鍒╁垎鏋� + /// </summary> + /// <param name="filePath">inp鏂囦欢</param> + /// <param name="timeSpan">鏃跺埢</param> + /// <param name="input">杈撳叆鏁版嵁</param> + /// <param name="output">杈撳嚭鏁版嵁</param> + /// <returns></returns> + public static string Analysis(string filePath, TimeSpan timeSpan, Input input, out Output output) + { + output = new Output(); + if (!File.Exists(filePath)) + { + return $"鏂囦欢涓嶅瓨鍦�!"; + } + + + var err = EpanetMethods.ENopen(filePath, "", ""); + if (err != 0) + { + return $"ENopen:{new EnException(err).Message}!"; + } + + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + + EpanetMethods.ENopenH(); + EpanetMethods.ENinitH(0); + + + + if (input.Links != null && input.Links.Any()) + { + foreach (var link in input.Links) + { + var i = link.Index; + EpanetMethods.ENsetlinkvalue(i, LinkValue.Status, link.InitStatus); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Setting, link.InitSetting); + } + } + + + EpanetMethods.ENsolveH(); + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.Index = i; + outNode.ID = idBuild.ToString(); + + output.Nodes.Add(outNode); + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.Index = i; + outLink.ID = idBuild.ToString(); + output.Links.Add(outLink); + + } + + + return string.Empty; + } + + /// <summary> + /// 鍗曟椂鍒绘按鍒╁垎鏋� + /// </summary> + /// <param name="filePath">inp鏂囦欢</param> + /// <param name="timeSpan">鏃跺埢</param> + /// <param name="input">杈撳叆鏁版嵁</param> + /// <param name="output">杈撳嚭鏁版嵁</param> + /// <returns></returns> + public static string Analysis_bak(string filePath, TimeSpan timeSpan, Input input, out Output output) + { + output = new Output(); + if (!File.Exists(filePath)) + { + return $"鏂囦欢涓嶅瓨鍦�!"; + } + + + var err = EpanetMethods.ENopen(filePath, "", ""); + if (err != 0) + { + return $"ENopen:{new EnException(err).Message}!"; + } + + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + if (input != null) + { + if (input.Links != null && input.Links.Any()) + { + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + EpanetMethods.ENgetlinktype(i, out LinkType type); + if (type != LinkType.Pump) + continue; + var inputLink = input.Links.Find(x => x.ID == idBuild.ToString()); + if (inputLink != null) + { + EpanetMethods.ENgetlinkvalue(i, LinkValue.Diameter, out float diameter); + EpanetMethods.ENgetlinkvalue(i, LinkValue.Length, out float length); + EpanetMethods.ENgetlinkvalue(i, LinkValue.Roughness, out float roughness); + EpanetMethods.ENgetlinkvalue(i, LinkValue.MinorLoss, out float minorLoss); + EpanetMethods.ENgetlinkvalue(i, LinkValue.InitSetting, out float initSetting); + EpanetMethods.ENgetlinkvalue(i, LinkValue.Kbulk, out float kbulk); + EpanetMethods.ENgetlinkvalue(i, LinkValue.Kwall, out float kwall); + + inputLink.Index = i; + inputLink.Diameter = diameter; + inputLink.Length = length; + inputLink.Roughness = roughness; + inputLink.MinorLoss = minorLoss; + //inputLink.InitStatus = rhs.InitStatus; + inputLink.InitSetting = inputLink.InitSetting; + inputLink.Kbulk = kbulk; + inputLink.Kwall = kwall; + } + } + + + foreach (var link in input.Links) + { + var i = link.Index; + EpanetMethods.ENsetlinkvalue(i, LinkValue.Diameter, link.Diameter); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Length, link.Length); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Roughness, link.Roughness); + EpanetMethods.ENsetlinkvalue(i, LinkValue.MinorLoss, link.MinorLoss); + EpanetMethods.ENsetlinkvalue(i, LinkValue.InitStatus, link.InitStatus); + EpanetMethods.ENsetlinkvalue(i, LinkValue.InitSetting, 0); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Kbulk, link.Kbulk); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Kwall, link.Kwall); + } + } + if (input.Nodes != null && input.Nodes.Any()) + { + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + var id = idBuild.ToString(); + var inputNode = input.Nodes.Find(x => x.ID == id); + if (inputNode != null) + { + EpanetMethods.ENgetnodevalue(i, NodeValue.Elevation, out float elevation); + EpanetMethods.ENgetnodevalue(i, NodeValue.Pattern, out float pattern); + EpanetMethods.ENgetnodevalue(i, NodeValue.Emitter, out float emitter); + + inputNode.Index = i; + inputNode.Elevation = elevation; + inputNode.Pattern = pattern; + inputNode.Emitter = emitter; + } + } + foreach (var node in input.Nodes) + { + var i = node.Index; + EpanetMethods.ENsetnodevalue(i, NodeValue.Elevation, node.Elevation); + EpanetMethods.ENsetnodevalue(i, NodeValue.BaseDemand, node.BaseDemand); + EpanetMethods.ENsetnodevalue(i, NodeValue.Pattern, node.Pattern); + EpanetMethods.ENsetnodevalue(i, NodeValue.Emitter, node.Emitter); + } + } + } + + + int tstep = 0; + EpanetMethods.ENopenH(); + EpanetMethods.ENinitH(0); + + do + { + + /* EpanetMethods.ENsetlinkvalue(177, LinkValue.Status, 0); + EpanetMethods.ENsetlinkvalue(177, LinkValue.Setting, 0); + */ + EpanetMethods.ENrunH(out int t); + + //娉佃绠楅渶瑕佸湪寤舵椂寰幆鍐� + /*EpanetMethods.ENsetlinkvalue(177, LinkValue.Status, 0); + EpanetMethods.ENsetlinkvalue(177, LinkValue.Setting, 0); + + */ + if (input.Links != null && input.Links.Any()) + { + foreach (var link in input.Links) + { + var i = link.Index; + EpanetMethods.ENsetlinkvalue(i, LinkValue.Status, link.InitStatus); + EpanetMethods.ENsetlinkvalue(i, LinkValue.Setting, link.InitSetting); + } + } + + if (t == timeSpan.TotalSeconds) + { + + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.ID = idBuild.ToString(); + outNode.Time = TimeSpan.FromSeconds(t); + + output.Nodes.Add(outNode); + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.ID = idBuild.ToString(); + outLink.Time = TimeSpan.FromSeconds(t); + output.Links.Add(outLink); + + + + } + } + EpanetMethods.ENnextH(out tstep); + + } while (tstep > 0); + EpanetMethods.ENcloseH(); + + return string.Empty; + } + } + +} diff --git a/IStation.Epanet/schedule/StationScheduleHelper.cs b/IStation.Epanet/schedule/StationScheduleHelper.cs new file mode 100644 index 0000000..c15a995 --- /dev/null +++ b/IStation.Epanet/schedule/StationScheduleHelper.cs @@ -0,0 +1,96 @@ +锘縰sing IStation.Epanet.Analysis; +using IStation.Epanet.Enums; +using System.Text; +using Yw; + +namespace IStation.Epanet +{ + /// <summary> + /// 姘村姏妯″瀷璋冨害 + /// </summary> + public class StationScheduleHelper + { + + public static string Schedule(int stationId,string filePath) + { + + + var err = EpanetMethods.ENopen(filePath, "", ""); + if (err != 0) + { + return $"ENopen:{new EnException(err).Message}!"; + } + + + int tstep = 0; + EpanetMethods.ENopenH(); + EpanetMethods.ENinitH(0); + + do + { + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENrunH(out int t); + + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != 0) + continue; + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.ID = idBuild.ToString(); + outNode.Time = TimeSpan.FromSeconds(t); + + output.Nodes.Add(outNode); + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != 0) + continue; + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.ID = idBuild.ToString(); + outLink.Time = TimeSpan.FromSeconds(t); + output.Links.Add(outLink); + } + + EpanetMethods.ENnextH(out tstep); + + } while (tstep > 0); + EpanetMethods.ENcloseH(); + + return string.Empty; + } + + } + +} diff --git a/IStation.Epanet/schedule/input/Input.cs b/IStation.Epanet/schedule/input/Input.cs new file mode 100644 index 0000000..13f4bd5 --- /dev/null +++ b/IStation.Epanet/schedule/input/Input.cs @@ -0,0 +1,20 @@ +锘縩amespace IStation.Epanet.Analysis +{ + public class Input + { + public Input() + { + this.Links = new List<InputLink>(); + this.Nodes = new List<InputNode>(); + } + public Input(Input rhs) + { + this.Links = rhs.Links.ToList(); + this.Nodes = rhs.Nodes.ToList(); + } + + public List<InputLink> Links { get; set; } + public List<InputNode> Nodes { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/input/InputLink.cs b/IStation.Epanet/schedule/input/InputLink.cs new file mode 100644 index 0000000..ae3f7b3 --- /dev/null +++ b/IStation.Epanet/schedule/input/InputLink.cs @@ -0,0 +1,53 @@ +锘縰sing System.ComponentModel.DataAnnotations; + +namespace IStation.Epanet.Analysis +{ + public class InputLink + { + public InputLink() { } + public InputLink(InputLink rhs) + { + this.ID = rhs.ID; + this.Index = rhs.Index; + this.Diameter = rhs.Diameter; + this.Length = rhs.Length; + this.Roughness = rhs.Roughness; + this.MinorLoss = rhs.MinorLoss; + this.InitStatus = rhs.InitStatus; + this.InitSetting = rhs.InitSetting; + this.Kbulk = rhs.Kbulk; + this.Kwall = rhs.Kwall; + } + + [Display(Name = "鏍囪瘑")] + public string ID { get; set; } + + [Display(Name = "绱㈠紩")] + public int Index { get; set; } + + [Display(Name = "鐩村緞")] + public float Diameter { get; set; } + + [Display(Name = "闀垮害")] + public float Length { get; set; } + + [Display(Name = "绮楃硻绯绘暟闀垮害")] + public float Roughness { get; set; } + + [Display(Name = "灞�閮ㄦ崯澶辩郴鏁�")] + public float MinorLoss { get; set; } + + [Display(Name = "鍒濆绠℃鐘舵��")] + public float InitStatus { get; set; } + + [Display(Name = "绠¢亾绮楃硻搴�/鍒濆姘存车杞��/鍒濆闃�闂ㄨ缃�")] + public float InitSetting { get; set; } + + [Display(Name = "涓绘祦鍙嶅簲绯绘暟")] + public float Kbulk { get; set; } + + [Display(Name = "绠″鍙嶅簲绯绘暟")] + public float Kwall { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/input/InputNode.cs b/IStation.Epanet/schedule/input/InputNode.cs new file mode 100644 index 0000000..3748ace --- /dev/null +++ b/IStation.Epanet/schedule/input/InputNode.cs @@ -0,0 +1,37 @@ +锘縰sing System.ComponentModel.DataAnnotations; + +namespace IStation.Epanet.Analysis +{ + public class InputNode + { + public InputNode() { } + public InputNode(InputNode rhs) + { + this.ID = rhs.ID; + this.Index = rhs.Index; + this.Elevation = rhs.Elevation; + this.BaseDemand = rhs.BaseDemand; + this.Pattern = rhs.Pattern; + this.Emitter = rhs.Emitter; + } + + [Display(Name = "鏍囪瘑")] + public string ID { get; set; } + + [Display(Name = "绱㈠紩")] + public int Index { get; set; } + + [Display(Name = "鏍囬珮")] + public float Elevation { get; set; } + + [Display(Name = "鍩虹闇�姘撮噺")] + public float BaseDemand { get; set; } + + [Display(Name = "鏃堕棿妯″紡绱㈠紩")] + public float Pattern { get; set; } + + [Display(Name = "鎵╂暎鍣ㄧ郴鏁�")] + public float Emitter { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/out/OutLink.cs b/IStation.Epanet/schedule/out/OutLink.cs new file mode 100644 index 0000000..5481655 --- /dev/null +++ b/IStation.Epanet/schedule/out/OutLink.cs @@ -0,0 +1,86 @@ +锘縰sing IStation.Epanet.Enums; +using System.ComponentModel.DataAnnotations; + +namespace IStation.Epanet.Analysis +{ + public class OutLink + { + public OutLink() { } + public OutLink(OutLink rhs) + { + this.Time = rhs.Time; + this.ID = rhs.ID; + this.Index = rhs.Index; + this.LinkType = rhs.LinkType; + this.Diameter = rhs.Diameter; + this.Length = rhs.Length; + this.Roughness = rhs.Roughness; + this.MinorLoss = rhs.MinorLoss; + this.InitStatus = rhs.InitStatus; + this.InitSetting = rhs.InitSetting; + this.Kbulk = rhs.Kbulk; + this.Kwall = rhs.Kwall; + this.Flow = rhs.Flow; + this.Velocity = rhs.Velocity; + this.HeadLoss = rhs.HeadLoss; + this.Status = rhs.Status; + this.Setting = rhs.Setting; + this.Energy = rhs.Energy; + } + + [Display(Name = "鏃堕棿")] + public TimeSpan Time { get; set; } + + [Display(Name = "鏍囪瘑")] + public string ID { get; set; } + + [Display(Name = "绱㈠紩")] + public int Index { get; set; } + + [Display(Name = "绫诲瀷")] + public LinkType LinkType { get; set; } + + [Display(Name = "鐩村緞")] + public decimal Diameter { get; set; } + + [Display(Name = "闀垮害")] + public decimal Length { get; set; } + + [Display(Name = "绮楃硻绯绘暟闀垮害")] + public decimal Roughness { get; set; } + + [Display(Name = "灞�閮ㄦ崯澶辩郴鏁�")] + public decimal MinorLoss { get; set; } + + [Display(Name = "鍒濆绠℃鐘舵��")] + public string InitStatus { get; set; } + + [Display(Name = "绠¢亾绮楃硻搴�/鍒濆姘存车杞��/鍒濆闃�闂ㄨ缃�")] + public string InitSetting { get; set; } + + [Display(Name = "涓绘祦鍙嶅簲绯绘暟")] + public decimal Kbulk { get; set; } + + [Display(Name = "绠″鍙嶅簲绯绘暟")] + public decimal Kwall { get; set; } + + [Display(Name = "娴侀噺")] + public decimal Flow { get; set; } + + [Display(Name = "娴侀��")] + public decimal Velocity { get; set; } + + [Display(Name = "姘村ご鎹熷け")] + public decimal HeadLoss { get; set; } + + [Display(Name = "褰撳墠姘存车鎴栬�呴榾闂ㄧ姸鎬�")] + public decimal Status { get; set; } + + [Display(Name = "褰撳墠姘存车杞�熸垨鑰呴榾闂ㄨ缃�")] + public decimal Setting { get; set; } + + [Display(Name = "娑堣�楄兘閲忥紝浠ュ崈鐡﹁")] + public decimal Energy { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/out/OutNode.cs b/IStation.Epanet/schedule/out/OutNode.cs new file mode 100644 index 0000000..0c8dc5a --- /dev/null +++ b/IStation.Epanet/schedule/out/OutNode.cs @@ -0,0 +1,125 @@ +锘縰sing IStation.Epanet.Enums; +using System.ComponentModel.DataAnnotations; + +namespace IStation.Epanet.Analysis +{ + public class OutNode + { + public OutNode() { } + public OutNode(OutNode rhs) + { + this.Time = rhs.Time; + this.ID = rhs.ID; + this.Index = rhs.Index; + this.NodeType = rhs.NodeType; + this.Elevation = rhs.Elevation; + this.BaseDemand = rhs.BaseDemand; + this.Pattern = rhs.Pattern; + this.Emitter = rhs.Emitter; + this.InitQual = rhs.InitQual; + this.SourceQual = rhs.SourceQual; + this.SourcePat = rhs.SourcePat; + this.SourceType = rhs.SourceType; + this.TankLevel = rhs.TankLevel; + this.Demand = rhs.Demand; + this.Head = rhs.Head; + this.Pressure = rhs.Pressure; + this.Quality = rhs.Quality; + this.SourceMass = rhs.SourceMass; + this.InitVolume = rhs.InitVolume; + this.MixModel = rhs.MixModel; + this.MixZonevol = rhs.MixZonevol; + this.TankDiam = rhs.TankDiam; + this.MinVolume = rhs.MinVolume; + this.VolCurve = rhs.VolCurve; + this.MinLevel = rhs.MinLevel; + this.MaxLevel = rhs.MaxLevel; + this.MixFraction = rhs.MixFraction; + this.TankKbulk = rhs.TankKbulk; + } + + [Display(Name = "鏃堕棿")] + public TimeSpan Time { get; set; } + + [Display(Name = "鏍囪瘑")] + public string ID { get; set; } + + [Display(Name = "绱㈠紩")] + public int Index { get; set; } + + [Display(Name = "绫诲瀷")] + public NodeType NodeType { get; set; } + + [Display(Name = "鏍囬珮")] + public string Elevation { get; set; } + + [Display(Name = "鍩虹闇�姘撮噺")] + public string BaseDemand { get; set; } + + [Display(Name = "鏃堕棿妯″紡绱㈠紩")] + public string Pattern { get; set; } + + [Display(Name = "鎵╂暎鍣ㄧ郴鏁�")] + public string Emitter { get; set; } + + [Display(Name = "鍒濆姘磋川")] + public string InitQual { get; set; } + + [Display(Name = "婧愬ご姘磋川")] + public string SourceQual { get; set; } + + [Display(Name = "婧愬ご妯″紡")] + public string SourcePat { get; set; } + + [Display(Name = "婧愬ご绫诲瀷")] + public string SourceType { get; set; } + + [Display(Name = "姘存睜鐨勫垵濮嬫按浣�")] + public string TankLevel { get; set; } + + [Display(Name = "瀹為檯闇�姘撮噺")] + public string Demand { get; set; } + + [Display(Name = "姘村姏姘村ご")] + public decimal Head { get; set; } + + [Display(Name = "鍘嬪己")] + public decimal Pressure { get; set; } + + [Display(Name = "瀹為檯姘磋川")] + public string Quality { get; set; } + + [Display(Name = "姣忓垎閽熷寲瀛︽垚鍒嗘簮澶寸殑璐ㄩ噺娴侀噺")] + public string SourceMass { get; set; } + + [Display(Name = "鍒濆姘撮噺")] + public string InitVolume { get; set; } + + [Display(Name = "娣峰悎妯″瀷")] + public string MixModel { get; set; } + + [Display(Name = "鍙岄殧灞傛按绠辩殑鍏ュ彛/鍑哄彛鍖哄煙瀹圭Н")] + public string MixZonevol { get; set; } + + [Display(Name = "姘寸鐩村緞")] + public string TankDiam { get; set; } + + [Display(Name = "鏈�灏忔按閲�")] + public string MinVolume { get; set; } + + [Display(Name = "瀹圭Н鏇茬嚎")] + public string VolCurve { get; set; } + + [Display(Name = "鏈�浣庢按浣�")] + public string MinLevel { get; set; } + + [Display(Name = "鏈�楂樻按浣�")] + public string MaxLevel { get; set; } + + [Display(Name = "鍙屾牸姘存Ы涓叆鍙�/鍑哄彛鍖烘墍鍗犳�诲绉殑姣斾緥")] + public string MixFraction { get; set; } + + [Display(Name = "浣撶Н鍙嶅簲閫熺巼绯绘暟")] + public string TankKbulk { get; set; } + } +} diff --git a/IStation.Epanet/schedule/out/Output.cs b/IStation.Epanet/schedule/out/Output.cs new file mode 100644 index 0000000..325c8d6 --- /dev/null +++ b/IStation.Epanet/schedule/out/Output.cs @@ -0,0 +1,20 @@ +锘縩amespace IStation.Epanet.Analysis +{ + public class Output + { + public Output() + { + this.Links = new List<OutLink>(); + this.Nodes = new List<OutNode>(); + } + public Output(Output rhs) + { + this.Links = rhs.Links.ToList(); + this.Nodes = rhs.Nodes.ToList(); + } + + public List<OutLink> Links { get; set; } + public List<OutNode> Nodes { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/time-value/DayValue.cs b/IStation.Epanet/schedule/time-value/DayValue.cs new file mode 100644 index 0000000..546969f --- /dev/null +++ b/IStation.Epanet/schedule/time-value/DayValue.cs @@ -0,0 +1,11 @@ +锘縩amespace IStation.Epanet +{ + public class DayValue + { + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + public List<TimeValue> TimeValues { get; set; } + + } +} diff --git a/IStation.Epanet/schedule/time-value/DayValueHelper.cs b/IStation.Epanet/schedule/time-value/DayValueHelper.cs new file mode 100644 index 0000000..ca3c1b3 --- /dev/null +++ b/IStation.Epanet/schedule/time-value/DayValueHelper.cs @@ -0,0 +1,47 @@ +锘縰sing Yw; + +namespace IStation.Epanet +{ + public class DayValueHelper + { + + public static List<DayValue> GetDayValues(int station) + { + string folderPath; + if (station==1) + { + folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣涓�杈�"; + } + else + { + folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣浜岃緭"; + } + + if (!Directory.Exists(folderPath)) + return default; + var fileList=Directory.GetFiles(folderPath); + if (fileList == null || !fileList.Any()) + return default; + + var list = new List<DayValue>(); + foreach (var file in fileList) + { + var fileName = Path.GetFileNameWithoutExtension(file); + if (!DateTime.TryParse(fileName, out DateTime day)) + continue; + var json=File.ReadAllText(file); + var timeValues = JsonHelper.Json2Object<List<TimeValue>>(json); + if (timeValues == null || !timeValues.Any()) + continue; + var dayValue = new DayValue(); + dayValue.Year = day.Year; + dayValue.Month = day.Month; + dayValue.Day = day.Day; + dayValue.TimeValues = timeValues; + list.Add(dayValue); + } + + return list; + } + } +} diff --git a/IStation.Epanet/schedule/time-value/TimeValue.cs b/IStation.Epanet/schedule/time-value/TimeValue.cs new file mode 100644 index 0000000..ab418b8 --- /dev/null +++ b/IStation.Epanet/schedule/time-value/TimeValue.cs @@ -0,0 +1,27 @@ +锘縩amespace IStation.Epanet +{ + public class TimeValue + { + public TimeValue() + { + + } + public TimeValue(TimeValue rhs) + { + this.Time = rhs.Time; + this.Value = rhs.Value; + } + + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鍊煎瓧鍏� + /// </summary> + public Dictionary<string, double> Value { get; set; } + + } +} diff --git a/IStation.Epanet/util/TraceExtensions.cs b/IStation.Epanet/util/TraceExtensions.cs new file mode 100644 index 0000000..4005a28 --- /dev/null +++ b/IStation.Epanet/util/TraceExtensions.cs @@ -0,0 +1,167 @@ +锘縰sing System.Diagnostics; + +namespace IStation.Epanet.Log +{ + /// <summary> + /// Shortcut methods for <see cref="TraceSource"/> logger + /// </summary> + public static class TraceExtensions + { + + private static object[] GetCaller() + { + var frame = new StackFrame(2, true); + + return new object[] { + frame.GetMethod().Name, + frame.GetFileName(), + frame.GetFileLineNumber() + }; + } + + #region Critical + + [Conditional("TRACE")] + public static void Critical(this TraceSource src, string message) + { + src.TraceEvent(TraceEventType.Critical, 0, message); + } + + [Conditional("TRACE")] + public static void Critical(this TraceSource src, string format, params object[] args) + { + src.TraceEvent(TraceEventType.Critical, 0, format, args); + } + + [Conditional("TRACE")] + public static void Critical(this TraceSource src, object data) + { + src.TraceData(TraceEventType.Critical, 0, data); + } + + [Conditional("TRACE")] + public static void Critical(this TraceSource src, params object[] data) + { + src.TraceData(TraceEventType.Critical, 0, data); + } + + #endregion + + #region Error + + [Conditional("TRACE")] + public static void Error(this TraceSource src, string message) + { + src.TraceEvent(TraceEventType.Error, 0, message); + } + + [Conditional("TRACE")] + public static void Error(this TraceSource src, string format, params object[] args) + { + src.TraceEvent(TraceEventType.Error, 0, format, args); + } + + [Conditional("TRACE")] + public static void Error(this TraceSource src, string format, object arg) + { + src.TraceEvent(TraceEventType.Error, 0, format, arg); + } + + [Conditional("TRACE")] + public static void Error(this TraceSource src, object data) + { + src.TraceData(TraceEventType.Error, 0, data); + } + + public static void Error(this TraceSource src, params object[] data) + { + src.TraceData(TraceEventType.Error, 0, data); + } + + #endregion + + #region Warning + + [Conditional("TRACE")] + public static void Warning(this TraceSource src, string message) + { + src.TraceEvent(TraceEventType.Warning, 0, message); + } + + [Conditional("TRACE")] + public static void Warning(this TraceSource src, string format, params object[] args) + { + src.TraceEvent(TraceEventType.Warning, 0, format, args); + } + + [Conditional("TRACE")] + public static void Warning(this TraceSource src, object data) + { + src.TraceData(TraceEventType.Warning, 0, data); + } + + [Conditional("TRACE")] + public static void Warning(this TraceSource src, params object[] data) + { + src.TraceData(TraceEventType.Warning, 0, data); + } + + #endregion + + #region Information + + [Conditional("TRACE")] + public static void Information(this TraceSource src, string message) + { + src.TraceEvent(TraceEventType.Information, 0, message); + } + + [Conditional("TRACE")] + public static void Information(this TraceSource src, string format, params object[] args) + { + src.TraceEvent(TraceEventType.Information, 0, format, args); + } + + [Conditional("TRACE")] + public static void Information(this TraceSource src, object data) + { + src.TraceData(TraceEventType.Information, 0, data); + } + + [Conditional("TRACE")] + public static void Information(this TraceSource src, params object[] data) + { + src.TraceData(TraceEventType.Information, 0, data); + } + + #endregion + + #region Verbose + + [Conditional("TRACE")] + public static void Verbose(this TraceSource src, string message) + { + src.TraceEvent(TraceEventType.Verbose, 0, message); + } + + [Conditional("TRACE")] + public static void Verbose(this TraceSource src, string format, params object[] args) + { + src.TraceEvent(TraceEventType.Verbose, 0, format, args); + } + + [Conditional("TRACE")] + public static void Verbose(this TraceSource src, object data) + { + src.TraceData(TraceEventType.Verbose, 0, data); + } + + [Conditional("TRACE")] + public static void Verbose(this TraceSource src, params object[] data) + { + src.TraceData(TraceEventType.Verbose, 0, data); + } + + #endregion + } +} diff --git a/IStation.Epanet/util/Utilities.cs b/IStation.Epanet/util/Utilities.cs new file mode 100644 index 0000000..c93f398 --- /dev/null +++ b/IStation.Epanet/util/Utilities.cs @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2016 Vyacheslav Shevelyov (slavash at aha dot ru) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +using IStation.Epanet.Enums; +using System.Globalization; + +namespace IStation.Epanet.Util +{ + + ///<summary>Epanet utilities methods.</summary> + public static class Utilities + { + /// <summary>Check if two strings match (case independent), based on the shortest string length.</summary> + /// <param name="a">string A.</param> + /// <param name="b">string B.</param> + /// <returns>Boolean is the two strings are similar.</returns> + public static bool Match2(this string a, string b) + { + if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false; + + return a.Length > b.Length + ? a.StartsWith(b, StringComparison.OrdinalIgnoreCase) + : b.StartsWith(a, StringComparison.OrdinalIgnoreCase); + } + + /// <summary> + /// Sees if substr matches any part of str (not case sensitive). + /// </summary> + /// <param name="str">string being searched</param> + /// <param name="substr">substring being searched for</param> + /// <returns>returns <c>true</c> if substr found in str, <c>false</c> if not</returns> + public static bool Match(this string str, string substr) + { + + /* Fail if substring is empty */ + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(substr)) + return false; + + /* Skip leading blanks of str. */ + str = str.TrimStart(); + + /* Check if substr matches remainder of str. */ + return str.StartsWith(substr, StringComparison.OrdinalIgnoreCase); + } + + public static int FindMatch(this string str, params string[] substrings) + { + for (int i = 0; i < substrings.Length; i++) + { + if (Match(str, substrings[i])) + { + return i; + } + } + + return -1; + } + + /// <summary>Converts time from units to hours.</summary> + /// <param name="time">string containing a time value</param> + /// <param name="units">string containing time units (PM or AM)</param> + /// <returns>numerical value of time in hours (1.0 is 3600 seconds), or -1 if an error occurs </returns> + public static double GetHour(string time, string units = null) + { + const int COUNT = 3; + double[] y = new double[COUNT]; + + // Separate clock time into hrs, min, sec. + string[] s = (time ?? string.Empty).Split(':'); + + int n; + for (n = 0; n < COUNT && n < s.Length; n++) + { + if (!s[n].ToDouble(out y[n])) return -1; + } + + // If decimal time with units attached then convert to hours. + if (n == 1) + { + if (string.IsNullOrEmpty(units)) return y[0]; + + if (Match(units, Keywords.w_SECONDS)) return y[0] / 3600.0; + if (Match(units, Keywords.w_MINUTES)) return y[0] / 60.0; + if (Match(units, Keywords.w_HOURS)) return y[0]; + if (Match(units, Keywords.w_DAYS)) return y[0] * 24.0; + } + + // Convert hh:mm:ss format to decimal hours + if (n > 1) y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0; + + // If am/pm attached then adjust hour accordingly + // (12 am is midnight, 12 pm is noon) + if (string.IsNullOrEmpty(units)) return y[0]; + + if (units.Equals(Keywords.w_AM, StringComparison.OrdinalIgnoreCase)) + { + if (y[0] >= 13.0) return -1.0; + if (y[0] >= 12.0) return y[0] - 12.0; + return y[0]; + } + + if (units.Equals(Keywords.w_PM, StringComparison.OrdinalIgnoreCase)) + { + if (y[0] >= 13.0) return -1.0; + if (y[0] >= 12.0) return y[0]; + return y[0] + 12.0; + } + + return -1.0; + } + + public static TimeSpan TimeOfDay(this TimeSpan t) + { + return new TimeSpan(t.Ticks % TimeSpan.TicksPerDay); + } + + /// <summary>Converts time from units to hours.</summary> + /// <param name="time">string containing a time value</param> + /// <param name="units">string containing time units (PM or AM)</param> + /// <returns>numerical value of time in hours (1.0 is 3600 seconds), or -1 if an error occurs </returns> + public static TimeSpan ToTimeSpan(string time, string units = null) + { + const int COUNT = 3; + double[] y = new double[COUNT]; + + // Separate clock time into hrs, min, sec. + string[] s = (time ?? string.Empty).Split(':'); + + int n; + for (n = 0; n < COUNT && n < s.Length; n++) + { + if (!s[n].ToDouble(out y[n])) + return TimeSpan.MinValue; + } + + // If decimal time with units attached then convert to hours. + if (n == 1) + { + if (string.IsNullOrEmpty(units)) return TimeSpan.FromHours(y[0]); + if (Match(units, Keywords.w_SECONDS)) return TimeSpan.FromSeconds(y[0]); + if (Match(units, Keywords.w_MINUTES)) return TimeSpan.FromMinutes(y[0]); + if (Match(units, Keywords.w_HOURS)) return TimeSpan.FromHours(y[0]); + if (Match(units, Keywords.w_DAYS)) return TimeSpan.FromDays(y[0]); + } + + // Convert hh:mm:ss format to decimal hours + if (n > 1) + y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0; + + // If am/pm attached then adjust hour accordingly + // (12 am is midnight, 12 pm is noon) + if (string.IsNullOrEmpty(units)) + return TimeSpan.FromHours(y[0]); + + if (units.Equals(Keywords.w_AM, StringComparison.OrdinalIgnoreCase)) + { + if (y[0] >= 13.0) return TimeSpan.MinValue; + if (y[0] >= 12.0) return TimeSpan.FromHours(y[0] - 12.0); + return TimeSpan.FromHours(y[0]); + } + + if (units.Equals(Keywords.w_PM, StringComparison.OrdinalIgnoreCase)) + { + if (y[0] >= 13.0) return TimeSpan.MinValue; + if (y[0] >= 12.0) return TimeSpan.FromHours(y[0]); + return TimeSpan.FromHours(y[0] + 12.0); + } + + return TimeSpan.MinValue; + } + + + public static string GetClockTime(this TimeSpan ts) => string.Format("{0:00}:{1:00}:{2:00}", (int)ts.TotalHours, ts.Minutes, ts.Seconds); + + ///<summary>Get value signal, if bigger than 0 returns 1, -1 otherwise.</summary> + /// <param name="val">Any real number.</param> + /// <returns>-1 or 1</returns> + public static double Sign(this double val) => val < 0 ? -1d : 1d; + + public static void Reverse<T>(this LinkedList<T> linkedList) + { + if (linkedList == null || linkedList.Count < 2) return; + + LinkedListNode<T> next; + + var head = linkedList.First; + + while ((next = head.Next) != null) + { + linkedList.Remove(next); + linkedList.AddFirst(next.Value); + } + } + + public static bool ToDouble(this string s, out double result) + { + return double.TryParse( + s, + NumberStyles.Float | NumberStyles.AllowThousands, + NumberFormatInfo.InvariantInfo, + out result); + } + + private const double DOUBLE_EPSILON = double.Epsilon * 100; + + public static bool IsZero(this double value) + { + return Math.Abs(value) < DOUBLE_EPSILON; + } + + public static bool EqualsTo(this double value, double other) + { + return Math.Abs(value - other) < DOUBLE_EPSILON; + } + + public static bool IsZero(this TimeSpan value) { return value.Ticks == 0L; } + + } + +} \ No newline at end of file diff --git a/IStation.Schedule.sln b/IStation.Schedule.sln index 185743a..abbaacd 100644 --- a/IStation.Schedule.sln +++ b/IStation.Schedule.sln @@ -17,42 +17,102 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.Service", "IStation.Service\IStation.Service.csproj", "{C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IStation.Win", "Test\IStation.Win\IStation.Win.csproj", "{35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.Win", "Test\IStation.Win\IStation.Win.csproj", "{35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.Epanet", "IStation.Epanet\IStation.Epanet.csproj", "{8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.Server.Validation", "IStation.Server.Validation\IStation.Server.Validation.csproj", "{12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.TopShelf.Validation", "IStation.TopShelf.Validation\IStation.TopShelf.Validation.csproj", "{D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Debug|x86.Build.0 = Debug|Any CPU {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Release|Any CPU.Build.0 = Release|Any CPU + {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Release|x86.ActiveCfg = Release|Any CPU + {937EBE3C-9454-493D-ACF3-D0C6D25517BD}.Release|x86.Build.0 = Release|Any CPU {9C62590A-7F9C-4577-A870-64C27182961D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C62590A-7F9C-4577-A870-64C27182961D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C62590A-7F9C-4577-A870-64C27182961D}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C62590A-7F9C-4577-A870-64C27182961D}.Debug|x86.Build.0 = Debug|Any CPU {9C62590A-7F9C-4577-A870-64C27182961D}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C62590A-7F9C-4577-A870-64C27182961D}.Release|Any CPU.Build.0 = Release|Any CPU + {9C62590A-7F9C-4577-A870-64C27182961D}.Release|x86.ActiveCfg = Release|Any CPU + {9C62590A-7F9C-4577-A870-64C27182961D}.Release|x86.Build.0 = Release|Any CPU {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Debug|x86.ActiveCfg = Debug|Any CPU + {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Debug|x86.Build.0 = Debug|Any CPU {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Release|Any CPU.Build.0 = Release|Any CPU + {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Release|x86.ActiveCfg = Release|Any CPU + {C1925E4B-446C-4B8D-9C50-92DA608E3A6B}.Release|x86.Build.0 = Release|Any CPU {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Debug|x86.Build.0 = Debug|Any CPU {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Release|Any CPU.Build.0 = Release|Any CPU + {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Release|x86.ActiveCfg = Release|Any CPU + {E9A19273-A66B-4A49-8FCA-9F530983C07B}.Release|x86.Build.0 = Release|Any CPU {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Debug|x86.Build.0 = Debug|Any CPU {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Release|Any CPU.Build.0 = Release|Any CPU + {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Release|x86.ActiveCfg = Release|Any CPU + {34BCD6D5-2061-42AE-AFB4-9A37B32321C5}.Release|x86.Build.0 = Release|Any CPU {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Debug|x86.Build.0 = Debug|Any CPU {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Release|Any CPU.Build.0 = Release|Any CPU + {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Release|x86.ActiveCfg = Release|Any CPU + {C8C9A8D3-12EC-4107-ADF5-DC195A4A7DF6}.Release|x86.Build.0 = Release|Any CPU {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Debug|x86.ActiveCfg = Debug|Any CPU + {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Debug|x86.Build.0 = Debug|Any CPU {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Release|Any CPU.ActiveCfg = Release|Any CPU {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Release|Any CPU.Build.0 = Release|Any CPU + {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Release|x86.ActiveCfg = Release|Any CPU + {35E6A93C-A115-43E7-AE6B-67CEE31C3B7C}.Release|x86.Build.0 = Release|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Debug|x86.ActiveCfg = Debug|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Debug|x86.Build.0 = Debug|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Release|Any CPU.Build.0 = Release|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Release|x86.ActiveCfg = Release|Any CPU + {8CD556A4-8985-4EEE-A7E7-03BA3DCC8323}.Release|x86.Build.0 = Release|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Debug|x86.Build.0 = Debug|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Release|Any CPU.Build.0 = Release|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Release|x86.ActiveCfg = Release|Any CPU + {12CB7D5E-17E4-4B5B-9FD3-CD02586F98F4}.Release|x86.Build.0 = Release|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Debug|x86.Build.0 = Debug|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Release|Any CPU.Build.0 = Release|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Release|x86.ActiveCfg = Release|Any CPU + {D09D4DD5-9F4D-442E-A11D-547AEB3FF18C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/IStation.Server.Validation/00-core/ConfigHelper.cs b/IStation.Server.Validation/00-core/ConfigHelper.cs new file mode 100644 index 0000000..a631e72 --- /dev/null +++ b/IStation.Server.Validation/00-core/ConfigHelper.cs @@ -0,0 +1,19 @@ +锘縩amespace IStation.Server +{ + /// <summary> + /// AppConfig + /// </summary> + internal class ConfigHelper + { + + /// <summary> + /// 鏃惰鍒扖ron琛ㄨ揪寮� + /// </summary> + public static string MinuteCron + { + get { return Settings.ParasHelper.Task.MinuteCron; } + } + + + } +} diff --git a/IStation.Server.Validation/00-core/IJobHelper.cs b/IStation.Server.Validation/00-core/IJobHelper.cs new file mode 100644 index 0000000..83f8c81 --- /dev/null +++ b/IStation.Server.Validation/00-core/IJobHelper.cs @@ -0,0 +1,18 @@ +锘縩amespace IStation.Server +{ + /// <summary> + /// 浠诲姟杈呭姪绫绘帴鍙� + /// </summary> + internal interface IJobHelper + { + /// <summary> + /// 寮�濮嬩换鍔� + /// </summary> + Task StartJob(); + + /// <summary> + /// 鍙栨秷浠诲姟 + /// </summary> + Task CancelJob(); + } +} diff --git a/IStation.Server.Validation/00-core/JobHelper.cs b/IStation.Server.Validation/00-core/JobHelper.cs new file mode 100644 index 0000000..205e3de --- /dev/null +++ b/IStation.Server.Validation/00-core/JobHelper.cs @@ -0,0 +1,30 @@ +锘縩amespace IStation.Server +{ + /// <summary> + /// 浠诲姟杈呭姪绫� + /// </summary> + public class JobHelper + { + private readonly List<IJobHelper> _jobHelpers = new() + { + new HydraulicModelValidationJobHelper() + }; + + /// <summary> + /// 寮�濮嬩换鍔� + /// </summary> + public void StartJob() + { + _jobHelpers.ForEach(async x => await x.StartJob()); + } + + /// <summary> + /// 鍙栨秷浠诲姟 + /// </summary> + public void CancelJob() + { + _jobHelpers.ForEach(async x => await x.CancelJob()); + } + + } +} diff --git a/IStation.Server.Validation/00-core/Log.cs b/IStation.Server.Validation/00-core/Log.cs new file mode 100644 index 0000000..1ba3338 --- /dev/null +++ b/IStation.Server.Validation/00-core/Log.cs @@ -0,0 +1,55 @@ +锘縩amespace IStation.Server +{ + /// <summary> + /// + /// </summary> + public class Log + { + /// <summary> + /// 鍐欏叆淇℃伅鏃ュ織 + /// </summary> + /// <param name="verification_id"></param> + /// <param name="title"></param> + /// <param name="info"></param> + public static void Info(long verification_id, string info) + { + Yw.LogHelper.Info(GetLogInfo(verification_id, info)); + } + + /// <summary> + /// 鍐欏叆璋冭瘯鏃ュ織 + /// </summary> + /// <param name="requestId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + public static void Debug(long requestId, string info) + { + Yw.LogHelper.Debug(GetLogInfo(requestId, info)); + } + + /// <summary> + /// 鍐欏叆閿欒鏃ュ織 + /// </summary> + /// <param name="requestId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + /// <param name="ex"></param> + public static void Error(long requestId, string info, Exception ex = null) + { + Yw.LogHelper.Error(GetLogInfo(requestId, info), ex); + } + + + /// <summary> + /// + /// </summary> + /// <param name="requestId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + /// <returns></returns> + private static string GetLogInfo(long requestId, string info) + { + return $"{requestId} >> {info}"; + } + } +} diff --git a/IStation.Server.Validation/01-validation/HydraulicModelValidationHelperJob.cs b/IStation.Server.Validation/01-validation/HydraulicModelValidationHelperJob.cs new file mode 100644 index 0000000..aec3dfb --- /dev/null +++ b/IStation.Server.Validation/01-validation/HydraulicModelValidationHelperJob.cs @@ -0,0 +1,205 @@ +锘縰sing Quartz; + +namespace IStation.Server +{ + /// <summary> + /// 妯″瀷楠岃瘉杈呭姪绫� + /// </summary> + [DisallowConcurrentExecution]//姝ょ壒鎬ф爣璇� 蹇呴』绛夊緟杩欐浠诲姟鎵ц瀹屾垚鍚庯紝鎵嶈兘鎵ц涓嬫浠诲姟 + public class HydraulicModelValidationHelperJob : IJob + { + private static readonly string _hydraulic_model_file = Path.Combine( + Settings.ParasHelper.LocalFile.DataFolderDirectory, + Settings.ParasHelper.LocalFile.HydraulicModelFile); + + private static readonly Dictionary<string, double> _pump_nr_dict = new() { + { "Pump11",524}, + { "Pump12",524}, + { "Pump13",524}, + { "Pump14",524}, + //{ "Pump15",545}, + //{ "Pump16",524}, + { "Pump17",524}, + { "Pump18",524}, + + { "Pump21",740}, + { "Pump22",495}, + { "Pump23",495}, + { "Pump24",495}, + { "Pump25",495}, + { "Pump26",495}, + { "Pump27",740} + }; + + private static readonly List<string> _MPa=new() { }; + private static readonly List<string> _kPa=new() { }; + + /// <summary> + /// + /// </summary> + public Task Execute(IJobExecutionContext context) + { + return Task.Run(() => + { + var verification_id = Yw.YitIdHelper.NextId(); + var current_time = DateTime.Now; + try + { + if (!File.Exists(_hydraulic_model_file)) + { + Log.Info(verification_id, $"妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷鏂囦欢涓嶅瓨鍦�,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + var hydraulic_model_validation_config = new IStation.Service.HydraulicModelValidationConfig().Get(); + if (hydraulic_model_validation_config == null) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷楠岃瘉鏂囦欢涓嶅瓨鍦�,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + if (!Settings.ParasHelper.ZyDocking.Enable) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,Scada瀵规帴鎺ュ彛鏈惎鐢�,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + + var hydraulic_model_scada_list = new List<Model.HydraulicModelScada>(); + + var url = Settings.ParasHelper.ZyDocking.ScadaHttpUrl; + var response_text = Yw.Untity.HttpRequestHelper.Get(url); + var zy_scada_output = Yw.JsonHelper.Json2Object<IStation.Dto.ScadaScheduleInput>(response_text); + if (zy_scada_output.data != null && zy_scada_output.data.Any()) + { + var data_dict = zy_scada_output.data; + foreach (var scada_dict in data_dict) + { + var vals = scada_dict.Value.ElementAt(0).Value; + var time = scada_dict.Value.ElementAt(1).Value; + var key = scada_dict.Value.ElementAt(2).Value; + var hydraulic_model_scada = new Model.HydraulicModelScada(); + hydraulic_model_scada.VerificationID = verification_id; + hydraulic_model_scada.Tag = key; + if (DateTime.TryParse(time, out DateTime t)) + hydraulic_model_scada.Time = t; + if (double.TryParse(vals, out double v)) + hydraulic_model_scada.Value = v; + hydraulic_model_scada_list.Add(hydraulic_model_scada); + } + } + + + if (!hydraulic_model_scada_list.Any()) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,鎺ユ敹Scada鏁版嵁涓虹┖,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + else + { + var json = Yw.JsonHelper.Object2Json(hydraulic_model_scada_list); + Log.Debug(verification_id, json); + } + + + var flow_id_mapping_dict = hydraulic_model_validation_config.FlowIdMappingDict; + var pressure_id_mapping_dict = hydraulic_model_validation_config.PressureIdMappingDict; + var pattern_id_mapping_dict = hydraulic_model_validation_config.PatternIdMappingDict; + + var pressure_id_kPa_list= hydraulic_model_validation_config.PressureIdkPaList; + + var pattern_list = new List<IStation.Hydraulic.Pattern>(); + var time_value = new IStation.Hydraulic.TimeValue + { + Time = current_time, + Value = new Dictionary<string, double>() + }; + + foreach (var hydraulic_model_scada in hydraulic_model_scada_list) + { + var tag = hydraulic_model_scada.Tag; + var value = hydraulic_model_scada.Value; + foreach (var item in flow_id_mapping_dict) + { + if (item.Value != tag) + continue; + time_value.Value.Add(tag, value ?? 0); + } + + foreach (var item in pressure_id_mapping_dict) + { + if (item.Value != tag) + continue; + var pressure_value = value ?? 0; + if (pressure_id_kPa_list.Contains(tag)) + { + pressure_value /= 1000; + } + pressure_value = IStation.Curve.PumpCalculateHelper.Mpa2M(pressure_value); + time_value.Value.Add(tag, pressure_value); + } + + foreach (var item in pattern_id_mapping_dict) + { + if (item.Value != tag) + continue; + var pattern_id = item.Key; + var factor = value; + if (_pump_nr_dict.ContainsKey(pattern_id)) + { + factor /= _pump_nr_dict[pattern_id]; + factor = factor < 0 ? 0 : factor; + } + var pattern = new IStation.Hydraulic.Pattern + { + Id = pattern_id, + FactorList = new List<float>() + }; + if (factor.HasValue) + pattern.FactorList.Add((float)factor.Value); + pattern_list.Add(pattern); + } + } + + + var hydraulic_model_record_list = IStation.Hydraulic.ModeVerifyHelper.Verify(verification_id, _hydraulic_model_file, flow_id_mapping_dict, pressure_id_mapping_dict, pattern_list, time_value); + if (hydraulic_model_record_list == null || !hydraulic_model_record_list.Any()) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,妯″瀷楠岃瘉鏁版嵁涓虹┖锛岃嚜鍔ㄨ烦杩囧綋鍓嶆暟鎹�!"); + return; + } + + var hydraulic_model_validation = new Model.HydraulicModelValidation(); + hydraulic_model_validation.ID = verification_id; + hydraulic_model_validation.Time = current_time; + //hydraulic_model_validation.Config = Yw.JsonHelper.Object2Json(hydraulic_model_validation_config); + + var bol = new IStation.Service.HydraulicModelValidation().Insert(hydraulic_model_validation) > 0; + if (!bol) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷楠岃瘉,淇濆瓨澶辫触!"); + } + + bol = new IStation.Service.HydraulicModelScada().Inserts(hydraulic_model_scada_list); + if (!bol) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷Scada,淇濆瓨澶辫触!"); + } + + bol = new IStation.Service.HydraulicModelRecord().Inserts(hydraulic_model_record_list); + if (!bol) + { + Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷璁板綍,淇濆瓨澶辫触!"); + } + + Log.Info(verification_id, $"妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷楠岃瘉,[{current_time:G}]楠岃瘉鎴愬姛!"); + } + catch (Exception ex) + { + Log.Error(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,鍑洪敊", ex); + var e = new JobExecutionException(ex); + throw e; + } + }); + } + + + } +} diff --git a/IStation.Server.Validation/01-validation/HydraulicModelValidationJobHelper.cs b/IStation.Server.Validation/01-validation/HydraulicModelValidationJobHelper.cs new file mode 100644 index 0000000..f111885 --- /dev/null +++ b/IStation.Server.Validation/01-validation/HydraulicModelValidationJobHelper.cs @@ -0,0 +1,63 @@ +锘縰sing Quartz; + +namespace IStation.Server +{ + /// <summary> + /// 鐩戞祴浠诲姟杈呭姪绫� + /// </summary> + internal class HydraulicModelValidationJobHelper : IJobHelper + { + private const string _jobName = "HydraulicModelValidationJobName"; + private const string _jobGroup = "HydraulicModelValidationJobGroup"; + private const string _triggerName = "HydraulicModelValidationJobTrigger"; + + private static IScheduler _sched;//璋冨害鍣� + + /// <summary> + /// 寮�濮嬩换鍔� + /// </summary> + public async Task StartJob() + { + if (_sched != null) + return; + + // 1.鍒涘缓scheduler鐨勫紩鐢� + var fac = new Quartz.Impl.StdSchedulerFactory(); + _sched = await fac.GetScheduler(); + + //2.鍚姩 scheduler + await _sched.Start(); + + //3.鍒涘缓浠诲姟 + var job = JobBuilder.Create<HydraulicModelValidationHelperJob>() + .WithIdentity(_jobName, _jobGroup) + .Build(); + + //4.鍒涘缓Trigger + var trigger = TriggerBuilder.Create() + .WithIdentity(_triggerName, _jobGroup) + .WithCronSchedule(ConfigHelper.MinuteCron) + .Build(); + + //5.鍔犲叆璋冨害绠$悊鍣� + await _sched.ScheduleJob(job, trigger); + + } + + /// <summary> + /// 鍙栨秷浠诲姟 + /// </summary> + public async Task CancelJob() + { + if (_sched == null) + return; + var triggerKey = new TriggerKey(_triggerName, _jobGroup); + if (await _sched.CheckExists(triggerKey)) + { + await _sched.UnscheduleJob(triggerKey); + } + + } + + } +} diff --git a/IStation.Server.Validation/GlobalUsings.cs b/IStation.Server.Validation/GlobalUsings.cs new file mode 100644 index 0000000..94469c9 --- /dev/null +++ b/IStation.Server.Validation/GlobalUsings.cs @@ -0,0 +1,3 @@ +锘縢lobal using Yw.Untity; +global using Yw.Service; +global using IStation.Service; \ No newline at end of file diff --git a/IStation.Server.Validation/IStation.Server.Validation.csproj b/IStation.Server.Validation/IStation.Server.Validation.csproj new file mode 100644 index 0000000..d6e3542 --- /dev/null +++ b/IStation.Server.Validation/IStation.Server.Validation.csproj @@ -0,0 +1,23 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net6.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>disable</Nullable> + </PropertyGroup> + + <ItemGroup> + <Compile Remove="01-validation\MonitorRealRecordCacheHelper.cs" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Yw.Quartz.Core" Version="3.0.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\IStation.Dto\IStation.Dto.csproj" /> + <ProjectReference Include="..\IStation.Service\IStation.Service.csproj" /> + </ItemGroup> + +</Project> diff --git a/IStation.Server.Validation/Program.cs b/IStation.Server.Validation/Program.cs new file mode 100644 index 0000000..5120759 --- /dev/null +++ b/IStation.Server.Validation/Program.cs @@ -0,0 +1,17 @@ +锘�// See https://aka.ms/new-console-template for more information + +using IStation.Server; +using Yw; + +LogHelper.Info("寮�濮嬪惎鍔ㄩ檲琛屽満鍐呮ā鍨嬪湪绾块獙璇佹湇鍔�..."); + +var jobHelper = new JobHelper(); +jobHelper.StartJob(); +AppDomain.CurrentDomain.ProcessExit += (sender, e) => +{ + jobHelper.CancelJob(); +}; + +LogHelper.Info("闄堣鍦哄唴妯″瀷鍦ㄧ嚎楠岃瘉鏈嶅姟鍚姩鎴愬姛锛�"); + +Console.ReadLine(); diff --git a/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml b/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..639934f --- /dev/null +++ b/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,12 @@ +锘�<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project> + <PropertyGroup> + <Configuration>Release</Configuration> + <Platform>Any CPU</Platform> + <PublishDir>bin\Release\net6.0\publish\</PublishDir> + <PublishProtocol>FileSystem</PublishProtocol> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user b/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..72be30a --- /dev/null +++ b/IStation.Server.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,10 @@ +锘�<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project> + <PropertyGroup> + <History>True|2024-04-19T03:43:33.5970161Z;True|2024-03-14T11:41:14.5733246+08:00;True|2024-01-26T11:48:51.9660549+08:00;True|2024-01-18T14:24:12.3497938+08:00;True|2023-12-28T14:31:33.7967635+08:00;True|2023-12-26T21:36:06.3108131+08:00;True|2023-09-13T17:01:56.7348687+08:00;True|2023-09-06T10:07:25.3107716+08:00;True|2023-08-30T15:38:20.8940514+08:00;True|2023-08-10T14:40:54.4439418+08:00;True|2023-08-09T15:51:10.9031753+08:00;</History> + <LastFailureDetails /> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/IStation.Service/00-core/ConfigHelper.cs b/IStation.Service/00-core/ConfigHelper.cs index 92af9cd..f8a6a45 100644 --- a/IStation.Service/00-core/ConfigHelper.cs +++ b/IStation.Service/00-core/ConfigHelper.cs @@ -186,6 +186,53 @@ return config; } } + + /// <summary> + /// Hydraulic SQLite杩炴帴閰嶇疆 + /// </summary> + internal static ConnectionConfig HydraulicConnectionConfig + { + get + { + var config = new ConnectionConfig + { + DbType = SqlSugar.DbType.Sqlite,//鏁版嵁搴撶被鍨� + ConnectionString = GetConnectionString(Settings.ParasHelper.DataBase.SQLite.HydraulicConnectString), + IsAutoCloseConnection = true,//鏄惁鑷姩鍏抽棴 + MoreSettings = new ConnMoreSettings() + { + //PgSqlIsAutoToLower = false //鏁版嵁搴撳瓨鍦ㄥぇ鍐欏瓧娈电殑 锛岄渶瑕佹妸杩欎釜璁句负false 锛屽苟涓斿疄浣撳拰瀛楁鍚嶇О瑕佷竴鏍� + }, + AopEvents = new AopEvents + { + OnLogExecuting = (sql, p) => + { + var sqlString = UtilMethods.GetNativeSql(sql, p); + //LogHelper.Debug(sqlString); + //Console.WriteLine(sql); + } + }, + ConfigureExternalServices = new ConfigureExternalServices() + { + EntityService = (property, column) => + { + //闄や富閿閮藉彲绌� + if (!column.IsPrimarykey) + { + column.IsNullable = true; + } + + //if (column.DbColumnName == "Config") + //{ + // column.DataType = StaticConfig.CodeFirst_BigString; + //} + + } + } + }; + return config; + } + } /// <summary> /// 鍒濆鍖� Analysis 鏁版嵁搴� @@ -210,7 +257,6 @@ } } - /// <summary> /// 鍒濆鍖� Schedule 鏁版嵁搴� /// </summary> @@ -235,6 +281,28 @@ } } + /// <summary> + /// 鍒濆鍖� Hydraulic 鏁版嵁搴� + /// </summary> + public static void InitHydraulicDB(bool backup = false) + { + var sqlSugarClient = new SqlSugarClient(HydraulicConnectionConfig); + Type[] types = new Type[] { + typeof(Entity.HydraulicModelValidation), + typeof(Entity.HydraulicModelScada), + typeof(Entity.HydraulicModelRecord) + }; + sqlSugarClient.DbMaintenance.CreateDatabase(); + if (backup) + { + sqlSugarClient.CodeFirst.BackupTable().InitTables(types); + } + else + { + sqlSugarClient.CodeFirst.InitTables(types); + } + } + #endregion #region SQLite-Sql diff --git a/IStation.Service/07-algorithm/00-core/DbInitHelper.cs b/IStation.Service/00-core/DbInitHelper.cs similarity index 80% rename from IStation.Service/07-algorithm/00-core/DbInitHelper.cs rename to IStation.Service/00-core/DbInitHelper.cs index d4670bc..a8f6b1b 100644 --- a/IStation.Service/07-algorithm/00-core/DbInitHelper.cs +++ b/IStation.Service/00-core/DbInitHelper.cs @@ -1,4 +1,4 @@ -锘縩amespace IStation.Algorithm +锘縩amespace IStation.Service { /// <summary> /// 鏁版嵁搴撳垵濮嬪寲杈呭姪绫� @@ -13,8 +13,10 @@ { IStation.ConfigHelper.DeleteDataBase(Settings.ParasHelper.DataBase.SQLite.AnalysisConnectString); IStation.ConfigHelper.DeleteDataBase(Settings.ParasHelper.DataBase.SQLite.ScheduleConnectString); + IStation.ConfigHelper.DeleteDataBase(Settings.ParasHelper.DataBase.SQLite.HydraulicConnectString); IStation.ConfigHelper.InitAnalysisDB(); IStation.ConfigHelper.InitScheduleDB(); + IStation.ConfigHelper.InitHydraulicDB(); var station = new IStation.Service.Station().Get(); var helper = new IStation.Algorithm.AnalysisHelper(); diff --git a/IStation.Service/00-core/eModelStatus.cs b/IStation.Service/00-core/eModelStatus.cs new file mode 100644 index 0000000..6e08f25 --- /dev/null +++ b/IStation.Service/00-core/eModelStatus.cs @@ -0,0 +1,11 @@ +锘縩amespace IStation +{ + /// <summary> + /// 妯″瀷鐘舵�� + /// </summary> + public enum eModelStatus + { + + } + +} diff --git a/IStation.Service/00-core/eValueType.cs b/IStation.Service/00-core/eValueType.cs new file mode 100644 index 0000000..76fab3d --- /dev/null +++ b/IStation.Service/00-core/eValueType.cs @@ -0,0 +1,26 @@ +锘縩amespace IStation +{ + /// <summary> + /// 鍊肩被鍨� + /// </summary> + public enum eValueType + { + + /// <summary> + /// 娴侀噺 + /// </summary> + Flow = 1, + + /// <summary> + /// 鎵▼ + /// </summary> + Head = 2, + + /// <summary> + /// 寮�鍏崇姸鎬� + /// </summary> + OnOff = 3 + + } + +} diff --git a/IStation.Service/01-entity/03-hydraulic/HydraulicModelRecord.cs b/IStation.Service/01-entity/03-hydraulic/HydraulicModelRecord.cs new file mode 100644 index 0000000..80079d0 --- /dev/null +++ b/IStation.Service/01-entity/03-hydraulic/HydraulicModelRecord.cs @@ -0,0 +1,84 @@ +锘縩amespace IStation.Entity +{ + /// <summary> + /// 姘村姏妯″瀷璁板綍 + /// </summary> + public class HydraulicModelRecord : System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelRecord() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelRecord(HydraulicModelRecord rhs) + { + this.VerificationID = rhs.VerificationID; + this.Time = rhs.Time; + this.ValueType = rhs.ValueType; + this.ModelId = rhs.ModelId; + this.ScadaId = rhs.ScadaId; + this.ModeValue = rhs.ModeValue; + this.ScadaValue = rhs.ScadaValue; + this.DifferenceValue = rhs.DifferenceValue; + } + + /// <summary> + /// 楠岃瘉鏍囪瘑 + /// </summary> + public long VerificationID { get; set; } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鍊肩被鍨� + /// </summary> + public int ValueType { get; set; } + + /// <summary> + /// 妯″瀷鏍囪瘑 + /// </summary> + public string ModelId { get; set; } + + /// <summary> + /// Scada鏍囪瘑 + /// </summary> + public string ScadaId { get; set; } + + /// <summary> + /// 妯″瀷鍊� + /// </summary> + public double? ModeValue { get; set; } + + /// <summary> + /// Scada鍊� + /// </summary> + public double? ScadaValue { get; set; } + + /// <summary> + /// 宸�� + /// </summary> + public double? DifferenceValue { get; set; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelRecord Clone() + { + return (HydraulicModelRecord)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } + +} \ No newline at end of file diff --git a/IStation.Service/01-entity/03-hydraulic/HydraulicModelScada.cs b/IStation.Service/01-entity/03-hydraulic/HydraulicModelScada.cs new file mode 100644 index 0000000..9c5a979 --- /dev/null +++ b/IStation.Service/01-entity/03-hydraulic/HydraulicModelScada.cs @@ -0,0 +1,60 @@ +锘縩amespace IStation.Entity +{ + /// <summary> + /// + /// </summary> + public class HydraulicModelScada : System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelScada() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelScada(HydraulicModelScada rhs) + { + this.VerificationID = rhs.VerificationID; + this.Time = rhs.Time; + this.Tag = rhs.Tag; + this.Value = rhs.Value; + } + + /// <summary> + /// 璋冨害璇锋眰鏍囪瘑 + /// </summary> + public long VerificationID { get; set; } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鏍囪瘑 + /// </summary> + public string Tag { get; set; } + + /// <summary> + /// 鍊� + /// </summary> + public double? Value { get; set; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelScada Clone() + { + return (HydraulicModelScada)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } + +} \ No newline at end of file diff --git a/IStation.Service/01-entity/03-hydraulic/HydraulicModelValidation.cs b/IStation.Service/01-entity/03-hydraulic/HydraulicModelValidation.cs new file mode 100644 index 0000000..082edf1 --- /dev/null +++ b/IStation.Service/01-entity/03-hydraulic/HydraulicModelValidation.cs @@ -0,0 +1,50 @@ +锘縩amespace IStation.Entity +{ + /// <summary> + /// 姘村姏妯″瀷楠岃瘉 + /// </summary> + public class HydraulicModelValidation :BaseEntity, System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelValidation() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelValidation(HydraulicModelValidation rhs) + { + this.Time = rhs.Time; + this.Config = rhs.Config; + } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 閰嶇疆Json + /// </summary> + [SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)] + public string Config { get; set; } + + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelValidation Clone() + { + return (HydraulicModelValidation)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } + +} \ No newline at end of file diff --git a/IStation.Service/02-model/00-basic/00-station/Station.cs b/IStation.Service/02-model/00-basic/00-station/Station.cs index 51a14c7..8b02b73 100644 --- a/IStation.Service/02-model/00-basic/00-station/Station.cs +++ b/IStation.Service/02-model/00-basic/00-station/Station.cs @@ -46,28 +46,28 @@ /// <summary> /// 闄堣1杈撴按杩愯鏍囧織 /// </summary> - public List<int> S1Flags { get; set; } = new List<int>() { 11, 12, 13, 14, 15, 16, 17, 18 }; + public List<int> S1Flags { get; set; } = new List<int>();//{ 11, 12, 13, 14, 15, 16, 17, 18 }; /// <summary> /// 闄堣1杈撴按杩愯鏍囧織 閮ㄥ垎1 /// </summary> - public List<int> S1FlagsPart1 { get; set; } = new List<int>() { 11, 12, 13, 14, 16, 17, 18 }; + public List<int> S1FlagsPart1 { get; set; } = new List<int>();//{ 11, 12, 13, 14, 16, 17, 18 }; /// <summary> /// 闄堣1杈撴按杩愯鏍囧織 閮ㄥ垎2 /// </summary> - public List<int> S1FlagsPart2 { get; set; } = new List<int>() { 15 }; + public List<int> S1FlagsPart2 { get; set; } = new List<int>();//{ 15 }; /// <summary> /// 闄堣2杈撴按杩愯鏍囧織 /// </summary> - public List<int> S2Flags { get; set; } = new List<int>() { 21, 22, 23, 24, 25, 26, 27 }; + public List<int> S2Flags { get; set; } = new List<int>();//{ 21, 22, 23, 24, 25, 26, 27 }; /// <summary> /// 闄堣2杈撴按杩愯鏍囧織 閮ㄥ垎1 /// </summary> - public List<int> S2FlagsPart1 { get; set; } = new List<int>() { 22, 23, 24, 25, 26 }; + public List<int> S2FlagsPart1 { get; set; } = new List<int>();//{ 22, 23, 24, 25, 26 }; /// <summary> /// 闄堣2杈撴按杩愯鏍囧織 閮ㄥ垎2 /// </summary> - public List<int> S2FlagsPart2 { get; set; } = new List<int>() { 21, 27 }; + public List<int> S2FlagsPart2 { get; set; } = new List<int>();//{ 21, 27 }; /// <summary> /// 闄堣1杈撴按 diff --git a/IStation.Service/02-model/00-basic/03-verification/HydraulicModelValidationConfig.cs b/IStation.Service/02-model/00-basic/03-verification/HydraulicModelValidationConfig.cs new file mode 100644 index 0000000..ff40733 --- /dev/null +++ b/IStation.Service/02-model/00-basic/03-verification/HydraulicModelValidationConfig.cs @@ -0,0 +1,29 @@ +锘縩amespace IStation.Model +{ + /// <summary> + /// 姘村姏妯″瀷楠岃瘉 + /// </summary> + public class HydraulicModelValidationConfig + { + /// <summary> + /// 娴侀噺鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string, string> FlowIdMappingDict { get; set; } + + /// <summary> + /// 鍘嬪姏鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string, string> PressureIdMappingDict { get; set; } + + /// <summary> + /// 妯″紡鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string,string> PatternIdMappingDict { get; set; } + + /// <summary> + /// 鍘嬪姏鍗冨笗鏍囪瘑鍒楄〃 + /// </summary> + public List<string> PressureIdkPaList { get; set; } + + } +} diff --git a/IStation.Service/02-model/03-hydraulic/HydraulicModelRecord.cs b/IStation.Service/02-model/03-hydraulic/HydraulicModelRecord.cs new file mode 100644 index 0000000..212a388 --- /dev/null +++ b/IStation.Service/02-model/03-hydraulic/HydraulicModelRecord.cs @@ -0,0 +1,100 @@ +锘縩amespace IStation.Model +{ + /// <summary> + /// 姘村姏妯″瀷璁板綍 + /// </summary> + public class HydraulicModelRecord : System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelRecord() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelRecord(HydraulicModelRecord rhs) + { + this.VerificationID = rhs.VerificationID; + this.Time = rhs.Time; + this.ValueType = rhs.ValueType; + this.ModelId = rhs.ModelId; + this.ScadaId = rhs.ScadaId; + this.ModeValue = rhs.ModeValue; + this.ScadaValue = rhs.ScadaValue; + this.DifferenceValue = rhs.DifferenceValue; + } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public void Reset(HydraulicModelRecord rhs) + { + this.VerificationID= rhs.VerificationID; + this.Time = rhs.Time; + this.ValueType = rhs.ValueType; + this.ModelId = rhs.ModelId; + this.ScadaId = rhs.ScadaId; + this.ModeValue = rhs.ModeValue; + this.ScadaValue = rhs.ScadaValue; + this.DifferenceValue = rhs.DifferenceValue; + } + + /// <summary> + /// 楠岃瘉鏍囪瘑 + /// </summary> + public long VerificationID { get; set; } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鍊肩被鍨� + /// </summary> + public eValueType ValueType { get; set; } + + /// <summary> + /// 妯″瀷鏍囪瘑 + /// </summary> + public string ModelId { get; set; } + + /// <summary> + /// Scada鏍囪瘑 + /// </summary> + public string ScadaId { get; set; } + + /// <summary> + /// 妯″瀷鍊� + /// </summary> + public double? ModeValue { get; set; } + + /// <summary> + /// Scada鍊� + /// </summary> + public double? ScadaValue { get; set; } + + /// <summary> + /// 宸�� + /// </summary> + public double? DifferenceValue { get; set; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelRecord Clone() + { + return (HydraulicModelRecord)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + + } +} \ No newline at end of file diff --git a/IStation.Service/02-model/03-hydraulic/HydraulicModelScada.cs b/IStation.Service/02-model/03-hydraulic/HydraulicModelScada.cs new file mode 100644 index 0000000..6756549 --- /dev/null +++ b/IStation.Service/02-model/03-hydraulic/HydraulicModelScada.cs @@ -0,0 +1,60 @@ +锘縩amespace IStation.Model +{ + /// <summary> + /// + /// </summary> + public class HydraulicModelScada : System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelScada() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelScada(HydraulicModelScada rhs) + { + this.VerificationID = rhs.VerificationID; + this.Time = rhs.Time; + this.Tag = rhs.Tag; + this.Value = rhs.Value; + } + + /// <summary> + /// 璋冨害璇锋眰鏍囪瘑 + /// </summary> + public long VerificationID { get; set; } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鏍囪瘑 + /// </summary> + public string Tag { get; set; } + + /// <summary> + /// 鍊� + /// </summary> + public double? Value { get; set; } + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelScada Clone() + { + return (HydraulicModelScada)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } + +} \ No newline at end of file diff --git a/IStation.Service/02-model/03-hydraulic/HydraulicModelValidation.cs b/IStation.Service/02-model/03-hydraulic/HydraulicModelValidation.cs new file mode 100644 index 0000000..370e7ce --- /dev/null +++ b/IStation.Service/02-model/03-hydraulic/HydraulicModelValidation.cs @@ -0,0 +1,59 @@ +锘縩amespace IStation.Model +{ + /// <summary> + /// 姘村姏妯″瀷楠岃瘉 + /// </summary> + public class HydraulicModelValidation :BaseModel, System.ICloneable + { + /// <summary> + /// + /// </summary> + public HydraulicModelValidation() { } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public HydraulicModelValidation(HydraulicModelValidation rhs) + { + this.Time = rhs.Time; + this.Config = rhs.Config; + } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public void Reset(HydraulicModelValidation rhs) + { + this.Time = rhs.Time; + this.Config = rhs.Config; + } + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 閰嶇疆Json + /// </summary> + public string Config { get; set; } + + + /// <summary> + /// + /// </summary> + /// <returns></returns> + public HydraulicModelValidation Clone() + { + return (HydraulicModelValidation)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } + +} \ No newline at end of file diff --git a/IStation.Service/03-settings/helper/ParasHelper.cs b/IStation.Service/03-settings/helper/ParasHelper.cs index f67ecd7..a384e72 100644 --- a/IStation.Service/03-settings/helper/ParasHelper.cs +++ b/IStation.Service/03-settings/helper/ParasHelper.cs @@ -30,6 +30,14 @@ } /// <summary> + /// Task + /// </summary> + public static Paras_Task Task + { + get { return FileHelper.Get().Task; } + } + + /// <summary> /// 淇濆瓨 /// </summary> public static bool Save() diff --git a/IStation.Service/03-settings/paras/Paras.cs b/IStation.Service/03-settings/paras/Paras.cs index 7e7621f..8a2c8fd 100644 --- a/IStation.Service/03-settings/paras/Paras.cs +++ b/IStation.Service/03-settings/paras/Paras.cs @@ -21,6 +21,11 @@ public Paras_DataBase DataBase { get; set; } /// <summary> + /// Task + /// </summary> + public Paras_Task Task { get; set; } + + /// <summary> /// CAL /// </summary> public Paras_CAL CAL { get; set; } diff --git a/IStation.Service/03-settings/paras/Paras_DataBase_SQLite.cs b/IStation.Service/03-settings/paras/Paras_DataBase_SQLite.cs index 32703a2..1922af7 100644 --- a/IStation.Service/03-settings/paras/Paras_DataBase_SQLite.cs +++ b/IStation.Service/03-settings/paras/Paras_DataBase_SQLite.cs @@ -15,5 +15,10 @@ /// </summary> public string ScheduleConnectString { get; set; } + /// <summary> + /// Hydraulic 杩炴帴瀛楃涓� + /// </summary> + public string HydraulicConnectString { get; set; } + } } diff --git a/IStation.Service/03-settings/paras/Paras_LocalFile.cs b/IStation.Service/03-settings/paras/Paras_LocalFile.cs index 9b40d95..2cbb6bb 100644 --- a/IStation.Service/03-settings/paras/Paras_LocalFile.cs +++ b/IStation.Service/03-settings/paras/Paras_LocalFile.cs @@ -58,6 +58,17 @@ /// 璋冨害閰嶇疆鏂囦欢 /// </summary> public string ScheduleConfigFile { get; set; } = "ScheduleConfig.json"; - + + /// <summary> + /// 妯″瀷鏂囦欢 + /// </summary> + public string HydraulicModelFile { get; set; } = "HydraulicModel.inp"; + + /// <summary> + /// 妯″瀷楠岃瘉鏂囦欢 + /// </summary> + public string HydraulicModelValidationConfigFile { get; set; } = "HydraulicModelValidationConfig.json"; + + } } diff --git a/IStation.Service/03-settings/paras/Paras_Task.cs b/IStation.Service/03-settings/paras/Paras_Task.cs new file mode 100644 index 0000000..803a3ac --- /dev/null +++ b/IStation.Service/03-settings/paras/Paras_Task.cs @@ -0,0 +1,14 @@ +锘縩amespace IStation.Settings +{ + /// <summary> + /// + /// </summary> + public class Paras_Task + { + /// <summary> + /// + /// </summary> + public string MinuteCron { get; set; } = "4 * * * * ?"; + + } +} diff --git a/IStation.Service/03-settings/paras/Paras_Docking.cs b/IStation.Service/03-settings/paras/Paras_ZyDocking.cs similarity index 62% rename from IStation.Service/03-settings/paras/Paras_Docking.cs rename to IStation.Service/03-settings/paras/Paras_ZyDocking.cs index 45195a4..9a2f384 100644 --- a/IStation.Service/03-settings/paras/Paras_Docking.cs +++ b/IStation.Service/03-settings/paras/Paras_ZyDocking.cs @@ -8,12 +8,7 @@ /// <summary> /// 鑾峰彇 Scada鏁版嵁 Http鍦板潃 /// </summary> - public string ScadaHttpUrl { get; set; } - - /// <summary> - /// 鑾峰彇 璋冨害瑙勫垯 Http鍦板潃 - /// </summary> - public string ScheduleRuleHttpUrl { get; set; } + public string ScadaHttpUrl { get; set; } /// <summary> /// 鏄惁鍚敤 diff --git a/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelRecord.cs b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelRecord.cs new file mode 100644 index 0000000..db64d7f --- /dev/null +++ b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelRecord.cs @@ -0,0 +1,34 @@ +锘縩amespace IStation.DAL +{ + /// <summary> + /// + /// </summary> + public interface IHydraulicModelRecord + { + /// <summary> + /// + /// </summary> + ConnectionConfig ConnectionConfig { get; } + + /// <summary> + /// + /// </summary> + bool Inserts(List<Entity.HydraulicModelRecord> list); + + /// <summary> + /// + /// </summary> + bool BulkInserts(List<Entity.HydraulicModelRecord> list); + + /// <summary> + /// + /// </summary> + bool DeleteAll(); + + /// <summary> + /// + /// </summary> + List<Entity.HydraulicModelRecord> GetAll(); + + } +} \ No newline at end of file diff --git a/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelScada.cs b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelScada.cs new file mode 100644 index 0000000..4ddc455 --- /dev/null +++ b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelScada.cs @@ -0,0 +1,34 @@ +锘縩amespace IStation.DAL +{ + /// <summary> + /// + /// </summary> + public interface IHydraulicModelScada + { + /// <summary> + /// + /// </summary> + ConnectionConfig ConnectionConfig { get; } + + /// <summary> + /// + /// </summary> + bool Inserts(List<Entity.HydraulicModelScada> list); + + /// <summary> + /// + /// </summary> + bool BulkInserts(List<Entity.HydraulicModelScada> list); + + /// <summary> + /// + /// </summary> + bool DeleteAll(); + + /// <summary> + /// + /// </summary> + List<Entity.HydraulicModelScada> GetAll(); + + } +} \ No newline at end of file diff --git a/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelValidation.cs b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelValidation.cs new file mode 100644 index 0000000..b283e83 --- /dev/null +++ b/IStation.Service/04-dal/01-interface/03-hydraulic/IHydraulicModelValidation.cs @@ -0,0 +1,11 @@ +锘縩amespace IStation.DAL +{ + /// <summary> + /// + /// </summary> + public interface IHydraulicModelValidation : IBaseDAL<Entity.HydraulicModelValidation> + { + + + } +} \ No newline at end of file diff --git a/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelRecord.cs b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelRecord.cs new file mode 100644 index 0000000..aeaf72c --- /dev/null +++ b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelRecord.cs @@ -0,0 +1,76 @@ +锘縩amespace IStation.DAL.PostgreSql +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelRecord : IHydraulicModelRecord + { + /// <summary> + /// + /// </summary> + public ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.PostgreSqlConnectionConfig; } + } + + + /// <summary> + /// 鎵归噺鎻掑叆 + /// </summary> + public bool Inserts(List<Entity.HydraulicModelRecord> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Insertable(list).ExecuteCommand() > 0; + } + } + + + /// <summary> + /// 澶ф壒閲忔彃鍏� + /// </summary> + public bool BulkInserts(List<Entity.HydraulicModelRecord> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Fastest<Entity.HydraulicModelRecord>().BulkCopy(list) > 0; + } + } + + + /// <summary> + /// 鍒犻櫎鍏ㄩ儴 + /// </summary> + public bool DeleteAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.DbMaintenance.TruncateTable<Entity.HydraulicModelRecord>(); + } + } + + + /// <summary> + /// 鏌ヨ鍏ㄩ儴 + /// </summary> + /// <returns></returns> + public List<Entity.HydraulicModelRecord> GetAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Queryable<Entity.HydraulicModelRecord>().ToList(); + } + } + + } +} diff --git a/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelScada.cs b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelScada.cs new file mode 100644 index 0000000..541849a --- /dev/null +++ b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelScada.cs @@ -0,0 +1,76 @@ +锘縩amespace IStation.DAL.PostgreSql +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelScada : IHydraulicModelScada + { + /// <summary> + /// + /// </summary> + public ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.PostgreSqlConnectionConfig; } + } + + + /// <summary> + /// 鎵归噺鎻掑叆 + /// </summary> + public bool Inserts(List<Entity.HydraulicModelScada> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Insertable(list).ExecuteCommand() > 0; + } + } + + + /// <summary> + /// 澶ф壒閲忔彃鍏� + /// </summary> + public bool BulkInserts(List<Entity.HydraulicModelScada> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Fastest<Entity.HydraulicModelScada>().BulkCopy(list) > 0; + } + } + + + /// <summary> + /// 鍒犻櫎鍏ㄩ儴 + /// </summary> + public bool DeleteAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.DbMaintenance.TruncateTable<Entity.HydraulicModelScada>(); + } + } + + + /// <summary> + /// 鏌ヨ鍏ㄩ儴 + /// </summary> + /// <returns></returns> + public List<Entity.HydraulicModelScada> GetAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Queryable<Entity.HydraulicModelScada>().ToList(); + } + } + + } +} diff --git a/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelValidation.cs b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelValidation.cs new file mode 100644 index 0000000..c74639b --- /dev/null +++ b/IStation.Service/04-dal/02-postgresql/03-hydraulic/HydraulicModelValidation.cs @@ -0,0 +1,17 @@ +锘縩amespace IStation.DAL.PostgreSql +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelValidation : Yw.DAL.PostgreSql.BaseDAL<Entity.HydraulicModelValidation>, IHydraulicModelValidation + { + /// <summary> + /// + /// </summary> + public override ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.PostgreSqlConnectionConfig; } + + } + } +} diff --git a/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelRecord.cs b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelRecord.cs new file mode 100644 index 0000000..ba60780 --- /dev/null +++ b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelRecord.cs @@ -0,0 +1,75 @@ +锘縩amespace IStation.DAL.SQLite +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelRecord : IHydraulicModelRecord + { + /// <summary> + /// + /// </summary> + public ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.HydraulicConnectionConfig; } + + } + + /// <summary> + /// 鎵归噺鎻掑叆 + /// </summary> + public bool Inserts(List<Entity.HydraulicModelRecord> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Insertable(list).ExecuteCommand() > 0; + } + } + + /// <summary> + /// 澶ф壒閲忔彃鍏� + /// </summary> + public bool BulkInserts(List<Entity.HydraulicModelRecord> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Fastest<Entity.HydraulicModelRecord>().BulkCopy(list) > 0; + } + } + + + /// <summary> + /// 鍒犻櫎鍏ㄩ儴 + /// </summary> + public bool DeleteAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.DbMaintenance.TruncateTable<Entity.HydraulicModelRecord>(); + } + } + + + /// <summary> + /// 鏌ヨ鍏ㄩ儴 + /// </summary> + /// <returns></returns> + public List<Entity.HydraulicModelRecord> GetAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Queryable<Entity.HydraulicModelRecord>().ToList(); + } + } + + } +} \ No newline at end of file diff --git a/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelScada.cs b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelScada.cs new file mode 100644 index 0000000..e083fe4 --- /dev/null +++ b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelScada.cs @@ -0,0 +1,75 @@ +锘縩amespace IStation.DAL.SQLite +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelScada : IHydraulicModelScada + { + /// <summary> + /// + /// </summary> + public ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.HydraulicConnectionConfig; } + + } + + /// <summary> + /// 鎵归噺鎻掑叆 + /// </summary> + public bool Inserts(List<Entity.HydraulicModelScada> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Insertable(list).ExecuteCommand() > 0; + } + } + + /// <summary> + /// 澶ф壒閲忔彃鍏� + /// </summary> + public bool BulkInserts(List<Entity.HydraulicModelScada> list) + { + if (list == null || list.Count < 1) + { + return false; + } + + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Fastest<Entity.HydraulicModelScada>().BulkCopy(list) > 0; + } + } + + + /// <summary> + /// 鍒犻櫎鍏ㄩ儴 + /// </summary> + public bool DeleteAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.DbMaintenance.TruncateTable<Entity.HydraulicModelScada>(); + } + } + + + /// <summary> + /// 鏌ヨ鍏ㄩ儴 + /// </summary> + /// <returns></returns> + public List<Entity.HydraulicModelScada> GetAll() + { + using (SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig)) + { + return sqlSugarClient.Queryable<Entity.HydraulicModelScada>().ToList(); + } + } + + } +} \ No newline at end of file diff --git a/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelValidation.cs b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelValidation.cs new file mode 100644 index 0000000..1c0fb39 --- /dev/null +++ b/IStation.Service/04-dal/03-sqlite/03-hydraulic/HydraulicModelValidation.cs @@ -0,0 +1,42 @@ +锘縩amespace IStation.DAL.SQLite +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelValidation : Yw.DAL.SQLite.BaseDAL<Entity.HydraulicModelValidation>, IHydraulicModelValidation + { + /// <summary> + /// + /// </summary> + public override ConnectionConfig ConnectionConfig + { + get { return ConfigHelper.HydraulicConnectionConfig; } + } + + + /// <summary> + /// + /// </summary> + /// <param name="entity"></param> + /// <returns></returns> + public override long Insert(Entity.HydraulicModelValidation entity) + { + if (entity == null) + { + return 0L; + } + + using SqlSugarClient sqlSugarClient = new SqlSugarClient(ConnectionConfig); + if (entity.ID > 0) + { + if (sqlSugarClient.Insertable(entity).ExecuteCommand() <= 0) + { + return 0L; + } + return entity.ID; + } + return sqlSugarClient.Insertable(entity).ExecuteReturnSnowflakeId(); + } + + } +} \ No newline at end of file diff --git a/IStation.Service/05-service/00-basic/HydraulicModelValidationConfig.cs b/IStation.Service/05-service/00-basic/HydraulicModelValidationConfig.cs new file mode 100644 index 0000000..8fcb3ad --- /dev/null +++ b/IStation.Service/05-service/00-basic/HydraulicModelValidationConfig.cs @@ -0,0 +1,43 @@ +锘縩amespace IStation.Service +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelValidationConfig + { + Model.HydraulicModelValidationConfig _model = null; + private readonly string _filePath = Path.Combine( + Settings.ParasHelper.LocalFile.DataFolderDirectory, + Settings.ParasHelper.LocalFile.HydraulicModelValidationConfigFile); + /// <summary> + /// 鑾峰彇 + /// </summary> + public Model.HydraulicModelValidationConfig Get() + { + if (_model == null) + { + if (!File.Exists(_filePath)) + return default; + var json = File.ReadAllText(_filePath); + _model = JsonHelper.Json2Object<Model.HydraulicModelValidationConfig>(json); + } + return _model; + } + + /// <summary> + /// 淇濆瓨 + /// </summary> + /// <param name="model"></param> + /// <returns></returns> + public bool Save(Model.HydraulicModelValidationConfig model) + { + _model = model; + if (model == null) + return false; + var json = JsonHelper.Object2Json(model); + File.WriteAllText(_filePath, json); + return true; + } + + } +} \ No newline at end of file diff --git a/IStation.Service/05-service/01-analysis/AnalysisConclusion.cs b/IStation.Service/05-service/01-analysis/AnalysisConclusion.cs index ddc9fbc..451e01b 100644 --- a/IStation.Service/05-service/01-analysis/AnalysisConclusion.cs +++ b/IStation.Service/05-service/01-analysis/AnalysisConclusion.cs @@ -30,6 +30,21 @@ }, ConfigHelper.CacheKeepTime, ConfigHelper.CacheRandomTime); } + + /// <summary> + /// 鏌ヨ + /// </summary> + public List<Model.AnalysisConclusion> GetList(string runFlag) + { + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IAnalysisConclusion>(); + var tableName = dal.GetTableName(runFlag); + var dict = GetDictCache(); + if (!dict.ContainsKey(tableName)) + return default; + + return dict[tableName]?.OrderBy(x => x.Power).ToList(); + } + /// <summary> /// 鏌ヨ /// </summary> diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord.cs new file mode 100644 index 0000000..c2d5601 --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord.cs @@ -0,0 +1,54 @@ +锘縩amespace IStation.Service +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelRecord + { + #region Cache + + private static List<Model.HydraulicModelRecord> GetCache() + { + return CacheHelper<Model.HydraulicModelRecord>.GetSet(() => + { + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelRecord>(); + var entity_list = dal.GetAll(); + var model_list = Entity2Models(entity_list); + if (model_list == null) + { + model_list = new List<Model.HydraulicModelRecord>(); + } + return model_list; + }, ConfigHelper.CacheKeepTime, ConfigHelper.CacheRandomTime); + } + + + #endregion + + /// <summary> + /// 鑾峰彇鎵�鏈� + /// </summary> + public List<Model.HydraulicModelRecord> GetAll() + { + var all = GetCache(); + return all.ToList(); + } + + + /// <summary> + /// 鎻掑叆澶氭潯 + /// </summary> + public bool Inserts(List<Model.HydraulicModelRecord> list) + { + if (list == null || list.Count < 1) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelRecord>(); + var entity_list = Model2Entities(list); + var bol = dal.Inserts(entity_list); + return bol; + } + + + + } +} \ No newline at end of file diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord_Instance.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord_Instance.cs new file mode 100644 index 0000000..a4cd7c1 --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelRecord_Instance.cs @@ -0,0 +1,63 @@ +锘縩amespace IStation.Service +{ + public partial class HydraulicModelRecord + { + + //Entity to GetModel + private static Model.HydraulicModelRecord Entity2Model(Entity.HydraulicModelRecord entity) + { + if (entity == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelRecord, Model.HydraulicModelRecord>()) + .CreateMapper(); + var model = mapper.Map<Entity.HydraulicModelRecord, Model.HydraulicModelRecord>(entity); + return model; + } + + //Entities to GetModels + private static List<Model.HydraulicModelRecord> Entity2Models(List<Entity.HydraulicModelRecord> entities) + { + if (entities == null || entities.Count() < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelRecord, Model.HydraulicModelRecord>()) + .CreateMapper(); + var models = mapper.Map<List<Entity.HydraulicModelRecord>, List<Model.HydraulicModelRecord>>(entities); + return models; + } + + //Model to Entity + private static Entity.HydraulicModelRecord Model2Entity(Model.HydraulicModelRecord model) + { + if (model == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelRecord, Entity.HydraulicModelRecord>() + ).CreateMapper(); + var entity = mapper.Map<Model.HydraulicModelRecord, Entity.HydraulicModelRecord>(model); + return entity; + } + + //Models to Entities + private static List<Entity.HydraulicModelRecord> Model2Entities(List<Model.HydraulicModelRecord> models) + { + if (models == null || models.Count < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelRecord, Entity.HydraulicModelRecord>() + ).CreateMapper(); + var entities = mapper.Map<List<Model.HydraulicModelRecord>, List<Entity.HydraulicModelRecord>>(models); + return entities; + } + + //Model to Entity + private static void Model2Entity(Model.HydraulicModelRecord model, Entity.HydraulicModelRecord entity) + { + if (model == null || entity == null) + return; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelRecord, Entity.HydraulicModelRecord>() + ).CreateMapper(); + mapper.Map(model, entity); + } + + + + } +} diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelScada.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelScada.cs new file mode 100644 index 0000000..9cd13ce --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelScada.cs @@ -0,0 +1,54 @@ +锘縩amespace IStation.Service +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelScada + { + #region Cache + + private static List<Model.HydraulicModelScada> GetCache() + { + return CacheHelper<Model.HydraulicModelScada>.GetSet(() => + { + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelScada>(); + var entity_list = dal.GetAll(); + var model_list = Entity2Models(entity_list); + if (model_list == null) + { + model_list = new List<Model.HydraulicModelScada>(); + } + return model_list; + }, ConfigHelper.CacheKeepTime, ConfigHelper.CacheRandomTime); + } + + + #endregion + + /// <summary> + /// 鑾峰彇鎵�鏈� + /// </summary> + public List<Model.HydraulicModelScada> GetAll() + { + var all = GetCache(); + return all.ToList(); + } + + + /// <summary> + /// 鎻掑叆澶氭潯 + /// </summary> + public bool Inserts(List<Model.HydraulicModelScada> list) + { + if (list == null || list.Count < 1) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelScada>(); + var entity_list = Model2Entities(list); + var bol = dal.Inserts(entity_list); + return bol; + } + + + + } +} \ No newline at end of file diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelScada_Instance.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelScada_Instance.cs new file mode 100644 index 0000000..ca2e390 --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelScada_Instance.cs @@ -0,0 +1,63 @@ +锘縩amespace IStation.Service +{ + public partial class HydraulicModelScada + { + + //Entity to GetModel + private static Model.HydraulicModelScada Entity2Model(Entity.HydraulicModelScada entity) + { + if (entity == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelScada, Model.HydraulicModelScada>()) + .CreateMapper(); + var model = mapper.Map<Entity.HydraulicModelScada, Model.HydraulicModelScada>(entity); + return model; + } + + //Entities to GetModels + private static List<Model.HydraulicModelScada> Entity2Models(List<Entity.HydraulicModelScada> entities) + { + if (entities == null || entities.Count() < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelScada, Model.HydraulicModelScada>()) + .CreateMapper(); + var models = mapper.Map<List<Entity.HydraulicModelScada>, List<Model.HydraulicModelScada>>(entities); + return models; + } + + //Model to Entity + private static Entity.HydraulicModelScada Model2Entity(Model.HydraulicModelScada model) + { + if (model == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelScada, Entity.HydraulicModelScada>() + ).CreateMapper(); + var entity = mapper.Map<Model.HydraulicModelScada, Entity.HydraulicModelScada>(model); + return entity; + } + + //Models to Entities + private static List<Entity.HydraulicModelScada> Model2Entities(List<Model.HydraulicModelScada> models) + { + if (models == null || models.Count < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelScada, Entity.HydraulicModelScada>() + ).CreateMapper(); + var entities = mapper.Map<List<Model.HydraulicModelScada>, List<Entity.HydraulicModelScada>>(models); + return entities; + } + + //Model to Entity + private static void Model2Entity(Model.HydraulicModelScada model, Entity.HydraulicModelScada entity) + { + if (model == null || entity == null) + return; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelScada, Entity.HydraulicModelScada>() + ).CreateMapper(); + mapper.Map(model, entity); + } + + + + } +} diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation.cs new file mode 100644 index 0000000..36f9686 --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation.cs @@ -0,0 +1,219 @@ +锘縩amespace IStation.Service +{ + /// <summary> + /// + /// </summary> + public partial class HydraulicModelValidation + { + #region Cache + + private static List<Model.HydraulicModelValidation> GetCache() + { + return CacheHelper<Model.HydraulicModelValidation>.GetSet(() => + { + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity_list = dal.GetAll(); + var model_list = Entity2Models(entity_list); + if (model_list == null) + { + model_list = new List<Model.HydraulicModelValidation>(); + } + return model_list; + }, ConfigHelper.CacheKeepTime, ConfigHelper.CacheRandomTime); + } + + private static void UpdateCache(long ID) + { + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity = dal.GetByID(ID); + var model = Entity2Model(entity); + var all = GetCache(); + var rhs = all.Find(x => x.ID == ID); + if (rhs == null) + { + all.Add(model); + } + else + { + rhs.Reset(model); + } + CacheHelper<Model.HydraulicModelValidation>.Trigger(); + } + + private static void UpdateCache(List<long> Ids) + { + if (Ids == null || Ids.Count < 1) + return; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity_list = dal.GetByIds(Ids); + var model_list = Entity2Models(entity_list); + var all = GetCache(); + all.RemoveAll(x => Ids.Contains(x.ID)); + if (model_list != null && model_list.Count > 0) + { + all.AddRange(model_list); + } + CacheHelper<Model.HydraulicModelValidation>.Trigger(); + } + + private static void RemoveCache(long ID) + { + var all = GetCache(); + all.RemoveAll(x => x.ID == ID); + CacheHelper<Model.HydraulicModelValidation>.Trigger(); + } + + private static void RemoveCache(List<long> Ids) + { + if (Ids == null || Ids.Count < 1) + return; + var all = GetCache(); + all.RemoveAll(x => Ids.Contains(x.ID)); + CacheHelper<Model.HydraulicModelValidation>.Trigger(); + } + + /// <summary> + /// + /// </summary> + public static void PublishCache(string Key) + { + CacheHelper<Model.HydraulicModelValidation>.Publish(Key); + } + + #endregion + + #region Query + + /// <summary> + /// 鑾峰彇鎵�鏈� + /// </summary> + public List<Model.HydraulicModelValidation> GetAll() + { + var all = GetCache(); + return all.ToList(); + } + + /// <summary> + /// 閫氳繃 ID 鑾峰彇 + /// </summary> + public Model.HydraulicModelValidation GetByID(long ID) + { + var all = GetAll(); + return all.Find(x => x.ID == ID); + } + + /// <summary> + /// 閫氳繃 Ids 鑾峰彇 + /// </summary> + public List<Model.HydraulicModelValidation> GetByIds(List<long> Ids) + { + if (Ids == null || Ids.Count < 1) + return default; + var all = GetAll(); + return all.Where(x => Ids.Contains(x.ID)).ToList(); + } + + #endregion + + #region Insert + + /// <summary> + /// 鎻掑叆涓�鏉℃暟鎹� + /// </summary> + public long Insert(Model.HydraulicModelValidation model) + { + if (model == null) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity = Model2Entity(model); + var id = dal.Insert(entity); + if (id > 0) + { + UpdateCache(id); + } + return id; + } + + /// <summary> + /// 鎻掑叆澶氭潯 + /// </summary> + public bool Inserts(List<Model.HydraulicModelValidation> list) + { + if (list == null || list.Count < 1) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity_list = Model2Entities(list); + var ids = dal.InsertsR(entity_list); + if (ids != null && ids.Count > 0) + { + UpdateCache(ids); + return true; + } + return false; + } + + #endregion + + #region Update + + /// <summary> + /// 鏇存柊涓�鏉� + /// </summary> + public bool Update(Model.HydraulicModelValidation model) + { + if (model == null) + return default; + if (model.ID < 1) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity_rhs = Model2Entity(model); + var bol = dal.Update(entity_rhs); + if (bol) + { + UpdateCache(model.ID); + } + return bol; + } + + /// <summary> + /// 鏇存柊澶氭潯 + /// </summary> + public bool Updates(List<Model.HydraulicModelValidation> list) + { + if (list == null || list.Count < 1) + return default; + if (list.ToList().Exists(x => x.ID < 1)) + return default; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var entity_list = Model2Entities(list.ToList()); + var bol = dal.Updates(entity_list); + if (bol) + { + UpdateCache(list.Select(x => x.ID).ToList()); + } + return bol; + } + + #endregion + + #region Delete + + /// <summary> + /// 閫氳繃 ID 鍒犻櫎 + /// </summary> + public bool DeleteByID(long ID, out string Msg) + { + Msg = string.Empty; + var dal = DALCreateHelper.CreateDAL<IStation.DAL.IHydraulicModelValidation>(); + var bol = dal.DeleteByID(ID); + if (bol) + { + RemoveCache(ID); + } + return bol; + } + + #endregion + + } +} \ No newline at end of file diff --git a/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation_Instance.cs b/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation_Instance.cs new file mode 100644 index 0000000..ce2c759 --- /dev/null +++ b/IStation.Service/05-service/03-hydraulic/HydraulicModelValidation_Instance.cs @@ -0,0 +1,63 @@ +锘縩amespace IStation.Service +{ + public partial class HydraulicModelValidation + { + + //Entity to GetModel + private static Model.HydraulicModelValidation Entity2Model(Entity.HydraulicModelValidation entity) + { + if (entity == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelValidation, Model.HydraulicModelValidation>()) + .CreateMapper(); + var model = mapper.Map<Entity.HydraulicModelValidation, Model.HydraulicModelValidation>(entity); + return model; + } + + //Entities to GetModels + private static List<Model.HydraulicModelValidation> Entity2Models(List<Entity.HydraulicModelValidation> entities) + { + if (entities == null || entities.Count() < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Entity.HydraulicModelValidation, Model.HydraulicModelValidation>()) + .CreateMapper(); + var models = mapper.Map<List<Entity.HydraulicModelValidation>, List<Model.HydraulicModelValidation>>(entities); + return models; + } + + //Model to Entity + private static Entity.HydraulicModelValidation Model2Entity(Model.HydraulicModelValidation model) + { + if (model == null) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelValidation, Entity.HydraulicModelValidation>() + ).CreateMapper(); + var entity = mapper.Map<Model.HydraulicModelValidation, Entity.HydraulicModelValidation>(model); + return entity; + } + + //Models to Entities + private static List<Entity.HydraulicModelValidation> Model2Entities(List<Model.HydraulicModelValidation> models) + { + if (models == null || models.Count < 1) + return default; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelValidation, Entity.HydraulicModelValidation>() + ).CreateMapper(); + var entities = mapper.Map<List<Model.HydraulicModelValidation>, List<Entity.HydraulicModelValidation>>(models); + return entities; + } + + //Model to Entity + private static void Model2Entity(Model.HydraulicModelValidation model, Entity.HydraulicModelValidation entity) + { + if (model == null || entity == null) + return; + var mapper = new MapperConfiguration(cfg => cfg.CreateMap<Model.HydraulicModelValidation, Entity.HydraulicModelValidation>() + ).CreateMapper(); + mapper.Map(model, entity); + } + + + + } +} diff --git a/IStation.Service/07-algorithm/02-schedule/ScheduleHelper.cs b/IStation.Service/07-algorithm/02-schedule/ScheduleHelper.cs index c22e2ed..a6a5961 100644 --- a/IStation.Service/07-algorithm/02-schedule/ScheduleHelper.cs +++ b/IStation.Service/07-algorithm/02-schedule/ScheduleHelper.cs @@ -428,6 +428,10 @@ //棰戠巼闄愬埗 var conclusionList = new List<Model.AnalysisConclusion>(); + //if (pump_bp_dict[flag] == false) + //{ + // conclusionList = _service_analysis_conclusion.GetList(runFlag); + //}else if (exist_frequency_limit_list && frequency_limit_flag_dict.ContainsKey(flag)) { var limit = frequency_limit_flag_dict[flag]; @@ -549,7 +553,7 @@ } } } - if (total_flow < target_flow) + if (total_flow < target_flow*0.99) continue; if (optimal_combine_part1 == null && optimal_combine_part2 == null) diff --git a/IStation.Service/08-hydraulic/00-core/Log.cs b/IStation.Service/08-hydraulic/00-core/Log.cs new file mode 100644 index 0000000..a19245e --- /dev/null +++ b/IStation.Service/08-hydraulic/00-core/Log.cs @@ -0,0 +1,45 @@ +锘縩amespace IStation.Hydraulic +{ + /// <summary> + /// + /// </summary> + public class Log + { + private static string ModeVerifyName = "ModeVerify"; + + /// <summary> + /// 鍐欏叆淇℃伅鏃ュ織 + /// </summary> + /// <param name="verifyId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + public static void Info(long verifyId, string title, string info) + { + Yw.LogHelper.Custom(ModeVerifyName, GetLogInfo(verifyId, title, info)); + } + + /// <summary> + /// 鍐欏叆璋冭瘯鏃ュ織 + /// </summary> + /// <param name="verifyId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + public static void Debug(long verifyId, string title, string info) + { + Yw.LogHelper.Custom(ModeVerifyName, (GetLogInfo(verifyId, title, info))); + } + + /// <summary> + /// + /// </summary> + /// <param name="verifyId"></param> + /// <param name="title"></param> + /// <param name="info"></param> + /// <returns></returns> + private static string GetLogInfo(long verifyId, string title, string info) + { + return $"{verifyId}-{title} >> {info}"; + } + + } +} diff --git a/IStation.Service/08-hydraulic/01-/DayValue.cs b/IStation.Service/08-hydraulic/01-/DayValue.cs new file mode 100644 index 0000000..78fa68b --- /dev/null +++ b/IStation.Service/08-hydraulic/01-/DayValue.cs @@ -0,0 +1,35 @@ +锘縩amespace IStation.Hydraulic +{ + /// <summary> + /// + /// </summary> + public class DayValue + { + /// <summary> + /// 骞� + /// </summary> + public int Year { get; set; } + + /// <summary> + /// 鏈� + /// </summary> + public int Month { get; set; } + + /// <summary> + /// 鏃� + /// </summary> + public int Day { get; set; } + + /// <summary> + /// 妯″紡鍒楄〃 + /// </summary> + public List<Pattern> PatternList { get; set; } + + /// <summary> + /// 鏃堕棿鍊煎垪琛� + /// </summary> + public List<TimeValue> TimeValueList { get; set; } + + } + +} diff --git a/IStation.Service/08-hydraulic/01-/DayValueHelper.cs b/IStation.Service/08-hydraulic/01-/DayValueHelper.cs new file mode 100644 index 0000000..0c936cb --- /dev/null +++ b/IStation.Service/08-hydraulic/01-/DayValueHelper.cs @@ -0,0 +1,45 @@ +锘縩amespace IStation.Hydraulic +{ + public class DayValueHelper + { + + public static List<DayValue> GetDayValues(int station) + { + string folderPath; + if (station==1) + { + folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣涓�杈�"; + } + else + { + folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣浜岃緭"; + } + + if (!Directory.Exists(folderPath)) + return default; + var fileList=Directory.GetFiles(folderPath); + if (fileList == null || !fileList.Any()) + return default; + + var list = new List<DayValue>(); + foreach (var file in fileList) + { + var fileName = Path.GetFileNameWithoutExtension(file); + if (!DateTime.TryParse(fileName, out DateTime day)) + continue; + var json=File.ReadAllText(file); + var timeValues = JsonHelper.Json2Object<List<TimeValue>>(json); + if (timeValues == null || !timeValues.Any()) + continue; + var dayValue = new DayValue(); + dayValue.Year = day.Year; + dayValue.Month = day.Month; + dayValue.Day = day.Day; + dayValue.TimeValueList = timeValues; + list.Add(dayValue); + } + + return list; + } + } +} diff --git a/IStation.Service/08-hydraulic/01-/Pattern.cs b/IStation.Service/08-hydraulic/01-/Pattern.cs new file mode 100644 index 0000000..2569c56 --- /dev/null +++ b/IStation.Service/08-hydraulic/01-/Pattern.cs @@ -0,0 +1,38 @@ +锘縩amespace IStation.Hydraulic +{ + /// <summary> + /// + /// </summary> + public class Pattern + { + /// <summary> + /// + /// </summary> + public Pattern() + { + + } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public Pattern(Pattern rhs) + { + this.Id = rhs.Id; + this.FactorList = rhs.FactorList; + } + + + /// <summary> + /// 鏍囪瘑 + /// </summary> + public string Id { get; set; } + + /// <summary> + /// 涔樺瓙鍒楄〃 + /// </summary> + public List<float> FactorList { get; set; } + + } +} diff --git a/IStation.Service/08-hydraulic/01-/TimeValue.cs b/IStation.Service/08-hydraulic/01-/TimeValue.cs new file mode 100644 index 0000000..f00eb6a --- /dev/null +++ b/IStation.Service/08-hydraulic/01-/TimeValue.cs @@ -0,0 +1,38 @@ +锘縩amespace IStation.Hydraulic +{ + /// <summary> + /// + /// </summary> + public class TimeValue + { + /// <summary> + /// + /// </summary> + public TimeValue() + { + + } + + /// <summary> + /// + /// </summary> + /// <param name="rhs"></param> + public TimeValue(TimeValue rhs) + { + this.Time = rhs.Time; + this.Value = rhs.Value; + } + + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鍊煎瓧鍏� + /// </summary> + public Dictionary<string, double> Value { get; set; } + + } +} diff --git a/IStation.Service/08-hydraulic/ModeVerifyHelper.cs b/IStation.Service/08-hydraulic/ModeVerifyHelper.cs new file mode 100644 index 0000000..833ced4 --- /dev/null +++ b/IStation.Service/08-hydraulic/ModeVerifyHelper.cs @@ -0,0 +1,343 @@ +锘縰sing IStation.Epanet; +using IStation.Epanet.Enums; + +namespace IStation.Hydraulic +{ + /// <summary> + /// 妯″瀷楠岃瘉杈呭姪绫� + /// </summary> + public partial class ModeVerifyHelper + { + + /// <summary> + /// 鍘嗗彶鏁版嵁楠岃瘉 + /// </summary> + /// <param name="verify_id">楠岃瘉鏍囪瘑</param> + /// <param name="file_path">鏂囦欢</param> + /// <param name="flow_id_mapping_dict">娴侀噺Scada鏍囪瘑瀛楀吀</param> + /// <param name="pressure_id_mapping_dict">鍘嬪姏Scada鏍囪瘑瀛楀吀</param> + /// <param name="day_value_list">姣忔棩鍒嗘瀽鏁版嵁</param> + /// <returns></returns> + public static List<Model.HydraulicModelRecord> Verify(long verify_id, string file_path, Dictionary<string, string> flow_id_mapping_dict, Dictionary<string, string> pressure_id_mapping_dict, List<DayValue> day_value_list) + { + var log_title = "鍒濆鍖�"; + var log_msg = string.Empty; + var hydraulic_record_list = new List<Model.HydraulicModelRecord>(); + if (flow_id_mapping_dict == null || !flow_id_mapping_dict.Any()) + { + log_msg = "娴侀噺鏍囩瀛楀吀涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + if (pressure_id_mapping_dict == null || !pressure_id_mapping_dict.Any()) + { + log_msg = "鍘嬪姏鏍囩瀛楀吀涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + if (day_value_list == null || !day_value_list.Any()) + { + log_msg = "鏃ラ獙璇佹暟鎹负绌�!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + + var err = EpanetMethods.ENopen(file_path, "", ""); + if (err != 0) + { + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + return default; + } + err = EpanetMethods.ENopenH(); + if (err != 0) + { + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + return default; + } + + log_msg = "鍒濆鍖栨垚鍔�"; + Log.Info(verify_id, log_title, log_msg); + + log_title = "楠岃瘉"; + var model_time_step = 0; + var model_id_build = new StringBuilder(31); + foreach (var day_value in day_value_list) + { + var pattern_list = day_value.PatternList; + var time_value_list = day_value.TimeValueList; + if (pattern_list == null || time_value_list == null) + { + continue; + } + var pattern_init = true; + foreach (var pattern in pattern_list) + { + var patternId = pattern.Id; + var pattern_factor_array = pattern.FactorList.ToArray(); + var pattern_factor_array_count = pattern_factor_array.Length == 0 ? 1 : pattern_factor_array.Length; + err = EpanetMethods.ENgetpatternindex(patternId, out int pattern_index); + if (err != ErrorCode.Ok) + { + pattern_init = false; + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + continue; + } + err = EpanetMethods.ENsetpattern(pattern_index, pattern_factor_array, pattern_factor_array_count); + if (err != ErrorCode.Ok) + { + pattern_init = false; + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + continue; + } + } + + if (!pattern_init) + { + log_msg = $"{day_value.Year}_{day_value.Month}_{day_value.Day}:妯″紡鍒濆鍖栧け璐�!"; + Log.Info(verify_id, log_title, log_msg); + continue; + } + + EpanetMethods.ENinitH(0); + do + { + EpanetMethods.ENrunH(out int current_ts); + EpanetMethods.ENgetcount(CountType.Link, out int link_count); + EpanetMethods.ENgetcount(CountType.Node, out int node_count); + + var current_time = TimeSpan.FromSeconds(current_ts); + var time_value = time_value_list.Find(x => x.Time.TimeOfDay == current_time); + if (time_value != null) + { + var time = time_value.Time; + var scada_value_dcit = time_value.Value; + for (int link_index = 1; link_index <= link_count; link_index++) + { + if (EpanetMethods.ENgetlinkid(link_index, model_id_build) != ErrorCode.Ok) + continue; + var model_id = model_id_build.ToString(); + + if (flow_id_mapping_dict.ContainsKey(model_id)) + { + var scada_id = flow_id_mapping_dict[model_id]; + var scada_value = scada_value_dcit[scada_id]; + EpanetMethods.ENgetlinkvalue(link_index, LinkValue.Flow, out float model_value); + + var record = new IStation.Model.HydraulicModelRecord(); + record.VerificationID = verify_id; + record.Time = time; + record.ModelId = model_id; + record.ScadaId = scada_id; + record.ValueType = IStation.eValueType.Flow; + record.ModeValue = (double)model_value; + record.ScadaValue = scada_value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + hydraulic_record_list.Add(record); + } + } + + for (int node_index = 1; node_index <= node_count; node_index++) + { + if (EpanetMethods.ENgetnodeid(node_index, model_id_build) != ErrorCode.Ok) + continue; + var model_id = model_id_build.ToString(); + + + if (pressure_id_mapping_dict.ContainsKey(model_id)) + { + var scada_id = pressure_id_mapping_dict[model_id]; + var scada_value = scada_value_dcit[scada_id]; + + EpanetMethods.ENgetnodevalue(node_index, NodeValue.Head, out float model_value); + + var record = new IStation.Model.HydraulicModelRecord(); + record.VerificationID = verify_id; + record.Time = time; + record.ModelId = model_id; + record.ScadaId = scada_id; + record.ValueType = IStation.eValueType.Head; + record.ModeValue = (double)model_value; + record.ScadaValue = scada_value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + hydraulic_record_list.Add(record); + } + } + } + + EpanetMethods.ENnextH(out model_time_step); + + } while (model_time_step > 0); + } + EpanetMethods.ENcloseH(); + return hydraulic_record_list; + } + + + /// <summary> + /// 瀹炴椂楠岃瘉 + /// </summary> + /// <param name="verify_id">楠岃瘉鏍囪瘑</param> + /// <param name="file_path">鏂囦欢</param> + /// <param name="flow_id_mapping_dict">娴侀噺Scada鏍囪瘑瀛楀吀</param> + /// <param name="pressure_id_mapping_dict">鍘嬪姏Scada鏍囪瘑瀛楀吀</param> + /// <param name="pattern_list"></param> + /// <param name="time_value">楠岃瘉鏁版嵁</param> + /// <returns></returns> + public static List<Model.HydraulicModelRecord> Verify(long verify_id, string file_path, Dictionary<string, string> flow_id_mapping_dict, Dictionary<string, string> pressure_id_mapping_dict, List<Pattern> pattern_list, TimeValue time_value) + { + var log_title = "鍒濆鍖�"; + var log_msg = string.Empty; + var hydraulic_record_list = new List<Model.HydraulicModelRecord>(); + if (flow_id_mapping_dict == null || !flow_id_mapping_dict.Any()) + { + log_msg = "娴侀噺鏍囩瀛楀吀涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + if (pressure_id_mapping_dict == null || !pressure_id_mapping_dict.Any()) + { + log_msg = "鍘嬪姏鏍囩瀛楀吀涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + if (pattern_list == null || !pattern_list.Any()) + { + log_msg = "妯″紡鏁版嵁涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + if (time_value == null) + { + log_msg = "楠岃瘉鏁版嵁涓虹┖!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + + var err = EpanetMethods.ENopen(file_path, "", ""); + if (err != 0) + { + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + return default; + } + err = EpanetMethods.ENopenH(); + if (err != 0) + { + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + return default; + } + + log_msg = "鍒濆鍖栨垚鍔�"; + Log.Info(verify_id, log_title, log_msg); + + log_title = "楠岃瘉"; + var model_id_build = new StringBuilder(31); + var pattern_init = true; + foreach (var pattern in pattern_list) + { + var pattern_id = pattern.Id; + var pattern_factor_array = pattern.FactorList.ToArray(); + var pattern_factor_array_count = pattern_factor_array.Length == 0 ? 1 : pattern_factor_array.Length; + err = EpanetMethods.ENgetpatternindex(pattern_id, out int pattern_index); + + if (err != ErrorCode.Ok) + { + pattern_init = false; + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + continue; + } + err = EpanetMethods.ENsetpattern(pattern_index, pattern_factor_array, pattern_factor_array_count); + if (err != ErrorCode.Ok) + { + pattern_init = false; + log_msg = err.GetMsg(); + Log.Info(verify_id, log_title, log_msg); + continue; + } + } + + if (!pattern_init) + { + log_msg = $"{time_value.Time.ToString("G")}:妯″紡鍒濆鍖栧け璐�!"; + Log.Info(verify_id, log_title, log_msg); + return default; + } + + EpanetMethods.ENinitH(0); + EpanetMethods.ENrunH(out _); + EpanetMethods.ENgetcount(CountType.Link, out int link_count); + EpanetMethods.ENgetcount(CountType.Node, out int node_count); + + var time = time_value.Time; + var scada_value_dcit = time_value.Value; + for (int link_index = 1; link_index <= link_count; link_index++) + { + if (EpanetMethods.ENgetlinkid(link_index, model_id_build) != ErrorCode.Ok) + continue; + var model_id = model_id_build.ToString(); + + + if (flow_id_mapping_dict.ContainsKey(model_id)) + { + var scada_id = flow_id_mapping_dict[model_id]; + var scada_value = scada_value_dcit[scada_id]; + EpanetMethods.ENgetlinkvalue(link_index, LinkValue.Flow, out float model_value); + model_value = Math.Abs(model_value); + + var record = new IStation.Model.HydraulicModelRecord(); + record.VerificationID = verify_id; + record.Time = time; + record.ModelId = model_id; + record.ScadaId = scada_id; + record.ValueType = IStation.eValueType.Flow; + record.ModeValue = (double)model_value; + record.ScadaValue = scada_value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + hydraulic_record_list.Add(record); + } + + EpanetMethods.ENgetlinktype(link_index, out LinkType linkType); + if (linkType == LinkType.Pump) + { + EpanetMethods.ENgetlinkvalue(link_index, LinkValue.InitStatus, out float on_off); + } + } + + for (int node_index = 1; node_index <= node_count; node_index++) + { + if (EpanetMethods.ENgetnodeid(node_index, model_id_build) != ErrorCode.Ok) + continue; + var model_id = model_id_build.ToString(); + + if (pressure_id_mapping_dict.ContainsKey(model_id)) + { + var scada_id = pressure_id_mapping_dict[model_id]; + var scada_value = scada_value_dcit[scada_id]; + + EpanetMethods.ENgetnodevalue(node_index, NodeValue.Head, out float model_value); + + var record = new IStation.Model.HydraulicModelRecord(); + record.VerificationID = verify_id; + record.Time = time; + record.ModelId = model_id; + record.ScadaId = scada_id; + record.ValueType = IStation.eValueType.Head; + record.ModeValue = (double)model_value; + record.ScadaValue = scada_value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + hydraulic_record_list.Add(record); + } + } + + EpanetMethods.ENcloseH(); + return hydraulic_record_list; + } + + } +} diff --git a/IStation.Service/IStation.Service.csproj b/IStation.Service/IStation.Service.csproj index fda7c1a..2ff2cd0 100644 --- a/IStation.Service/IStation.Service.csproj +++ b/IStation.Service/IStation.Service.csproj @@ -21,7 +21,7 @@ <ItemGroup> <Content Include="paras_schedule_settings.json"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + <CopyToOutputDirectory>Never</CopyToOutputDirectory> </Content> </ItemGroup> @@ -62,4 +62,8 @@ <Folder Include="06-curve\" /> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\IStation.Epanet\IStation.Epanet.csproj" /> + </ItemGroup> + </Project> diff --git a/IStation.Service/paras_schedule_settings.json b/IStation.Service/paras_schedule_settings.json index 0155db4..d32dfb5 100644 --- a/IStation.Service/paras_schedule_settings.json +++ b/IStation.Service/paras_schedule_settings.json @@ -3,14 +3,15 @@ "ZyDocking": { "ScadaHttpUrl": "http://192.168.201.235:8009/dataController/getCurrentJsonDataOfRedis/涔夌淮妯″瀷", - "ScheduleRuleHttpUrl": "", "Enable": false }, "LocalFile": { "DataFolder": "Data", "StationFile": "Station.json", "ScadaFile": "Scada.json", - "ScheduleConfigFile": "ScheduleConfig.json" + "ScheduleConfigFile": "ScheduleConfig.json", + "HydraulicModelFile": "HydraulicModel.inp", + "HydraulicModelValidationConfigFile": "HydraulicModelValidationConfig.json" }, "DataBase": { "DbType": "SQLite", @@ -23,9 +24,13 @@ }, "SQLite": { "AnalysisConnectString": "analysis.db;", - "ScheduleConnectString": "schedule.db" + "ScheduleConnectString": "schedule.db", + "HydraulicConnectString": "hydraulic.db" } }, + "Task": { + "MinuteCron": "4 * * * * ?" + }, "CAL": { "CALType": "HttpClient", //HttpClient锟斤拷LocalClient "HttpClient": { diff --git a/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj b/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj new file mode 100644 index 0000000..b22629e --- /dev/null +++ b/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj @@ -0,0 +1,21 @@ +锘�<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net6.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>disable</Nullable> + <AssemblyName>$(MSBuildProjectName)</AssemblyName> + <RootNamespace>Yw.TopShelf</RootNamespace> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Yw.TopShelf" Version="3.0.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\IStation.Server.Validation\IStation.Server.Validation.csproj" /> + </ItemGroup> + +</Project> diff --git a/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj.user b/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj.user new file mode 100644 index 0000000..7098eac --- /dev/null +++ b/IStation.TopShelf.Validation/IStation.TopShelf.Validation.csproj.user @@ -0,0 +1,6 @@ +锘�<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <_LastSelectedProfileId>D:\WorkCode\IStation\Service.Ch.V1.1\IStation.TopShelf.Validation\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/IStation.TopShelf.Validation/Program.cs b/IStation.TopShelf.Validation/Program.cs new file mode 100644 index 0000000..79e64ae --- /dev/null +++ b/IStation.TopShelf.Validation/Program.cs @@ -0,0 +1,17 @@ +锘�// See https://aka.ms/new-console-template for more information +using IStation.TopShelf; +using Topshelf; + + +HostFactory.Run(x => +{ + x.Service<Service>(); + x.RunAsLocalSystem(); + x.SetDescription("闄堣鍦哄唴妯″瀷鍦ㄧ嚎楠岃瘉(涔夌淮)"); + x.SetDisplayName("IStation.Server.Validation"); + x.SetServiceName("IStation.Server.Validation"); + // x.EnableServiceRecovery(r => r.RestartService(TimeSpan.FromSeconds(1))); + x.StartAutomatically(); +}); + + diff --git a/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml b/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..639934f --- /dev/null +++ b/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,12 @@ +锘�<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project> + <PropertyGroup> + <Configuration>Release</Configuration> + <Platform>Any CPU</Platform> + <PublishDir>bin\Release\net6.0\publish\</PublishDir> + <PublishProtocol>FileSystem</PublishProtocol> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user b/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..2d54a1f --- /dev/null +++ b/IStation.TopShelf.Validation/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,10 @@ +锘�<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project> + <PropertyGroup> + <History>True|2024-06-17T09:03:32.7665489Z||;True|2024-06-17T16:06:13.3233722+08:00||;True|2024-06-17T15:58:50.8131001+08:00||;True|2024-06-17T15:20:11.0604790+08:00||;False|2024-06-17T15:19:53.9885120+08:00||;True|2024-06-01T14:06:22.1757171+08:00||;True|2024-05-09T17:23:22.4572642+08:00||;True|2024-05-08T10:32:27.3427464+08:00||;True|2024-04-22T15:24:28.3028779+08:00||;True|2024-04-22T15:21:12.4874723+08:00||;True|2024-04-04T09:48:58.0747264+08:00||;True|2024-03-12T17:45:01.4978239+08:00||;True|2024-02-27T10:39:21.3900360+08:00||;True|2024-02-27T10:38:50.1319032+08:00||;True|2024-02-27T10:33:20.5262028+08:00||;True|2024-02-27T10:33:10.6520911+08:00||;True|2024-02-22T16:40:54.4150532+08:00||;True|2024-02-22T13:07:07.3185089+08:00||;True|2024-01-22T11:57:54.5115348+08:00||;True|2024-01-19T10:08:43.2565541+08:00||;True|2024-01-08T10:08:58.6441363+08:00||;True|2023-12-26T10:23:02.1564519+08:00||;True|2023-12-11T15:38:15.8009325+08:00||;True|2023-12-11T15:38:04.3448592+08:00||;True|2023-12-11T15:23:00.5953875+08:00||;True|2023-11-23T15:52:15.8632850+08:00||;True|2023-11-22T14:20:00.0546024+08:00||;True|2023-11-16T10:20:12.5599155+08:00||;True|2023-11-16T10:20:00.1259223+08:00||;True|2023-10-31T16:49:04.0183144+08:00||;True|2023-09-26T14:02:41.2629423+08:00||;True|2023-09-25T14:49:13.2860387+08:00||;True|2023-09-25T14:15:15.3086369+08:00||;True|2023-09-25T13:11:53.5778345+08:00||;True|2023-09-25T13:10:32.2454704+08:00||;True|2023-08-09T15:54:23.4688622+08:00||;True|2023-04-14T11:28:54.3192620+08:00||;True|2022-09-22T10:31:25.8968031+08:00||;True|2022-09-03T18:29:36.9117498+08:00||;True|2022-07-01T20:34:32.8837385+08:00||;True|2022-07-01T14:13:00.3575225+08:00||;True|2022-06-06T13:46:50.1646055+08:00||;True|2022-06-02T09:45:18.0850685+08:00||;True|2022-05-23T20:24:38.0459928+08:00||;</History> + <LastFailureDetails /> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/IStation.TopShelf.Validation/Service.cs b/IStation.TopShelf.Validation/Service.cs new file mode 100644 index 0000000..d8d1e1a --- /dev/null +++ b/IStation.TopShelf.Validation/Service.cs @@ -0,0 +1,38 @@ +锘縰sing Topshelf; +using IStation.Server; + +namespace IStation.TopShelf +{ + /// <summary> + /// + /// </summary> + public class Service : ServiceControl + { + private readonly JobHelper _jobHelper = new(); + + /// <summary> + /// + /// </summary> + /// <param name="hostControl"></param> + /// <returns></returns> + public bool Start(HostControl hostControl) + { + Yw.LogHelper.Info("鍚姩:闄堣鍦哄唴妯″瀷鍦ㄧ嚎楠岃瘉鏈嶅姟"); + _jobHelper.StartJob(); + return true; + } + + /// <summary> + /// + /// </summary> + /// <param name="hostControl"></param> + /// <returns></returns> + public bool Stop(HostControl hostControl) + { + Yw.LogHelper.Info("鍋滄:闄堣鍦哄唴妯″瀷鍦ㄧ嚎楠岃瘉鏈嶅姟"); + _jobHelper.CancelJob(); + return true; + } + + } +} diff --git a/Test/IStation.Test/DayValue.cs b/Test/IStation.Test/DayValue.cs new file mode 100644 index 0000000..4814bbf --- /dev/null +++ b/Test/IStation.Test/DayValue.cs @@ -0,0 +1,11 @@ +锘縩amespace IStation.Test +{ + public class DayValue + { + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + public List<TimeValue> TimeValues { get; set; } + + } +} diff --git a/Test/IStation.Test/DayValueHelper.cs b/Test/IStation.Test/DayValueHelper.cs new file mode 100644 index 0000000..a2efd78 --- /dev/null +++ b/Test/IStation.Test/DayValueHelper.cs @@ -0,0 +1,70 @@ +锘縰sing Yw; + +namespace IStation.Test +{ + public class DayValueHelper + { + + public static List<Hydraulic.DayValue>? GetDayValues() + { + var fileList = new List<string>(); + var folderPath1 = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣涓�杈�"; + var folderPath2 = $"{AppDomain.CurrentDomain.BaseDirectory}Eapnet楠岃瘉鏁版嵁\\闄堣浜岃緭"; + var fileList1 = Directory.GetFiles(folderPath1); + var fileList2 = Directory.GetFiles(folderPath2); + fileList.AddRange(fileList1); + fileList.AddRange(fileList2); + + var timeVlaueList=new List<TimeValue>(); + foreach (var file in fileList) + { + var fileName = Path.GetFileNameWithoutExtension(file); + if (!DateTime.TryParse(fileName, out DateTime day)) + continue; + var json = File.ReadAllText(file); + var timeValues = JsonHelper.Json2Object<List<TimeValue>>(json); + if (timeValues == null || !timeValues.Any()) + continue; + timeVlaueList.AddRange(timeValues); + } + + if (timeVlaueList == null || !timeVlaueList.Any()) + return default; + var dayValues = new List<IStation.Hydraulic.DayValue>(); + var timeVlaueDayGroups = timeVlaueList.GroupBy(x =>new { x.Time.Year,x.Time.Month,x.Time.Day}); + foreach (var day in timeVlaueDayGroups) + { + var timeValueList = new List<IStation.Hydraulic.TimeValue>(); + var timeVlaueGroup=day.GroupBy(x=>x.Time.ToOADate()); + foreach (var tvItem in timeVlaueGroup) + { + var t = DateTime.FromOADate(tvItem.Key); + var value = new Dictionary<string, double>(); + foreach (var tv in tvItem) + { + foreach (var v in tv.Value) + { + value.Add(v.Key, v.Value); + } + } + + var timeValue = new IStation.Hydraulic.TimeValue(); + timeValue.Time = t; + timeValue.Value = value; + timeValueList.Add(timeValue); + } + + + var dv = new IStation.Hydraulic.DayValue(); + dv.Year = day.Key.Year; + dv.Month = day.Key.Month; + dv.Day = day.Key.Day; + dv.TimeValueList = timeValueList; + dayValues.Add(dv); + } + + return dayValues; + } + + } +} diff --git a/Test/IStation.Test/EpanetMethods.cs b/Test/IStation.Test/EpanetMethods.cs new file mode 100644 index 0000000..47ebf30 --- /dev/null +++ b/Test/IStation.Test/EpanetMethods.cs @@ -0,0 +1,1184 @@ +锘縰sing AStation.Epanet.Enums; +using System.Runtime.InteropServices; +using System.Text; + +namespace AStation.Epanet +{ + /// <summary> + /// AStation.Epanet 绋嬪簭鍛樺伐鍏峰寘(EPANET2.2.DLL)涓殑鍑芥暟澹版槑銆� + /// 杩欎簺鏄瀯鎴� DLL 鐨勫閮ㄥ嚱鏁般�� + /// </summary> + public class EpanetMethods + { + /// <summary>鏂囦欢鍚嶇О ("epanet2.2.dll").</summary> + private const string EPANETDLL = "epanet2.2.dll"; + + /// <summary>Epanet dll 璋冪敤绾﹀畾</summary> + private const CallingConvention CONVENTION = CallingConvention.StdCall; + + /// <summary>Epanet dll 鏂规硶鐨勫瓧绗︿覆瀛楃闆�</summary> + private const CharSet CHARSET = CharSet.Ansi; + + /// <summary>Epanet dll 鐨勫瓧绗︿覆绫诲瀷</summary> + private const UnmanagedType STRING = UnmanagedType.LPStr; + + // private const int MAXFNAME = 259; + + /// <summary>Epanet dll id 瀛楃涓�(Node.Id銆丩ink.Id 绛�)鐨勬渶澶у瓧绗︽暟</summary> + public const int MAXID = 31; + + /// <summary>Epanet dll 閿欒瀛楃涓茬殑鏈�澶у瓧绗︽暟锛�*.inp 鏂囦欢涓殑琛屾暟銆�</summary> + public const int MAXMSG = 79; + + /// <summary>Delegate for <see cref="ENepanet" /> progress callback.</summary> + /// <param name="message">浠� AStation.Epanet dll 浼犻�掔殑鐘舵�佷俊鎭��</param> + [UnmanagedFunctionPointer(CONVENTION)] + public delegate void ViewProgCallback([MarshalAs(STRING)] string message); + + #region 鎵撳紑鍜屽叧闂� AStation.Epanet 绯荤粺鐨勫姛鑳� + + /// <summary>鎵ц瀹屾暣鐨凟PANETH妯℃嫙</summary> + /// <param name="f1">杈撳叆鏂囦欢鍚�</param> + /// <param name="f2">杈撳嚭鎶ュ憡鏂囦欢鍚�</param> + /// <param name="f3">鍙�変簩杩涘埗杈撳嚭鏂囦欢鍚�</param> + /// <param name="pviewprog"> + /// Pointer to a user-supplied function (<see cref="ViewProgCallback" />) which accepts + /// a character string as its argument; see note below. + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// The <paramref name="pviewprog" /> argument is a pointer to a callback + /// function that takes a character string (char *) as its only parameter. + /// The function would reside in and be used by the calling + /// program to display the progress messages that AStation.Epanet generates + /// as it carries out its computations. If this feature is not + /// needed then the argument should be <c>null</c>. + /// </para> + /// <para> + /// ENepanet is a stand-alone function and does not interact with + /// any of the other functions in the toolkit. + /// </para> + /// <para> + /// If there is no need to save AStation.Epanet's binary output file + /// then <paramref name="f3" /> can be an empty string (""). + /// </para> + /// <para> + /// The vfunc function pointer allows the calling program + /// to display a progress message generated by AStation.Epanet during its + /// computations. + /// </para> + /// <example> + /// A typical function for a console application might look as follows: + /// <code> + /// static void WriteConsole(string s) { + /// System.Console.WriteLine(s); + /// } + /// </code> + /// and somewhere in the calling program the following declarations would appear: + /// <code> + /// Pviewprog vfunc = WriteConsole; + /// ENepanet(f1, f2, f3, vfunc); + /// </code> + /// If such a function is not desired then this argument should be null. + /// </example> + /// <para> + /// <see cref="ENepanet" /> is used mainly to link the AStation.Epanet engine to third-party + /// user interfaces that build network input files and display the results of a + /// network analysis. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENepanet( + [MarshalAs(STRING)] string f1, + [MarshalAs(STRING)] string f2, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string f3, + [Optional, DefaultParameterValue(null)] ViewProgCallback pviewprog); + + /// <summary>鎵撳紑宸ュ叿绠憋紝涓轰簡鍒嗘瀽鐗瑰畾閰嶆按绯荤粺</summary> + /// <param name="f1">杈撳叆鏂囦欢鍚� </param> + /// <param name="f2">杈撳嚭鎶ュ憡鏂囦欢鍚�</param> + /// <param name="f3">鍙�変簩杩涘埗杈撳嚭鏂囦欢鍚�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// If there is no need to save AStation.Epanet's binary Output file + /// then <paramref name="f3" /> can be an empty string (""). + /// <see cref="ENopen" /> must be called before any of the other + /// toolkit functions (except <see cref="ENepanet" />) are used. + /// </remarks> + /// <seealso cref="ENclose" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true), PreserveSig] + public static extern ErrorCode ENopen( + [MarshalAs(STRING)] string f1, + [MarshalAs(STRING)] string f2, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string f3); + + /// <summary>灏嗘墍鏈夊綋鍓嶇缃戣緭鍏ユ暟鎹啓鍏ュ埌鏂囦欢锛屽埄鐢‥PANETH杈撳叆鏂囦欢鐨勬牸寮�</summary> + /// <param name="filename">鏁版嵁淇濆瓨鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// The data saved reflect any changes made by calls to + /// the <c>ENsetxxx</c> family of functions since AStation.Epanet + /// data was first loaded using <see cref="ENopen" />. + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsaveinpfile([MarshalAs(STRING)] string filename); + + /// <summary>鍏抽棴宸ュ叿绠辩郴缁�(鍖呮嫭鎵�鏈夋鍦ㄥ鐞嗙殑鏂囦欢)</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <see cref="ENclose" /> must be called when all processing has been completed, + /// even if an error condition was encountered. + /// </remarks> + /// <seealso cref="ENopen" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENclose(); + + #endregion + + #region 杩愯姘村姏鍒嗘瀽鐨勫姛鑳� + + /// <summary> + /// 鎵ц涓�娆″畬鏁寸殑姘村姏妯℃嫙锛屾墍鏈夋椂娈电殑缁撴灉鍐欏叆浜岃繘鍒舵按鍔涙枃浠� + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍒╃敤<see cref="ENsolveH" />浜х敓鐙珛瀹屾暣鐨勬按鍔涚粨鏋滐紝鎴栬�呬綔涓烘按璐ㄥ垎鏋愮殑杈撳叆銆備负浜嗗皢姘村姏缁撴灉鍐欏叆 + /// 鎶ュ憡鏂囦欢锛屽叾鍚庝篃璋冨叆鍒�<see cref="ENsaveH" />鍜�<see cref="ENreport" />銆� + /// 涓嶈浣跨敤 <see cref="ENopenH" />, <see cref="ENinitH" />, <see cref="ENrunH" />, <see cref="ENnextH" />, 鍜� <see cref="ENcloseH" /> + /// 杩炴帴鍒�<see cref="ENsolveH" />銆� + /// </remarks> + /// <example> + /// <code> + /// ENopen("net1.inp", "net1.rpt", ""); + /// ENsolveH(); + /// ENsolveQ(); + /// ENreport(); + /// ENclose(); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsolveH(); + + /// <summary> + /// 灏嗘按鍔涙ā鎷熺粨鏋滀粠浜岃繘鍒舵按鍔涙枃浠惰浆鎹㈠埌浜岃繘鍒惰緭鍑烘枃浠讹紝鍏朵腑缁撴灉浠呬粎浠ュ潎鍖�鏃堕棿闂撮殧杩涜鎶ュ憡 + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// <see cref="ENsaveH" />浠呭湪姘村姏鍒嗘瀽鍚庢墽琛岋紝骞跺湪鍧囧寑鎶ュ憡闂撮殧鐨勭粨鏋滈渶瑕佽浆鎹负EPANETH鐨勪簩杩涘埗 + /// 杈撳嚭鏂囦欢鏃朵娇鐢ㄣ�傝繖绉嶆儏鍐碉紝鍒╃敤<see cref="ENreport" />灏嗚緭鍑烘姤鍛婂啓鍏PANETH鎶ュ憡鏂囦欢銆� + /// </para> + /// <para> + /// 鍦‥PANETH杈撳叆鏂囦欢([TIMES]鑺�)鎴栬�呴�氳繃鍒╃敤<see cref="ENsettimeparam" />鍑芥暟锛屽彲浠ヨ缃姤鍛婃椂闂淬�� + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENsettimeparam" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsaveH(); + + /// <summary>鎵撳紑姘村姏鍒嗘瀽绯荤粺</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦ㄧ涓�娆℃墽琛屾按鍔涘垎鏋愪箣鍓嶈皟鐢�<see cref="ENopenH" />锛� + /// 鍒╃敤<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />绯诲垪銆� + /// 鍦ㄨ皟鐢�<see cref="ENcloseH" />锛屼互鍏抽棴姘村姏鍒嗘瀽绯荤粺涔嬪墠锛屽彲浠ヨ繘琛屽娆″垎鏋愩�� + /// </para> + /// <para> + /// 濡傛灉<see cref="ENsolveH" />鐢ㄤ簬杩愯瀹屾暣鐨勬按鍔涘垎鏋愶紝涓嶈璋冪敤璇ュ嚱鏁般�� + /// </para> + /// </remarks> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENopenH(); + + /// <summary>鍦ㄦ墽琛屾按鍔涘垎鏋愪箣鍓嶏紝鍒濆鍖栬搫姘存睜姘翠綅銆佺娈电姸鎬佸拰璁剧疆锛屼互鍙婃ā鎷熼挓琛ㄦ椂闂�</summary> + /// <param name="saveflag"> + /// 0-1鏍囧織锛岃鏄庢按鍔涚粨鏋滄槸鍚︿繚瀛樺埌姘村姏鏂囦欢 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍒╃敤<see cref="ENrunH" />鍜�<see cref="ENnextH" />.鎵ц姘村姏鍒嗘瀽涔嬪墠锛屽簲璋冪敤<see cref="ENinitH"/>銆� + /// 璋冪敤<see cref="ENinitH" />涔嬪墠锛屽繀椤诲凡缁忚皟鐢�<see cref="ENopenH" />銆� + /// 濡傛灉鏁翠釜姘村姏鍒嗘瀽璋冪敤浜�<see cref="ENsolveH" />锛屽皢涓嶅啀璋冪敤<see cref="ENinitH" />銆� + /// </para> + /// <para> + /// 濡傛灉灏嗚繘琛岄殢鍚庣殑姘磋川杩愯锛宻aveflag 璁剧疆涓�1锛屽埄鐢�<see cref="ENreport" />浜х敓鎶ュ憡锛� + /// 鎴栬�呭埄鐢�<see cref="ENsavehydfile" />淇濆瓨浜岃繘鍒舵按鍔涙枃浠躲�� + /// </para> + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENinitH(int saveflag); + + /// <summary>鎵ц绠�鍗曟椂娈垫按鍔涘垎鏋愶紝妫�绱㈠綋鍓嶆ā鎷熼挓琛ㄦ椂闂�<paramref name="t" /></summary> + /// <param name="t"> + /// 褰撳墠妯℃嫙閽熻〃鏃堕棿锛屼互绉掕 (no need to supply a value for <paramref name="t" />). + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍒╃敤<see cref="ENrunH" />缁撳悎<see cref="ENnextH" />鍦╠o ..while寰幆涓紝涓轰簡鍒嗘瀽寤舵椂妯℃嫙涓瘡涓�鏃舵鐨勬按鍔涚壒鎬с�� + /// 璇ヨ繃绋嬭嚜鍔ㄦ洿鏂版ā鎷熼挓琛ㄦ椂闂达紝鍥犳灏唗澶勭悊涓哄彧璇诲彉閲忋�� + /// </para> + /// <para> + /// 鍦ㄦ墽琛�<see cref="ENrunH" /> 鈥� <see cref="ENnextH" />寰幆浠ュ墠锛屽繀椤昏皟鐢�<see cref="ENinitH" />銆� + /// 瀵逛簬璇ュ嚱鏁颁娇鐢ㄧ殑渚嬪瓙锛屽弬瑙�<see cref="ENnextH" />銆� + /// </para> + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENnextH" /> + /// <seealso cref="ENcloseH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENrunH(out int t); + + /// <summary> + /// 纭畾寤舵椂妯℃嫙涓嬩竴娆℃按鍔涙椂闂村彂鐢熷墠鐨勬椂闂撮暱搴� + /// </summary> + /// <param name="tstep"> + /// 鏃堕棿(浠ョ璁�)锛岀洿鍒颁笅涓�娆℃按鍔涙椂闂村彂鐢燂紱鎴栬�呬负0锛屽鏋滃湪妯℃嫙鏃舵鐨勬湯绔�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁颁笌<see cref="ENrunH" />涓�璧蜂娇鐢紝鎵ц寤舵椂姘村姏鍒嗘瀽(瑙佷互涓嬩緥瀛�) + /// </para> + /// <para> + /// tstep鐨勬暟鍊硷紝搴斿鐞嗕负鍙鍙橀噺銆備綔涓轰互涓嬭緝灏忓�艰嚜鍔ㄨ绠�: + /// <list type="bullet"> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�姘村姏鏃堕棿姝ラ暱寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�鎶ュ憡鏃堕棿姝ラ暱寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒颁笅涓�闇�姘撮噺鍙戠敓鏀瑰彉寮�濮�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒版按姹犳敞婊℃垨鑰呮帓绌�;</item> + /// <item>鏃堕棿闂撮殧锛岀洿鍒拌鍒欐帶鍒跺仠姝�;</item> + /// </list> + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tstep; + /// + /// ENopenH(); + /// ENinitH(0); + /// + /// do { + /// ENrunH(out t); + /// /* Retrieve hydraulic results for time t */ + /// ENnextH(out tstep); + /// } while (tstep > 0); + /// + /// ENcloseH(); + /// </code> + /// </example> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENcloseH" /> + /// <seealso cref="ENsettimeparam" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENnextH(out int tstep); + + /// <summary>鍏抽棴姘村姏鍒嗘瀽绯荤粺锛岄噴鏀炬墍鏈夊垎閰嶅唴瀛�</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄥ埄鐢�<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />鎵ц鎵�鏈夋按鍔涘垎鏋愪箣鍚庯紝璋冪敤<see cref="ENcloseH" />銆� + /// 濡傛灉浣跨敤 <see cref="ENsolveH" />锛屼笉瑕佽皟鐢ㄨ鍑芥暟銆� + /// </remarks> + /// <seealso cref="ENopenH" /> + /// <seealso cref="ENinitH" /> + /// <seealso cref="ENrunH" /> + /// <seealso cref="ENnextH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENcloseH(); + + /// <summary>灏嗗綋鍓嶄簩杩涘埗姘村姏鏂囦欢鐨勫唴瀹逛繚瀛樺埌涓�涓枃浠�</summary> + /// <param name="fileName">姘村姏缁撴灉搴旇淇濆瓨鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 灏嗚鍑芥暟鐢ㄤ簬灏嗗綋鍓嶆按鍔涚粨鏋滈泦淇濆瓨鍒颁竴涓枃浠讹紝渚夸簬鍚庡鐞嗘垨鑰呬互鍚庢椂闂翠娇鐢紝閫氳繃璋冪敤<see cref="ENusehydfile" />鍑芥暟 + /// </para> + /// <para> + /// 姘村姏鏂囦欢鍖呭惈浜嗚妭鐐归渶姘撮噺鍜屾按澶达紝浠ュ強绠℃娴侀噺锛岀姸鎬佸拰璁剧疆锛屽浜庢墍鏈夋按鍔涙椂闂存闀匡紝鐢氳嚦涓棿鏁板�笺�� + /// </para> + /// <para> + /// 璋冪敤璇ュ嚱鏁颁箣鍓嶏紝姘村姏缁撴灉蹇呴』浜х敓鍜屼繚瀛橈紝閫氳繃璋冪敤<see cref="ENsolveH" /> + /// 鎴栬��<see cref="ENinitH" /> - <see cref="ENrunH" /> - <see cref="ENnextH" />搴忓垪锛� + /// 鍏锋湁<see cref="ENinitH" />鐨勪繚瀛樻爣蹇楀弬鏁拌缃负1(true)銆� + /// </para> + /// </remarks> + /// <seealso cref="ENusehydfile" /> + /// <seealso cref="ENsolveH" /> + /// <seealso cref="ENinitH" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsavehydfile([MarshalAs(STRING)] string fileName); + + /// <summary>灏嗘寚瀹氭枃浠剁殑鍐呭浣滀负褰撳墠浜岃繘鍒舵按鍔涙枃浠�</summary> + /// <param name="fileName">鍚簡褰撳墠绠$綉姘村姏鍒嗘瀽缁撴灉鐨勬枃浠跺悕</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤璇ュ嚱鏁帮紝鎷掔粷鍘熷厛淇濆瓨鐨勬按鍔涘垎鏋愮粨鏋滈泦銆� + /// 杩欎簺缁撴灉琚鏌ワ紝涓轰簡鏌ョ湅鏄惁瀹冧滑鍖归厤涓�涓嬪弬鏁帮紝鐩稿叧浜庡綋鍓嶈鍒嗘瀽鐨勭缃戯細鑺傜偣鎬绘暟銆佹按姹犲拰姘村簱鎬绘暟銆佺娈垫�绘暟銆佹按娉垫�绘暟銆侀榾闂ㄦ�绘暟鍜屾ā鎷熷巻鏃躲�� + /// </para> + /// <para> + /// 褰撴按鍔涘垎鏋愮郴缁熶粛鏃у紑鍚椂锛屼笉鑳藉璋冪敤璇ュ嚱鏁�(鍗�<see cref="ENopenH" />宸茬粡琚皟鐢紝浣嗘槸 <see cref="ENcloseH" /> 杩樻病鏈�)銆� + /// </para> + /// </remarks> + /// <seealso cref="ENsavehydfile" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENusehydfile([MarshalAs(STRING)] string fileName); + + #endregion + + #region 杩愯姘磋川鍒嗘瀽鐨勫姛鑳� + + /// <summary> + /// 鎵ц瀹屾暣鐨勬按璐ㄦā鎷燂紝缁撴灉鍏锋湁鍧囧寑鎶ュ憡闂撮殧锛屽啓鍏PANETH浜岃繘鍒惰緭鍑烘枃浠� + /// </summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄨ皟鐢�<see cref="ENsolveQ">涔嬪墠锛屽繀椤诲凡缁忔墽琛屾按鍔涘垎鏋愶紝骞朵繚瀛樺埌浜岃繘鍒舵按鍔涙枃浠躲�� + /// 涓轰簡灏嗘按鍔涘拰姘磋川缁撴灉鍐欏叆鎶ュ憡鏂囦欢锛屽悗闈㈣窡鐫�璋冪敤<see cref="ENreport" />銆� + /// 涓嶈兘澶熷皢<see cref="ENopenQ" />, <see cref="ENinitQ" />, <see cref="ENrunQ" />, <see cref="ENnextQ" />鍜�<see cref="ENcloseQ" />锛屼笌<see cref="ENsolveQ" />涓�璧蜂娇鐢ㄣ�� + /// </remarks> + /// <example> + /// <code> + /// ENopen("net1.inp", "net1.rpt", ""); + /// ENsolveH(); + /// ENsolveQ(); + /// ENreport(); + /// ENclose(); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsolveQ(); + + /// <summary>鎵撳紑姘磋川鍒嗘瀽绯荤粺</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENopenQ" />涔嬪墠锛岄鍏堟墽琛屾按璐ㄥ垎鏋愶紝渚濇鍒╃敤<see cref="ENinitQ" /> - <see cref="ENrunQ" /> - <see cref="ENnextQ" />(or <see cref="ENstepQ" />)銆� + /// 涓轰簡鍏抽棴姘磋川鍒嗘瀽绯荤粺璋冪敤<see cref="ENcloseQ" />涔嬪墠锛屽彲浠ヨ繘琛屽閲嶆按璐ㄥ垎鏋愩�� + /// <para> + /// 濡傛灉鍒╃敤<see cref="ENsolveQ" />杩涜瀹屾暣姘磋川鍒嗘瀽锛屼笉瑕佽皟鐢ㄨ鍑芥暟銆� + /// </para> + /// </remarks> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENopenQ(); + + /// <summary>鍦ㄦ墽琛屾按璐ㄥ垎鏋愪箣鍓嶏紝鍒濆鍖栨按璐ㄥ拰妯℃嫙閽熻〃鏃堕棿</summary> + /// <param name="saveFlag"> + /// 0-1鏍囧織锛岃鏄庡垎鏋愮粨鏋滄槸鍚︿互鍧囧寑鎶ュ憡鏃舵淇濆瓨鍒癊PANETH浜岃繘鍒惰緭鍑烘枃浠躲�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦ㄥ埄鐢�<see cref="ENrunQ" />鑱斿悎<see cref="ENnextQ" />鎴栬��<see cref="ENstepQ" />鎵ц姘磋川鍒嗘瀽涔嬪墠锛岃皟鐢�<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 蹇呴』鍦ㄨ皟鐢�<see cref="ENinitQ" />涔嬪墠璋冪敤<see cref="ENopenQ" />銆� + /// 濡傛灉璋冪敤<see cref="ENsolveQ" />鎵ц瀹屾暣姘磋川鍒嗘瀽锛屼笉闇�璋冪敤<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 濡傛灉甯屾湜鍒╃敤<see cref="ENreport" />浜х敓鎶ュ憡锛屾垨鑰呭笇鏈涘皢璁$畻缁撴灉淇濆瓨涓轰簩杩涘埗杈撳嚭鏂囦欢锛岃缃� saveflag涓�1銆� + /// </para> + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENinitQ(int saveFlag); + + /// <summary> + /// 鍦ㄤ笅涓�鏃舵姘磋川鍒嗘瀽鐨勫紑濮嬶紝浣挎按鍔涘拰姘磋川鍒嗘瀽缁撴灉鍙敤锛屽叾涓椂娈电殑寮�濮嬭繑鍥炰负<paramref name="t" />. + /// </summary> + /// <param name="t"> + /// 褰撳墠妯℃嫙閽熻〃鏃堕棿锛屼互绉掕 (no need to supply a value for <paramref name="t" />). + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦╠o...while寰幆涓埄鐢�<see cref="ENrunQ" />鍜�<see cref="ENnextQ" />锛屼负浜嗚闂欢鏃舵ā鎷熸瘡涓�姘村姏鏃舵寮�濮嬫椂鐨勬按璐ㄧ粨鏋溿�� + /// 鎴栬�呭湪do...while寰幆涓皢瀹冧笌<see cref="ENstepQ" />涓�璧蜂娇鐢紝涓轰簡璁块棶姣忎竴姘磋川鏃堕棿姝ラ暱寮�濮嬫椂鐨勭粨鏋溿�� + /// 鎬庢牱缂栧埗杩欑寰幆鐨勪緥瀛愶紝鍙傝姣忎竴鍑芥暟璇存槑銆� + /// </para> + /// <para> + /// 鍦ㄦ墽琛�<see cref="ENrunQ" /> - <see cref="ENnextQ" /> (or <see cref="ENstepQ" />)寰幆涔嬪墠锛屽繀椤昏皟鐢�<see cref="ENinitQ" />銆� + /// </para> + /// <para> + /// 妯℃嫙鐨勫綋鍓嶆椂鍒�<paramref name="t" />鐢变繚瀛樺湪姘村姏鍒嗘瀽涓殑淇℃伅纭畾锛岀劧鍚庤繘琛屾按璐ㄥ垎鏋愩�傚畠涓哄彧璇诲彉閲忋�� + /// </para> + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENnextQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENrunQ(out int t); + + /// <summary>姘磋川妯℃嫙鍓嶈繘鍒颁笅涓�姘村姏鏃舵鐨勫紑濮�</summary> + /// <param name="tstep"> + /// 鏃跺埢(浠ョ璁�)锛岀洿鍒颁笅涓�姘村姏浜嬩欢鍙戠敓锛涙垨鑰呬负闆讹紝濡傛灉鍦ㄦā鎷熸椂娈电殑鏈 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁扮敤鍦�<c>do...while</c>寰幆涓紝鍒╃敤<see cref="ENrunQ" />鎵ц寤舵椂姘磋川鍒嗘瀽銆� + /// 鍏佽鍦ㄦā鎷熺殑姣忎竴姘村姏鏃舵璁块棶姘磋川缁撴灉銆� + /// 姘磋川婕旂畻鍜屽弽搴斾互鏇村皬鐨勬椂闂存闀匡紝鍦ㄥ唴閮ㄦ墽琛屻�� + /// 鍒╃敤<see cref="ENstepQ" />浠f浛璇ュ嚱鏁帮紝濡傛灉闇�瑕佽闂瘡涓�姘磋川浜嬩欢姝ラ暱鍚庣殑缁撴灉銆� + /// </para> + /// <para> + /// <paramref name="tstep" />鐨勫�兼槸鏍规嵁姘磋川鍒嗘瀽涔嬪墠鐨勬按鍔涘垎鏋愭墍淇濆瓨鐨勪俊鎭‘瀹氱殑銆傚皢鍏惰涓哄彧璇诲彉閲忋�� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tstep; + /// ENsolveH(); /* Generate and save hydraulics */ + /// ENopenQ(); + /// ENinitQ(false); + /// + /// do { + /// ENrunQ(out t); + /// /* Monitor results at time t, which */ + /// /* begins a new hydraulic time period */ + /// ENnextQ(out tstep); + /// } while (tstep > 0); + /// + /// ENcloseQ(); + /// </code> + /// </example> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENnextQ(out int tstep); + + /// <summary> + /// 姘磋川妯℃嫙鍓嶈繘涓�涓按璐ㄦ椂闂存闀裤�備繚鐣欐暣涓ā鎷熶腑鐨勬椂闂磋寖鍥村湪<paramref name="tleft" />涓� + /// </summary> + /// <param name="tleft">淇濈暀鍦ㄦ暣涓ā鎷熷巻鏃朵腑鐨勭銆� 鏈虹炕锛氭�绘ā鎷熸椂闂寸殑鍓╀綑绉掓暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璇ュ嚱鏁扮敤鍦�<see cref="ENrunQ" />鐨�<c>do...while</c>寰幆涓紝鎵ц寤舵椂姘磋川妯℃嫙銆� + /// 鍏佽璁块棶妯℃嫙姣忎竴姘磋川鏃堕棿姝ラ暱鐨勬按璐ㄧ粨鏋滐紝鑰屼笉鏄儚<see cref="ENnextQ" />鐨勬瘡涓�姘村姏鏃舵鐨勫紑濮嬨�� + /// </para> + /// <para> + /// 浣跨敤鍙傛暟<paramref name="tleft" />锛岀‘瀹氫綍鏃朵笉鍐嶉渶瑕佽皟鐢�<see cref="ENrunQ" />锛屽洜涓鸿揪鍒版ā鎷熸椂娈电殑鏈熬(鍗冲綋tleft = 0 鏃�)銆� + /// </para> + /// <para> + /// 灏� <paramref name="tleft" />澶勭悊涓哄彧璇诲彉閲�(涓嶉渶瑕佽祴鍊�)銆� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// int t, tleft; + /// + /// ENsolveH(); /* Generate & save hydraulics */ + /// ENopenQ(); + /// ENinitQ(false); + /// + /// do { + /// ENrunQ(out t); + /// /* Monitor results at time t */ + /// ENstepQ(out tleft); + /// } while (tleft > 0); + /// + /// ENcloseQ(); + /// </code> + /// </example> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENcloseQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENstepQ(out int tleft); + + /// <summary>鍏抽棴姘磋川鍒嗘瀽绯荤粺锛岄噴鏀炬墍鏈夊垎閰嶇殑鍐呭瓨</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 鍦ㄥ埄鐢�<see cref="ENinitQ" /> - <see cref="ENrunQ" /> - <see cref="ENnextQ" /> + /// (or <see cref="ENstepQ" />) 绯诲垪鍑芥暟璋冪敤鎵ц鎵�鏈夋按璐ㄥ垎鏋愪箣鍚庯紝璋冪敤<see cref="ENcloseQ" />銆� + /// 濡傛灉浣跨敤浜�<see cref="ENsolveQ" />锛屽皢涓嶉渶瑕佽皟鐢ㄨ鍑芥暟銆� + /// </remarks> + /// <seealso cref="ENopenQ" /> + /// <seealso cref="ENinitQ" /> + /// <seealso cref="ENrunQ" /> + /// <seealso cref="ENstepQ" /> + /// <seealso cref="ENnextQ" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENcloseQ(); + + #endregion + + #region 鐢熸垚杈撳嚭鎶ュ憡鐨勫姛鑳� + + /// <summary>灏嗕竴琛屾枃鏈啓鍏ユ姤鍛婃枃浠�</summary> + /// <param name="line">鏂囨湰</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENwriteline([MarshalAs(STRING)] string line); + + /// <summary>灏嗘ā鎷熺粨鏋滅殑鏍煎紡鍖栨枃鏈姤鍛婂啓鍏ュ埌鎶ュ憡鏂囦欢</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENreport" />涔嬪墠锛屽繀椤诲凡缁忚繘琛屼簡瀹屾暣鐨勬按鍔涘垎鏋愭垨鑰呭畬鏁寸殑姘村姏姘磋川鍒嗘瀽锛岀粨鏋滀繚瀛樺湪鏂囦欢涓�� + /// 鍦ㄥ墠鑰呮儏鍐典腑锛屼篃蹇呴』棣栧厛璋冪敤<see cref="ENsaveH" />锛屼负浜嗗皢缁撴灉浠庢按鍔涙枃浠惰浆鎹㈠埌杈撳嚭鏂囦欢銆� + /// </para> + /// <para> + /// 鎶ュ憡鐨勬牸寮忓彈鍒癊PANETH杈撳叆鏂囦欢[REPORT]鑺傜殑鍛戒护鎵�鎺у埗锛屾垨鑰呴�氳繃涓�<see cref="ENsetreport" /> 鍑芥暟涓�璧峰叕甯冪殑绫讳技鍛戒护銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENreport(); + + /// <summary>灏嗘姤鍛婇�夐」閲嶇疆涓洪粯璁ゅ��</summary> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 娓呴櫎浠讳綍鍑虹幇鍦‥PANETH杈撳叆鏂囦欢[REPORT]鑺備腑鐨勬姤鍛婃牸寮忓懡浠わ紝鎴栬�呭埄鐢�<see cref="ENsetreport" />鍑芥暟鎵�鍏竷鐨� + /// </para> + /// <para>璋冪敤璇ュ嚱鏁板悗锛岄粯璁ゆ姤鍛婇�夐」鐢熸晥銆�</para> + /// <para> + /// 杩欎簺涓�: + /// <list type="bullet"> + /// <item>娌℃湁鐘舵�佹姤鍛�</item> + /// <item>娌℃湁鑳介噺鎶ュ憡</item> + /// <item>娌℃湁鑺傜偣鎶ュ憡</item> + /// <item>娌℃湁绠℃鎶ュ憡</item> + /// <item>鑺傜偣鍙橀噺鎶ュ憡涓�2浣嶅皬鏁颁綅</item> + /// <item>绠℃鍙橀噺鎶ュ憡涓轰袱浣嶅皬鏁颁綅 (鎽╂摝鍥犲瓙涓�3浣�)</item> + /// <item>鎶ュ憡鐨勮妭鐐瑰彉閲忎负鏍囬珮銆佹按澶淬�佸帇寮哄拰姘磋川</item> + /// <item>鎶ュ憡鐨勭娈靛彉閲忎负娴侀噺銆佹祦閫熷拰姘村ご鎹熷け</item> + /// </list> + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENsetreport" /> + /// <seealso cref="ENsetstatusreport" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENresetreport(); + + /// <summary>鍏竷鎶ュ憡鏍煎紡鍛戒护,鏍煎紡鍛戒护涓嶦PANETH杈撳叆鏂囦欢鐨刐REPORT]鑺備腑浣跨敤鐨勭浉鍚�</summary> + /// <param name="command">鎶ュ憡鏍煎紡鍛戒护鏂囨湰</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 璋冪敤<see cref="ENresetreport" />锛屾槑纭師鏉ユ姤鍛婃牸寮忓懡浠わ紝鍑虹幇鍦ㄨ緭鍏ユ枃浠朵腑锛屾垨鑰呴�氳繃璋冪敤 <see cref="ENsetreport" /> 鎴栬�� <see cref="ENsetstatusreport" />鍏竷 + /// </para> + /// <para> + /// 妯℃嫙鐨勬牸寮忕粨鏋滃彲浠ュ啓鍏ュ埌鎶ュ憡鏂囦欢锛屽埄鐢�<see cref="ENreport" />鍑芥暟 + /// </para> + /// </remarks> + /// <seealso cref="ENreport" /> + /// <seealso cref="ENresetreport" /> + /// <seealso cref="ENsetstatusreport" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsetreport([MarshalAs(STRING)] string command); + + #endregion + + #region 妫�绱etwork淇℃伅鐨勫姛鑳� + + /// <summary> + /// 璇诲彇鍒嗛厤缁欐渶杩戜竴娆� 鏇存柊婧愪唬鐮佺殑缂栧彿銆� + /// 璇ョ紪鍙风敱甯搁噺<c> CODEVERSION</c> 璁剧疆鐨勶紝浠� 20001 寮�濮嬶紝 姣忔洿鏂颁竴娆″鍔� 1. + /// </summary> + /// <param name="v">Version number of the source code.</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /> (should always be 0).</returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetversion(out int v); + + /// <summary> + /// 妫�绱㈢畝鍗曟帶鍒惰鍙ョ殑鍙傛暟銆� + /// 鎺у埗鐨勭储寮曞湪浠ュ強鍓╀綑鐨勫弬鏁拌繑鍥炴帶鍒跺弬鏁般�� + /// </summary> + /// <param name="cindex"> + /// 鎺у埗璇彞绱㈠紩 (鎺у埗鏄粠1寮�濮嬬殑绱㈠紩锛屾搴忎负杈撳叆鍒癊PENETH杈撳叆鏂囦欢鐨刐CONTROLS]鑺備腑) + /// </param> + /// <param name="ctype">鎺у埗绫诲瀷浠g爜<see cref="ControlType" /></param> + /// <param name="lindex">鎺у埗绠℃鐨勭储寮�</param> + /// <param name="setting">鎺у埗璁剧疆鏁板��</param> + /// <param name="nindex">鎺у埗鑺傜偣鐨勭储寮� + /// Index of controlling node (0 for <see cref="ControlType.Timer" /> + /// or <see cref="ControlType.TimeOfDay" /> control). + /// </param> + /// <param name="level"> + /// 瀵逛簬鍩轰簬鏃堕棿鎺у埗鐨勬帶鍒惰涓�(浠ョ璁�)鐨勬帶鍒舵按浣嶆垨鑰呮按浣嶆帶鍒剁殑鍘嬪姏鎴栬�呮帶鍒舵椂闂寸殑鏁板�� + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// Controls are indexed starting from 1 in the order in which they were + /// entered into the [CONTROLS] section of the AStation.Epanet input file. + /// </para> + /// <para> + /// 瀵逛簬绠¢亾锛岃缃负0锛屾剰鍛崇潃绠¢亾琚叧闂紱涓�1鎰忓懗鐫�瀹冩槸寮�鍚殑銆� + /// 瀵逛簬姘存车锛岃缃寘鍚簡姘存车杞�燂紝涓�0锛屾剰鍛崇潃姘存车琚叧闂紝涓�1鎰忓懗鐫�澶勪簬甯歌杞�熴�� + /// 瀵逛簬闃�闂紝璁剧疆鏄寚闃�闂ㄧ殑鍘嬪姏銆佹祦閲忔垨鑰呮崯澶辩郴鏁板�硷紝鍙栧喅浜庨榾闂ㄧ被鍨� + /// </para> + /// <para> + /// 瀵逛簬璁℃椂鍣ㄦ垨鑰呴挓琛ㄦ椂闂存帶鍒讹紝nindex鍙傛暟绛変簬0 + /// </para> + /// <para> + /// See <see cref="ENsetcontrol" /> for an example of using this function. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetcontrol( + int cindex, + out ControlType ctype, + out int lindex, + out float setting, + out int nindex, + out float level); + + /// <summary>妫�绱㈡寚瀹氱被鍨嬬殑绠$綉缁勪欢鏁伴噺</summary> + /// <param name="code">缁勪欢缂栧彿<see cref="CountType" /></param> + /// <param name="count">绠$綉涓璫ountcode缁勪欢鐨勬暟閲�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 绠$綉涓繛鎺ヨ妭鐐圭殑鏁伴噺绛変簬鑺傜偣鎬绘暟鍑忓幓姘存睜鍜屾按搴撴�绘暟銆� + /// </para> + /// <para> + /// 瀵逛簬鍦ㄨ緭鍏ユ枃浠舵弿杩扮殑缁勪欢锛屽伐鍏风鍐呮病鏈夎鏂芥坊鍔犳垨鑰呭垹闄ゅ姛鑳姐�� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetcount(CountType code, out int count); + + /// <summary>妫�绱㈢壒瀹氬垎鏋愰�夐」鐨勬暟鍊�</summary> + /// <param name="code">閫夐」缂栧彿<see cref="MiscOption" /></param> + /// <param name="value">閫夐」鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetoption(MiscOption code, out float value); + + /// <summary>妫�绱㈡寚瀹氬垎鏋愭椂闂村弬鏁扮殑鏁板��</summary> + /// <param name="code">鏃堕棿鍙傛暟缂栧彿 <see cref="TimeParameter" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgettimeparam(TimeParameter code, out int value); + + /// <summary>妫�绱㈣〃鏄庝娇鐢ㄥ崟浣嶇殑浠g爜鏁帮紝涓轰簡琛ㄨ揪鎵�鏈夋祦閲�</summary> + /// <param name="code">娴侀噺鑼冨洿浠g爜鍙风殑鏁板��<see cref="FlowUnitsType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鍦‥PANETH杈撳叆鏂囦欢鐨刐OPTIONS]鑺傛寚瀹氭祦閲忓崟浣嶃�� + /// </para> + /// <para> + /// 娴侀噺鍗曚綅鍙栧崌鎴栬�呯珛鏂圭背锛岃鏄庢墍鏈夊叾瀹冮噺灏嗛噰鐢ㄥ叕鍒跺崟浣嶏紱鍚﹀垯浣跨敤缇庡埗鍗曚綅銆�(瑙佽閲忓崟浣�)銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetflowunits(out FlowUnitsType code); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忕殑绱㈠紩</summary> + /// <param name="id">妯″紡ID</param> + /// <param name="index">妯″紡绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetpatternindex([MarshalAs(STRING)] string id, out int index); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忕殑ID鏍囩</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="id">妯″紡ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternid(int index, [MarshalAs(STRING)] StringBuilder id); + + /// <summary>妫�绱㈢壒瀹氭椂闂存ā寮忎腑鐨勬椂娈垫�绘暟</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="len">妯″紡涓椂娈垫�绘暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternlen(int index, out int len); + + /// <summary>妫�绱㈡椂闂存ā寮忎腑鐗瑰畾鏃舵鐨勪箻瀛�</summary> + /// <param name="index">妯″紡绱㈠紩</param> + /// <param name="period">鏃堕棿妯″紡鑼冨洿鍐呯殑鏃舵</param> + /// <param name="value">鏃舵鐨勪箻瀛�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>妯″紡绱㈠紩鍜屾椂娈垫槸浠�1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENsetpatternvalue" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetpatternvalue(int index, int period, out float value); + + /// <summary>妫�绱㈣皟鐢ㄦ按璐ㄥ垎鏋愮殑绫诲瀷</summary> + /// <param name="qualcode">姘磋川鍒嗘瀽浠g爜<see cref="QualType" /></param> + /// <param name="tracenode">婧愬ご璺熻釜鍒嗗瓙涓窡韪殑鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// 褰搎ualcode涓嶄负EN_TRACE鏃讹紝tracenode鐨勬暟鍊煎皢涓�0 + /// The <paramref name="tracenode" /> value will be 0 when <paramref name="qualcode" /> + /// is not <see cref="QualType.Trace" />. + /// </remarks> + /// <seealso cref="ENsetqualtype" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetqualtype(out QualType qualcode, out int tracenode); + + /// <summary>妫�绱笌鐗瑰畾閿欒鎴栬�呰鍛婁唬鐮佺浉鍏崇殑淇℃伅鏂囨湰</summary> + /// <param name="err">閿欒鎴栬�呰鍛婁唬鐮�</param> + /// <param name="errmsg">瀵瑰簲浜巈rrcode鐨勯敊璇垨鑰呰鍛婁俊鎭殑鏂囨湰</param> + /// <param name="n">errmsg 鐨勬渶澶ч暱搴� (should be <see cref="MAXMSG" /> characters).</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>閿欒淇℃伅瀛楃涓茬殑闀垮害鑷冲皯搴斾负 <see cref="MAXMSG" /> 涓瓧绗�.</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgeterror(ErrorCode err, [MarshalAs(STRING), Out] StringBuilder errmsg, int n); + + #endregion + + #region 妫�绱ode鏁版嵁鐨勫姛鑳� + + /// <summary>妫�绱㈠叿鏈夋寚瀹欼D鐨勮妭鐐圭储寮�</summary> + /// <param name="id">鑺傜偣ID</param> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetnodeid" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetnodeindex([MarshalAs(STRING)] string id, out int index); + + /// <summary> + /// 妫�绱㈠叿鏈夋寚瀹氱储寮曠殑ID鏍囩 + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="id">鑺傜偣ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + /// <seealso cref="ENgetnodeindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetnodeid(int index, [MarshalAs(STRING), Out] StringBuilder id); + + /// <summary> + /// 妫�绱㈡寚瀹氳妭鐐圭殑鑺傜偣绫诲瀷缂栧彿 + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣绫诲瀷缂栧彿 <see cref="NodeType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetnodetype(int index, out NodeType code); + + /// <summary> + /// 妫�绱㈢壒瀹氱娈靛弬鏁板�� + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠e彿 <see cref="NodeValue" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟 + /// </para> + /// <para> + /// 杩斿洖鍊肩殑鍗曚綅鍙栧喅浜庡湪 AStation.Epanet 杈撳叆鏂囦欢涓敤浜庢祦閲忕殑鍗曚綅銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetnodevalue(int index, NodeValue code, out float value); + + #endregion + + #region 妫�绱ink鏁版嵁鐨勫姛鑳� + + /// <summary> + /// 妫�绱㈠叿鏈夌壒瀹欼D鐨勭娈电储寮� + /// </summary> + /// <param name="id">绠℃ID</param> + /// <param name="index">绠℃绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetlinkid" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetlinkindex([MarshalAs(STRING)] string id, out int index); + + /// <summary>妫�绱㈠叿鏈夌壒瀹氭寚鏍囩殑绠℃鐨処D鏍囩</summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="id">绠℃ID</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para><paramref name="id" /> must be able to hold <see cref="MAXID" /> characters.</para> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, BestFitMapping = false, + ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENgetlinkid(int index, [MarshalAs(STRING), Out] StringBuilder id); + + /// <summary>妫�绱㈢壒瀹氱娈电殑绫诲瀷缂栧彿</summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">绠℃绫诲瀷浠e彿<see cref="LinkType" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinktype(int index, out LinkType code); + + /// <summary> + /// 妫�绱㈡寚瀹氱娈电鐐硅妭鐐圭殑绱㈠紩 + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="fnode">绠℃璧峰鑺傜偣绱㈠紩</param> + /// <param name="tnode">绠℃缁堟鑺傜偣绱㈠紩</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>鑺傜偣鍜岀娈电储寮曟槸浠�1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 绠℃璧峰鍜岀粓姝㈣妭鐐瑰畾涔夊湪EPANETH杈撳叆鏂囦欢涓�� + /// 娌℃湁鑰冭檻绠℃鐨勫疄闄呮祦鍚戙�� + /// </para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinknodes(int index, out int fnode, out int tnode); + + /// <summary> + /// 妫�绱㈡寚瀹氱娈靛弬鏁板�� + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">绠℃鍙傛暟浠e彿<see cref="LinkValue" /></param> + /// <param name="value">鍙傛暟鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// EN_DIAMETER 0 鐩村緞 + /// EN_LENGTH 1 闀垮害 + /// EN_ROUGHNESS 2 绮楃硻绯绘暟 + /// EN_MINORLOSS 3 灞�閮ㄦ崯澶辩郴鏁� + /// EN_INITSTATUS 4 鍒濆绠℃鐘舵�� (0 = 鍏抽棴, 1 = 寮�鍚�) + /// EN_INITSETTING 5 鍒濆绠¢亾绮楃硻搴﹀垵濮嬫按娉佃浆閫熷垵濮嬮榾闂ㄨ缃� + /// EN_KBULK 6 涓绘祦鍙嶅簲绯绘暟 + /// EN_KWALL 7 绠″鍙嶅簲绯绘暟 + /// EN_FLOW 8 娴侀噺 + /// EN_VELOCITY 9 娴侀�� + /// EN_HEADLOSS 10 姘村ご鎹熷け + /// EN_STATUS 11 瀹為檯绠℃鐘舵�� (0 = 鍏抽棴, 1 = 寮�鍚�) + /// EN_SETTING 12 绠¢亾绮楃硻绯绘暟瀹為檯姘存车杞�熷疄闄呴榾闂ㄨ缃� + /// EN_ENERGY 13 娑堣�楄兘閲忥紝浠ュ崈鐡﹁ + /// (鍙傛暟8 - 13 (EN_FLOW鍒癊N_ENERGY)涓鸿绠楀�笺�傚叾浠栦负璁捐鍙傛暟) + /// </para> + /// <para> + /// 濡傛灉浠庢寚瀹氱娈佃捣濮嬭妭鐐规祦鍚戞寚瀹氱粓姝㈣妭鐐癸紝娴侀噺涓烘锛涘惁鍒欎负璐熴�� + /// 杩斿洖鐨勬暟鍊煎崟浣嶏紝鍙栧喅浜嶦PANETH杈撳叆鏂囦欢涓祦閲忎娇鐢ㄧ殑鍗曚綅銆� + /// </para> + /// </remarks> + /// <seealso cref="ENgetlinkindex" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENgetlinkvalue(int index, LinkValue code, out float value); + + #endregion + + #region 鏇存敼network鏁版嵁鐨勫姛鑳� + + /// <summary>璁剧疆鐗瑰畾绠�鍗曟帶鍒惰鍙ョ殑鍙傛暟</summary> + /// <param name="cindex"> + /// 鎺у埗绱㈠紩鏄粠1寮�濮嬬殑锛岄『搴忎腑瀹冧滑琚緭鍏ワ紝鍦‥PANETH鐨勮緭鍏ユ枃浠剁殑[CONTROLS]鑺傘�� + /// </param> + /// <param name="ctype">鎺у埗绫诲瀷浠g爜 <see cref="ControlType" /></param> + /// <param name="lindex">琚帶鍒剁娈电殑绱㈠紩</param> + /// <param name="setting">鎺у埗璁剧疆鐨勬暟鍊�</param> + /// <param name="nindex">鎺у埗鑺傜偣鐨勭储寮� + /// index of controlling node (0 for <see cref="ControlType.Timer" /> + /// or <see cref="ControlType.TimeOfDay" /> control) + /// </param> + /// <param name="level"> + /// 瀵逛簬姘翠綅鎺у埗鐨勬帶鍒舵按浣嶆垨鑰呭帇鍔涚殑鏁板�硷紝鎴栬�呮帶鍒惰涓虹殑鏃堕棿(浠ョ璁�)锛屽浜庡熀浜庢椂闂寸殑鎺у埗 + /// </param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 瀵逛簬绠¢亾, setting涓�0锛屾剰鍛崇潃绠¢亾鍏抽棴锛屼负1鎰忓懗鐫�瀹冩槸寮�鍚殑銆� + /// 瀵逛簬姘存车, setting鍖呭惈浜嗘按娉佃浆閫燂紝涓�0鎰忓懗鐫�姘存车鍏抽棴锛屼负1鎰忓懗鐫�鍦ㄥ父瑙勮浆閫熶笅寮�鍚�� + /// 瀵逛簬闃�闂�, setting鎸囬榾闂ㄥ帇鍔涖�佹祦閲忔垨鑰呮崯澶辩郴鏁帮紝鍙栧喅浜庨榾闂ㄧ被鍨嬨�� + /// </para> + /// <para> + /// 瀵逛簬璁℃椂鍣ㄦ垨鑰呴挓琛ㄦ椂闂存帶鍒讹紝璁剧疆<paramref name="nindex" /> 鍙傛暟涓�0 + /// </para> + /// <para> + /// 瀵逛簬姘翠綅鎺у埗锛屽鏋滄帶鍒惰妭鐐� <paramref name="nindex" />涓烘按姹狅紝閭d箞level鍙傛暟搴斾负楂樹簬姹犲簳鐨勬按浣�(鑰屼笉鏄爣楂�)銆傚惁鍒檒evel搴斾负杩炴帴鑺傜偣鍘嬪姏銆� + /// </para> + /// <para> + /// 涓洪櫎鍘诲叧浜庣壒瀹氱娈电殑鎺у埗锛岃缃�<paramref name="lindex" />鍙傛暟涓�0銆傚嚱鏁颁腑鍏跺畠鍙傛暟鐨勬暟鍊煎皢琚拷鐣ャ�� + /// </para> + /// </remarks> + /// <example> + /// 鏈緥灏咵Ngetcontrol and ENsetcontrol鐢ㄤ簬鏀瑰彉鑺傜偣鐨勪綆姘翠綅璁剧疆锛屾帶鍒剁娈碉紝鍏锋湁绱㈠紩 thelink 鍒版柊鐨勬暟鍊糿ewlevel銆� + /// This example uses <see cref="ENgetcontrol" /> and <see cref="ENsetcontrol" /> + /// to change the low level setting on the node that controls a link with + /// index <c>thelink</c> to a new value <c>newlevel</c>. + /// <code> + /// int numctrls, lindex, nindex, thelink = 1; + /// float setting, level, newlevel = 1f; + /// ControlType ctype; + /// + /// ENgetcount(CountType.Control, out numctrls); + /// + /// for (int i = 1; i <= numctrls; i++) { + /// ENgetcontrol(i, out ctype, out lindex, out setting, out nindex, out level); + /// if (ctype == ControlType.Lowlevel && lindex == thelink) { + /// ENsetcontrol(i, ctype, lindex, setting, nindex, newlevel); + /// break; + /// } + /// } + /// </code> + /// </example> + /// <seealso cref="ENgetcontrol" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetcontrol( + int cindex, + ControlType ctype, + int lindex, + float setting, + int nindex, + float level); + + /// <summary> + /// 璁剧疆鐗瑰畾鑺傜偣鐨勫弬鏁板�� + /// </summary> + /// <param name="index">鑺傜偣绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠g爜 <see cref="NodeValue" /></param> + /// <param name="v">parameter value</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>鑺傜偣绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鎻愪緵鐨勬暟鍊煎崟浣嶅彇鍐充簬鍦� AStation.Epanet 杈撳叆鏂囦欢涓敤浜庢祦閲忕殑鍗曚綅銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetnodevalue(int index, NodeValue code, float v); + + /// <summary> + /// 璁剧疆鎸囧畾绠℃鐨勫弬鏁板�� + /// </summary> + /// <param name="index">绠℃绱㈠紩</param> + /// <param name="code">鑺傜偣鍙傛暟浠g爜<see cref="LinkValue" /></param> + /// <param name="v">parameter value</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>绠℃绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鏁板�煎崟浣嶅彇鍐充簬鍦‥PANETH杈撳叆鏂囦欢涓娇鐢ㄧ殑娴侀噺鑼冨洿(鍙傝璁¢噺鍗曚綅) + /// </para> + /// <para> + /// + /// ps(pdf):瀵逛簬瀛樺湪鐨勭娈电姸鎬佹垨鑰呰缃紝鍦ㄦā鎷熷紑濮嬩箣鍓嶏紝鍒╃敤EN_INITSTATUS鍜� EN_INITSETTING璁剧疆璁捐鏁板�笺�� + /// 鍦ㄦā鎷熸墽琛岀殑鍚屾椂锛屽埄鐢‥N_STATUS鍜孍N_SETTING 鏀瑰彉杩欎簺鏁板��(鍦‥NrunH - ENnextH寰幆鑼冨洿鍐�)銆� + /// + /// ps(鑻辨枃缈昏瘧):浣跨敤 <see cref="LinkValue.InitStatus" /> 鍜� <see cref="LinkValue.InitSetting" /> + /// 璁剧疆link鐘舵�佹垨璁剧疆鐨勮璁″�硷紝杩欎簺鍊煎湪浠跨湡寮�濮嬪墠灏卞凡瀛樺湪銆� 浣跨敤 <see cref="LinkValue.Status" /> 鍜� <see cref="LinkValue.Setting" /> + /// 鍙互鍦ㄨ繍琛屼豢鐪熸椂锛堝湪 <see cref="ENrunH" /> - <see cref="ENnextH" /> 寰幆鍐咃級鏇存敼杩欎簺鍊笺�� + /// + /// Use <see cref="LinkValue.InitStatus" /> and <see cref="LinkValue.InitSetting" /> + /// to set the design value for a link's status or setting that exists prior to the + /// start of a simulation. Use <see cref="LinkValue.Status" /> and + /// <see cref="LinkValue.Setting" /> to change these values while a simulation + /// is being run (within the <see cref="ENrunH" /> - <see cref="ENnextH" /> loop). + /// + /// </para> + /// <para> + /// 濡傛灉鎺у埗闃�鍏锋湁鏄庣‘鐨勭姸鎬侊紝璁剧疆涓�<see cref="StatusType.Open" />鎴栬��<see cref="StatusType.Closed" />銆� + /// 闇�瑕佸啀娆″惎鐢紝鍦ㄦā鎷熻繃绋嬩腑蹇呴』閲囩敤<see cref="LinkValue.Setting" />鍙傛暟鎻愪緵铏氭嫙鐨勯榾闂ㄨ缃暟鍊笺�� + /// 瀵逛簬绠¢亾<see cref="LinkValue.Roughness" />鎴栬��<see cref="LinkValue.InitSetting" />鐢ㄤ簬鏀瑰彉绮楃硻绯绘暟銆� + /// </para> + /// <para> + /// For pipes, either <see cref="LinkValue.Roughness" /> + /// or <see cref="LinkValue.InitSetting" /> can be used to change roughness. + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetlinkvalue(int index, LinkValue code, float v); + + /// <summary> + /// 娣诲姞涓�涓柊鐨勬椂闂存ā寮忥紝闄勫姞鍒扮幇鏈夋ā寮忕殑鏈熬 + /// </summary> + /// <param name="id">鏂版ā寮忕殑 ID 鍚嶇О</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>ID 鏍囩搴斿寘鍚笉瓒呰繃 31 涓瓧绗�</para> + /// <para>鏂版ā寮忓皢鍖呭惈涓�涓椂闂存锛屽叾涔樻暟绯绘暟涓� 1</para> + /// <para> + /// 浣跨敤 <see cref="ENsetpattern" /> 鍑芥暟锛岄鍏堢敤 <see cref="ENgetpatternindex" /> 鍑芥暟鑾峰彇妯″紡鐨勭储寮曪紝鐒跺悗鐢ㄤ竴缁勭壒瀹氱殑涔樻暟濉厖妯″紡銆� + /// </para> + /// </remarks> + /// <example> + /// <code> + /// string patId = "NewPattern"; + /// float[] patFactors = new[] {0.8f, 1.1f, 1.4f, 1.1f, 0.8f, 0.7f}; + /// int patIndex; + /// ENaddpattern(patId); + /// ENgetpatternindex(patId, out patIndex); + /// ENsetpattern(patIndex, patFactors, 6); + /// </code> + /// </example> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsetpattern([MarshalAs(STRING)] string id); + + /// <summary> + /// 璁剧疆鎸囧畾鏃堕棿妯″紡鐨勬墍鏈変箻瀛� + /// </summary> + /// <param name="index">鏃堕棿妯″紡绱㈠紩</param> + /// <param name="f">鏁翠釜妯″紡鐨勪箻瀛�</param> + /// <param name="n">妯″紡涓洜瀛愭�绘暟</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// <paramref name="f" /> Factors鐐瑰埌鍩轰簬闆剁殑鏁扮粍锛屽寘鍚簡nfactors鐨勫厓绱� + /// </para> + /// <para> + /// 浣跨敤璇ュ嚱鏁伴噸鏂板畾涔�(鍜岄噸鏂拌缃昂瀵�)鏃堕棿妯″紡锛涘埄鐢�<see cref="ENsetpatternvalue" />锛屼慨鏀规ā寮忔寚瀹氭椂娈电殑妯″紡鍥犲瓙銆� + /// </para> + /// </remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENgetpatternvalue" /> + /// <seealso cref="ENsetpatternvalue" /> + /// <seealso cref="ENaddpattern" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetpattern( + int index, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R4, SizeParamIndex = 2), In] float[] f, + int n); + + /// <summary> + /// 璁剧疆鏃堕棿妯″紡鍐呯壒瀹氭椂娈电殑涔樺瓙鍥犲瓙 + /// </summary> + /// <param name="index">鏃堕棿妯″紡绱㈠紩</param> + /// <param name="period">鏃堕棿妯″紡鍐呯殑鏃舵</param> + /// <param name="value">鏃舵鐨勪箻瀛愬洜瀛�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para>妯″紡绱㈠紩鏄粠1寮�濮嬬殑杩炵画鏁存暟</para> + /// <para> + /// 鍒╃敤 <see cref="ENsetpattern" /> 閲嶆柊璁剧疆鏃堕棿妯″紡鍐呯殑鎵�鏈夊洜瀛�. + /// </para> + /// </remarks> + /// <seealso cref="ENgetpatternindex" /> + /// <seealso cref="ENgetpatternlen" /> + /// <seealso cref="ENgetpatternvalue" /> + /// <seealso cref="ENsetpattern" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetpatternvalue(int index, int period, float value); + + /// <summary>璁剧疆鏃堕棿鍙傛暟鍊�</summary> + /// <param name="code">鏃堕棿鍙傛暟浠g爜<see cref="TimeParameter" /></param> + /// <param name="value">鏃堕棿鍙傛暟鍊硷紝浠ョ璁�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// Do not change time parameters after calling <see cref="ENinitH" /> in a + /// hydraulic analysis or <see cref="ENinitQ" /> in a water quality analysis. + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsettimeparam(TimeParameter code, int value); + + /// <summary>璁剧疆鐗瑰畾鍒嗘瀽閫夐」鐨勬暟鍊�</summary> + /// <param name="code">閫夐」缂栧彿<see cref="MiscOption" /></param> + /// <param name="v">閫夐」鍊�</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetoption(MiscOption code, float v); + + /// <summary>璁剧疆姘村姏鐘舵�佹姤鍛婄殑姘村钩</summary> + /// <param name="statuslevel">鐘舵�佹姤鍛婄殑姘村钩<see cref="StatusLevel" /></param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 褰撴按鍔涙ā鎷熼�愭笎灞曞紑鏃讹紝鐘舵�佹姤鍛婂皢绠$綉鍏冪礌鐨勬按鍔涚姸鎬佸彉鍖栧啓鍏ュ埌鎶ュ憡鏂囦欢 + /// 鍏锋湁涓夌姘村钩鐨勬姤鍛�<see cref="StatusLevel" />: + /// <list type="number"> + /// <item>鏃犵姸鎬佹姤鍛�</item> + /// <item>甯歌鎶ュ憡</item> + /// <item>瀹屾暣鐘舵�佹姤鍛�</item> + /// </list> + /// </para> + /// <para> + /// 瀹屾暣鐘舵�佹姤鍛婂寘鍚簡姹傝В绯荤粺姘村姏鏂圭▼缁勶紝鍦ㄦā鎷熺殑姣忎竴鏃堕棿姝ラ暱鐨勬敹鏁涗俊鎭�備富瑕佺敤浜庤皟璇曠洰鐨勩�� + /// </para> + /// <para> + /// 濡傛灉灏嗗湪搴旂敤绋嬪簭涓墽琛岃澶氭姘村姏鍒嗘瀽锛屽缓璁姸鎬佹姤鍛婂叧闂�(<paramref name="statuslevel" /> = <see cref="StatusLevel.None" />)銆� + /// </para> + /// </remarks> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true)] + public static extern ErrorCode ENsetstatusreport(StatusLevel statuslevel); + + /// <summary>璁剧疆琚皟鐢ㄦ按璐ㄥ垎鏋愮殑绫诲瀷</summary> + /// <param name="qualcode">姘磋川鍒嗘瀽浠g爜 <see cref="QualType" /></param> + /// <param name="chemname">琚垎鏋愬寲瀛︽垚鍒嗗悕绉�</param> + /// <param name="chemunits">鍖栧鎴愬垎璁¢噺鍗曚綅</param> + /// <param name="tracenode">婧愬ご璺熻釜鍒嗘瀽涓璺熻釜鑺傜偣鐨処D</param> + /// <returns>閿欒浠g爜<see cref="ErrorCode" /></returns> + /// <remarks> + /// <para> + /// 濡傛灉涓嶈繘琛屽寲瀛︽垚鍒嗗垎鏋愶紝鍖栧鎴愬垎鍚嶇О鍜屽崟浣嶅彲浠ヤ负绌哄瓧绗︿覆銆傚鏋滀笉杩涜婧愬ご璺熻釜鍒嗘瀽锛岃窡韪妭鐐逛繚鐣欏悕绉般�� + /// </para> + /// <para> + /// 娉ㄦ剰璺熻釜鑺傜偣閫氳繃ID鎸囧畾鑰屼笉鏄储寮曘�� + /// </para> + /// </remarks> + /// <seealso cref="ENgetqualtype" /> + [DllImport(EPANETDLL, CallingConvention = CONVENTION, ExactSpelling = true, CharSet = CHARSET, + BestFitMapping = false, ThrowOnUnmappableChar = true)] + public static extern ErrorCode ENsetqualtype( + QualType qualcode, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string chemname, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string chemunits, + [MarshalAs(STRING), Optional, DefaultParameterValue("")] string tracenode); + + #endregion + + } + +} diff --git a/Test/IStation.Test/HydraulicModelValidationHelperJob.cs b/Test/IStation.Test/HydraulicModelValidationHelperJob.cs new file mode 100644 index 0000000..d33124e --- /dev/null +++ b/Test/IStation.Test/HydraulicModelValidationHelperJob.cs @@ -0,0 +1,179 @@ +锘縩amespace IStation.Test +{ + /// <summary> + /// 妯″瀷楠岃瘉杈呭姪绫� + /// </summary> + public class HydraulicModelValidationHelperJob + { + private static readonly string _hydraulic_model_file = Path.Combine( + Settings.ParasHelper.LocalFile.DataFolderDirectory, + Settings.ParasHelper.LocalFile.HydraulicModelFile); + + private static readonly Dictionary<string, double> _pump_nr_dict = new() { + { "Pump11",524}, + { "Pump12",524}, + { "Pump13",524}, + { "Pump14",524}, + //{ "Pump15",545}, + //{ "Pump16",524}, + { "Pump17",524}, + { "Pump18",524}, + + { "Pump21",740}, + { "Pump22",495}, + { "Pump23",495}, + { "Pump24",495}, + { "Pump25",495}, + { "Pump26",495}, + { "Pump27",740} + }; + + /// <summary> + /// + /// </summary> + public static void Execute() + { + + var verification_id = Yw.YitIdHelper.NextId(); + var current_time = DateTime.Now; + + if (!File.Exists(_hydraulic_model_file)) + { + //Log.Info(verification_id, $"妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷鏂囦欢涓嶅瓨鍦�,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + var hydraulic_model_validation_config = new IStation.Service.HydraulicModelValidationConfig().Get(); + if (hydraulic_model_validation_config == null) + { + //Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,姘村姏妯″瀷楠岃瘉鏂囦欢涓嶅瓨鍦�,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + + var hydraulic_model_scada_list = new List<Model.HydraulicModelScada>(); + + var scada_debug_json_file = Path.Combine(Settings.ParasHelper.LocalFile.DataFolderDirectory, "scada_debug.txt"); + var scada_debug_json = File.ReadAllText(scada_debug_json_file); + + + var zy_scada_output = Yw.JsonHelper.Json2Object<IStation.Dto.ScadaScheduleInput>(scada_debug_json); + + if (zy_scada_output.data != null && zy_scada_output.data.Any()) + { + var data_dict = zy_scada_output.data; + foreach (var scada_dict in data_dict) + { + var vals = scada_dict.Value.ElementAt(0).Value; + var time = scada_dict.Value.ElementAt(1).Value; + var key = scada_dict.Value.ElementAt(2).Value; + var hydraulic_model_scada = new Model.HydraulicModelScada(); + hydraulic_model_scada.VerificationID = verification_id; + hydraulic_model_scada.Tag = key; + if (DateTime.TryParse(time, out DateTime t)) + hydraulic_model_scada.Time = t; + if (double.TryParse(vals, out double v)) + hydraulic_model_scada.Value = v; + hydraulic_model_scada_list.Add(hydraulic_model_scada); + } + } + + if (!hydraulic_model_scada_list.Any()) + { + //Log.Info(verification_id, "妯″瀷楠岃瘉璁″垝浠诲姟涓�,鎺ユ敹Scada鏁版嵁涓虹┖,鑷姩璺宠繃褰撳墠鏁版嵁!"); + return; + } + else + { + var json = Yw.JsonHelper.Object2Json(hydraulic_model_scada_list); + // Log.Debug(verification_id, json); + } + + var flow_id_mapping_dict = hydraulic_model_validation_config.FlowIdMappingDict; + var pressure_id_mapping_dict = hydraulic_model_validation_config.PressureIdMappingDict; + var pattern_id_mapping_dict = hydraulic_model_validation_config.PatternIdMappingDict; + var pressure_id_kPa_list = hydraulic_model_validation_config.PressureIdkPaList; + + var pattern_list = new List<IStation.Hydraulic.Pattern>(); + var time_value = new IStation.Hydraulic.TimeValue + { + Time = current_time, + Value = new Dictionary<string, double>() + }; + + foreach (var hydraulic_model_scada in hydraulic_model_scada_list) + { + var tag = hydraulic_model_scada.Tag; + var value = hydraulic_model_scada.Value; + foreach (var item in flow_id_mapping_dict) + { + if (item.Value != tag) + continue; + time_value.Value.Add(tag, value ?? 0); + } + + foreach (var item in pressure_id_mapping_dict) + { + if (item.Value != tag) + continue; + var pressure_value = value ?? 0; + if (pressure_id_kPa_list.Contains(tag)) + { + pressure_value /= 1000; + } + pressure_value = IStation.Curve.PumpCalculateHelper.Mpa2M(pressure_value); + time_value.Value.Add(tag, pressure_value); + } + + foreach (var item in pattern_id_mapping_dict) + { + if (item.Value != tag) + continue; + var pattern_id = item.Key; + var factor = value; + if (_pump_nr_dict.ContainsKey(pattern_id)) + { + factor /= _pump_nr_dict[pattern_id]; + factor = factor < 0 ? 0 : factor; + } + var pattern = new IStation.Hydraulic.Pattern + { + Id = pattern_id, + FactorList = new List<float>() + }; + if (factor.HasValue) + pattern.FactorList.Add((float)factor.Value); + pattern_list.Add(pattern); + } + } + + + var hydraulic_model_record_list = IStation.Hydraulic.ModeVerifyHelper.Verify(verification_id, _hydraulic_model_file, flow_id_mapping_dict, pressure_id_mapping_dict, pattern_list, time_value); + if (hydraulic_model_record_list == null || !hydraulic_model_record_list.Any()) + { + + return; + } + + var hydraulic_model_validation = new Model.HydraulicModelValidation(); + hydraulic_model_validation.ID = verification_id; + hydraulic_model_validation.Time = current_time; + //hydraulic_model_validation.Config = Yw.JsonHelper.Object2Json(hydraulic_model_validation_config); + + var bol = new IStation.Service.HydraulicModelValidation().Insert(hydraulic_model_validation) > 0; + if (!bol) + { + } + + bol = new IStation.Service.HydraulicModelScada().Inserts(hydraulic_model_scada_list); + if (!bol) + { + } + + bol = new IStation.Service.HydraulicModelRecord().Inserts(hydraulic_model_record_list); + if (!bol) + { + } + + + } + } +} diff --git a/Test/IStation.Test/IStation.Test.csproj b/Test/IStation.Test/IStation.Test.csproj index a2fc5fe..7a5b1fe 100644 --- a/Test/IStation.Test/IStation.Test.csproj +++ b/Test/IStation.Test/IStation.Test.csproj @@ -5,9 +5,19 @@ <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> + <PlatformTarget>x86</PlatformTarget> </PropertyGroup> <ItemGroup> + <Compile Remove="EpanetMethods.cs" /> + <Compile Remove="ModelVerification.cs" /> + <Compile Remove="Program - 澶嶅埗.cs" /> + <Compile Remove="Program _mode.cs" /> + <Compile Remove="Program_bak.cs" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\IStation.Dto\IStation.Dto.csproj" /> <ProjectReference Include="..\..\IStation.Service\IStation.Service.csproj" /> </ItemGroup> diff --git a/Test/IStation.Test/ModelVerification.cs b/Test/IStation.Test/ModelVerification.cs new file mode 100644 index 0000000..eb98618 --- /dev/null +++ b/Test/IStation.Test/ModelVerification.cs @@ -0,0 +1,26 @@ +锘縩amespace IStation.Test +{ + /// <summary> + /// + /// </summary> + public class ModelVerification + { + /// <summary> + /// 娴侀噺鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string, string> FlowIdMapping { get; set; } + + + /// <summary> + /// 鍘嬪姏鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string, string> PressureIdMapping { get; set; } + + + /// <summary> + /// 妯″紡鏍囪瘑鏄犲皠 + /// </summary> + public Dictionary<string,string> PatternIdMapping { get; set; } + + } +} diff --git "a/Test/IStation.Test/Program - \345\244\215\345\210\266.cs" "b/Test/IStation.Test/Program - \345\244\215\345\210\266.cs" new file mode 100644 index 0000000..5e234f6 --- /dev/null +++ "b/Test/IStation.Test/Program - \345\244\215\345\210\266.cs" @@ -0,0 +1,175 @@ +锘縰sing IStation.Test; +using Yw.Untity; + +var station1 = "Pump11,Pump12,Pump13,Pump14,Pump15,Pump16,Pump17,Pump18,R3,R2,R1,SFJD1,SFJD2,SFJD3"; +var pattern_id_list1 = StringListHelper.ToList(station1); + +var station2 = "RPump21,SFPump21,Pump21,RPump22,SFPump22,Pump22,RPump23,SFPump23,Pump23,RPump24,SFPump24,Pump24,RPump25,SFPump25,Pump25,RPump26,SFPump26,Pump26,RPump27,SFPump27,Pump27,SFDN2400,SFDN2700"; +var pattern_id_list2 = StringListHelper.ToList(station2); + +var pattern_id_list = new List<string>(); +pattern_id_list.AddRange(pattern_id_list1); +pattern_id_list.AddRange(pattern_id_list2); + +var pressure_tag_list1 = new List<string>() { "SPJD1", "SPJD2", "SPJD3", "SPPump11", "SPPump12", "SPPump13", "SPPump14", "SPPump15", "SPPump16", "SPPump17", "SPPump18" }; +var pressure_tag_list2 = new List<string>() { "SPDN2400", "SPDN2700", "SPPump21", "SPPump22", "SPPump23", "SPPump24", "SPPump25", "SPPump26", "SPPump27" }; + +var pressure_tag_list = new List<string>(); +pressure_tag_list.AddRange(pressure_tag_list1); +pressure_tag_list.AddRange(pressure_tag_list2); + + +var flow_tag_list1 = new List<string>() { "SFJD1", "SFJD2", "SFJD3" }; +var flow_tag_list2 = new List<string>() { "SFDN2400", "SFDN2700", "SFPump21", "SFPump22", "SFPump23", "SFPump24", "SFPump25", "SFPump26", "SFPump27" }; + +var flow_tag_list = new List<string>(); +flow_tag_list.AddRange(flow_tag_list1); +flow_tag_list.AddRange(flow_tag_list2); + + +var pressure_scada_id_dict1 = new Dictionary<string, string>() +{ + {"Jjd1","SPJD1"}, + {"Jjd2","SPJD2"}, + {"Jjd3","SPJD3"}, + {"Jpump11","SPPump11" }, + {"Jpump12","SPPump12" }, + {"Jpump13","SPPump13" }, + {"Jpump14","SPPump14" }, + {"Jpump15","SPPump15" }, + {"Jpump16","SPPump16" }, + {"Jpump17","SPPump17" }, + {"Jpump18","SPPump18" } +}; +var pressure_scada_id_dict2 = new Dictionary<string, string>() +{ + {"Jdn2400","SPDN2400"}, + {"Jdn2700","SPDN2700"}, + {"Jpump21","SPPump21"}, + {"Jpump22","SPPump22"}, + {"Jpump23","SPPump23"}, + {"Jpump24","SPPump24"}, + {"Jpump25","SPPump25"}, + {"Jpump26","SPPump26"}, + {"Jpump27","SPPump27"} +}; + +var flow_scada_id_dict1 = new Dictionary<string, string>() +{ + {"Pjd1","SFJD1"}, + {"Pjd2","SFJD2"}, + {"Pjd3","SFJD3"} +}; +var flow_scada_id_dict2 = new Dictionary<string, string>() +{ + {"Pdn2400","SFDN2400"}, + {"Pdn2700","SFDN2700"}, + {"Ppump21","SFPump21"}, + {"Ppump22","SFPump22"}, + {"Ppump23","SFPump23"}, + {"Ppump24","SFPump24"}, + {"Ppump25","SFPump25"}, + {"Ppump26","SFPump26"}, + {"Ppump27","SFPump27"} +}; + +var pressure_scada_id_dict = new Dictionary<string, string>(); +foreach (var item in pressure_scada_id_dict1) +{ + pressure_scada_id_dict.Add(item.Key, item.Value); +} +foreach (var item in pressure_scada_id_dict2) +{ + pressure_scada_id_dict.Add(item.Key, item.Value); +} + +var flow_scada_id_dict = new Dictionary<string, string>(); +foreach (var item in flow_scada_id_dict1) +{ + flow_scada_id_dict.Add(item.Key, item.Value); +} +foreach (var item in flow_scada_id_dict2) +{ + flow_scada_id_dict.Add(item.Key, item.Value); +} + + + + + +//var modelVerification = new IStation.Model.HydraulicModelValidationConfig(); +//modelVerification.FlowIdMappingDict = flow_scada_id_dict; +//modelVerification.PressureIdMappingDict=pressure_scada_id_dict; +//modelVerification.PatternIdMappingDict = new Dictionary<string, string>(); + + +//foreach (var item in modelVerification.FlowIdMappingDict) +//{ +// modelVerification.FlowIdMappingDict[item.Key] = ""; +//} + +//foreach (var item in modelVerification.PressureIdMappingDict) +//{ +// modelVerification.PressureIdMappingDict[item.Key] = ""; +//} + +//foreach (var item in pattern_id_list) +//{ +// modelVerification.PatternIdMappingDict[item] = ""; +//} + +var bol = new IStation.Service.HydraulicModelValidationConfig().Get(); + +var a = bol; + +//var dayValueList = IStation.Test.DayValueHelper.GetDayValues(); + +//foreach (var dayValue in dayValueList) +//{ +// var patternList = new List<IStation.Hydraulic.Pattern>(); +// foreach (var pattern_id in pattern_id_list) +// { +// var pattern = new IStation.Hydraulic.Pattern(); +// pattern.Id = pattern_id; +// pattern.FactorList = new List<float>(); +// patternList.Add(pattern); +// } + +// var timeValueList = dayValue.TimeValueList.OrderBy(x => x.Time).ToList(); +// foreach (var timeValue in timeValueList) +// { +// var value_dict = timeValue.Value; +// foreach (var item in value_dict) +// { +// var pattern_id = item.Key; +// var pattern_Factor = item.Value; +// var pattern = patternList.Find(x => x.Id == pattern_id); +// if (pattern != null) +// { +// pattern.FactorList.Add((float)pattern_Factor); +// } +// } +// } +// dayValue.PatternList = patternList; +//} + + +//var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}ch2_v3_20240614.inp"; +//// var vList = IStation.Hydraulic.ModeVerifyHelper.Verify(1, filePath, flow_scada_id_dict, pressure_scada_id_dict, dayValueList); + +//var first_dv = dayValueList.First(); +//var first_tv = first_dv.TimeValueList.First(); +//var first_pat = first_dv.PatternList.Select(x => new IStation.Hydraulic.Pattern() +//{ +// Id = x.Id, +// FactorList = new List<float>() { x.FactorList.First() } +//}).ToList(); + + +//var vList = IStation.Hydraulic.ModeVerifyHelper.Verify(2, filePath, flow_scada_id_dict, pressure_scada_id_dict, first_pat, first_tv); + +////var json=Yw.JsonHelper.Object2Json(vList); +////File.WriteAllText("D:\\sc.json", json); + +//var +// a = vList; \ No newline at end of file diff --git a/Test/IStation.Test/Program _mode.cs b/Test/IStation.Test/Program _mode.cs new file mode 100644 index 0000000..634a2b1 --- /dev/null +++ b/Test/IStation.Test/Program _mode.cs @@ -0,0 +1,350 @@ +锘� + + +//var fileName =$"{AppDomain.CurrentDomain.BaseDirectory}w.inp"; +using IStation.Epanet; +using IStation.Epanet.Analysis; +using IStation.Epanet.Enums; +using System.Text; +using Yw; +using Yw.Untity; + + + +IStation.ConfigHelper.InitHydraulicDB(); + + +var station1 = "Pump11,Pump12,Pump13,Pump14,Pump15,Pump16,Pump17,Pump18,R3,R2,R1,SFJD1,SFJD2,SFJD3"; +var mode_tag_list1 = StringListHelper.ToList(station1); + +var station2 = "RPump21,SFPump21,Pump21,RPump22,SFPump22,Pump22,RPump23,SFPump23,Pump23,RPump24,SFPump24,Pump24,RPump25,SFPump25,Pump25,RPump26,SFPump26,Pump26,RPump27,SFPump27,Pump27,SFDN2400,SFDN2700"; +var mode_tag_list2 = StringListHelper.ToList(station2); + +var mode_tag_list = new List<string>(); +mode_tag_list.AddRange(mode_tag_list1); +mode_tag_list.AddRange(mode_tag_list2); + +var pressure_tag_list1 = new List<string>() { "SPJD1", "SPJD2", "SPJD3", "SPPump11", "SPPump12", "SPPump13", "SPPump14", "SPPump15", "SPPump16", "SPPump17", "SPPump18" }; +var pressure_tag_list2 = new List<string>() { "SPDN2400","SPDN2700","SPPump21","SPPump22","SPPump23","SPPump24","SPPump25","SPPump26","SPPump27" }; + +var pressure_tag_list = new List<string>(); +pressure_tag_list.AddRange(pressure_tag_list1); +pressure_tag_list.AddRange(pressure_tag_list2); + + +var flow_tag_list1 = new List<string>() { "SFJD1", "SFJD2", "SFJD3" }; +var flow_tag_list2 = new List<string>() { "SFDN2400", "SFDN2700", "SFPump21", "SFPump22", "SFPump23", "SFPump24", "SFPump25", "SFPump26", "SFPump27" }; + +var flow_tag_list = new List<string>(); +flow_tag_list.AddRange(flow_tag_list1); +flow_tag_list.AddRange(flow_tag_list2); + + +var pressure_node_dict1=new Dictionary<string, string>() +{ + {"SPJD1","Jjd1"}, + {"SPJD2","Jjd1"}, + {"SPJD3","Jjd3"}, + {"SPPump11","Jpump11"}, + {"SPPump12","Jpump12"}, + {"SPPump13","Jpump13"}, + {"SPPump14","Jpump14"}, + {"SPPump15","Jpump15"}, + {"SPPump16","Jpump16"}, + {"SPPump17","Jpump17"}, + {"SPPump18","Jpump18"} +}; + +var pressure_node_dict2 = new Dictionary<string, string>() +{ + {"SPDN2400","Jdn2400"}, + {"SPDN2700","Jdn2700"}, + {"SPPump21","Jpump21"}, + {"SPPump22","Jpump22"}, + {"SPPump23","Jpump23"}, + {"SPPump24","Jpump24"}, + {"SPPump25","Jpump25"}, + {"SPPump26","Jpump26"}, + {"SPPump27","Jpump27"} +}; + + + +var flow_node_dict1 = new Dictionary<string, string>() +{ + {"SFJD1","Pjd1"}, + {"SFJD2","Pjd2"}, + {"SFJD3","Pjd3"} +}; + +var flow_node_dict2 = new Dictionary<string, string>() +{ + {"SFDN2400","Pdn2400"}, + {"SFDN2700","Pdn2700"}, + {"SFPump21","Ppump21"}, + {"SFPump22","Ppump22"}, + {"SFPump23","Ppump23"}, + {"SFPump24","Ppump24"}, + {"SFPump25","Ppump25"}, + {"SFPump26","Ppump26"}, + {"SFPump27","Ppump27"} +}; + + + + + +var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}ch2_v3_20240614.inp"; +var dayValues = DayValueHelper.GetDayValues(1); +if (dayValues == null || !dayValues.Any()) + return; + + +var err = EpanetMethods.ENopen(filePath, "", ""); +if (err != 0) +{ + var msg = err.GetMsg(); + return; +} + + +var records=new List<IStation.Model.HydraulicRecord>(); + +var mode_float_dict = new Dictionary<string, List<float>>(); +foreach (var tag in mode_tag_list) +{ + mode_float_dict[tag] = new List<float>(); +} + + +int patIndex; +EpanetMethods.ENopenH(); +foreach (var dayValue in dayValues) +{ + + foreach (var item in mode_float_dict) + item.Value.Clear(); + + //鏇存柊鏃堕棿妯″紡 + var timeValueList = dayValue.TimeValues; + for (int i = 0; i < timeValueList.Count; i++) + { + var valueDict = timeValueList[i].Value; + foreach (var item in valueDict) + { + var tag = item.Key; + var value = item.Value; + if (mode_tag_list.Contains(tag)) + { + var fv = (float)value; + mode_float_dict[tag].Add(fv); + } + } + } + + foreach (var mode in mode_float_dict) + { + var patId = mode.Key; + var patFactors = mode.Value.ToArray(); + var patFactorsCount = mode.Value.Count; + patFactorsCount = patFactorsCount == 0 ? 1 : patFactorsCount; + err = EpanetMethods.ENgetpatternindex(patId, out patIndex); + if (err != ErrorCode.Ok) + { + var msg = err.GetMsg(); + return; + } + err = EpanetMethods.ENsetpattern(patIndex, patFactors, patFactorsCount); + if (err != ErrorCode.Ok) + { + var msg = err.GetMsg(); + return; + } + } + + var output = new Output(); + int tstep = 0; + EpanetMethods.ENinitH(0); + do + { + const int MAXID = 31; + var idBuild = new StringBuilder(MAXID); + var jsonBuild = new StringBuilder(); + + EpanetMethods.ENrunH(out int t); + EpanetMethods.ENgetcount(CountType.Node, out int nodeCount); + EpanetMethods.ENgetcount(CountType.Link, out int linkCount); + + var time = TimeSpan.FromSeconds(t); + TimeValue timeVlaue = null; + if (time.TotalSeconds != 86400) + { + timeVlaue = timeValueList.Find(x => x.Time.TimeOfDay == time); + if (timeVlaue == null) + continue; + } + else + { + + + } + + + for (int i = 1; i <= nodeCount; i++) + { + if (EpanetMethods.ENgetnodeid(i, idBuild) != ErrorCode.Ok) + continue; + var nodeId = idBuild.ToString(); + if (!Exist(nodeId, out string tag)) + continue; + + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (NodeValue f in Enum.GetValues(typeof(NodeValue))) + { + EpanetMethods.ENgetnodevalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetnodetype(i, out NodeType type); + var outNode = JsonHelper.Json2Object<OutNode>(jsonBuild.ToString()); + outNode.NodeType = type; + outNode.ID = nodeId; + outNode.Time = time; + output.Nodes.Add(outNode); + + if (timeVlaue != null && timeVlaue.Value.ContainsKey(tag)) + { + var value = timeVlaue.Value[tag]; + var record = new IStation.Model.HydraulicRecord(); + record.Time = timeVlaue.Time; + record.ModeId = nodeId; + record.ScadaId = tag; + record.ValueType = IStation.eValueType.Head; + record.ModeValue = (double)outNode.Head; + record.ScadaValue = value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + records.Add(record); + } + + } + + for (int i = 1; i <= linkCount; i++) + { + if (EpanetMethods.ENgetlinkid(i, idBuild) != ErrorCode.Ok) + continue; + var linkId = idBuild.ToString(); + if (!Exist(linkId, out string tag)) + continue; + // Console.WriteLine(tag); + + jsonBuild.Clear(); + jsonBuild.Append("{"); + foreach (LinkValue f in Enum.GetValues(typeof(LinkValue))) + { + EpanetMethods.ENgetlinkvalue(i, f, out float v); + jsonBuild.Append($"\"{f}\":\"{v}\","); + } + jsonBuild.Remove(jsonBuild.Length - 1, 1); + jsonBuild.Append("}"); + + EpanetMethods.ENgetlinktype(i, out LinkType type); + var outLink = JsonHelper.Json2Object<OutLink>(jsonBuild.ToString()); + outLink.LinkType = type; + outLink.ID = linkId; + outLink.Time = time; + output.Links.Add(outLink); + + + if (timeVlaue != null && timeVlaue.Value.ContainsKey(tag)) + { + var value = timeVlaue.Value[tag]; + var record = new IStation.Model.HydraulicRecord(); + record.Time = timeVlaue.Time; + record.ModeId = linkId; + record.ScadaId = tag; + record.ValueType = IStation.eValueType.Flow; + record.ModeValue = (double)outLink.Flow; + record.ScadaValue = value; + record.DifferenceValue = record.ScadaValue - record.ModeValue; + records.Add(record); + } + + + } + + EpanetMethods.ENnextH(out tstep); + + } while (tstep > 0); + + + var json = JsonHelper.Object2FormatJson(output); + File.WriteAllText("D:\\cw.json", json); + break; + +} +EpanetMethods.ENcloseH(); + +var r = records; + +Console.WriteLine("OK"); +//var json = Yw.JsonHelper.Object2FormatJson(output); +//Console.WriteLine(json); +Console.ReadKey(); + + + +bool Exist(string id,out string tag) +{ + var exist = false; + tag = string.Empty; + foreach (var item in pressure_node_dict1) + { + if (item.Value == id) + { + tag = item.Key; + exist = true; + } + } + + if (exist) + return true; + + foreach (var item in pressure_node_dict2) + { + if (item.Value == id) + { + tag = item.Key; + exist = true; + } + } + + if (exist) + return true; + + foreach (var item in flow_node_dict1) + { + if (item.Value == id) + { + tag = item.Key; + exist = true; + } + } + + if (exist) + return true; + + foreach (var item in flow_node_dict2) + { + if (item.Value == id) + { + tag = item.Key; + exist = true; + } + } + + return exist; +} \ No newline at end of file diff --git a/Test/IStation.Test/Program.cs b/Test/IStation.Test/Program.cs index 4604440..d7052d0 100644 --- a/Test/IStation.Test/Program.cs +++ b/Test/IStation.Test/Program.cs @@ -1,230 +1,32 @@ -锘�//var chConfig = ScheduleConfigHelper.Create(); -//var bol=new IStation.Service.ScheduleConfig().Save(chConfig); -//Console.WriteLine(bol); +锘縰sing IStation.Test; -using Dm; -using Yw.Untity; -using static System.Net.Mime.MediaTypeNames; +IStation.ConfigHelper.InitHydraulicDB(); +HydraulicModelValidationHelperJob.Execute(); -var station=new IStation.Service.Station().Get(); -var helper = new IStation.Algorithm.ScheduleHelper(); +//var service = new IStation.Service.HydraulicModelValidationConfig(); +//var config = service.Get(); +//config.PressureIdkPaList = new List<string>(){ +// "闀挎睙绠$綉鍥�.鍢夊畾鏁版嵁.1绾垮帇鍔�", +// "闀挎睙绠$綉鍥�.鍢夊畾鏁版嵁.2绾垮帇鍔�", +// "闀挎睙绠$綉鍥�.鍢夊畾鏁版嵁.3绾垮帇鍔�", +// "HF涓�杈撴按11鍙峰彉棰戞帶鍒剁敾闈�.11鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按12鍙峰彉棰戞帶鍒剁敾闈�.12鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按13鍙峰彉棰戞帶鍒剁敾闈�.13鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按14鍙峰彉棰戞帶鍒跺浘.14鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按15鍙峰伐棰戞帶鍒跺浘.15鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按16鍙峰伐棰戞帶鍒跺浘.16鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按17鍙峰彉棰戞帶鍒跺浘.17鍙锋车鍑哄彛鍘嬪姏", +// "HF涓�杈撴按18鍙峰彉棰戞帶鍒跺浘.18鍙锋车鍑哄彛鍘嬪姏" +// //"闀挎睙绠$綉鍥�.2400鎬荤.鍑哄巶鍘嬪姏", +// //"闀挎睙绠$綉鍥�.2600鎬荤.鍑哄巶鍘嬪姏", +// //"浜岃緭姘�21鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�22鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�23鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�24鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�25鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�26鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏", +// //"浜岃緭姘�27鍙锋按娉佃繍琛屽弬鏁�.鍑烘按鍘嬪姏" +//}; - - -List<List<int>> same_section_flag_combine_list1 = new() -{ - new (){11, 13, 15, 17}, - new (){12, 14, 16 ,18}, -}; - - -List<List<int>> same_section_flag_combine_list2 = new() -{ - new (){21, 22, 23, 24}, - new (){25, 26, 27 }, -}; - - -var must_close_flag_list=new List<int>() { 25, 26, 27 , 21, 22, 23, 24 }; -var combine=new List<int>() { 25, 26, 27, 21, 22, 23 }; - -var difC = Math.Abs(combine.Count - must_close_flag_list.Count); - - -var c1 = combine.Intersect(must_close_flag_list).Count() ; - -var count = combine.Except(must_close_flag_list).Count(); -var count2 = must_close_flag_list.Except(combine).Count(); -var count3 = must_close_flag_list.Except(combine).Count(); -if (count == difC) -{ - -} -//var same_section_flag1 = same_section_flag_combine_list[0]; -//var same_section_flag2 = same_section_flag_combine_list[1]; - - -//var same_section_flag_remark1 = IntListHelper.ToString(same_section_flag1); -//var same_section_flag_remark2 = IntListHelper.ToString(same_section_flag2); - -//var pumps = station.S1; -//var pump_flag_list = pumps.Select(x => x.Flag).ToList(); - -//var minCombineCount = same_section_flag_combine_list.Min(x => x.Count); -//var maxCombineCount = same_section_flag_combine_list.Max(x=>x.Count); - - -//Test(same_section_flag_combine_list1, 8); -//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); -//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); -//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); - -//Test(same_section_flag_combine_list2, 7); - -//void Test(List<List<int >> same_section_flag_combine_list, int pumpCount) -//{ -// Dictionary<int, List<int[]>> dict = new(); -// for (int pump_count = 2; pump_count <= pumpCount; pump_count++) -// { -// dict[pump_count] = new List<int[]>(); -// if (pump_count == 2) -// { -// foreach (var same_section in same_section_flag_combine_list) -// { -// var combine = GetCombine(same_section, 2); -// dict[pump_count].AddRange(combine); -// } -// } -// else if (pump_count == 3) -// { -// foreach (var same_section in same_section_flag_combine_list) -// { -// var combine = GetCombine(same_section, 3); -// dict[pump_count].AddRange(combine); -// } -// } -// else if (pump_count == 4) -// { -// foreach (var same_section in same_section_flag_combine_list) -// { -// var combine3 = GetCombine(same_section, 3); -// dict[pump_count].AddRange(combine3); -// if (same_section.Count > 3) -// { -// var combine4 = GetCombine(same_section, 4); -// dict[pump_count].AddRange(combine4); -// } -// } -// } -// else if (pump_count == 5) -// { -// foreach (var same_section in same_section_flag_combine_list) -// { -// if (same_section.Count > 3) -// { -// var combine4 = GetCombine(same_section, 4); -// dict[pump_count].AddRange(combine4); -// } -// } -// } -// else if (pump_count == 6) -// { -// foreach (var same_section in same_section_flag_combine_list) -// { -// if (same_section.Count > 3) -// { -// var combine4 = GetCombine(same_section, 4); -// dict[pump_count].AddRange(combine4); -// } -// } -// } - -// } - - -// foreach (var item in dict) -// { -// Console.WriteLine($">>>>>>>>>>>>>>>>>>>{item.Key}"); -// foreach (var value in item.Value) -// { -// Console.WriteLine(IntListHelper.ToString(value)); -// } -// } -//} - -//List<int[]> GetCombine(List<int> flags,int count) -//{ -// var combine = IStation.Curve.PermutationAndCombination<int>.GetCombination(flags.ToArray(), count); -// return combine; -//} - - -//Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>"); -//Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>"); - - - -//for (int pumpCount = 2; pumpCount <= pumps.Count; pumpCount++) -//{ - -// switch (pumpCount) -// { -// case 1: -// { -// Console.WriteLine("----1----"); -// } -// break; -// case 2: -// { -// Console.WriteLine("----2----"); -// var combine_list = IStation.Curve.PermutationAndCombination<int>.GetCombination(pump_flag_list.ToArray(), pumpCount);//鎺掑垪缁勫悎 -// foreach (var combine in combine_list) -// { -// var combine_remark = IntListHelper.ToString(combine.OrderBy(x => x)); -// if (same_section_flag_remark1.Contains(combine_remark)) -// { -// Console.WriteLine(combine_remark); -// continue; -// } -// if (same_section_flag_remark2.Contains(combine_remark)) -// { -// Console.WriteLine(combine_remark); -// continue; -// } -// } -// } -// break; -// case 3: -// { -// Console.WriteLine("----3----"); -// var combine_list = IStation.Curve.PermutationAndCombination<int>.GetCombination(pump_flag_list.ToArray(), pumpCount);//鎺掑垪缁勫悎 -// foreach (var combine in combine_list) -// { -// var combine_remark = IntListHelper.ToString(combine.OrderBy(x => x)); -// if (same_section_flag_remark1.Contains(combine_remark)) -// { -// Console.WriteLine(combine_remark); -// continue; -// } -// if (same_section_flag_remark2.Contains(combine_remark)) -// { -// Console.WriteLine(combine_remark); -// continue; -// } -// } -// } -// break; -// case 4: -// { -// Console.WriteLine("----4----"); -// var part1 = IStation.Curve.PermutationAndCombination<int>.GetCombination(same_section_flag1.ToArray(), 3); -// part1.ForEach(x => Console.WriteLine(IntListHelper.ToString(x))); -// var part2 = IStation.Curve.PermutationAndCombination<int>.GetCombination(same_section_flag2.ToArray(), 3); -// part2.ForEach(x => Console.WriteLine(IntListHelper.ToString(x))); -// Console.WriteLine(same_section_flag_remark1); -// Console.WriteLine(same_section_flag_remark2); -// } -// break; -// case 5: -// { - -// } -// break; -// case 6: -// { - -// } -// break; -// default: -// { - -// } -// break; -// } - -//} - - -Console.ReadKey(); - +//service.Save(config); \ No newline at end of file diff --git a/Test/IStation.Test/Program_bak.cs b/Test/IStation.Test/Program_bak.cs new file mode 100644 index 0000000..0baf63d --- /dev/null +++ b/Test/IStation.Test/Program_bak.cs @@ -0,0 +1,231 @@ +锘�//var chConfig = ScheduleConfigHelper.Create(); +//var bol=new IStation.Service.ScheduleConfig().Save(chConfig); +//Console.WriteLine(bol); + +using Yw.Untity; +using static System.Net.Mime.MediaTypeNames; + +var station=new IStation.Service.Station().Get(); +var helper = new IStation.Algorithm.ScheduleHelper(); + + + + +List<List<int>> same_section_flag_combine_list1 = new() +{ + new (){11, 13, 15, 17}, + new (){12, 14, 16 ,18}, +}; + + +List<List<int>> same_section_flag_combine_list2 = new() +{ + new (){21, 22, 23, 24}, + new (){25, 26, 27 }, +}; + + +var must_close_flag_list=new List<int>() { 25, 26, 27 , 21, 22, 23, 24 }; +var combine=new List<int>() { 25, 26, 27, 21, 22, 23 }; + +var difC = Math.Abs(combine.Count - must_close_flag_list.Count); + + +var c1 = combine.Intersect(must_close_flag_list).Count() ; + +var count = combine.Except(must_close_flag_list).Count(); +var count2 = must_close_flag_list.Except(combine).Count(); +var count3 = must_close_flag_list.Except(combine).Count(); +if (count == difC) +{ + +} + + +//var same_section_flag1 = same_section_flag_combine_list[0]; +//var same_section_flag2 = same_section_flag_combine_list[1]; + + +//var same_section_flag_remark1 = IntListHelper.ToString(same_section_flag1); +//var same_section_flag_remark2 = IntListHelper.ToString(same_section_flag2); + +//var pumps = station.S1; +//var pump_flag_list = pumps.Select(x => x.Flag).ToList(); + +//var minCombineCount = same_section_flag_combine_list.Min(x => x.Count); +//var maxCombineCount = same_section_flag_combine_list.Max(x=>x.Count); + + +//Test(same_section_flag_combine_list1, 8); +//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); +//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); +//Console.WriteLine($">>>>>>>>>>>>>>>>>>>"); + +//Test(same_section_flag_combine_list2, 7); + +//void Test(List<List<int >> same_section_flag_combine_list, int pumpCount) +//{ +// Dictionary<int, List<int[]>> dict = new(); +// for (int pump_count = 2; pump_count <= pumpCount; pump_count++) +// { +// dict[pump_count] = new List<int[]>(); +// if (pump_count == 2) +// { +// foreach (var same_section in same_section_flag_combine_list) +// { +// var combine = GetCombine(same_section, 2); +// dict[pump_count].AddRange(combine); +// } +// } +// else if (pump_count == 3) +// { +// foreach (var same_section in same_section_flag_combine_list) +// { +// var combine = GetCombine(same_section, 3); +// dict[pump_count].AddRange(combine); +// } +// } +// else if (pump_count == 4) +// { +// foreach (var same_section in same_section_flag_combine_list) +// { +// var combine3 = GetCombine(same_section, 3); +// dict[pump_count].AddRange(combine3); +// if (same_section.Count > 3) +// { +// var combine4 = GetCombine(same_section, 4); +// dict[pump_count].AddRange(combine4); +// } +// } +// } +// else if (pump_count == 5) +// { +// foreach (var same_section in same_section_flag_combine_list) +// { +// if (same_section.Count > 3) +// { +// var combine4 = GetCombine(same_section, 4); +// dict[pump_count].AddRange(combine4); +// } +// } +// } +// else if (pump_count == 6) +// { +// foreach (var same_section in same_section_flag_combine_list) +// { +// if (same_section.Count > 3) +// { +// var combine4 = GetCombine(same_section, 4); +// dict[pump_count].AddRange(combine4); +// } +// } +// } + +// } + + +// foreach (var item in dict) +// { +// Console.WriteLine($">>>>>>>>>>>>>>>>>>>{item.Key}"); +// foreach (var value in item.Value) +// { +// Console.WriteLine(IntListHelper.ToString(value)); +// } +// } +//} + +//List<int[]> GetCombine(List<int> flags,int count) +//{ +// var combine = IStation.Curve.PermutationAndCombination<int>.GetCombination(flags.ToArray(), count); +// return combine; +//} + + +//Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>"); +//Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>"); + + + +//for (int pumpCount = 2; pumpCount <= pumps.Count; pumpCount++) +//{ + +// switch (pumpCount) +// { +// case 1: +// { +// Console.WriteLine("----1----"); +// } +// break; +// case 2: +// { +// Console.WriteLine("----2----"); +// var combine_list = IStation.Curve.PermutationAndCombination<int>.GetCombination(pump_flag_list.ToArray(), pumpCount);//鎺掑垪缁勫悎 +// foreach (var combine in combine_list) +// { +// var combine_remark = IntListHelper.ToString(combine.OrderBy(x => x)); +// if (same_section_flag_remark1.Contains(combine_remark)) +// { +// Console.WriteLine(combine_remark); +// continue; +// } +// if (same_section_flag_remark2.Contains(combine_remark)) +// { +// Console.WriteLine(combine_remark); +// continue; +// } +// } +// } +// break; +// case 3: +// { +// Console.WriteLine("----3----"); +// var combine_list = IStation.Curve.PermutationAndCombination<int>.GetCombination(pump_flag_list.ToArray(), pumpCount);//鎺掑垪缁勫悎 +// foreach (var combine in combine_list) +// { +// var combine_remark = IntListHelper.ToString(combine.OrderBy(x => x)); +// if (same_section_flag_remark1.Contains(combine_remark)) +// { +// Console.WriteLine(combine_remark); +// continue; +// } +// if (same_section_flag_remark2.Contains(combine_remark)) +// { +// Console.WriteLine(combine_remark); +// continue; +// } +// } +// } +// break; +// case 4: +// { +// Console.WriteLine("----4----"); +// var part1 = IStation.Curve.PermutationAndCombination<int>.GetCombination(same_section_flag1.ToArray(), 3); +// part1.ForEach(x => Console.WriteLine(IntListHelper.ToString(x))); +// var part2 = IStation.Curve.PermutationAndCombination<int>.GetCombination(same_section_flag2.ToArray(), 3); +// part2.ForEach(x => Console.WriteLine(IntListHelper.ToString(x))); +// Console.WriteLine(same_section_flag_remark1); +// Console.WriteLine(same_section_flag_remark2); +// } +// break; +// case 5: +// { + +// } +// break; +// case 6: +// { + +// } +// break; +// default: +// { + +// } +// break; +// } + +//} + + +Console.ReadKey(); + diff --git a/Test/IStation.Test/TimeValue.cs b/Test/IStation.Test/TimeValue.cs new file mode 100644 index 0000000..f9558dc --- /dev/null +++ b/Test/IStation.Test/TimeValue.cs @@ -0,0 +1,27 @@ +锘縩amespace IStation.Test +{ + public class TimeValue + { + public TimeValue() + { + + } + public TimeValue(TimeValue rhs) + { + this.Time = rhs.Time; + this.Value = rhs.Value; + } + + + /// <summary> + /// 鏃堕棿 + /// </summary> + public DateTime Time { get; set; } + + /// <summary> + /// 鍊煎瓧鍏� + /// </summary> + public Dictionary<string, double> Value { get; set; } + + } +} -- Gitblit v1.9.3