using IStation.Epanet.Enums;
using IStation.Epanet.Log;
using IStation.Epanet.Network.Structures;
using IStation.Epanet.Util;
using System.Diagnostics;
using System.Globalization;
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
/// Object categories
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
}
/// EPANET2 *.net project file signature.
private const string NETFILE_SIGNATURE = "";
/// EPANET2 *.net project file version ID.
private const int VERSIONID1 = 20005;
/// EPANET2 *.net project file version ID.
private const int VERSIONID2 = 20008;
/// Max. index for network options array.
private const int MAXOPTIONS = 38;
/// Max. index for node property array.
private const int MAXNODEPROPS = 25;
/// Max. index for link property array
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 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 tok = new List(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 tok = new List(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));
}
}
/// Delphi TReader partial implementation.
/// TReader is a specialized filer that reads component data from an associated stream.
private class Reader
{
private readonly BinaryReader _br;
public Reader(Stream stream, Encoding enc) { _br = new BinaryReader(stream, enc); }
///
/// Reads the type of the next item on the reader object's stream and returns
/// with the stream positioned after the value-type indicator.
///
private TValueType ReadValue() { return (TValueType)_br.ReadByte(); }
///
/// Returns the type of the next item in the reader object's stream without
/// moving the position of the stream.
///
///
private TValueType NextValue() { return (TValueType)unchecked((byte)_br.PeekChar()); }
/// Reads a tagged string value written by WriteString from the reader object's stream and returns its contents.
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();
}
}
///
/// Reads an integer-type number from the reader object's stream and returns its value.
///
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 ReadList()
{
var result = new List();
var tv = ReadValue();
if (tv != TValueType.vaList)
throw new InvalidDataException();
while (NextValue() != TValueType.vaNull)
result.Add(ReadString());
ReadValue();
return result;
}
///
/// Reads a boolean from the reader object's stream and returns that boolean value.
///
public bool ReadBoolean() { return ReadValue() == TValueType.vaTrue; }
///
/// Reads 10-byte extended value from file.
///
///
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;
}
}
///
/// Reads a floating-point number from the reader object's stream and returns its value.
///
private double ReadFloat()
{
switch (ReadValue())
{
case TValueType.vaExtended:
return ReadExtended();
default:
_br.BaseStream.Position--;
return ReadInt64();
}
}
///
/// Reads an 64-bit integer from the reader object's stream and returns its value.
///
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
/// TValueType defines the kinds of values written to and read from filer objects.
private enum TValueType : byte
{
///identifies the type as the const Variant Null.
vaNull,
///identifies the type as a list of tagged items ending with a NULL pointer.
vaList,
///identifies the type as an 8-bit integer.
vaInt8,
///identifies the type as a 16-bit integer.
vaInt16,
///identifies the type as a 32-bit integer.
vaInt32,
///identifies the type as a float or long double.
vaExtended,
///identifies the type as a short string
vaString,
///identifies the type as an identifier string.
vaIdent,
///identifies the type as the boolean false.
vaFalse,
///identifies the type as the boolean true.
vaTrue,
///identifies the type as a block of binary preceded by a length count.
vaBinary,
///identifies the type as a set.
vaSet,
///identifies the type as an AnsiString (long string).
vaLString,
///identifies the type as nil (Delphi) or NULL (C++).
vaNil,
///identifies the type as a TCollection.
vaCollection,
///identifies the type as a Single (float).
vaSingle,
///identifies the type as a Currency.
vaCurrency,
///identifies the type as a Date.
vaDate,
///identifies the type as a WideString.
vaWString,
///identifies the type as a 64-bit integer.
vaInt64,
///identifies the type as a UTF8String.
vaUTF8String
}
// ReSharper restore InconsistentNaming
}
}
}