From 7443cea9d4fb3f42b999e85dac35d15bd58efdf1 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 7 Nov 2018 23:09:47 -0500 Subject: [PATCH] Fixes #172 (adjust controls when node/link is deleted) & EN_addrule added - Deleting controls with node/link deletion made conditional. - New EN_addrule function added along with a test file. - Rule structures re-named & rules.c heavily modified. - Issue with exceeding limit on number of temporary file names fixed. - VB declaration and DEF files updated. --- ReleaseNotes2_2.md | 33 +- include/epanet2.bas | 14 +- include/epanet2.h | 613 +++++++++------ include/epanet2.vb | 13 +- src/epanet.c | 1180 +++++++++++++++++------------ src/errors.dat | 16 +- src/funcs.h | 19 +- src/hydcoeffs.c | 31 +- src/hydraul.c | 37 +- src/hydsolver.c | 19 +- src/hydstatus.c | 14 +- src/inpfile.c | 40 +- src/input1.c | 35 +- src/input2.c | 18 +- src/input3.c | 80 +- src/output.c | 34 +- src/quality.c | 2 +- src/qualreact.c | 4 +- src/qualroute.c | 22 +- src/report.c | 22 +- src/rules.c | 1370 ++++++++++++++++------------------ src/types.h | 144 ++-- tests/test_addrule.cpp | 109 +++ tests/test_setlinktype.cpp | 4 +- win_build/WinSDK/epanet2.def | 124 +-- 25 files changed, 2197 insertions(+), 1800 deletions(-) create mode 100644 tests/test_addrule.cpp diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 89adf5d..85fb84a 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -129,11 +129,12 @@ Both network files are available [here](https://doi.org/10.23719/1375314). ## New API functions |Function|Description| |--|--| -|`ENgetcurvetype`| | +|`ENinit`|| +|`ENsetflowunits`|| |`ENgetdemandmodel`|| |`ENsetdemandmodel`|| -|`ENsetflowunits`|| -|`ENaddcontrol`|| +|`ENgetdemandname`|| +|`ENsetdemandname`|| |`ENsetdemandpattern`|| |`ENgetrule`|| |`ENsetrulepriority`|| @@ -142,23 +143,25 @@ Both network files are available [here](https://doi.org/10.23719/1375314). |`ENsetpremiseindex`|| |`ENsetpremisestatus`|| |`ENsetpremisevalue`|| -|`ENgettrueaction`|| -|`ENsettrueaction`|| -|`ENgetfalseaction`|| -|`ENsetfalseaction`|| +|`ENgetthenaction`|| +|`ENsetthenaction`|| +|`ENgetelseaction`|| +|`ENsetelseaction`|| |`ENgetruleID`|| -|`ENinit`|| -|`ENsetheadcurveindex`|| +|`ENgetcurvetype`|| |`ENsetlinknodes`|| |`ENsetlinktype`|| |`ENaddnode`|| |`ENaddlink`|| -|`ENdeletelink`|| +|`ENaddpattern`|| +|`ENaddcontrol`|| +|`ENaddrule` || |`ENdeletenode`|| -| `ENsetnodeid` || -| `ENsetlinkid` || -|`ENgetdemandname`|| -|`ENsetdemandname`|| +|`ENdeletelink`|| +|`ENdeletecontrol`|| +|`ENdeleterule` || +|`ENsetnodeid` || +|`ENsetlinkid` || ## API Extensions (additional definitions) ### Link value types: @@ -180,6 +183,8 @@ Both network files are available [here](https://doi.org/10.23719/1375314). - `EN_FLOWCHANGE` - `EN_DEMANDDEFPAT` - `EN_MASSBALANCE` + - `EN_UNCONDITIONAL` + - `EN_CONDITIONAL` ### Curve types: - `EN_V_CURVE` - `EN_P_CURVE` diff --git a/include/epanet2.bas b/include/epanet2.bas index e3681ac..8483f9b 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -173,6 +173,9 @@ Public Const EN_E_CURVE = 2 ' efficiency curve Public Const EN_H_CURVE = 3 ' head loss curve Public Const EN_G_CURVE = 4 ' General\default curve +Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion +Public Const EN_CONDITIONAL = 1 ' Conditional object deletion + 'These are the external functions that comprise the DLL 'System Functions @@ -211,6 +214,7 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetstatusreport Lib "epanet2.dll" (ByVal code As Long) As Long Declare Function ENgetcount Lib "epanet2.dll" (ByVal code As Long, value As Long) As Long Declare Function ENgeterror Lib "epanet2.dll" (ByVal ErrCode As Long, ByVal ErrMsg As String, ByVal N As Long) As Long + Declare Function ENgetstatistic Lib "epanet2.dll" (ByVal code As Long, ByRef value As Single) As Long 'Analysis Options Functions Declare Function ENgetoption Lib "epanet2.dll" (ByVal code As Long, value As Single) As Long @@ -292,6 +296,8 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetcontrol Lib "epanet2.dll" (ByVal Cindex As Long, ByVal Ctype As Long, ByVal Lindex As Long, ByVal setting As Single, ByVal Nindex As Long, ByVal Level As Single) As Long 'Rue-Based Control Functions + Declare Function ENaddrule Lib "epanet2.dll" (ByVal rule As String) As Long + Declare Function ENdeleterule Lib "epanet2.dll" (ByVal index As Long) As Long Declare Function ENgetrule Lib "epanet2.dll" (ByVal index As Long, nPremises As Long, nTrueActions As Long, nFalseActions As Long, priority As Single) As Long Declare Function ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Long, ByVal id As String) As Long Declare Function ENsetrulepriority Lib "epanet2.dll" (ByVal index As Long, ByVal priority As Single) As Long @@ -300,7 +306,7 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetpremiseindex Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexPremise As Long, ByVal indexObj As Long) As Long Declare Function ENsetpremisestatus Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexPremise As Long, ByVal status As Long) As Long Declare Function ENsetpremisevalue Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexPremise As Long, ByVal value As Single) As Long - Declare Function ENgettrueaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, indexLink As Long, status As Long, setting As Single) As Long - Declare Function ENsettrueaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, ByVal indexLink As Long, ByVal status As Long, ByVal setting As Single) As Long - Declare Function ENgetfalseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, indexLink As Long, status As Long, setting As Single) As Long - Declare Function ENsetfalseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, ByVal indexLink As Long, ByVal status As Long, ByVal setting As Single) As Long + Declare Function ENgetthenaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, indexLink As Long, status As Long, setting As Single) As Long + Declare Function ENsetthenaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, ByVal indexLink As Long, ByVal status As Long, ByVal setting As Single) As Long + Declare Function ENgetelseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, indexLink As Long, status As Long, setting As Single) As Long + Declare Function ENsetelseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, ByVal indexLink As Long, ByVal status As Long, ByVal setting As Single) As Long diff --git a/include/epanet2.h b/include/epanet2.h index 6ddfd6b..0275f9c 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -172,8 +172,8 @@ typedef enum { } EN_NodeType; typedef enum { - EN_CVPIPE = 0, /* Link types. */ - EN_PIPE = 1, /* See LinkType in TYPES.H */ + EN_CVPIPE = 0, + EN_PIPE = 1, EN_PUMP = 2, EN_PRV = 3, EN_PSV = 4, @@ -185,28 +185,28 @@ typedef enum { typedef enum { EN_NONE = 0, /* Quality analysis types. */ - EN_CHEM = 1, /* See QualType in TYPES.H */ + EN_CHEM = 1, EN_AGE = 2, EN_TRACE = 3 } EN_QualityType; typedef enum { EN_CONCEN = 0, /* Source quality types. */ - EN_MASS = 1, /* See SourceType in TYPES.H. */ + EN_MASS = 1, EN_SETPOINT = 2, EN_FLOWPACED = 3 } EN_SourceType; -typedef enum { /* Head loss formula: */ +typedef enum { /* Head loss formula: */ EN_HW = 0, /* Hazen-Williams */ EN_DW = 1, /* Darcy-Weisbach */ EN_CM = 2 /* Chezy-Manning */ -} EN_FormType; /* See FormType in TYPES.H */ +} EN_HeadLossType; typedef enum { EN_CFS = 0, /* Flow units types. */ - EN_GPM = 1, /* See FlowUnitsType */ - EN_MGD = 2, /* in TYPES.H. */ + EN_GPM = 1, + EN_MGD = 2, EN_IMGD = 3, EN_AFD = 4, EN_LPS = 5, @@ -277,6 +277,17 @@ typedef enum { EN_G_CURVE = 4 /* General\default curve */ } EN_CurveType; +typedef enum { + EN_UNCONDITIONAL = 0, + EN_CONDITIONAL = 1 +} EN_ActionCodeType; + +typedef enum { + EN_NO_REPORT = 0, + EN_NORMAL_REPORT = 1, + EN_FULL_REPORT = 2 +} EN_StatusReport; + // --- Declare the EPANET toolkit functions #if defined(__cplusplus) extern "C" { @@ -286,9 +297,6 @@ extern "C" { @brief The EPANET Project wrapper object */ typedef void *EN_ProjectHandle; - - typedef struct EN_Pattern EN_Pattern; - typedef struct EN_Curve EN_Curve; /** @brief runs a complete EPANET simulation @@ -304,7 +312,7 @@ extern "C" { needed then the argument should be NULL. */ int DLLEXPORT ENepanet(const char *inpFile, const char *rptFile, - const char *binOutFile, void (*callback) (char *)); + const char *binOutFile, void (*callback) (char *)); /** @brief Initializes an EPANET session @@ -315,7 +323,7 @@ extern "C" { @return error code */ int DLLEXPORT ENinit(const char *rptFile, const char *binOutFile, - int UnitsType, int HeadlossFormula); + int UnitsType, int HeadlossFormula); /** @brief Opens EPANET input file & reads in network data @@ -324,7 +332,8 @@ extern "C" { @param binOutFile pointer to name of binary output file (to be created) @return error code */ - int DLLEXPORT ENopen(const char *inpFile, const char *rptFile, const char *binOutFile); + int DLLEXPORT ENopen(const char *inpFile, const char *rptFile, + const char *binOutFile); /** @brief Saves current data to "INP" formatted text file. @@ -362,8 +371,12 @@ extern "C" { /** @brief Initializes hydraulic analysis - @param initFlag 2-digit flag where 1st (left) digit indicates if link flows should be re-initialized (1) or not (0), and 2nd digit indicates if hydraulic results should be saved to file (1) or not (0). + @param initFlag 2-digit initialization flag @return Error code + + The initialization flag is a two digit number where the 1st (left) digit + indicates if link flows should be re-initialized (1) or not (0), and the + 2nd digit indicates if hydraulic results should be saved to file (1) or not (0). */ int DLLEXPORT ENinitH(int initFlag); @@ -494,15 +507,16 @@ extern "C" { /** @brief Retrieves parameters that define a simple control - @param controlIndex Control index (position of control statement in the input file, starting from 1) - @param[out] controlType Control type code (see EPANET2.H) + @param controlIndex Position of control in list of controls added to the project + @param[out] controlType Control type code (see EN_ControlType enumeration) @param[out] linkIndex Index of controlled link @param[out] setting Control setting on link @param[out] nodeIndex Index of controlling node (0 for TIMER or TIMEOFDAY control) @param[out] level Control level (tank level, junction pressure, or time (seconds)) @return Error code */ - int DLLEXPORT ENgetcontrol(int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); + int DLLEXPORT ENgetcontrol(int controlIndex, int *controlType, int *linkIndex, + EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); /** @brief Retrieves the number of components of a given type in the network. @@ -551,7 +565,7 @@ extern "C" { @return Error code */ int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); /** @brief Sets the type of demand model to use and its parameters @@ -562,7 +576,7 @@ extern "C" { @return Error code */ int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); /** @brief Retrieves the index of the time pattern with specified ID @@ -645,16 +659,18 @@ extern "C" { /** @brief Get the string ID of the specified node. @param index The index of the node (first node is index 1) - @param[out] id The string ID of the specified node. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. + @param[out] id The string ID of the specified node. @return Error code @see ENgetnodeindex + + The ID string must be sized to hold at least MAXID characters. */ int DLLEXPORT ENgetnodeid(int index, char *id); /** @brief Get the type of node with specified index. @param index The index of a node (first node is index 1) - @param[out] code The type code for the node. + @param[out] code The type code for the node (see the EN_NodeType enumeration) @return Error code */ int DLLEXPORT ENgetnodetype(int index, int *code); @@ -703,7 +719,8 @@ extern "C" { @param demandIndex The index of the demand category (starting at 1) @return Error code */ - int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, + EN_API_FLOAT_TYPE *baseDemand); /** @brief Get the index of the demand pattern assigned to a node for a category index. @@ -726,16 +743,18 @@ extern "C" { /** @brief Get the string ID of a link with specified index @param index The index of a link (first link is index 1) - @param[out] id The ID of the link. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. + @param[out] id The ID of the link. @return Error code @see ENgetlinkindex + + The ID string must be sized to hold at least MAXID characters. */ int DLLEXPORT ENgetlinkid(int index, char *id); /** @brief Get the link type code for a specified link @param index The index of a link (first link is index 1) - @param[out] code The type code of the link. + @param[out] code The type code of the link (see the EN_LinkType enumeration) @return Error code @see EN_LinkType */ @@ -743,12 +762,18 @@ extern "C" { /** @brief Set the link type code for a specified link - @param[in,out] index The index of a link before [in] and after [out] the type change. - @param code The new type code of the link. + @param[in,out] index The index of a link before [in] and after [out] the type change + @param code The new type code of the link (see EN_LinkType). + @param actionCode Action taken if any controls contain the link. @return Error code - @see EN_LinkType + @see the EN_LinkType enumeration + + If 'actionCode' is EN_UNCONDITIONAL then all simple and rule-based controls that + contain the link are deleted when the link's type is changed. If set to + EN_CONDITIONAL then the type change is cancelled if the link appears in any + control and an error code is returned. */ - int DLLEXPORT ENsetlinktype(int *index, EN_LinkType code); + int DLLEXPORT ENsetlinktype(int *index, EN_LinkType Code, int actionCode); /** @brief Get the indexes of a link's start- and end-nodes. @@ -779,7 +804,8 @@ extern "C" { @param[out] yValues The curve's y-values. Pointer must be freed by client. @return Error code. */ - int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, + EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); /** @brief Retrieves the curve index for a specified pump index. @@ -816,23 +842,27 @@ extern "C" { int DLLEXPORT ENgetcurvetype(int curveIndex, int *outType); /** - @brief Get the version number. This number is to be interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" + @brief Get the version number. @param[out] version The version of EPANET @return Error code. + + The version number is to be interpreted with implied decimals, i.e., + "20100" == "2(.)01(.)00" */ int DLLEXPORT ENgetversion(int *version); /** - @brief Specify parameters to add a new simple control + @brief Add a new simple control to the project. @param[out] cindex The index of the new control. First control is index 1. - @param ctype The type code to set for this control. + @param ctype The type of control to add (see EN_ControlType). @param lindex The index of a link to control. @param setting The control setting applied to the link. @param nindex The index of a node used to control the link, or 0 for TIMER / TIMEOFDAY control. @param level control point (tank level, junction pressure, or time in seconds). @return Error code. */ - int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); /** @brief Delete an existing simple control @@ -842,16 +872,17 @@ extern "C" { int DLLEXPORT ENdeletecontrol(int cindex); /** - @brief Specify parameters to define a simple control - @param cindex The index of the control to edit. First control is index 1. - @param ctype The type code to set for this control. + @brief Set the parameters of an existing simple control. + @param cindex The index of the control. First control is index 1. + @param ctype The type of control to use (see EN_ControlType). @param lindex The index of a link to control. @param setting The control setting applied to the link. @param nindex The index of a node used to control the link, or 0 for TIMER / TIMEOFDAY control. @param level control point (tank level, junction pressure, or time in seconds). @return Error code. */ - int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); /** @brief Change the ID name for a node. @@ -945,137 +976,142 @@ extern "C" { int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v); /** - @brief Sets the level of hydraulic status reporting. - @param code Status reporting code. + @brief Set the level of hydraulic status reporting. + @param code Status reporting code (see EN_StatusReport). @return Error code. */ int DLLEXPORT ENsetstatusreport(int code); /** - @brief Sets type of quality analysis called for - @param qualcode WQ parameter code, EN_QualityType - @param chemname Name of WQ constituent - @param chemunits Concentration units of WQ constituent - @param tracenode ID of node being traced (if applicable) + @brief Set the type of quality analysis called for. + @param qualcode Type of analysis to be made (see EN_QualityType). + @param chemname Name of the quality constituent. + @param chemunits Concentration units of the constituent. + @param tracenode ID of node being traced (if applicable). @return Error code. - @see EN_QualityType - chemname and chemunits only apply when WQ analysis is for chemical. tracenode only applies when WQ analysis is source tracing. + Note: "chemname" and "chemunits" only apply when "qualcode" is EN_CHEM. + "tracenode" only applies when 'qualcode" is EN_TRACE. */ - int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, char *tracenode); + int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, + char *tracenode); /** - @brief Get quality analysis information (type, chemical name, units, trace node ID) - @param[out] qualcode The EN_QualityType code being used. - @param[out] chemname The name of the WQ constituent. - @param[out] chemunits The cencentration units of the WQ constituent. - @param[out] tracenode The trace node ID. + @brief Get information about the type of water quality analysis requested. + @param[out] qualcode Type of analysis to be made (see EN_QualityType). + @param[out] chemname Name of the quality constituent. + @param[out] chemunits Concentration units of the constituent. + @param[out] tracenode ID of node being traced (if applicable). @return Error code. - @see EN_QualityType */ - int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, int *tracenode); + int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, + int *tracenode); /** - @brief Sets the node's demand name for a category. - @param nodeIndex The index of a node. - @param demandIdx The index of a demand category. - @param demandName The demand name for the selected category. + @brief Set the name of a node's demand category. + @param nodeIndex The node's index. + @param demandIdx Index of the node's demand. + @param demandName Name for the category the demand belongs to. @return Error code. @see ENgetdemandname */ int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, char *demandName); /** - @brief Retrieves the node's demand name for a category. - @param nodeIndex The index of a node. - @param demandIdx The index of a demand category. - @param demandName The demand name for the selected category. + @brief Retrieve the name of a node's demand category. + @param nodeIndex The node's index. + @param demandIdx Index of the node's demand. + @param demandName[out] Name of the category the demand belongs to. @return Error code. @see ENsetdemandname */ int DLLEXPORT ENgetdemandname(int nodeIndex, int demandIdx, char *demandName); /** - @brief Sets the node's base demand for a category. - @param nodeIndex The index of a node. - @param demandIdx The index of a demand category. - @param baseDemand The base demand multiplier for the selected category. + @brief Set a node's base demand for a demand category. + @param nodeIndex The node's index. + @param demandIndex The index of one of the node's demand categories. + @param baseDemand The base demand for the selected category. @return Error code. @see ENgetbasedemand */ - int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); + int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIndex, + EN_API_FLOAT_TYPE baseDemand); /** - @brief Sets the index of the demand pattern assigned to a node for a category index. - @param nodeIndex The index of a node (first node is index 1). - @param demandIndex The index of a category (first category is index 1). - @param pattIndex The index of the pattern for this node and category. + @brief Set the time pattern assigned to a node's demand category. + @param nodeIndex The node's index. + @param demandIndex The index of one of the node's demand categories. + @param pattIndex The index of a time pattern applied to this demand category. @return Error code */ - int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIdx, int patIndex); + int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIndex, int patIndex); /** - @brief Retrieves index of curve with specific ID. - @param id The ID of a curve. - @param[out] index The index of the named curve + @brief Retrieve the index of a curve given its name. + @param id The ID name of a curve. + @param[out] index The index of the named curve. @return Error code. @see ENgetcurveid */ int DLLEXPORT ENgetcurveindex(char *id, int *index); /** - @brief Retrieves ID of a curve with specific index. + @brief Retrieve the ID name of a curve given its index. @param index The index of a curve. - @param[out] id The ID of the curve specified. + @param[out] id The ID of the specified curve. @return Error code. @see ENsetcurveindex - NOTE: 'id' must be able to hold MAXID characters + NOTE: 'id' must be sized to hold MAXID characters. */ int DLLEXPORT ENgetcurveid(int index, char *id); /** - @brief Retrieves number of points in a curve + @brief Retrieve the number of points in a curve. @param index The index of a curve. - @param[out] len The length of the curve coordinate list + @param[out] len The number of data points assigned to the curve. @return Error code. @see ENgetcurvevalue */ int DLLEXPORT ENgetcurvelen(int index, int *len); /** - @brief retrieves x,y point for a specific point number and curve - @param curveIndex The index of a curve - @param pointIndex The index of a point in the curve + @brief Retrieve an x,y data point for a curve. + @param curveIndex The index of a curve. + @param pointIndex The index of a point in the curve. @param[out] x The x-value of the specified point. @param[out] y The y-value of the specified point. @return Error code. @see ENgetcurvelen ENsetcurvevalue */ - int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); /** - @brief Sets x,y point for a specific point and curve. + @brief Set the x and y values for a curve's data point. @param curveIndex The index of a curve. @param pointIndex The index of a point in the curve. @param x The x-value of the point. @param y The y-value of the point. @return Error code. */ - int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); /** - @brief Sets x,y values for a specified curve. + @brief Set the x,y values for all points on a curve. @param index The index of a curve. @param x An array of x-values for the curve. @param y An array of y-values for the curve. - @param len The length of the arrays x and y. + @param len The length of the arrays for x and y. @return Error code. */ - int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); + int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, + int len); /** - @brief Adds a new curve appended to the end of the existing curves. + @brief Add a new curve to the project. @param id The name of the curve to be added. @return Error code. @see ENgetcurveindex ENsetcurve @@ -1083,164 +1119,201 @@ extern "C" { int DLLEXPORT ENaddcurve(char *id); /** - @brief Gets the number of premises, true actions, and false actions and the priority of an existing rule-based control. - @param index The index of a rule-based control. - @param nPremises The number of conditions in a rule-based control. - @param nTrueActions The number of actions that are executed when the conditions in the rule-based control are met. - @param nFalseActions The number of actions that are executed when the conditions in the rule-based control are not met. - @param priority The priority of a rule-based control. + @brief Get summary information for a rule-based control. + @param index The control's index. + @param[out] nPremises Number of premises in the IF section of the control. + @param[out] nThenActions Number of THEN actions in the control. + @param nElseActions[out] Number of ELSE actions in the control. + @param priority[out] Rule's priority. @return Error code. */ - int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); + int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, + int *nFalseActions, EN_API_FLOAT_TYPE *priority); /** - @brief Sets the priority of the existing rule-based control. - @param index The index of a rule-based control. - @param priority The priority to be set in the rule-based control. + @brief Set the priority of a rule-based control. + @param index The control's index. + @param priority The priority assigned to the control. @return Error code. */ int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); /** - @brief Gets the components of a premise/condition in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param logop The logiv operator (IF/AND/OR) in the premise - @param object The object (e.g. TANK) the premise is looking at. - @param indexObj The index of the object (e.g. the index of the tank). - @param variable The variable to be checked (e.g. level). - @param relop The relashionship operator (e.g. LARGER THAN) in the premise. - @param status The status of the object to be checked (e.g. CLOSED) - @param value The value of the variable to be checked (e.g. 5.5) + @brief Get the components of a premise in a rule-based control. + @param ruleIndex The control's index. + @param premiseIndex The premise's index. + @param logop[out] Logical operator (IF/AND/OR) of the premise + @param object[out] Type of object (e.g. TANK) the premise is looking at. + @param objIndex[out] Index of the object (e.g. the index of the tank). + @param variable[out] Index of the variable to be checked (e.g. LEVEL). + @param relop[out] Relationship operator (e.g. ABOVE) in the premise. + @param status[out] Status of the object being checked (e.g. CLOSED) + @param value[out] Setting of the variable being checked (e.g. 5.5) @return Error code. */ - int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); + int DLLEXPORT ENgetpremise(int ruleIndex, int premiseIndex, int *logop, + int *object, int *objIndex, int *variable, + int *relop, int *status, EN_API_FLOAT_TYPE *value); /** - @brief Sets the components of a premise/condition in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param logop The logiv operator (IF/AND/OR) in the premise - @param object The object (e.g. TANK) the premise is looking at. - @param indexObj The index of the object (e.g. the index of the tank). - @param variable The variable to be checked (e.g. level). - @param relop The relashionship operator (e.g. LARGER THAN) in the premise. - @param status The status of the object to be checked (e.g. CLOSED) - @param value The value of the variable to be checked (e.g. 5.5) + @brief Set the components of a premise in a rule-based control. + @param ruleIndex The control's index. + @param premiseIndex The premise's index. + @param logop Logical operator (IF/AND/OR) of the premise + @param object Type of object (e.g. TANK) the premise is looking at. + @param objIndex Index of the object (e.g. the index of the tank). + @param variable Index of the variable to be checked (e.g. LEVEL). + @param relop Relationship operator (e.g. ABOVE) in the premise. + @param status Status of the object being checked (e.g. CLOSED) + @param value Setting of the variable being checked (e.g. 5.5) @return Error code. */ - int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); + int DLLEXPORT ENsetpremise(int ruleIndex, int premiseIndex, int logop, + int object, int objIndex, int variable, int relop, + int status, EN_API_FLOAT_TYPE value); /** - @brief Sets the index of an object in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param indexObj The index of the object (e.g. the index of the tank). + @brief Set the index of an object in a premise of a rule-based control. + @param ruleIndex The control's index. + @param premiseIndex The premise's index. + @param objIndex The index of the premise's object (e.g. the index of the tank). @return Error code. */ - int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj); + int DLLEXPORT ENsetpremiseindex(int ruleIndex, int premiseIndex, int objIndex); /** - @brief Sets the status in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param status The status of the object to be checked (e.g. CLOSED) + @brief Set the status in a premise of a rule-based control. + @param ruleIndex The control's index. + @param premiseIndex The premise's index. + @param status The status of the object being checked (e.g. CLOSED) @return Error code. */ - int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status); + int DLLEXPORT ENsetpremisestatus(int ruleIndex, int premiseIndex, int status); /** - @brief Sets the value in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param value The value of the variable to be checked (e.g. 5.5) + @brief Set the value in a premise of a rule-based control. + @param ruleIndex The control's index. + @param premiseIndex The premise's index. + @param value The value of the premise's variable being checked (e.g. 5.5) @return Error code. */ - int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); + int DLLEXPORT ENsetpremisevalue(int ruleIndex, int premiseIndex, + EN_API_FLOAT_TYPE value); /** - @brief Gets the components of a true-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) + @brief Get the components of a THEN action in a rule-based control. + @param ruleIndex The control's index. + @param actionIndex Index of the THEN action to retrieve. + @param linkIndex[out] Index of the link in the action (e.g. index of Pump 1) + @param status[out] Status of the link (e.g. CLOSED) + @param setting[out] Value of the link's setting (e.g. pump speed 0.9) @return Error code. */ - int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT ENgetthenaction(int ruleIndex, int actionIndex, int *linkIndex, + int *status, EN_API_FLOAT_TYPE *setting); /** - @brief Sets the components of a true-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) + @brief Set the components of a THEN action in a rule-based control. + @param ruleIndex The control's index. + @param actionIndex Index of the THEN action to modify. + @param linkIndex Index of the link in the action (e.g. index of Pump 1) + @param status Status assigned to the link (e.g. CLOSED) + @param setting Setting value assigned to the link (e.g. pump speed 0.9) @return Error code. */ - int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT ENsetthenaction(int ruleIndex, int actionIndex, int linkIndex, + int status, EN_API_FLOAT_TYPE setting); /** - @brief Gets the components of a false-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are not met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) + @brief Get the components of an ELSE action in a rule-based control. + @param ruleIndex The control's index. + @param actionIndex Index of the ELSE action to retrieve. + @param linkIndex[out] Index of the link in the action (e.g. index of Pump 1). + @param status[out] Status of the link (e.g. CLOSED). + @param setting[out] Value of the link's setting (e.g. pump speed 0.9) @return Error code. */ - int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT ENgetelseaction(int ruleIndex, int actionIndex, int *linkIndex, + int *status, EN_API_FLOAT_TYPE *setting); /** - @brief Sets the components of a false-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are not met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) + @brief Set the components of an ELSE action in a rule-based control. + @param ruleIndex The control's index. + @param actionIndex Index of the ELSE action being modified. + @param linkIndex Index of the link in the action (e.g. index of Pump 1) + @param status Status assigned to the link (e.g. CLOSED) + @param setting Setting value assigned to the link (e.g. pump speed 0.9) @return Error code. */ - int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT ENsetelseaction(int ruleIndex, int actionIndex, int linkIndex, + int status, EN_API_FLOAT_TYPE setting); /** - @brief Returns the ID of a rule. - @param indexRule The index of a rule-based control. - @param id The ID of the rule + @brief Get the ID name of a rule-based control. + @param index The rule's index. + @param id[out] The rule's ID name. @return Error code. */ - int DLLEXPORT ENgetruleID(int indexRule, char* id); + int DLLEXPORT ENgetruleID(int index, char* id); /** - @brief Adds a new node + @brief Delete a rule-based control. + @param index The rule's index. + @return Error code. + */ + int DLLEXPORT ENdeleterule(int index); + + /** + @brief Add a new node to the project. @param id The name of the node to be added. - @param nodeType The node type code + @param nodeType The type of node being added (see EN_NodeType) @return Error code. */ int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType); /** - @brief Adds a new link + @brief Add a new link to the project. @param id The name of the link to be added. - @param linkType The link type code - @param fromNode The id of the from node - @param toNode The id of the to node + @param linkType The type of link being added (see EN_LinkType) + @param fromNode The id of the link's starting node + @param toNode The id of the link's ending node @return Error code. */ int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, char *toNode); - + /** - @brief Deletes a node - @param nodeIndex The node index + @brief Add a new control rule to the project. + @param rule Text of the rule following the format used in an INP file. @return Error code. */ - int DLLEXPORT ENdeletenode(int nodeIndex); + int DLLEXPORT ENaddrule(char *rule); /** - @brief Deletes a link - @param linkIndex The link index + @brief Delete a node from the project. + @param index The index of the node to be deleted. + @param actionCode The action taken if any control contains the node and its links. @return Error code. + + If 'actionCode' is EN_UNCONDITIONAL then the node, its incident links and all + simple and rule-based controls that contain them are deleted. If set to + EN_CONDITIONAL then the node is not deleted if it or its incident links appear + in any controls and an error code is returned. + */ - int DLLEXPORT ENdeletelink(int linkIndex); + int DLLEXPORT ENdeletenode(int index, int actionCode); + + /** + @brief Delete a link from the project. + @param index The index of the link to be deleted. + @param ctrlsCode The action taken if any control contains the link. + @return Error code. + + If 'actionCode' is EN_UNCONDITIONAL then the link an all simple and rule-based + controls that contain it are deleted. If set to EN_CONDITIONAL then the link + is not deleted if it appears in any control and an error code is returned. + + */ + int DLLEXPORT ENdeletelink(int index, int actionCode); /*************************************************** @@ -1252,18 +1325,17 @@ extern "C" { int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph); int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, - const char *f2, const char *f3, void (*pviewprog)(char *)); + const char *f2, const char *f3, void (*pviewprog)(char *)); void DLLEXPORT EN_clearError(EN_ProjectHandle ph); int DLLEXPORT EN_checkError(EN_ProjectHandle ph, char** msg_buffer); - //int DLLEXPORT EN_epanet(EN_ProjectHandle ph, const char *f1, const char *f2, - // const char *f3, void(*pviewprog)(char *)); - int DLLEXPORT EN_init(EN_ProjectHandle ph, const char *rptFile, const char *binOutFile, - EN_FlowUnits UnitsType, EN_FormType HeadlossFormula); + int DLLEXPORT EN_init(EN_ProjectHandle ph, const char *rptFile, + const char *binOutFile, EN_FlowUnits unitsType, + EN_HeadLossType headLossType); int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *inpFile, - const char *rptFile, const char *binOutFile); + const char *rptFile, const char *binOutFile); int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, const char *filename); @@ -1272,7 +1344,7 @@ extern "C" { int DLLEXPORT EN_saveH(EN_ProjectHandle ph); int DLLEXPORT EN_openH(EN_ProjectHandle ph); - int DLLEXPORT EN_initH(EN_ProjectHandle ph, int EN_SaveOption); + int DLLEXPORT EN_initH(EN_ProjectHandle ph, int saveFlag); int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *currentTime); int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tStep); int DLLEXPORT EN_closeH(EN_ProjectHandle ph); @@ -1290,95 +1362,152 @@ extern "C" { int DLLEXPORT EN_report(EN_ProjectHandle ph); int DLLEXPORT EN_resetreport(EN_ProjectHandle ph); - int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *reportFormat); + int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *reportCommand); - int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); + int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int controlIndex, + int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, + int *nodeIndex, EN_API_FLOAT_TYPE *level); int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count); - int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option opt, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option opt, + EN_API_FLOAT_TYPE *value); int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value); int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code); int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code); int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index); int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id); int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len); - int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, + EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, + EN_API_FLOAT_TYPE *value); int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode); int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen); - int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE* value); + int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, + EN_API_FLOAT_TYPE* value); int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index); int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id); int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code); - int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, int *numDemands); - int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); - int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIndex, int *pattIndex); + int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, + EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, + int *numDemands); + int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, + int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, + int demandIndex, int *pattIndex); int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index); int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id); int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code); - int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, int *node2); - int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); - int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int pumpIndex, int *curveIndex); - int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int pumpIndex, int curveIndex); + int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, + int *node2); + int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, + EN_LinkProperty code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char* id, + int *nValues, EN_API_FLOAT_TYPE **xValues, + EN_API_FLOAT_TYPE **yValues); + int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int pumpIndex, + int *curveIndex); + int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int pumpIndex, + int curveIndex); int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int linkIndex, int *outType); int DLLEXPORT EN_getcurvetype(EN_ProjectHandle ph, int curveIndex, int *outType); int DLLEXPORT EN_getversion(int *version); - int DLLEXPORT EN_addcontrol(EN_ProjectHandle ph, int *cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); - int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT EN_addcontrol(EN_ProjectHandle ph, int *cindex, int ctype, + int lindex, EN_API_FLOAT_TYPE setting, int nindex, + EN_API_FLOAT_TYPE level); + int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, + int lindex, EN_API_FLOAT_TYPE setting, int nindex, + EN_API_FLOAT_TYPE level); int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid); - int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, + EN_API_FLOAT_TYPE v); int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid); - int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int node2); - int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType code); - int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, + int node2); + int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type, + int actionCode); + int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, + EN_API_FLOAT_TYPE v); int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id); - int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int len); - int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, + EN_API_FLOAT_TYPE *f, int len); + int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, + EN_API_FLOAT_TYPE value); int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value); int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v); int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code); - int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, char *chemunits, char *tracenode); + int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, + char *chemunits, char *tracenode); int DLLEXPORT EN_getdemandmodel(EN_ProjectHandle ph, int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); int DLLEXPORT EN_setdemandmodel(EN_ProjectHandle ph, int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); - int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx, char *demandName); - int DLLEXPORT EN_getdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx, char *demandName); - int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, char *chemname, char *chemunits, int *tracenode); - int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); - int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int patIndex); + int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, + int demandIdx, char *demandName); + int DLLEXPORT EN_getdemandname(EN_ProjectHandle ph, int nodeIndex, + int demandIdx, char *demandName); + int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, + char *chemname, char *chemunits, int *tracenode); + int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, + int demandIndex, EN_API_FLOAT_TYPE baseDemand); + int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, + int demandIndex, int patIndex); int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index); int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id); int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len); - int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); + int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int curveIndex, + int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int curveIndex, + int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y, int len); int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id); - int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); - int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE priority); - int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int indexRule, int indexPremise, int indexObj); - int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int indexRule, int indexPremise, int status); - int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_gettrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_settrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_setfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int indexRule, char* id); + int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, + int *nThenActions, int *nElseActions, EN_API_FLOAT_TYPE *priority); + int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, + EN_API_FLOAT_TYPE priority); + int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int *logop, int *object, int *objIndex, int *variable, int *relop, + int *status, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int logop, int object, int objIndex, int variable, int relop, + int status, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, int objIndex); + int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, int status); + int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_getthenaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int *linkIndex, int *status, + EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setthenaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int linkIndex, int status, + EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getelseaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int *linkIndex, int *status, + EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setelseaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int linkIndex, int status, + EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int index, char* id); int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType); - int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, char *fromNode, char *toNode); - int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int nodeIndex); - int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int linkIndex); - int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int controlIndex); + int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, + char *fromNode, char *toNode); + int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule); + int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode); + int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index, int actionCode); + int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index); + int DLLEXPORT EN_deleterule(EN_ProjectHandle ph, int index); #if defined(__cplusplus) } diff --git a/include/epanet2.vb b/include/epanet2.vb index 0c812a3..a3d4bef 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -176,6 +176,9 @@ Public Const EN_E_CURVE = 2 ' efficiency curve Public Const EN_H_CURVE = 3 ' head loss curve Public Const EN_G_CURVE = 4 ' General\default curve +Public Const EN_UNCONDITIONAL = 0 ' Unconditional object deletion +Public Const EN_CONDITIONAL = 1 ' Conditional object deletion + 'These are the external functions that comprise the DLL 'System Functions @@ -296,6 +299,8 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetcontrol Lib "epanet2.dll" (ByVal Cindex As Int32, ByVal CtlType As Int32, ByVal Lindex As Int32, ByVal Setting As Single, ByVal Nindex As Int32, ByVal Level As Single) As Int32 'Rule-Based Control Functions + Declare Function ENaddrule Lib "epanet2.dll" (ByVal rule As String) as Int32 + Declare Function ENdeleterule Lib "epanet2.dll" (ByVal index As Int32) As Int32 Declare Function ENgetrule Lib "epanet2.dll" (ByVal index As Int32, ByRef nPremises As Int32, ByRef nTrueActions As Int32, ByRef nFalseActions As Int32, ByRef priority As Single) As Int32 Declare Function ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal id As StringBuilder) As Int32 Declare Function ENsetrulepriority Lib "epanet2.dll" (ByVal index As Int32, ByVal priority As Single) As Int32 @@ -304,9 +309,9 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetpremiseindex Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexPremise As Int32, ByVal indexObj As Int32) As Int32 Declare Function ENsetpremisestatus Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexPremise As Int32, ByVal status As Int32) As Int32 Declare Function ENsetpremisevalue Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexPremise As Int32, ByVal value As Single) As Int32 - Declare Function ENgettrueaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByRef indexLink As Int32, ByRef status As Int32, ByRef setting As Single) As Int32 - Declare Function ENsettrueaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByVal indexLink As Int32, ByVal status As Int32, ByVal setting As Single) As Int32 - Declare Function ENgetfalseaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByRef indexLink As Int32, ByRef status As Int32, ByRef setting As Single) As Int32 - Declare Function ENsetfalseaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByVal indexLink As Int32, ByVal status As Int32, ByVal setting As Single) As Int32 + Declare Function ENgetthenaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByRef indexLink As Int32, ByRef status As Int32, ByRef setting As Single) As Int32 + Declare Function ENsetthenaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByVal indexLink As Int32, ByVal status As Int32, ByVal setting As Single) As Int32 + Declare Function ENgetelseaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByRef indexLink As Int32, ByRef status As Int32, ByRef setting As Single) As Int32 + Declare Function ENsetelseaction Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexAction As Int32, ByVal indexLink As Int32, ByVal status As Int32, ByVal setting As Single) As Int32 End Module diff --git a/src/epanet.c b/src/epanet.c index 407328a..550d1b2 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -124,11 +124,11 @@ execute function x and set the error code equal to its return value. #include #include -#include "enumstxt.h" #include "epanet2.h" +#include "types.h" #include "funcs.h" #include "text.h" -#include "types.h" +#include "enumstxt.h" // This single global variable is used only when the library is called // in "legacy mode" with the 2.1-style API. @@ -137,6 +137,7 @@ void *_defaultModel; // Local functions void errorLookup(int errcode, char *errmsg, int len); +int isInControls(EN_Project *pr, int objType, int index); /**************************************************************** @@ -162,43 +163,64 @@ void errorLookup(int errcode, char *errmsg, int len); ** needed then the argument should be NULL. **------------------------------------------------------------------------- */ -int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, void (*pviewprog)(char *)) +int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, + void (*pviewprog)(char *)) { - int errcode = 0; - int warncode = 0; - EN_Project *p = NULL; + int errcode = 0; + int warncode = 0; + EN_Project *p = NULL; - ERRCODE(EN_createproject(&_defaultModel)); + // Create a default project - exit on failure + errcode = EN_createproject(&_defaultModel); + if (errcode < 0) return 101; - ERRCODE(EN_runproject(_defaultModel, f1, f2, f3, pviewprog)); - if (errcode < 100) warncode = errcode; + // Run the project and record any warning + errcode = EN_runproject(_defaultModel, f1, f2, f3, pviewprog); + if (errcode < 100) warncode = errcode; - ERRCODE(EN_deleteproject(&_defaultModel)); + // Must delete the project even if run had errors + EN_deleteproject(&_defaultModel); - if (warncode) errcode = MAX(errcode, warncode); - return (errcode); + // Return the warning code if the run had no errors + if (warncode) errcode = MAX(errcode, warncode); + return errcode; } int DLLEXPORT ENinit(const char *f2, const char *f3, int UnitsType, - int HeadlossFormula) { - int errcode = 0; - ERRCODE(EN_createproject(&_defaultModel)); - ERRCODE(EN_init(_defaultModel, f2, f3, UnitsType, HeadlossFormula)); - return (errcode); + int HeadlossFormula) +{ + // Create a default project - exit on failure + int errcode = 0; + errcode = EN_createproject(&_defaultModel); + if (errcode < 0) return 101; + + // Initialize the project + errcode = EN_init(_defaultModel, f2, f3, UnitsType, HeadlossFormula); + return errcode; } -int DLLEXPORT ENopen(const char *f1, const char *f2, const char *f3) { - int errcode = 0; - ERRCODE(EN_createproject(&_defaultModel)); - EN_open(_defaultModel, f1, f2, f3); - return (errcode); +int DLLEXPORT ENopen(const char *f1, const char *f2, const char *f3) +{ + // Create a default project - exit on failure + int errcode = 0; + errcode = EN_createproject(&_defaultModel); + if (errcode < 0) return 101; + + // Read in network data from an input file + errcode = EN_open(_defaultModel, f1, f2, f3); + return errcode; } int DLLEXPORT ENsaveinpfile(const char *filename) { return EN_saveinpfile(_defaultModel, filename); } -int DLLEXPORT ENclose() { return EN_close(_defaultModel); } +int DLLEXPORT ENclose() +{ + EN_close(_defaultModel); + EN_deleteproject(&_defaultModel); + return 0; +} int DLLEXPORT ENsolveH() { return EN_solveH(_defaultModel); } @@ -277,13 +299,13 @@ int DLLEXPORT ENsetflowunits(int code) { return EN_setflowunits(_defaultModel, code); } -int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, EN_API_FLOAT_TYPE *preq, - EN_API_FLOAT_TYPE *pexp) { +int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) { return EN_getdemandmodel(_defaultModel, type, pmin, preq, pexp); } -int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, EN_API_FLOAT_TYPE preq, - EN_API_FLOAT_TYPE pexp) { +int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) { return EN_setdemandmodel(_defaultModel, type, pmin, preq, pexp); } @@ -425,8 +447,8 @@ int DLLEXPORT ENsetlinknodes(int index, int node1, int node2) { return EN_setlinknodes(_defaultModel, index, node1, node2); } -int DLLEXPORT ENsetlinktype(int *index, EN_LinkType type) { - return EN_setlinktype(_defaultModel, index, type); +int DLLEXPORT ENsetlinktype(int *index, EN_LinkType type, int actionCode) { + return EN_setlinktype(_defaultModel, index, type, actionCode); } int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { @@ -528,57 +550,72 @@ int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, return EN_setdemandname(_defaultModel, nodeIndex, demandIdx, demandName); } -int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, - int *nFalseActions, EN_API_FLOAT_TYPE *priority) { - return EN_getrule(_defaultModel, index, nPremises, nTrueActions, nFalseActions, priority); +int DLLEXPORT ENgetrule(int index, int *nPremises, int *nThenActions, + int *nElseActions, EN_API_FLOAT_TYPE *priority) { + return EN_getrule(_defaultModel, index, nPremises, nThenActions, nElseActions, + priority); } int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority){ return EN_setrulepriority(_defaultModel, index, priority); } -int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value){ - return EN_getpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value); +int DLLEXPORT ENgetpremise(int ruleIndex, int premiseIndex, int *logop, int *object, + int *objIndex, int *variable, int *relop, int *status, + EN_API_FLOAT_TYPE *value){ + return EN_getpremise(_defaultModel, ruleIndex, premiseIndex, logop, object, + objIndex, variable, relop, status, value); } -int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value){ - return EN_setpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value); +int DLLEXPORT ENsetpremise(int ruleIndex, int premiseIndex, int logop, int object, + int objIndex, int variable, int relop, int status, + EN_API_FLOAT_TYPE value){ + return EN_setpremise(_defaultModel, ruleIndex, premiseIndex, logop, object, + objIndex, variable, relop, status, value); } -int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj){ - return EN_setpremiseindex(_defaultModel, indexRule, indexPremise, indexObj); +int DLLEXPORT ENsetpremiseindex(int ruleIndex, int premiseIndex, int objIndex){ + return EN_setpremiseindex(_defaultModel, ruleIndex, premiseIndex, objIndex); } -int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status){ - return EN_setpremisestatus(_defaultModel, indexRule, indexPremise, status); +int DLLEXPORT ENsetpremisestatus(int ruleIndex, int premiseIndex, int status){ + return EN_setpremisestatus(_defaultModel, ruleIndex, premiseIndex, status); } -int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value){ - return EN_setpremisevalue(_defaultModel, indexRule, indexPremise, value); +int DLLEXPORT ENsetpremisevalue(int ruleIndex, int premiseIndex, EN_API_FLOAT_TYPE value){ + return EN_setpremisevalue(_defaultModel, ruleIndex, premiseIndex, value); } -int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, +int DLLEXPORT ENgetthenaction(int ruleIndex, int actionIndex, int *linkIndex, int *status, EN_API_FLOAT_TYPE *setting){ - return EN_gettrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); + return EN_getthenaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); } -int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, +int DLLEXPORT ENsetthenaction(int ruleIndex, int actionIndex, int linkIndex, int status, EN_API_FLOAT_TYPE setting){ - return EN_settrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); + return EN_setthenaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); } -int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, - int *status, EN_API_FLOAT_TYPE *setting){ - return EN_getfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +int DLLEXPORT ENgetelseaction(int ruleIndex, int actionIndex, int *linkIndex, + int *status, EN_API_FLOAT_TYPE *setting){ + return EN_getelseaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); } -int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, - int status, EN_API_FLOAT_TYPE setting){ - return EN_setfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +int DLLEXPORT ENsetelseaction(int ruleIndex, int actionIndex, int linkIndex, + int status, EN_API_FLOAT_TYPE setting){ + return EN_setelseaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); } -int DLLEXPORT ENgetruleID(int indexRule, char* id){ - return EN_getruleID(_defaultModel, indexRule, id); +int DLLEXPORT ENaddrule(char *rule) { + return EN_addrule(_defaultModel, rule); +} + +int DLLEXPORT ENgetruleID(int index, char* id){ + return EN_getruleID(_defaultModel, index, id); +} + +int DLLEXPORT ENdeleterule(int index) { + return EN_deleterule(_defaultModel, index); } int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) { @@ -590,12 +627,12 @@ int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, return EN_addlink(_defaultModel, id, linkType, fromNode, toNode); } -int DLLEXPORT ENdeletelink(int index) { - return EN_deletelink(_defaultModel, index); +int DLLEXPORT ENdeletelink(int index, int actionCode) { + return EN_deletelink(_defaultModel, index, actionCode); } -int DLLEXPORT ENdeletenode(int index) { - return EN_deletenode(_defaultModel, index); +int DLLEXPORT ENdeletenode(int index, int actionCode) { + return EN_deletenode(_defaultModel, index, actionCode); } /* @@ -614,10 +651,11 @@ int DLLEXPORT EN_createproject(EN_ProjectHandle *ph) if (project != NULL){ project->error_handle = new_errormanager(&errorLookup); *ph = project; + getTmpName(project->TmpHydFname); + getTmpName(project->TmpOutFname); + getTmpName(project->TmpStatFname); } - else - errorcode = -1; - + else errorcode = -1; return errorcode; } @@ -627,16 +665,17 @@ int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph) int errorcode = 0; EN_Project *p = (EN_Project*)(*ph); - if (p == NULL) - errorcode = -1; + if (p == NULL) errorcode = -1; else { + if (p->Openflag) EN_close(*ph); + remove(p->TmpHydFname); + remove(p->TmpOutFname); + remove(p->TmpStatFname); dst_errormanager(p->error_handle); free(p); - *ph = NULL; } - return 0; } @@ -664,63 +703,59 @@ int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, const char *f2, } int DLLEXPORT EN_init(EN_ProjectHandle ph, const char *f2, const char *f3, - EN_FlowUnits UnitsType, EN_FormType HeadlossFormula) + EN_FlowUnits unitsType, EN_HeadLossType headLossType) /*---------------------------------------------------------------- ** Input: ** f2 = pointer to name of report file ** f3 = pointer to name of binary output file - ** UnitsType = flow units flag - ** HeadlossFormula = headloss formula flag + ** unitsType = flow units type + ** headLossType = type of head loss formula ** Output: none ** Returns: error code - ** Purpose: opens EPANET + ** Purpose: initializes an EPANET project that isn't opened with + ** an input file. **---------------------------------------------------------------- */ { - int errcode = 0; -/*** Updated 9/7/00 ***/ -/* Reset math coprocessor */ -#ifdef DLL - _fpreset(); -#endif + int errcode = 0; + EN_Project *pr = (EN_Project*)ph; - EN_Project *pr = (EN_Project*)ph; + // Set system flags + pr->Openflag = TRUE; + pr->hydraulics.OpenHflag = FALSE; + pr->quality.OpenQflag = FALSE; + pr->save_options.SaveHflag = FALSE; + pr->save_options.SaveQflag = FALSE; + pr->Warnflag = FALSE; + pr->parser.Coordflag = TRUE; + pr->report.Messageflag = TRUE; + pr->report.Rptflag = 1; - /* Set system flags */ - pr->Openflag = TRUE; - pr->hydraulics.OpenHflag = FALSE; - pr->quality.OpenQflag = FALSE; - pr->save_options.SaveHflag = FALSE; - pr->save_options.SaveQflag = FALSE; - pr->Warnflag = FALSE; - pr->parser.Coordflag = TRUE; + // Open files + errcode = openfiles(pr, "", f2, f3); + + // Initialize memory used for project's data objects + initpointers(pr); + ERRCODE(netsize(pr)); + ERRCODE(allocdata(pr)); + if (errcode) return set_error(pr->error_handle, errcode); - /*** Updated 9/7/00 ***/ - pr->report.Messageflag = TRUE; - pr->report.Rptflag = 1; + // Set analysis options + setdefaults(pr); + pr->parser.Flowflag = unitsType; + pr->hydraulics.Formflag = headLossType; - /* Initialize global pointers to NULL. */ - initpointers(pr); + // Perform additional initializations + adjustdata(pr); + initreport(&pr->report); + initunits(pr); + inittanks(pr); + convertunits(pr); - ERRCODE(netsize(pr)); - ERRCODE(allocdata(pr)); - - setdefaults(pr); - - pr->parser.Flowflag = UnitsType; - pr->hydraulics.Formflag = HeadlossFormula; - - adjustdata(pr); - initreport(&pr->report); - initunits(pr); - inittanks(pr); - convertunits(pr); - - pr->parser.MaxPats = 0; - // initialize default pattern - getpatterns(pr); - - return set_error(pr->error_handle, errcode); + // Initialize the default demand pattern + pr->parser.MaxPats = 0; + getpatterns(pr); + return set_error(pr->error_handle, errcode); } int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const char *f3) @@ -807,10 +842,7 @@ int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, const char *filename) */ { EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return set_error(p->error_handle, 102); - + if (!p->Openflag) return set_error(p->error_handle, 102); return set_error(p->error_handle, saveinpfile(p, filename)); } @@ -823,52 +855,54 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) **---------------------------------------------------------------- */ { - out_file_t *out; + out_file_t *out; + EN_Project *p = (EN_Project*)ph; - EN_Project *p = (EN_Project*)ph; - if (p->Openflag) { - writetime(p, FMT105); - } - freedata(p); + // Free all project data + if (p->Openflag) writetime(p, FMT105); + freedata(p); - out = &p->out_files; - if (out->TmpOutFile != out->OutFile) { - if (out->TmpOutFile != NULL) { - fclose(out->TmpOutFile); + // Close output file + out = &p->out_files; + if (out->TmpOutFile != out->OutFile) + { + if (out->TmpOutFile != NULL) fclose(out->TmpOutFile); + out->TmpOutFile = NULL; + } + if (out->OutFile != NULL) + { + fclose(out->OutFile); + out->OutFile = NULL; } - remove(out->TmpFname); - } - out->TmpOutFile = NULL; - if (p->parser.InFile != NULL) { - fclose(p->parser.InFile); - p->parser.InFile = NULL; - } - if (p->report.RptFile != NULL && p->report.RptFile != stdout) { - fclose(p->report.RptFile); - p->report.RptFile = NULL; - } - if (out->HydFile != NULL) { - fclose(out->HydFile); - out->HydFile = NULL; - } - if (out->OutFile != NULL) { - fclose(out->OutFile); - out->OutFile = NULL; - } + // Close input file + if (p->parser.InFile != NULL) + { + fclose(p->parser.InFile); + p->parser.InFile = NULL; + } - if (out->Hydflag == SCRATCH) - remove(out->HydFname); - if (out->Outflag == SCRATCH) - remove(out->OutFname); - - p->Openflag = FALSE; - p->hydraulics.OpenHflag = FALSE; - p->save_options.SaveHflag = FALSE; - p->quality.OpenQflag = FALSE; - p->save_options.SaveQflag = FALSE; + // Close report file + if (p->report.RptFile != NULL && p->report.RptFile != stdout) + { + fclose(p->report.RptFile); + p->report.RptFile = NULL; + } - return set_error(p->error_handle, 0); + // Close hydraulics file + if (out->HydFile != NULL) + { + fclose(out->HydFile); + out->HydFile = NULL; + } + + // Reset system flags + p->Openflag = FALSE; + p->hydraulics.OpenHflag = FALSE; + p->save_options.SaveHflag = FALSE; + p->quality.OpenQflag = FALSE; + p->save_options.SaveQflag = FALSE; + return set_error(p->error_handle, 0); } /* @@ -3835,7 +3869,7 @@ int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code) { EN_Project *p = (EN_Project*)ph; - if (code >= 0 && code <= 2) + if (code >= EN_NO_REPORT && code <= EN_FULL_REPORT) p->report.Statflag = (char)code; else errcode = 202; @@ -4049,8 +4083,9 @@ int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) } /* Attempt to open input and report files */ - if ((par->InFile = fopen(f1, "rt")) == NULL) { - return 302; + if (strlen(f1) > 0) + { + if ((par->InFile = fopen(f1, "rt")) == NULL) return 302; } if (strlen(f2) == 0) rep->RptFile = stdout; @@ -4098,7 +4133,7 @@ int openhydfile(EN_Project *p) out->HydFile = NULL; switch (out->Hydflag) { case SCRATCH: - getTmpName(p, out->HydFname); + strcpy(out->HydFname, p->TmpHydFname); out->HydFile = fopen(out->HydFname, "w+b"); break; case SAVE: @@ -4161,62 +4196,47 @@ int openoutfile(EN_Project *p) **---------------------------------------------------------------- */ { - int errcode = 0; + int errcode = 0; - out_file_t *out = &p->out_files; - report_options_t *rep = &p->report; + out_file_t *out = &p->out_files; + report_options_t *rep = &p->report; - /* Close output file if already opened */ - if (out->OutFile != NULL) - fclose(out->OutFile); - out->OutFile = NULL; - if (out->TmpOutFile != NULL) - fclose(out->TmpOutFile); - out->TmpOutFile = NULL; + // Close output file if already opened + if (out->OutFile != NULL) fclose(out->OutFile); + out->OutFile = NULL; + if (out->TmpOutFile != NULL) fclose(out->TmpOutFile); + out->TmpOutFile = NULL; - if (out->Outflag == SCRATCH) { - remove(out->OutFname); - } - remove(out->TmpFname); - - /* If output file name was supplied, then attempt to */ - /* open it. Otherwise open a temporary output file. */ - // if (strlen(OutFname) != 0) - if (out->Outflag == SAVE) - { - if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { - errcode = 304; - } - } - // else if ( (OutFile = tmpfile()) == NULL) - else - { - getTmpName(p, out->OutFname); - if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) + // If output file name was supplied, then attempt to + // open it. Otherwise open a temporary output file. + if (out->Outflag == SAVE) { - errcode = 304; + if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) errcode = 304; } - } - /* Save basic network data & energy usage results */ - ERRCODE(savenetdata(p)); - out->OutOffset1 = ftell(out->OutFile); - ERRCODE(saveenergy(p)); - out->OutOffset2 = ftell(out->OutFile); + else + { + strcpy(out->OutFname, p->TmpOutFname); + if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) errcode = 304; + } - /* Open temporary file if computing time series statistic */ - if (!errcode) { - if (rep->Tstatflag != SERIES) { - // if ( (TmpOutFile = tmpfile()) == NULL) errcode = 304; - getTmpName(p, out->TmpFname); - out->TmpOutFile = fopen(out->TmpFname, "w+b"); - if (out->TmpOutFile == NULL) - errcode = 304; - } else - out->TmpOutFile = out->OutFile; - } + // Save basic network data & energy usage results + ERRCODE(savenetdata(p)); + out->OutOffset1 = ftell(out->OutFile); + ERRCODE(saveenergy(p)); + out->OutOffset2 = ftell(out->OutFile); - return errcode; + // Open temporary file if computing time series statistic + if (!errcode) + { + if (rep->Tstatflag != SERIES) + { + out->TmpOutFile = fopen(p->TmpStatFname, "w+b"); + if (out->TmpOutFile == NULL) errcode = 304; + } + else out->TmpOutFile = out->OutFile; + } + return errcode; } /* @@ -4279,7 +4299,7 @@ void initpointers(EN_Project *p) n->NodeHashTable = NULL; n->LinkHashTable = NULL; - initrules(&p->rules); + initrules(p); } int allocdata(EN_Project *p) @@ -4503,7 +4523,7 @@ void freedata(EN_Project *p) /* Free memory for rule base (see RULES.C) */ freerules(p); - + /* Free hash table memory */ if (net->NodeHashTable != NULL) hashtable_free(net->NodeHashTable); @@ -4516,7 +4536,7 @@ void freedata(EN_Project *p) ---------------------------------------------------------------- */ -char *getTmpName(EN_Project *p, char *fname) +char *getTmpName(char *fname) // // Input: fname = file name string // Output: returns pointer to file name @@ -4925,8 +4945,9 @@ int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_F return set_error(p->error_handle, 0); } -int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) { - +int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type, + int actionCode) +{ int i = *index, n1, n2; char id[MAXID+1]; char id1[MAXID+1]; @@ -4936,21 +4957,33 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) EN_Project *p = (EN_Project*)ph; EN_Network *net = &p->network; + // Check for valid input parameters if (!p->Openflag) return set_error(p->error_handle, 102); - if (type < 0 || type > EN_GPV) return set_error(p->error_handle, 211); + if (type < 0 || type > GPV || actionCode < EN_UNCONDITIONAL || + actionCode > EN_CONDITIONAL) + { + return set_error(p->error_handle, 251); + } - // Check if a link with the id exists + // Check for valid link index if (i <= 0 || i > net->Nlinks) return set_error(p->error_handle, 204); - // Get the current type of the link + // Check if current link type equals new type EN_getlinktype(p, i, &oldtype); if (oldtype == type) return set_error(p->error_handle, 0); + // Type change will be cancelled if link appears in any controls + if (actionCode == EN_CONDITIONAL) + { + actionCode = isInControls(p, LINK, i); + if (actionCode > 0) return set_error(p->error_handle, 261); + } + // Pipe changing from or to having a check valve - if (oldtype <= EN_PIPE && type <= EN_PIPE) + if (oldtype <= PIPE && type <= PIPE) { net->Link[i].Type = type; - if (type == EN_CVPIPE) net->Link[i].Stat = OPEN; + if (type == CVPIPE) net->Link[i].Stat = OPEN; return set_error(p->error_handle, 0); } @@ -4960,8 +4993,8 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) EN_getnodeid(ph, n1, id1); EN_getnodeid(ph, n2, id2); - // Delete the original link - EN_deletelink(ph, i); + // Delete the original link (and any controls containing it) + EN_deletelink(ph, i, actionCode); // Create a new link of new type and old id errcode = EN_addlink(ph, id, type, id1, id2); @@ -5211,9 +5244,13 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch return set_error(p->error_handle, 0); } -int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) +int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index, int actionCode) /*---------------------------------------------------------------- -** Input: index = index of the control +** Input: index = index of the link to delete +** actionCode = how to treat controls that contain the link: +** EN_UNCONDITIONAL deletes all such controls plus the link, +** EN_CONDITIONAL does not delete the link if it appears +** in a control and returns an error code ** Output: none ** Returns: error code ** Purpose: deletes a link from a project. @@ -5231,7 +5268,18 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) // Check that link exists if (!p->Openflag) return set_error(p->error_handle, 102); - if (index <= 0 || index > net->Nlinks) return set_error(p->error_handle, 203); + if (index <= 0 || index > net->Nlinks ) return set_error(p->error_handle, 204); + if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) + { + return set_error(p->error_handle, 251); + } + + // Deletion will be cancelled if link appears in any controls + if (actionCode == EN_CONDITIONAL) + { + actionCode = isInControls(p, LINK, index); + if (actionCode > 0) return set_error(p->error_handle, 261); + } // Get references to the link and its type link = &net->Link[index]; @@ -5244,7 +5292,7 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) for (i = index; i <= net->Nlinks - 1; i++) { net->Link[i] = net->Link[i + 1]; - // ... update hashtable + // ... update link's entry in the hash table hashtable_update(net->LinkHashTable, net->Link[i].ID, i); } @@ -5259,7 +5307,7 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) } // Delete any pump associated with the deleted link - if (linkType == EN_PUMP) + if (linkType == PUMP) { pumpindex = findpump(net,index); for (i = pumpindex; i <= net->Npumps - 1; i++) @@ -5270,7 +5318,7 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) } // Delete any valve (linkType > EN_PUMP) associated with the deleted link - if (linkType > EN_PUMP) + if (linkType > PUMP) { valveindex = findvalve(net,index); for (i = valveindex; i <= net->Nvalves - 1; i++) @@ -5300,16 +5348,21 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) return set_error(p->error_handle, 0); } -int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) +int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) /*---------------------------------------------------------------- -** Input: index = index of the control +** Input: index = index of the node to delete +** actionCode = how to treat controls that contain the link +** or its incident links: +** EN_UNCONDITIONAL deletes all such controls plus the node, +** EN_CONDITIONAL does not delete the node if it or any of +** its links appear in a control and returns an error code ** Output: none ** Returns: error code ** Purpose: deletes a node from a project. **---------------------------------------------------------------- */ { - int i, nodeType, tankindex; + int i, nodeType, tankindex, numControls = 0; EN_Project *p = (EN_Project*)ph; EN_Network *net = &p->network; Snode *node; @@ -5318,9 +5371,25 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) // Check that node exists if (!p->Openflag) return set_error(p->error_handle, 102); - if (index <= 0 || index > net->Nnodes) + if (index <= 0 || index > net->Nnodes) return set_error(p->error_handle, 204); + if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) { - return set_error(p->error_handle, 203); + return set_error(p->error_handle, 251); + } + + // Can't delete a water quality trace node + if (index == p->quality.TraceNode) return set_error(p->error_handle, 260); + + // Count number of simple & rule-based controls that contain the node + if (actionCode == EN_CONDITIONAL) + { + actionCode = isInControls(p, NODE, index); + for (i = 1; i <= net->Nlinks; i++) + { + if (net->Link[i].N1 == index || + net->Link[i].N2 == index) actionCode += isInControls(p, LINK, i); + } + if (actionCode > 0) return set_error(p->error_handle, 261); } // Get a reference to the node & its type @@ -5341,12 +5410,12 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) source = node->S; if (source != NULL) free(source); - // Shift position of higher entries in Node 7 Cord arrays down one + // Shift position of higher entries in Node & Coord arrays down one for (i = index; i <= net->Nnodes - 1; i++) { net->Node[i] = net->Node[i + 1]; net->Coord[i] = net->Coord[i + 1]; - // ... update hashtable + // ... update node's entry in the hash table hashtable_update(net->NodeHashTable, net->Node[i].ID, i); } @@ -5375,7 +5444,7 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) for (i = net->Nlinks; i >= 1; i--) { if (net->Link[i].N1 == index || - net->Link[i].N2 == index) EN_deletelink(ph, i); + net->Link[i].N2 == index) EN_deletelink(ph, i, EN_UNCONDITIONAL); } // Adjust indices of all link end nodes @@ -5398,14 +5467,7 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) } // Make necessary adjustments to rule-based controls (r_NODE = 6) - adjustrules(p, 6, index); - - // Set water quality analysis to NONE if deleted node is trace node - if (p->quality.Qualflag == TRACE && p->quality.TraceNode == index) - { - p->quality.TraceNode = 0; - p->quality.Qualflag = NONE; - } + adjustrules(p, 6, index); // see RULES.C // Reduce counts of node types if (nodeType == EN_JUNCTION) net->Njuncs--; @@ -5414,6 +5476,68 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) return set_error(p->error_handle, 0); } +int isInControls(EN_Project *pr, int objType, int index) +/*---------------------------------------------------------------- +** Input: objType = type of object (either NODE or LINK) +** index = the object's index +** Output: none +** Returns: 1 if any controls contain the object; 0 if not +** Purpose: determines if nay simple or rule-based controls +** contain a particular node or link. +**---------------------------------------------------------------- +*/ +{ + int i, ruleObject; + EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + Spremise *p; + Saction *a; + + // Check simple controls + for (i = 1; i <= net->Ncontrols; i++) + { + if (objType == NODE && net->Control[i].Node == index) return 1; + if (objType == LINK && net->Control[i].Link == index) return 1; + } + + // Check rule-based controls + for (i = 1; i <= net->Nrules; i++) + { + // Convert objType to a rule object type + if (objType == NODE) ruleObject = 6; + else ruleObject = 7; + + // Check rule's premises + p = net->Rule[i].Premises; + while (p != NULL) + { + if (ruleObject == p->object && p->index == index) return 1; + p = p->next; + } + + // Rule actions only need to be checked for link objects + if (objType == LINK) + { + // Check rule's THEN actions + a = net->Rule[i].ThenActions; + while (a != NULL) + { + if (a->link == index) return 1; + a = a->next; + } + + // Check rule's ELSE actions + a = net->Rule[i].ElseActions; + while (a != NULL) + { + if (a->link == index) return 1; + a = a->next; + } + } + } + return 0; +} + int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index) /*---------------------------------------------------------------- ** Input: index = index of the control @@ -5439,324 +5563,386 @@ int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index) return set_error(p->error_handle, 0); } -int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority) +/* + ---------------------------------------------------------------- + Functions for managing rule-based controls + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule) /*---------------------------------------------------------------- -** Input: index = index of the rule -** nPremises = number of conditions (IF AND OR) -** nTrueActions = number of actions with true conditions (THEN) -** nFalseActions = number of actions with false conditions (ELSE) -** priority = rule priority +** Input: rule = text of rule being added in the format +** used for the [RULES] section of an INP file ** Output: none ** Returns: error code +** Purpose: adds a new rule to the project. **---------------------------------------------------------------- */ { - int count; - Premise *p; - Action *c; - - EN_Project *pr = (EN_Project*)ph; + EN_Project *p = (EN_Project*)ph; - EN_Network *net = &pr->network; + char *line; + char *nextline; + char line2[MAXLINE+1]; + EN_Network *net = &p->network; + parser_data_t *parser = &p->parser; + rules_t *rules = &p->rules; - if (index > net->Nrules) - return set_error(pr->error_handle, 257); - *priority = (EN_API_FLOAT_TYPE)pr->rules.Rule[index].priority; - count = 1; - p = pr->rules.Rule[index].Pchain; - while (p->next != NULL) { - count++; - p = p->next; - } - *nPremises = count; - count = 1; - c = pr->rules.Rule[index].Tchain; - while (c->next != NULL) { - count++; - c = c->next; - } - *nTrueActions = count; + // Resize rules array + net->Rule = (Srule *)realloc(net->Rule, (net->Nrules + 2)*sizeof(Srule)); + rules->Errcode = 0; + rules->RuleState = 6; // = r_PRIORITY - c = pr->rules.Rule[index].Fchain; - count = 0; - if (c != NULL) { - count = 1; - while (c->next != NULL) { - count++; - c = c->next; + // Extract each line of the rule statement + line = rule; + while (line) + { + // Find where current line ends and next one begins + nextline = strchr(line, '\n'); + if (nextline) *nextline = '\0'; + + // Copy and tokenize the current line + strcpy(line2, line); + strcat(line2, "\n"); // Tokenizer won't work without this + parser->Ntokens = gettokens(line2, parser->Tok, MAXTOKS, parser->Comment); + + // Process the line to build up the rule's contents + if (parser->Ntokens > 0 && *parser->Tok[0] != ';') + { + ruledata(p); // Nrules gets updated in ruledata() + if (rules->Errcode) break; + } + + // Extract next line from the rule statement + if (nextline) *nextline = '\n'; + line = nextline ? (nextline + 1) : NULL; } - } - *nFalseActions = count; - return set_error(pr->error_handle, 0); + + // Delete new rule entry if there was an error + if (rules->Errcode) deleterule(p, net->Nrules); + + // Re-assign error code 201 (syntax error) to 250 (invalid format) + if (rules->Errcode == 201) rules->Errcode = 250; + return rules->Errcode; } -int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int indexRule, int idxPremise, int *logop, - int *object, int *indexObj, int *variable, - int *relop, int *status, EN_API_FLOAT_TYPE *value) { - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - Premise *p; - - EN_Project *pr = (EN_Project*)ph; - - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); - if (idxPremise > nPremises) { - return set_error(pr->error_handle, 258); - } - - p = pr->rules.Rule[indexRule].Pchain; - while (count < idxPremise) { - count++; - p = p->next; - } - *logop = p->logop; - *object = p->object; - *indexObj = p->index; - *variable = p->variable; - *relop = p->relop; - *status = p->status; - *value = (EN_API_FLOAT_TYPE)p[0].value; - return set_error(pr->error_handle, 0); -} -int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE priority) +int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, + int *nThenActions, int *nElseActions, + EN_API_FLOAT_TYPE *priority) /*---------------------------------------------------------------- ** Input: index = index of the rule +** Output: nPremises = number of premises conditions (IF AND OR) +** nThenActions = number of actions in THEN portion of rule +** nElseActions = number of actions in ELSE portion of rule +** priority = rule priority +** Returns: error code +** Purpose: gets information about a particular rule +**---------------------------------------------------------------- +*/ +{ + EN_Project *pr = (EN_Project*)ph; + + int count; + Spremise *p; + Saction *a; + EN_Network *net = &pr->network; + + if (index < 1 || index > net->Nrules) return set_error(pr->error_handle, 257); + *priority = (EN_API_FLOAT_TYPE)pr->network.Rule[index].priority; + + count = 1; + p = net->Rule[index].Premises; + while (p->next != NULL) + { + count++; + p = p->next; + } + *nPremises = count; + + count = 1; + a = net->Rule[index].ThenActions; + while (a->next != NULL) + { + count++; + a = a->next; + } + *nThenActions = count; + + a = net->Rule[index].ElseActions; + count = 0; + if (a != NULL) + { + count = 1; + while (a->next != NULL) + { + count++; + a = a->next; + } + } + *nElseActions = count; + return set_error(pr->error_handle, 0); +} + + +int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int *logop, int *object, int *objIndex, int *variable, + int *relop, int *status, EN_API_FLOAT_TYPE *value) +//----------------------------------------------------------------------------- +// Retrieve the properties of a particular rule premise. +//----------------------------------------------------------------------------- +{ + EN_Project *pr = (EN_Project*)ph; + Spremise *premises; + Spremise *p; + + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + + premises = pr->network.Rule[ruleIndex].Premises; + p = getpremise(premises, premiseIndex); + if (p == NULL) return set_error(pr->error_handle, 258); + + *logop = p->logop; + *object = p->object; + *objIndex = p->index; + *variable = p->variable; + *relop = p->relop; + *status = p->status; + *value = (EN_API_FLOAT_TYPE)p->value; + return set_error(pr->error_handle, 0); +} + + +int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, + EN_API_FLOAT_TYPE priority) +/*----------------------------------------------------------------------------- +** Input: index = index of the rule ** priority = rule priority ** Output: none ** Returns: error code -**---------------------------------------------------------------- +**----------------------------------------------------------------------------- */ { - EN_Project *pr = (EN_Project*)ph; + EN_Project *pr = (EN_Project*)ph; - if (index > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - pr->rules.Rule[index].priority = priority; - - return set_error(pr->error_handle, 0); + if (index <= 0 || index > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + pr->network.Rule[index].priority = priority; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int indexRule, int indexPremise, int logop, - int object, int indexObj, int variable, int relop, - int status, EN_API_FLOAT_TYPE value) { - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - Premise *p; +int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int logop, int object, int objIndex, int variable, + int relop, int status, EN_API_FLOAT_TYPE value) +//----------------------------------------------------------------------------- +// Sets the properties of a particular rule premise. +//----------------------------------------------------------------------------- +{ + EN_Project *pr = (EN_Project*)ph; + Spremise *premises; + Spremise *p; - EN_Project *pr; - pr = (EN_Project*)ph; + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) { - return set_error(pr->error_handle, 258); - } - p = pr->rules.Rule[indexRule].Pchain; - while (count < indexPremise) { - count++; - p = p->next; - } - p->logop = logop; - p->object = object; - p->index = indexObj; - p->variable = variable; - p->relop = relop; - p->status = status; - p->value = value; - return set_error(pr->error_handle, 0); + premises = pr->network.Rule[ruleIndex].Premises; + p = getpremise(premises, premiseIndex); + if (p == NULL) return set_error(pr->error_handle, 258); + + p->logop = logop; + p->object = object; + p->index = objIndex; + p->variable = variable; + p->relop = relop; + p->status = status; + p->value = value; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int indexRule, int indexPremise, int indexObj) { - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - Premise *p; +int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, int objIndex) +{ + EN_Project *pr = (EN_Project*)ph; + Spremise *premises; + Spremise *p; - EN_Project *pr = (EN_Project*)ph; + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } - if (indexRule > pr->network.Nrules) - return set_error(pr->error_handle, 257); - error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) { - return set_error(pr->error_handle, 258); - } - p = pr->rules.Rule[indexRule].Pchain; - while (count < indexPremise) { - count++; - p = p->next; - } - p->index = indexObj; - return set_error(pr->error_handle, 0); + premises = pr->network.Rule[ruleIndex].Premises; + p = getpremise(premises, premiseIndex); + if (p == NULL) return set_error(pr->error_handle, 258); + + p->index = objIndex; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int indexRule, int indexPremise, int status) { - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - Premise *p; +int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, int status) +{ + EN_Project *pr = (EN_Project*)ph; + Spremise *premises; + Spremise *p; - EN_Project *pr = (EN_Project*)ph; + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) { - return set_error(pr->error_handle, 258); - } - p = pr->rules.Rule[indexRule].Pchain; - while (count < indexPremise) { - count++; - p = p->next; - } - p->status = status; - return set_error(pr->error_handle, 0); + premises = pr->network.Rule[ruleIndex].Premises; + p = getpremise(premises, ruleIndex); + if (p == NULL) return set_error(pr->error_handle, 258); + + p->status = status; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int indexRule, int indexPremise, - EN_API_FLOAT_TYPE value) { - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - Premise *p; +int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int ruleIndex, + int premiseIndex, EN_API_FLOAT_TYPE value) +{ + EN_Project *pr = (EN_Project*)ph; + Spremise *premises; + Spremise *p; - EN_Project *pr = (EN_Project*)ph; + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } - if (indexRule > pr->network.Nrules) - return set_error(pr->error_handle, 257); - error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) { - return set_error(pr->error_handle, 258); - } - p = pr->rules.Rule[indexRule].Pchain; - while (count < indexPremise) { - count++; - p = p->next; - } - p->value = value; - return set_error(pr->error_handle, 0); + premises = pr->network.Rule[ruleIndex].Premises; + p = getpremise(premises, premiseIndex); + if (p == NULL) return set_error(pr->error_handle, 258); + + p->value = value; + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_gettrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, - int *status, EN_API_FLOAT_TYPE *setting) { - int count = 1, error = 0, nTrueAction, c, b; - EN_API_FLOAT_TYPE priority; - Action *a; +int DLLEXPORT EN_getthenaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int *linkIndex, + int *status, EN_API_FLOAT_TYPE *setting) +{ + EN_Project *pr = (EN_Project*)ph; + Saction *actions; + Saction *a; + + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + + actions = pr->network.Rule[ruleIndex].ThenActions; + a = getaction(actions, actionIndex); + if (a == NULL) return set_error(pr->error_handle, 258); + + *linkIndex = a->link; + *status = a->status; + *setting = (EN_API_FLOAT_TYPE)a->setting; + return set_error(pr->error_handle, 0); +} + +int DLLEXPORT EN_setthenaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int linkIndex, + int status, EN_API_FLOAT_TYPE setting) +{ + EN_Project *pr = (EN_Project*)ph; + Saction *actions; + Saction *a; + + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + + actions = pr->network.Rule[ruleIndex].ThenActions; + a = getaction(actions, actionIndex); + if (a == NULL) return set_error(pr->error_handle, 258); + + a->link = linkIndex; + a->status = status; + a->setting = setting; + return set_error(pr->error_handle, 0); +} + +int DLLEXPORT EN_getelseaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int *linkIndex, + int *status, EN_API_FLOAT_TYPE *setting) +{ EN_Project *pr = (EN_Project*)ph; + Saction *actions; + Saction *a; - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 252); + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); } - error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); - if (indexAction > nTrueAction) { - return set_error(pr->error_handle, 253); - } - a = pr->rules.Rule[indexRule].Tchain; - while (count < indexAction) { - count++; - a = a->next; - } - *indexLink = a->link; + + actions = pr->network.Rule[ruleIndex].ThenActions; + a = getaction(actions, actionIndex); + if (a == NULL) return set_error(pr->error_handle, 258); + + *linkIndex = a->link; *status = a->status; *setting = (EN_API_FLOAT_TYPE)a->setting; return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_settrueaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, - int status, EN_API_FLOAT_TYPE setting) { - int count = 1, error = 0, nTrueAction, c, b; - EN_API_FLOAT_TYPE priority; - Action *a; - +int DLLEXPORT EN_setelseaction(EN_ProjectHandle ph, int ruleIndex, + int actionIndex, int linkIndex, + int status, EN_API_FLOAT_TYPE setting) +{ EN_Project *pr = (EN_Project*)ph; + Saction *actions; + Saction *a; - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); + if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); } - error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); - if (indexAction > nTrueAction) { - return set_error(pr->error_handle, 258); - } - a = pr->rules.Rule[indexRule].Tchain; - while (count < indexAction) { - count++; - a = a->next; - } - a->link = indexLink; + + actions = pr->network.Rule[ruleIndex].ThenActions; + a = getaction(actions, actionIndex); + if (a == NULL) return set_error(pr->error_handle, 258); + + a->link = linkIndex; a->status = status; a->setting = setting; return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_getfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int *indexLink, - int *status, EN_API_FLOAT_TYPE *setting) { - int count = 1, error = 0, nFalseAction, c, b; - EN_API_FLOAT_TYPE priority; - Action *a; - - EN_Project *pr = (EN_Project*)ph; - - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); - if (indexAction > nFalseAction) { - return set_error(pr->error_handle, 258); - } - a = pr->rules.Rule[indexRule].Fchain; - while (count < indexAction) { - count++; - a = a->next; - } - *indexLink = a->link; - *status = a->status; - *setting = (EN_API_FLOAT_TYPE)a->setting; - return set_error(pr->error_handle, 0); +int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int index, char *id) +{ + EN_Project *pr = (EN_Project*)ph; + + strcpy(id, ""); + if (!pr->Openflag) return set_error(pr->error_handle, 102); + if (index < 1 || index > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + strcpy(id, pr->network.Rule[index].label); + return set_error(pr->error_handle, 0); } -int DLLEXPORT EN_setfalseaction(EN_ProjectHandle ph, int indexRule, int indexAction, int indexLink, - int status, EN_API_FLOAT_TYPE setting) { - int count = 1, error = 0, nFalseAction, c, b; - EN_API_FLOAT_TYPE priority; - Action *a; +int DLLEXPORT EN_deleterule(EN_ProjectHandle ph, int index) +{ + EN_Project *pr = (EN_Project*)ph; - EN_Project *pr = (EN_Project*)ph; - - if (indexRule > pr->network.Nrules) { - return set_error(pr->error_handle, 257); - } - error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); - if (indexAction > nFalseAction) { - return set_error(pr->error_handle, 258); - } - a = pr->rules.Rule[indexRule].Fchain; - while (count < indexAction) { - count++; - a = a->next; - } - a->link = indexLink; - a->status = status; - a->setting = setting; - - return set_error(pr->error_handle, 0); -} - -int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int indexRule, char *id) { - - EN_Project *pr = (EN_Project*)ph; - - strcpy(id, ""); - if (!pr->Openflag) - return set_error(pr->error_handle, 102); - if (indexRule < 1 || indexRule > pr->network.Nrules) - return set_error(pr->error_handle, 257); - strcpy(id, pr->rules.Rule[indexRule].label); - return set_error(pr->error_handle, 0); + if (index < 1 || index > pr->network.Nrules) + { + return set_error(pr->error_handle, 257); + } + deleterule(pr, index); + return set_error(pr->error_handle, 0); } /*************************** END OF EPANET.C ***************************/ diff --git a/src/errors.dat b/src/errors.dat index bd91e4b..0426003 100644 --- a/src/errors.dat +++ b/src/errors.dat @@ -25,7 +25,7 @@ DAT(208,ENERR_SPEC_UNDEF_NODE,"undefined Node") DAT(209,ENERR_ILLEGAL_VAL_NODE,"illegal value for Node") DAT(210,ENERR_SPEC_UNDEF_LINK,"specified for undefined Link") DAT(211,ENERR_ILLEGAL_VAL_LINK,"illegal value for Link") -DAT(212,ENERR_TRACE_NODE,"trace node") +DAT(212,ENERR_TRACE_NODE,"invalid trace node") DAT(213,ENERR_ILLEGAL_OPT,"illegal option value in section") DAT(214,ENERR_TOO_MANY_CHAR,"following line of section contains too many characters") DAT(215,ENERR_DUPLICATE_ID,"duplicate ID") @@ -33,6 +33,7 @@ DAT(216,ENERR_UNDEF_PUMP,"data specified for undefined Pump") DAT(217,ENERR_INVALID_DATA_PUMP,"invalid data for Pump") DAT(219,ENERR_ILLEGAL_CON_TANK,"illegally connected to a tank") DAT(220,ENERR_ILLEGAL_CON_VALVE,"illegally connected to another valve") +DAT(221,ENERR_RULE_CLAUSE, "mis-placed rule clause") DAT(222,ENERR_SAME_START_END,"same start and end nodes") @@ -48,12 +49,15 @@ DAT(241,ENERR_UNDEF_CONTROL,"refers to undefined control") DAT(250,ENERR_INVALID_FORMAT,"function call contains invalid format") DAT(251,ENERR_INVALID_CODE,"function call contains invalid parameter code") -DAT(253,ENERR_NO_DEMAND_CAT,"Function call error - No such demand category index") -DAT(254,ENERR_NO_COORDS,"Function call error - Node have no coordinates") -DAT(255,ENERR_COORDS_NOT_LOADED,"Function call error - Coordinates were not loaded") +DAT(253,ENERR_NO_DEMAND_CAT,"function applied to nonexistent demand category") +DAT(254,ENERR_NO_COORDS,"function applied to node with no coordinates") +DAT(255,ENERR_COORDS_NOT_LOADED,"function fails because no node coordinates were supplied") -DAT(257,ENERR_NO_RULE,"rule does not exist") -DAT(258,ENERR_NO_CONDITION_ACTION,"condition or action index specified in rule does not exist") +DAT(257,ENERR_NO_RULE,"function applied to nonexistent rule") +DAT(258,ENERR_NO_CONDITION_ACTION,"function applied to nonexistent rule clause") + +DAT(260,ENERR_DEL_TRACE_NODE,"cannot delete node assigned as a Trace Node") +DAT(261,ENERR_DEL_NODE_LINK, "cannot delete a node or link contained in a control") DAT(301,ENERR_FILES_ARE_SAME,"identical file names") DAT(302,ENERR_CANT_OPEN_INP,"cannot open input file") diff --git a/src/funcs.h b/src/funcs.h index 7390f5b..799be27 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -29,8 +29,6 @@ AUTHOR: L. Rossman #ifndef FUNCS_H #define FUNCS_H -#include "types.h" - void initpointers(EN_Project *pr); /* Initializes pointers */ int allocdata(EN_Project *pr); /* Allocates memory */ void freeTmplist(STmplist *); /* Frees items in linked list */ @@ -41,7 +39,7 @@ int openfiles(EN_Project *pr, const char *, int openhydfile(EN_Project *pr); /* Opens hydraulics file */ int openoutfile(EN_Project *pr); /* Opens binary output file */ int strcomp(const char *, const char *); /* Compares two strings */ -char* getTmpName(EN_Project *p, char* fname); /* Gets temporary file name */ +char* getTmpName(char* fname); /* Gets temporary file name */ double interp(int n, double x[], double y[], double xx); /* Interpolates a data curve */ @@ -118,16 +116,19 @@ void changestatus(EN_Network *net, int, StatType, double); /* Changes status of a link */ /* -------------- RULES.C --------------*/ -void initrules(rules_t *rules); /* Initializes rule base */ +void initrules(EN_Project *pr); /* Initializes rule base */ void addrule(parser_data_t *par, char *); /* Adds rule to rule base */ int allocrules(EN_Project *pr); /* Allocates memory for rule */ void adjustrules(EN_Project *pr, int, int); // Shifts object indices down void adjusttankrules(EN_Project *pr); // Shifts tank indices up int ruledata(EN_Project *pr); /* Processes rule input data */ int checkrules(EN_Project *pr, long); /* Checks all rules */ -void freerules(EN_Project *pr); /* Frees rule base memory */ -int writeRuleinInp(EN_Project *pr, FILE *f, /* Writes rule to an INP file */ - int RuleIdx); +void deleterule(EN_Project *pr, int); // Deletes a rule +void freerules(EN_Project *pr); /* Frees rule base memory */ +int writerule(EN_Project *pr, FILE *, int); /* Writes rule to an INP file */ +void ruleerrmsg(EN_Project *pr); /* Reports rule parser error */ +Spremise *getpremise(Spremise *, int); // Retrieves a rule's premise +Saction *getaction(Saction *, int); // Retrieves a rule's action /* ------------- REPORT.C --------------*/ int writereport(EN_Project *pr); /* Writes formatted report */ @@ -169,8 +170,8 @@ void setlinksetting(EN_Project *pr, int, double, int tanktimestep(EN_Project *pr, long *); /* Time till tanks fill/drain */ void getenergy(EN_Project *pr, int, double *, double *); /* Computes link energy use */ -double tankvolume(EN_Project *pr, int,double); /* Finds tank vol. from grade */ -double tankgrade(EN_Project *pr, int,double); /* Finds tank grade from vol. */ +double tankvolume(EN_Project *pr, int, double); /* Finds tank vol. from grade */ +double tankgrade(EN_Project *pr, int, double); /* Finds tank grade from vol. */ /* ----------- HYDSOLVER.C - ----------*/ int hydsolve(EN_Project *pr, int *,double *); /* Solves network equations */ diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index ed0c258..e168f12 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -14,6 +14,7 @@ HYDCOEFFS.C -- hydraulic coefficients for the EPANET Program #include #endif #include + #include "types.h" #include "funcs.h" @@ -82,8 +83,8 @@ void resistcoeff(EN_Project *pr, int k) // ... Link is a pipe. Compute resistance based on headloss formula. // Friction factor for D-W formula gets included during head loss // calculation. - case EN_CVPIPE: - case EN_PIPE: + case CVPIPE: + case PIPE: e = link->Kc; // Roughness coeff. d = link->Diam; // Diameter L = link->Len; // Length @@ -108,7 +109,7 @@ void resistcoeff(EN_Project *pr, int k) break; // ... Link is a pump. Use huge resistance. - case EN_PUMP: + case PUMP: link->R = CBIG; break; @@ -138,25 +139,25 @@ void headlosscoeffs(EN_Project *pr) { switch (net->Link[k].Type) { - case EN_CVPIPE: - case EN_PIPE: + case CVPIPE: + case PIPE: pipecoeff(pr, k); break; - case EN_PUMP: + case PUMP: pumpcoeff(pr, k); break; - case EN_PBV: + case PBV: pbvcoeff(pr, k); break; - case EN_TCV: + case TCV: tcvcoeff(pr, k); break; - case EN_GPV: + case GPV: gpvcoeff(pr, k); break; - case EN_FCV: - case EN_PRV: - case EN_PSV: + case FCV: + case PRV: + case PSV: if (hyd->LinkSetting[k] == MISSING) valvecoeff(pr, k); else hyd->solver.P[k] = 0.0; } @@ -317,13 +318,13 @@ void valvecoeffs(EN_Project *pr) // Call valve-specific function switch (link->Type) { - case EN_PRV: + case PRV: prvcoeff(pr, k, n1, n2); break; - case EN_PSV: + case PSV: psvcoeff(pr, k, n1, n2); break; - case EN_FCV: + case FCV: fcvcoeff(pr, k, n1, n2); break; default: continue; diff --git a/src/hydraul.c b/src/hydraul.c index 4f9ce46..2ed43fc 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -54,9 +54,10 @@ AUTHOR: L. Rossman #include #endif #include -#include "text.h" + #include "types.h" #include "funcs.h" +#include "text.h" #define QZERO 1.e-6 /* Equivalent to zero flow */ @@ -146,8 +147,8 @@ void inithyd(EN_Project *pr, int initflag) /* Start active control valves in ACTIVE position */ if ( - (link->Type == EN_PRV || link->Type == EN_PSV - || link->Type == EN_FCV) && (link->Kc != MISSING) + (link->Type == PRV || link->Type == PSV + || link->Type == FCV) && (link->Kc != MISSING) ) hyd->LinkStatus[i] = ACTIVE; /*** Updated 3/1/01 ***/ @@ -391,7 +392,7 @@ void initlinkflow(EN_Project *pr, int i, char s, double k) if (s == CLOSED) { hyd->LinkFlows[i] = QZERO; } - else if (link->Type == EN_PUMP) { + else if (link->Type == PUMP) { hyd->LinkFlows[i] = k * n->Pump[findpump(n,i)].Q0; } else { @@ -421,8 +422,8 @@ void setlinkflow(EN_Project *pr, int k, double dh) switch (link->Type) { - case EN_CVPIPE: - case EN_PIPE: + case CVPIPE: + case PIPE: /* For Darcy-Weisbach formula: */ /* use approx. inverse of formula. */ @@ -447,7 +448,7 @@ void setlinkflow(EN_Project *pr, int k, double dh) break; - case EN_PUMP: + case PUMP: /* Convert headloss to pump head gain */ dh = -dh; @@ -492,15 +493,15 @@ void setlinkstatus(EN_Project *pr, int index, char value, StatType *s, double * { EN_Network *net = &pr->network; Slink *link = &net->Link[index]; - EN_LinkType t = link->Type; + LinkType t = link->Type; /* Status set to open */ if (value == 1) { /* Adjust link setting for pumps & valves */ - if (t == EN_PUMP) { + if (t == PUMP) { *k = 1.0; } - if (t > EN_PUMP && t != EN_GPV) { + if (t > PUMP && t != GPV) { *k = MISSING; } /* Reset link flow if it was originally closed */ @@ -510,10 +511,10 @@ void setlinkstatus(EN_Project *pr, int index, char value, StatType *s, double * /* Status set to closed */ else if (value == 0) { /* Adjust link setting for pumps & valves */ - if (t == EN_PUMP) { + if (t == PUMP) { *k = 0.0; } - if (t > EN_PUMP && t != EN_GPV) { + if (t > PUMP && t != GPV) { *k = MISSING; } /* Reset link flow if it was originally open */ @@ -536,10 +537,10 @@ void setlinksetting(EN_Project *pr, int index, double value, StatType *s, doubl { EN_Network *net = &pr->network; Slink *link = &net->Link[index]; - EN_LinkType t = link->Type; + LinkType t = link->Type; /* For a pump, status is OPEN if speed > 0, CLOSED otherwise */ - if (t == EN_PUMP) + if (t == PUMP) { *k = value; if (value > 0 && *s <= CLOSED) { @@ -551,7 +552,7 @@ void setlinksetting(EN_Project *pr, int index, double value, StatType *s, doubl } /* For FCV, activate it */ - else if (t == EN_FCV) { + else if (t == FCV) { *k = value; *s = ACTIVE; } @@ -709,7 +710,7 @@ int controls(EN_Project *pr) s2 = control->Status; k1 = hyd->LinkSetting[k]; k2 = k1; - if (link->Type > EN_PIPE) { + if (link->Type > PIPE) { k2 = control->Setting; } if (s1 != s2 || k1 != k2) { @@ -891,7 +892,7 @@ void controltimestep(EN_Project *pr, long *tstep) /* Check if rule actually changes link status or setting */ k = control->Link; link = &net->Link[k]; - if ( (link->Type > EN_PIPE && hyd->LinkSetting[k] != control->Setting) + if ( (link->Type > PIPE && hyd->LinkSetting[k] != control->Setting) || (hyd->LinkStatus[k] != control->Status) ) { *tstep = t; } @@ -1103,7 +1104,7 @@ void getenergy(EN_Project *pr, int k, double *kw, double *eff) dh = ABS(hyd->NodeHead[link->N1] - hyd->NodeHead[link->N2]); /* For pumps, find effic. at current flow */ - if (link->Type == EN_PUMP) + if (link->Type == PUMP) { j = findpump(net,k); e = hyd->Epump; diff --git a/src/hydsolver.c b/src/hydsolver.c index 63ed951..f6c872d 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -18,9 +18,10 @@ The solver implements Todini's Global Gradient Algorithm. #include #endif #include -#include "text.h" + #include "types.h" #include "funcs.h" +#include "text.h" // Hydraulic balance error for network being analyzed typedef struct { @@ -241,7 +242,7 @@ int badvalve(EN_Project *pr, int n) report_options_t *rep = &pr->report; time_options_t *time = &pr->time_options; Slink *link; - EN_LinkType t; + LinkType t; for (i = 1; i <= net->Nvalves; i++) { @@ -252,7 +253,7 @@ int badvalve(EN_Project *pr, int n) if (n == n1 || n == n2) { t = link->Type; - if (t == EN_PRV || t == EN_PSV || t == EN_FCV) + if (t == PRV || t == PSV || t == FCV) { if (hyd->LinkStatus[k] == ACTIVE) { @@ -261,7 +262,7 @@ int badvalve(EN_Project *pr, int n) sprintf(pr->Msg, FMT61, clocktime(rep->Atime, time->Htime), link->ID); writeline(pr, pr->Msg); } - if (link->Type == EN_FCV) + if (link->Type == FCV) { hyd->LinkStatus[k] = XFCV; } @@ -332,15 +333,15 @@ int pswitch(EN_Project *pr) link = &net->Link[k]; change = 0; s = hyd->LinkStatus[k]; - if (link->Type == EN_PIPE) + if (link->Type == PIPE) { if (s != net->Control[i].Status) change = 1; } - if (link->Type == EN_PUMP) + if (link->Type == PUMP) { if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1; } - if (link->Type >= EN_PRV) + if (link->Type >= PRV) { if (hyd->LinkSetting[k] != net->Control[i].Setting) change = 1; else if (hyd->LinkSetting[k] == MISSING && s != net->Control[i].Status) @@ -353,7 +354,7 @@ int pswitch(EN_Project *pr) if (change) { hyd->LinkStatus[k] = net->Control[i].Status; - if (link->Type > EN_PIPE) + if (link->Type > PIPE) { hyd->LinkSetting[k] = net->Control[i].Setting; } @@ -450,7 +451,7 @@ void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum dq *= hyd->RelaxFactor; // Prevent flow in constant HP pumps from going negative - if (link->Type == EN_PUMP) + if (link->Type == PUMP) { n = findpump(net, k); if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlows[k]) diff --git a/src/hydstatus.c b/src/hydstatus.c index f5041c6..8a742f9 100644 --- a/src/hydstatus.c +++ b/src/hydstatus.c @@ -63,12 +63,12 @@ int valvestatus(EN_Project *pr) // Evaluate valve's new status switch (link->Type) { - case EN_PRV: + case PRV: hset = net->Node[n2].El + hyd->LinkSetting[k]; hyd->LinkStatus[k] = prvstatus(pr, k, status, hset, hyd->NodeHead[n1], hyd->NodeHead[n2]); break; - case EN_PSV: + case PSV: hset = net->Node[n1].El + hyd->LinkSetting[k]; hyd->LinkStatus[k] = psvstatus(pr, k, status, hset, hyd->NodeHead[n1], hyd->NodeHead[n2]); @@ -129,19 +129,19 @@ int linkstatus(EN_Project *pr) } // Check for status changes in CVs and pumps - if (link->Type == EN_CVPIPE) + if (link->Type == CVPIPE) { hyd->LinkStatus[k] = cvstatus(pr, hyd->LinkStatus[k], dh, hyd->LinkFlows[k]); } - if (link->Type == EN_PUMP && hyd->LinkStatus[k] >= OPEN && + if (link->Type == PUMP && hyd->LinkStatus[k] >= OPEN && hyd->LinkSetting[k] > 0.0) { hyd->LinkStatus[k] = pumpstatus(pr, k, -dh); } // Check for status changes in non-fixed FCVs - if (link->Type == EN_FCV && hyd->LinkSetting[k] != MISSING) + if (link->Type == FCV && hyd->LinkSetting[k] != MISSING) { hyd->LinkStatus[k] = fcvstatus(pr, k, status, hyd->NodeHead[n1], hyd->NodeHead[n2]); @@ -430,7 +430,7 @@ void tankstatus(EN_Project *pr, int k, int n1, int n2) if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol) { // Case 1: Link is a pump discharging into tank - if (link->Type == EN_PUMP) + if (link->Type == PUMP) { if (link->N2 == n1) hyd->LinkStatus[k] = TEMPCLOSED; } @@ -447,7 +447,7 @@ void tankstatus(EN_Project *pr, int k, int n1, int n2) if (hyd->NodeHead[n1] <= tank->Hmin + hyd->Htol) { // Case 1: Link is a pump discharging from tank - if (link->Type == EN_PUMP) + if (link->Type == PUMP) { if (link->N1 == n1) hyd->LinkStatus[k] = TEMPCLOSED; } diff --git a/src/inpfile.c b/src/inpfile.c index 5948f9d..cbce693 100644 --- a/src/inpfile.c +++ b/src/inpfile.c @@ -26,14 +26,12 @@ data describing a piping network to a file in EPANET's text format. #else #include #endif +#include -#include "hash.h" -#include "text.h" #include "types.h" #include "funcs.h" -#include -//#define EXTERN extern -//#include "vars.h" +#include "hash.h" +#include "text.h" /* Defined in enumstxt.h in EPANET.C */ extern char *LinkTxt[]; @@ -216,7 +214,7 @@ int saveinpfile(EN_Project *pr, const char *fname) fprintf(f, s_PIPES); for (i = 1; i <= net->Nlinks; i++) { link = &net->Link[i]; - if (link->Type <= EN_PIPE) { + if (link->Type <= PIPE) { d = link->Diam; kc = link->Kc; if (hyd->Formflag == DW) @@ -229,7 +227,7 @@ int saveinpfile(EN_Project *pr, const char *fname) sprintf(s1, "%12.4f %12.4f", kc, km); else sprintf(s1, "%12.4f %12.4f", kc, km); - if (link->Type == EN_CVPIPE) + if (link->Type == CVPIPE) sprintf(s2, "CV"); else if (link->Stat == CLOSED) sprintf(s2, "CLOSED"); @@ -295,12 +293,12 @@ int saveinpfile(EN_Project *pr, const char *fname) if (kc == MISSING) kc = 0.0; switch (link->Type) { - case EN_FCV: + case FCV: kc *= pr->Ucf[FLOW]; break; - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: kc *= pr->Ucf[PRESSURE]; break; default: @@ -312,7 +310,7 @@ int saveinpfile(EN_Project *pr, const char *fname) sprintf(s, " %-31s %-31s %-31s %12.4f %5s", link->ID, net->Node[link->N1].ID, net->Node[link->N2].ID, d * pr->Ucf[DIAM], LinkTxt[link->Type]); - if (link->Type == EN_GPV && (j = ROUND(link->Kc)) > 0) + if (link->Type == GPV && (j = ROUND(link->Kc)) > 0) sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km); else sprintf(s1, "%12.4f %12.4f", kc, km); @@ -355,12 +353,12 @@ int saveinpfile(EN_Project *pr, const char *fname) fprintf(f, s_STATUS); for (i = 1; i <= net->Nlinks; i++) { link = &net->Link[i]; - if (link->Type <= EN_PUMP) { + if (link->Type <= PUMP) { if (link->Stat == CLOSED) fprintf(f, "\n %-31s %s", link->ID, StatTxt[CLOSED]); /* Write pump speed here for pumps with old-style pump curve input */ - else if (link->Type == EN_PUMP) { + else if (link->Type == PUMP) { n = findpump(net, i); pump = &net->Pump[n]; if (pump->Hcurve == 0 && pump->Ptype != CONST_HP && @@ -419,12 +417,12 @@ int saveinpfile(EN_Project *pr, const char *fname) else { kc = control->Setting; switch (link->Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: kc *= pr->Ucf[PRESSURE]; break; - case EN_FCV: + case FCV: kc *= pr->Ucf[FLOW]; break; default: @@ -467,8 +465,8 @@ int saveinpfile(EN_Project *pr, const char *fname) fprintf(f, "\n\n"); fprintf(f, s_RULES); for (i = 1; i <= net->Nrules; i++) { - fprintf(f, "\nRULE %s", pr->rules.Rule[i].label); - errcode = writeRuleinInp(pr, f, i); + fprintf(f, "\nRULE %s", pr->network.Rule[i].label); + errcode = writerule(pr, f, i); fprintf(f, "\n"); } @@ -528,7 +526,7 @@ int saveinpfile(EN_Project *pr, const char *fname) fprintf(f, "\n ROUGHNESS CORRELATION %-.6f", qu->Rfactor); for (i = 1; i <= net->Nlinks; i++) { link = &net->Link[i]; - if (link->Type > EN_PIPE) + if (link->Type > PIPE) continue; if (link->Kb != qu->Kbulk) fprintf(f, "\n BULK %-31s %-.6f", link->ID, link->Kb * SECperDAY); diff --git a/src/input1.c b/src/input1.c index 89a4e1d..ce16fca 100644 --- a/src/input1.c +++ b/src/input1.c @@ -27,11 +27,11 @@ AUTHOR: L. Rossman #ifndef __APPLE__ #include #endif + +#include "types.h" +#include "funcs.h" #include "hash.h" #include "text.h" -#include "types.h" -#include "epanet2.h" -#include "funcs.h" #include /* @@ -107,8 +107,6 @@ void setdefaults(EN_Project *pr) strncpy(pr->Title[0], "", TITLELEN); strncpy(pr->Title[1], "", TITLELEN); strncpy(pr->Title[2], "", TITLELEN); - strncpy(out->TmpDir, "", MAXFNAME); - strncpy(out->TmpFname, "", MAXFNAME); strncpy(out->HydFname, "", MAXFNAME); strncpy(pr->MapFname, "", MAXFNAME); strncpy(qu->ChemName, t_CHEMICAL, MAXID); @@ -324,7 +322,7 @@ void adjustdata(EN_Project *pr) /* See if default reaction coeffs. apply */ for (i = 1; i <= net->Nlinks; i++) { Slink *link = &net->Link[i]; - if (link->Type > EN_PIPE) + if (link->Type > PIPE) continue; if (link->Kb == MISSING) link->Kb = qu->Kbulk; /* Bulk coeff. */ @@ -406,9 +404,8 @@ int inittanks(EN_Project *pr) /* Report error in levels if found */ if (levelerr) { - char errMsg[MAXMSG+1]; - EN_geterror(225, errMsg, MAXMSG); - sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[tank->Node].ID); + sprintf(pr->Msg, "%s node: %s", geterrmsg(225, pr->Msg), + net->Node[tank->Node].ID); writeline(pr, pr->Msg); errcode = 200; } @@ -629,7 +626,7 @@ void convertunits(EN_Project *pr) /* Convert units of link parameters */ for (k = 1; k <= net->Nlinks; k++) { link = &net->Link[k]; - if (link->Type <= EN_PIPE) { + if (link->Type <= PIPE) { /* Convert pipe parameter units: */ /* - for Darcy-Weisbach formula, convert roughness */ /* from millifeet (or mm) to ft (or m) */ @@ -647,7 +644,7 @@ void convertunits(EN_Project *pr) link->Kw /= SECperDAY; } - else if (link->Type == EN_PUMP) { + else if (link->Type == PUMP) { /* Convert units for pump curve parameters */ i = findpump(net, k); pump = &net->Pump[i]; @@ -676,12 +673,12 @@ void convertunits(EN_Project *pr) link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); if (link->Kc != MISSING) switch (link->Type) { - case EN_FCV: + case FCV: link->Kc /= pr->Ucf[FLOW]; break; - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: link->Kc /= pr->Ucf[PRESSURE]; break; default: @@ -716,12 +713,12 @@ void convertunits(EN_Project *pr) /* Convert units on valve settings */ if (control->Setting != MISSING) { switch (link->Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: control->Setting /= pr->Ucf[PRESSURE]; break; - case EN_FCV: + case FCV: control->Setting /= pr->Ucf[FLOW]; default: break; diff --git a/src/input2.c b/src/input2.c index 374ca87..846c0a1 100644 --- a/src/input2.c +++ b/src/input2.c @@ -31,12 +31,12 @@ The following utility functions are all called from INPUT3.C #ifndef __APPLE__ #include #endif +#include + +#include "types.h" +#include "funcs.h" #include "hash.h" #include "text.h" -#include "types.h" -#include "epanet2.h" -#include "funcs.h" -#include #define MAXERRS 10 /* Max. input errors reported */ @@ -206,9 +206,6 @@ int readdata(EN_Project *pr) /* Check if max. length exceeded */ if (strlen(line) >= MAXLINE) { -// char errMsg[MAXMSG+1]; -// EN_geterror(214, errMsg, MAXMSG); -// sprintf(pr->Msg, "%s section: %s", errMsg, SectTxt[sect]); sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg), SectTxt[sect]); writeline(pr, pr->Msg); writeline(pr, line); @@ -317,7 +314,12 @@ int newline(EN_Project *pr, int sect, char *line) case _CONTROLS: return (controldata(pr)); case _RULES: - return (ruledata(pr)); /* See RULES.C */ + if (ruledata(pr) > 0) + { + ruleerrmsg(pr); + return 200; + } + else return 0; case _SOURCES: return (sourcedata(pr)); case _EMITTERS: diff --git a/src/input3.c b/src/input3.c index 4ba7f04..f18667f 100644 --- a/src/input3.c +++ b/src/input3.c @@ -26,12 +26,12 @@ All functions in this module are called from newline() in INPUT2.C. #ifndef __APPLE__ #include #endif -#include "epanet2.h" +#include + +#include "types.h" #include "funcs.h" #include "hash.h" #include "text.h" -#include "types.h" -#include /* Defined in enumstxt.h in EPANET.C */ extern char *MixTxt[]; @@ -98,7 +98,7 @@ int juncdata(EN_Project *pr) node->S = NULL; node->Ke = 0.0; node->Rpt = 0; - node->Type = EN_JUNCTION; + node->Type = JUNCTION; strcpy(node->Comment, par->Comment); @@ -214,7 +214,7 @@ int tankdata(EN_Project *pr) node->C0 = 0.0; /* Init. quality. */ node->S = NULL; /* WQ source data */ node->Ke = 0.0; /* Emitter coeff. */ - node->Type = (diam == 0) ? EN_RESERVOIR : EN_TANK; + node->Type = (diam == 0) ? RESERVOIR : TANK; strcpy(node->Comment, par->Comment); tank->Node = i; /* Node index. */ tank->H0 = initlevel; /* Init. level. */ @@ -258,7 +258,7 @@ int pipedata(EN_Project *pr) int j1, /* Start-node index */ j2, /* End-node index */ n; /* # data items */ - EN_LinkType type = EN_PIPE; /* Link type */ + LinkType type = PIPE; /* Link type */ StatType status = OPEN; /* Link status */ double length, /* Link length */ diam, /* Link diameter */ @@ -298,7 +298,7 @@ int pipedata(EN_Project *pr) /* Case where either loss coeff. or status supplied */ if (n == 7) { if (match(par->Tok[6], w_CV)) - type = EN_CVPIPE; + type = CVPIPE; else if (match(par->Tok[6], w_CLOSED)) status = CLOSED; else if (match(par->Tok[6], w_OPEN)) @@ -312,7 +312,7 @@ int pipedata(EN_Project *pr) if (!getfloat(par->Tok[6], &lcoeff)) return (202); if (match(par->Tok[7], w_CV)) - type = EN_CVPIPE; + type = CVPIPE; else if (match(par->Tok[7], w_CLOSED)) status = CLOSED; else if (match(par->Tok[7], w_OPEN)) @@ -401,7 +401,7 @@ int pumpdata(EN_Project *pr) link->Km = 0.0; /* Horsepower. */ link->Kb = 0.0; link->Kw = 0.0; - link->Type = EN_PUMP; /* Link type. */ + link->Type = PUMP; /* Link type. */ link->Stat = OPEN; /* Link status. */ link->Rpt = 0; /* Report flag. */ strcpy(link->Comment, par->Comment); @@ -514,24 +514,24 @@ int valvedata(EN_Project *pr) return (222); if (match(par->Tok[4], w_PRV)) - type = EN_PRV; + type = PRV; else if (match(par->Tok[4], w_PSV)) - type = EN_PSV; + type = PSV; else if (match(par->Tok[4], w_PBV)) - type = EN_PBV; + type = PBV; else if (match(par->Tok[4], w_FCV)) - type = EN_FCV; + type = FCV; else if (match(par->Tok[4], w_TCV)) - type = EN_TCV; + type = TCV; else if (match(par->Tok[4], w_GPV)) - type = EN_GPV; + type = GPV; else return (201); /* Illegal valve type.*/ if (!getfloat(par->Tok[3], &diam)) return (202); if (diam <= 0.0) return (202); /* Illegal diameter.*/ - if (type == EN_GPV) { /* Headloss curve for GPV */ + if (type == GPV) { /* Headloss curve for GPV */ t = findID(par->Tok[5], par->Curvelist); if (t == NULL) { return (206); @@ -549,7 +549,7 @@ int valvedata(EN_Project *pr) /* Check that PRV, PSV, or FCV not connected to a tank & */ /* check for illegal connections between pairs of valves.*/ if ((j1 > net->Njuncs || j2 > net->Njuncs) && - (type == EN_PRV || type == EN_PSV || type == EN_FCV)) + (type == PRV || type == PSV || type == FCV)) return (219); if (!valvecheck(pr, type, j1, j2)) return (220); @@ -820,7 +820,7 @@ int controldata(EN_Project *pr) n; /* # data items */ StatType status = ACTIVE; /* Link status */ ControlType c_type; /* control type */ - EN_LinkType l_type; /* Link Type */ + LinkType l_type; /* Link Type */ double setting = MISSING, /* Link setting */ time = 0.0, /* Simulation time */ level = 0.0; /* Pressure or tank level */ @@ -836,24 +836,24 @@ int controldata(EN_Project *pr) if (k == 0) return (204); l_type = net->Link[k].Type; - if (l_type == EN_CVPIPE) { + if (l_type == CVPIPE) { return (207); /* Cannot control check valve. */ } /*** Updated 9/7/00 ***/ /* Parse control setting into a status level or numerical setting. */ if (match(par->Tok[2], w_OPEN)) { status = OPEN; - if (l_type == EN_PUMP) + if (l_type == PUMP) setting = 1.0; - if (l_type == EN_GPV) + if (l_type == GPV) setting = net->Link[k].Kc; } else if (match(par->Tok[2], w_CLOSED)) { status = CLOSED; - if (l_type == EN_PUMP) + if (l_type == PUMP) setting = 0.0; - if (l_type == EN_GPV) + if (l_type == GPV) setting = net->Link[k].Kc; - } else if (l_type == EN_GPV) { + } else if (l_type == GPV) { return (206); } else if (!getfloat(par->Tok[2], &setting)) { return (202); @@ -863,7 +863,7 @@ int controldata(EN_Project *pr) /* Set status for pump in case speed setting was supplied */ /* or for pipe if numerical setting was supplied */ - if (l_type == EN_PUMP || l_type == EN_PIPE) { + if (l_type == PUMP || l_type == PIPE) { if (setting != MISSING) { if (setting < 0.0) return (202); @@ -1321,12 +1321,12 @@ int statusdata(EN_Project *pr) if ((j = findlink(net, par->Tok[0])) == 0) return (0); /* Cannot change status of a Check Valve */ - if (net->Link[j].Type == EN_CVPIPE) + if (net->Link[j].Type == CVPIPE) return (211); /*** Updated 9/7/00 ***/ /* Cannot change setting for a GPV */ - if (net->Link[j].Type == EN_GPV && status == ACTIVE) + if (net->Link[j].Type == GPV && status == ACTIVE) return (211); changestatus(net, j, status, y); @@ -1398,7 +1398,7 @@ int energydata(EN_Project *pr) k = findlink(net,par->Tok[1]); /* Check that pump exists */ if (k == 0) return (216); - if (Link[k].Type != EN_PUMP) + if (Link[k].Type != PUMP) return (216); j = findpump(net, k); } else @@ -2150,7 +2150,7 @@ int valvecheck(EN_Project *pr, int type, int j1, int j2) EN_Network *net = &pr->network; int k, vj1, vj2; - EN_LinkType vtype; + LinkType vtype; /* Examine each existing valve */ for (k = 1; k <= net->Nvalves; k++) { @@ -2161,35 +2161,35 @@ int valvecheck(EN_Project *pr, int type, int j1, int j2) vtype = link->Type; /* Cannot have two PRVs sharing downstream nodes or in series */ - if (vtype == EN_PRV && type == EN_PRV) { + if (vtype == PRV && type == PRV) { if (vj2 == j2 || vj2 == j1 || vj1 == j2) return (0); } /* Cannot have two PSVs sharing upstream nodes or in series */ - if (vtype == EN_PSV && type == EN_PSV) { + if (vtype == PSV && type == PSV) { if (vj1 == j1 || vj1 == j2 || vj2 == j1) return (0); } /* Cannot have PSV connected to downstream node of PRV */ - if (vtype == EN_PSV && type == EN_PRV && vj1 == j2) + if (vtype == PSV && type == PRV && vj1 == j2) return (0); - if (vtype == EN_PRV && type == EN_PSV && vj2 == j1) + if (vtype == PRV && type == PSV && vj2 == j1) return (0); /*** Updated 3/1/01 ***/ /* Cannot have PSV connected to downstream node of FCV */ /* nor have PRV connected to upstream node of FCV */ - if (vtype == EN_FCV && type == EN_PSV && vj2 == j1) + if (vtype == FCV && type == PSV && vj2 == j1) return (0); - if (vtype == EN_FCV && type == EN_PRV && vj1 == j2) + if (vtype == FCV && type == PRV && vj1 == j2) return (0); /*** Updated 4/14/05 ***/ - if (vtype == EN_PSV && type == EN_FCV && vj1 == j2) + if (vtype == PSV && type == FCV && vj1 == j2) return (0); - if (vtype == EN_PRV && type == EN_FCV && vj2 == j1) + if (vtype == PRV && type == FCV && vj2 == j1) return (0); } return (1); @@ -2213,10 +2213,10 @@ void changestatus(EN_Network *net, int j, StatType status, double y) { Slink *link = &net->Link[j]; - if (link->Type == EN_PIPE || link->Type == EN_GPV) { + if (link->Type == PIPE || link->Type == GPV) { if (status != ACTIVE) link->Stat = status; - } else if (link->Type == EN_PUMP) { + } else if (link->Type == PUMP) { if (status == ACTIVE) { link->Kc = y; status = OPEN; @@ -2226,7 +2226,7 @@ void changestatus(EN_Network *net, int j, StatType status, double y) link->Kc = 1.0; } link->Stat = status; - } else if (link->Type >= EN_PRV) { + } else if (link->Type >= PRV) { link->Kc = y; link->Stat = status; if (status != ACTIVE) { diff --git a/src/output.c b/src/output.c index a9ed2a8..db5920b 100644 --- a/src/output.c +++ b/src/output.c @@ -19,12 +19,12 @@ AUTHOR: L. Rossman #else #include #endif - -#include "funcs.h" -#include "text.h" -#include "types.h" #include + +#include "types.h" +#include "funcs.h" #include "hash.h" +#include "text.h" /* write x[1] to x[n] to file */ size_t f_save(REAL4 *x, int n, FILE *file) { @@ -139,7 +139,7 @@ int savenetdata(EN_Project *pr) f_save(x, net->Nlinks, outFile); for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type != EN_PUMP) + if (net->Link[i].Type != PUMP) x[i] = (REAL4)(net->Link[i].Diam * pr->Ucf[DIAM]); else x[i] = 0.0f; @@ -500,7 +500,7 @@ int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) break; case VELOCITY: for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type == EN_PUMP) + if (net->Link[i].Type == PUMP) x[i] = 0.0f; else { q = ABS(hyd->LinkFlows[i]); @@ -515,9 +515,9 @@ int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) x[i] = 0.0f; else { h = hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]; - if (net->Link[i].Type != EN_PUMP) + if (net->Link[i].Type != PUMP) h = ABS(h); - if (net->Link[i].Type <= EN_PIPE) + if (net->Link[i].Type <= PIPE) x[i] = (REAL4)(1000.0 * h / net->Link[i].Len); else x[i] = (REAL4)(h * ucf); @@ -537,22 +537,22 @@ int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) double setting = hyd->LinkSetting[i]; if (setting != MISSING) switch (net->Link[i].Type) { - case EN_CVPIPE: - case EN_PIPE: + case CVPIPE: + case PIPE: x[i] = (REAL4)setting; break; - case EN_PUMP: + case PUMP: x[i] = (REAL4)setting; break; - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: x[i] = (REAL4)(setting * pr->Ucf[PRESSURE]); break; - case EN_FCV: + case FCV: x[i] = (REAL4)(setting * pr->Ucf[FLOW]); break; - case EN_TCV: + case TCV: x[i] = (REAL4)setting; break; default: @@ -573,7 +573,7 @@ int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) /* u = velocity, g = grav. accel., h = head */ /*loss, d = diam., & L = pipe length */ for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type <= EN_PIPE && ABS(hyd->LinkFlows[i]) > TINY) { + if (net->Link[i].Type <= PIPE && ABS(hyd->LinkFlows[i]) > TINY) { h = ABS(hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]); f = 39.725 * h * pow(net->Link[i].Diam, 5) / net->Link[i].Len / SQR(hyd->LinkFlows[i]); x[i] = (REAL4)f; diff --git a/src/quality.c b/src/quality.c index b58acbb..6bb82df 100644 --- a/src/quality.c +++ b/src/quality.c @@ -495,7 +495,7 @@ double findsourcequal(EN_Project *pr, int n, double volin, double volout, long t { // Concentration Source: case CONCEN: - if (net->Node[n].Type == EN_JUNCTION) + if (net->Node[n].Type == JUNCTION) { // ... source requires a negative demand at the node if (hyd->NodeDemand[n] < 0.0) diff --git a/src/qualreact.c b/src/qualreact.c index 0778ed4..54ef567 100644 --- a/src/qualreact.c +++ b/src/qualreact.c @@ -52,7 +52,7 @@ char setreactflag(EN_Project *pr) { for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type <= EN_PIPE) + if (net->Link[i].Type <= PIPE) { if (net->Link[i].Kb != 0.0 || net->Link[i].Kw != 0.0) return 1; } @@ -127,7 +127,7 @@ void reactpipes(EN_Project *pr, long dt) for (k = 1; k <= net->Nlinks; k++) { // Skip non-pipe links (pumps & valves) - if (net->Link[k].Type != EN_PIPE) continue; + if (net->Link[k].Type != PIPE) continue; rsum = 0.0; vsum = 0.0; diff --git a/src/qualroute.c b/src/qualroute.c index 4e2e836..e408628 100644 --- a/src/qualroute.c +++ b/src/qualroute.c @@ -94,7 +94,7 @@ void transport(EN_Project *pr, long tstep) } // ... if node is a junction, add on any external outflow (e.g., demands) - if (net->Node[n].Type == EN_JUNCTION) + if (net->Node[n].Type == JUNCTION) { volout += MAX(0.0, hyd->NodeDemand[n]); } @@ -204,7 +204,7 @@ double findnodequal(EN_Project *pr, int n, double volin, quality_t *qual = &pr->quality; // Node is a junction - update its water quality - if (net->Node[n].Type == EN_JUNCTION) + if (net->Node[n].Type == JUNCTION) { // ... dilute inflow with any external negative demand volin -= MIN(0.0, hyd->NodeDemand[n]) * tstep; @@ -217,7 +217,7 @@ double findnodequal(EN_Project *pr, int n, double volin, } // Node is a tank - use its mixing model to update its quality - else if (net->Node[n].Type == EN_TANK) + else if (net->Node[n].Type == TANK) { qual->NodeQual[n] = mixtank(pr, n, volin, massin, volout); } @@ -232,7 +232,7 @@ double findnodequal(EN_Project *pr, int n, double volin, { // ... quality added to network is difference between tracer // concentration (100 mg/L) and current node quality - if (net->Node[n].Type == EN_RESERVOIR) qual->SourceQual = 100.0; + if (net->Node[n].Type == RESERVOIR) qual->SourceQual = 100.0; else qual->SourceQual = MAX(100.0 - qual->NodeQual[n], 0.0); qual->NodeQual[n] = 100.0; } @@ -246,14 +246,14 @@ double findnodequal(EN_Project *pr, int n, double volin, // Combine source quality with node quality switch (net->Node[n].Type) { - case EN_JUNCTION: + case JUNCTION: qual->NodeQual[n] += qual->SourceQual; return qual->NodeQual[n]; - case EN_TANK: + case TANK: return qual->NodeQual[n] + qual->SourceQual; - case EN_RESERVOIR: + case RESERVOIR: qual->NodeQual[n] = qual->SourceQual; return qual->SourceQual; } @@ -382,21 +382,21 @@ void updatemassbalance(EN_Project *pr, int n, double massin, switch (net->Node[n].Type) { // Junctions lose mass from outflow demand & gain it from source inflow - case EN_JUNCTION: + case JUNCTION: masslost = MAX(0.0, hyd->NodeDemand[n]) * tstep * qual->NodeQual[n]; massadded = qual->SourceQual * volout; break; // Reservoirs add mass from quality source if specified or from a fixed // initial quality - case EN_RESERVOIR: + case RESERVOIR: masslost = massin; if (qual->SourceQual > 0.0) massadded = qual->SourceQual * volout; else massadded = qual->NodeQual[n] * volout; break; // Tanks add mass only from external source inflow - case EN_TANK: + case TANK: massadded = qual->SourceQual * volout; break; } @@ -664,7 +664,7 @@ void initsegs(EN_Project *pr) { qual->FirstSeg[k] = NULL; qual->LastSeg[k] = NULL; - if (net->Link[k].Type == EN_PIPE) + if (net->Link[k].Type == PIPE) { v = LINKVOL(k); j = net->Link[k].N2; diff --git a/src/report.c b/src/report.c index cbb0e11..0669f3f 100644 --- a/src/report.c +++ b/src/report.c @@ -32,13 +32,13 @@ formatted string S to the report file. #else #include #endif +#include +#include +#include "types.h" #include "funcs.h" #include "hash.h" #include "text.h" -#include "types.h" -#include -#include #undef WINDOWS #ifdef _WIN32 @@ -692,7 +692,7 @@ void writelinktable(EN_Project *pr, Pfloat *x) } /* Note if link is a pump or valve */ - if ((j = Link[i].Type) > EN_PIPE) { + if ((j = Link[i].Type) > PIPE) { strcat(s, " "); strcat(s, LinkTxt[j]); } @@ -909,12 +909,12 @@ void writestatchange(EN_Project *pr, int k, char s1, char s2) setting = LinkSetting[k]; // Link[k].Kc; switch (Link[k].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: setting *= Ucf[PRESSURE]; break; - case EN_FCV: + case FCV: setting *= Ucf[FLOW]; default: break; @@ -1243,9 +1243,9 @@ void marknodes(EN_Project *pr, int m, int *nodelist, char *marked) /* Check if valve connection is in correct direction */ switch (net->Link[k].Type) { - case EN_CVPIPE: - case EN_PRV: - case EN_PSV: + case CVPIPE: + case PRV: + case PSV: if (j == net->Link[k].N1) { continue; } diff --git a/src/rules.c b/src/rules.c index 84dbe68..d443b7e 100644 --- a/src/rules.c +++ b/src/rules.c @@ -30,10 +30,10 @@ AUTHOR: L. Rossman #include #endif +#include "types.h" #include "funcs.h" #include "hash.h" #include "text.h" -#include "types.h" enum Rulewords { r_RULE, @@ -96,20 +96,23 @@ static int newpremise(EN_Project *pr, int); static int newaction(EN_Project *pr); static int newpriority(EN_Project *pr); static int evalpremises(EN_Project *pr, int); -static void updateactlist(rules_t *rules, int, Action *); -static int checkaction(rules_t *rules, int, Action *); -static int checkpremise(EN_Project *pr, Premise *); -static int checktime(EN_Project *pr, Premise *); -static int checkstatus(EN_Project *pr, Premise *); -static int checkvalue(EN_Project *pr, Premise *); +static void updateactionlist(EN_Project *pr, int, Saction *); +static int onactionlist(EN_Project *pr, int, Saction *); +static int checkpremise(EN_Project *pr, Spremise *); +static int checktime(EN_Project *pr, Spremise *); +static int checkstatus(EN_Project *pr, Spremise *); +static int checkvalue(EN_Project *pr, Spremise *); static int takeactions(EN_Project *pr); -static void clearactlist(rules_t *rules); -static void ruleerrmsg(EN_Project *pr, int); +static void clearactionlist(rules_t *rules); static void clearrule(EN_Project *pr, int); -static void deleterule(EN_Project *pr, int); + +static void writepremise(Spremise *p, FILE *f, EN_Network *net); +static void writeaction(Saction *a, FILE *f, EN_Network *net); +static void getobjtxt(int objtype, int subtype, char *objtxt); +static void gettimetxt(double hrs, char *timetxt); -void initrules(rules_t *rules) +void initrules(EN_Project *pr) /* **-------------------------------------------------------------- ** Initializes rule base. @@ -117,8 +120,8 @@ void initrules(rules_t *rules) **-------------------------------------------------------------- */ { - rules->RuleState = r_PRIORITY; - rules->Rule = NULL; + pr->rules.RuleState = r_PRIORITY; + pr->network.Rule = NULL; } void addrule(parser_data_t *par, char *tok) @@ -142,15 +145,12 @@ int allocrules(EN_Project *pr) **-------------------------------------------------------------- */ { - rules_t *rules = &pr->rules; - parser_data_t *par = &pr->parser; + EN_Network *net = &pr->network; + int n = pr->parser.MaxRules + 1; - rules->Rule = (aRule *)calloc(par->MaxRules + 1, sizeof(aRule)); - if (rules->Rule == NULL) { - return (101); - } else { - return (0); - } + net->Rule = (Srule *)calloc(n, sizeof(Srule)); + if (net->Rule == NULL) return (101); + return 0; } void freerules(EN_Project *pr) @@ -163,7 +163,7 @@ void freerules(EN_Project *pr) { int i; for (i = 1; i <= pr->network.Nrules; i++) clearrule(pr, i); - free(pr->rules.Rule); + free(pr->network.Rule); } int checkrules(EN_Project *pr, long dt) @@ -174,38 +174,40 @@ int checkrules(EN_Project *pr, long dt) **----------------------------------------------------- */ { - EN_Network *net = &pr->network; - time_options_t *time = &pr->time_options; - rules_t *rules = &pr->rules; + EN_Network *net = &pr->network; + time_options_t *time = &pr->time_options; + rules_t *rules = &pr->rules; - int i, r; /* Number of actions actually taken */ + int i, + actionCount = 0; // Number of actions actually taken - /* Start of rule evaluation time interval */ - rules->Time1 = time->Htime - dt + 1; + // Start of rule evaluation time interval + rules->Time1 = time->Htime - dt + 1; - /* Iterate through each rule */ - rules->ActList = NULL; - r = 0; - for (i = 1; i <= net->Nrules; i++) { - /* If premises true, add THEN clauses to action list. */ - if (evalpremises(pr, i) == TRUE) { - updateactlist(rules, i, rules->Rule[i].Tchain); + // Iterate through each rule + rules->ActionList = NULL; + for (i = 1; i <= net->Nrules; i++) + { + // If premises true, add THEN clauses to action list + if (evalpremises(pr, i) == TRUE) + { + updateactionlist(pr, i, net->Rule[i].ThenActions); + } + + // If premises false, add ELSE actions to list + else + { + if (net->Rule[i].ElseActions != NULL) + { + updateactionlist(pr, i, net->Rule[i].ElseActions); + } + } } - /* If premises false, add ELSE actions to list. */ - else { - if (rules->Rule[i].Fchain != NULL) { - updateactlist(rules, i, rules->Rule[i].Fchain); - } - } - } - - /* Execute actions then clear list. */ - if (rules->ActList != NULL) { - r = takeactions(pr); - } - clearactlist(rules); - return (r); + // Execute actions then clear action list + if (rules->ActionList != NULL) actionCount = takeactions(pr); + clearactionlist(rules); + return actionCount; } int ruledata(EN_Project *pr) @@ -217,88 +219,99 @@ int ruledata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - - rules_t *rules = &pr->rules; - char **Tok = par->Tok; + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; - int key, /* Keyword code */ - err; + int key, // Keyword code + err; - /* Exit if current rule has an error */ - if (rules->RuleState == r_ERROR) - return (0); + // Exit if current rule has an error */ + if (rules->RuleState == r_ERROR) return 0; - /* Find the key word that begins the rule statement */ - err = 0; - key = findmatch(Tok[0], Ruleword); - switch (key) { - case -1: - err = 201; /* Unrecognized keyword */ - break; - case r_RULE: - net->Nrules++; - newrule(pr); - rules->RuleState = r_RULE; - break; - case r_IF: - if (rules->RuleState != r_RULE) { - err = 221; /* Mis-placed IF clause */ - break; - } - rules->RuleState = r_IF; - err = newpremise(pr,r_AND); - break; - case r_AND: - if (rules->RuleState == r_IF) - err = newpremise(pr, r_AND); - else if (rules->RuleState == r_THEN || rules->RuleState == r_ELSE) - err = newaction(pr); - else - err = 221; - break; - case r_OR: - if (rules->RuleState == r_IF) - err = newpremise(pr, r_OR); - else - err = 221; - break; - case r_THEN: - if (rules->RuleState != r_IF) { - err = 221; /* Mis-placed THEN clause */ - break; - } - rules->RuleState = r_THEN; - err = newaction(pr); - break; - case r_ELSE: - if (rules->RuleState != r_THEN) { - err = 221; /* Mis-placed ELSE clause */ - break; - } - rules->RuleState = r_ELSE; - err = newaction(pr); - break; - case r_PRIORITY: - if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE) { - err = 221; - break; - } - rules->RuleState = r_PRIORITY; - err = newpriority(pr); - break; - default: - err = 201; - } + // Find the key word that begins the rule statement + err = 0; + key = findmatch(Tok[0], Ruleword); + switch (key) + { + case -1: + err = 201; // Unrecognized keyword + break; - /* Set RuleState to r_ERROR if errors found */ - if (err) { - rules->RuleState = r_ERROR; - ruleerrmsg(pr,err); - err = 200; - } - return (err); + case r_RULE: + net->Nrules++; + newrule(pr); + rules->RuleState = r_RULE; + rules->Errcode = 0; + break; + + case r_IF: + if (rules->RuleState != r_RULE) + { + err = 221; // Mis-placed IF clause + break; + } + rules->RuleState = r_IF; + err = newpremise(pr, r_AND); + break; + + case r_AND: + if (rules->RuleState == r_IF) err = newpremise(pr, r_AND); + else if (rules->RuleState == r_THEN || rules->RuleState == r_ELSE) + { + err = newaction(pr); + } + else err = 221; + break; + + case r_OR: + if (rules->RuleState == r_IF) err = newpremise(pr, r_OR); + else err = 221; + break; + + case r_THEN: + if (rules->RuleState != r_IF) + { + err = 221; // Mis-placed THEN clause + break; + } + rules->RuleState = r_THEN; + err = newaction(pr); + break; + + case r_ELSE: + if (rules->RuleState != r_THEN) + { + err = 221; // Mis-placed ELSE clause + break; + } + rules->RuleState = r_ELSE; + err = newaction(pr); + break; + + case r_PRIORITY: + if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE) + { + err = 221; + break; + } + rules->RuleState = r_PRIORITY; + err = newpriority(pr); + break; + + default: + err = 201; + } + + // Set RuleState to r_ERROR if errors found + if (err) + { + rules->RuleState = r_ERROR; + rules->Errcode = err; + err = 200; + } + return err; } @@ -313,14 +326,14 @@ void adjustrules(EN_Project *pr, int objtype, int index) int i, delete; EN_Network *net = &pr->network; rules_t *rules = &pr->rules; - Premise *p; - Action *a; + Spremise *p; + Saction *a; // Delete rules that refer to objtype and index for (i = net->Nrules; i >= 1; i--) { delete = FALSE; - p = rules->Rule[i].Pchain; + p = net->Rule[i].Premises; while (p != NULL && !delete) { if (objtype == p->object && p->index == index) delete = TRUE; @@ -328,13 +341,13 @@ void adjustrules(EN_Project *pr, int objtype, int index) } if (objtype == r_LINK) { - a = rules->Rule[i].Tchain; + a = net->Rule[i].ThenActions; while (a != NULL && !delete) { if (a->link == index) delete = TRUE; a = a->next; } - a = rules->Rule[i].Fchain; + a = net->Rule[i].ElseActions; while (a != NULL && !delete) { if (a->link == index) delete = TRUE; @@ -347,7 +360,7 @@ void adjustrules(EN_Project *pr, int objtype, int index) // Adjust all higher object indices to reflect deletion of object index for (i = 1; i <= net->Nrules; i++) { - p = rules->Rule[i].Pchain; + p = net->Rule[i].Premises; while (p != NULL) { if (objtype == p->object && p->index > index) p->index--; @@ -355,13 +368,13 @@ void adjustrules(EN_Project *pr, int objtype, int index) } if (objtype == r_LINK) { - a = rules->Rule[i].Tchain; + a = net->Rule[i].ThenActions; while (a != NULL) { if (a->link > index) a->link--; a = a->next; } - a = rules->Rule[i].Fchain; + a = net->Rule[i].ElseActions; while (a != NULL) { if (a->link > index) a->link--; @@ -371,31 +384,6 @@ void adjustrules(EN_Project *pr, int objtype, int index) } } -void adjusttankrules(EN_Project *pr) -/* -**----------------------------------------------------------- -** Adjusts tank indices in rule premises. -** Called by EN_addnode in EPANET.C. -**----------------------------------------------------------- -*/ -{ - int i, njuncs; - EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; - Premise *p; - - njuncs = net->Njuncs; - for (i = 1; i <= net->Nrules; i++) - { - p = rules->Rule[i].Pchain; - while (p != NULL) - { - if (p->object == r_NODE && p->index > njuncs) p->index++; - p = p->next; - } - } -} - void deleterule(EN_Project *pr, int index) /* **----------------------------------------------------------- @@ -405,8 +393,7 @@ void deleterule(EN_Project *pr, int index) { int i; EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; - aRule *lastRule; + Srule *lastRule; // Free memory allocated to rule's premises & actions clearrule(pr, index); @@ -414,34 +401,59 @@ void deleterule(EN_Project *pr, int index) // Shift position of higher indexed rules down one for (i = index; i <= net->Nrules - 1; i++) { - rules->Rule[i] = rules->Rule[i + 1]; + net->Rule[i] = net->Rule[i + 1]; } // Remove premises & actions from last (inactive) entry in Rule array - lastRule = &rules->Rule[net->Nrules]; - lastRule->Pchain = NULL; - lastRule->Tchain = NULL; - lastRule->Fchain = NULL; + lastRule = &net->Rule[net->Nrules]; + lastRule->Premises = NULL; + lastRule->ThenActions = NULL; + lastRule->ElseActions = NULL; // Reduce active rule count by one net->Nrules--; } -void clearactlist(rules_t *rules) +void adjusttankrules(EN_Project *pr) +/* +**----------------------------------------------------------- +** Adjusts tank indices in rule premises. +** Called by EN_addnode in EPANET.C. +**----------------------------------------------------------- +*/ +{ + int i, njuncs; + EN_Network *net = &pr->network; + Spremise *p; + + njuncs = net->Njuncs; + for (i = 1; i <= net->Nrules; i++) + { + p = net->Rule[i].Premises; + while (p != NULL) + { + if (p->object == r_NODE && p->index > njuncs) p->index++; + p = p->next; + } + } +} + +void clearactionlist(rules_t *rules) /* **---------------------------------------------------------- ** Clears memory used for action list **---------------------------------------------------------- */ { - ActItem *a; - ActItem *anext; - a = rules->ActList; - while (a != NULL) { - anext = a->next; - free(a); - a = anext; - } + SactionList *nextItem; + SactionList *actionItem; + actionItem = rules->ActionList; + while (actionItem != NULL) + { + nextItem = actionItem->next; + free(actionItem); + actionItem = nextItem; + } } void clearrule(EN_Project *pr, int i) @@ -451,29 +463,32 @@ void clearrule(EN_Project *pr, int i) **----------------------------------------------------------- */ { - rules_t *rules = &pr->rules; - Premise *p; - Premise *pnext; - Action *a; - Action *anext; + EN_Network *net = &pr->network; + Spremise *p; + Spremise *pnext; + Saction *a; + Saction *anext; - p = rules->Rule[i].Pchain; - while (p != NULL) { - pnext = p->next; - free(p); - p = pnext; + p = net->Rule[i].Premises; + while (p != NULL) + { + pnext = p->next; + free(p); + p = pnext; } - a = rules->Rule[i].Tchain; - while (a != NULL) { - anext = a->next; - free(a); - a = anext; + a = net->Rule[i].ThenActions; + while (a != NULL) + { + anext = a->next; + free(a); + a = anext; } - a = rules->Rule[i].Fchain; - while (a != NULL) { - anext = a->next; - free(a); - a = anext; + a = net->Rule[i].ElseActions; + while (a != NULL) + { + anext = a->next; + free(a); + a = anext; } } @@ -484,18 +499,18 @@ void newrule(EN_Project *pr) **---------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - strncpy(rules->Rule[net->Nrules].label, Tok[1], MAXID); - rules->Rule[net->Nrules].Pchain = NULL; - rules->Rule[net->Nrules].Tchain = NULL; - rules->Rule[net->Nrules].Fchain = NULL; - rules->Rule[net->Nrules].priority = 0.0; - rules->Plast = NULL; - rules->Tlast = NULL; - rules->Flast = NULL; + EN_Network *net = &pr->network; + char **Tok = pr->parser.Tok; + Srule *rule = &net->Rule[net->Nrules]; + + strncpy(rule->label, Tok[1], MAXID); + rule->Premises = NULL; + rule->ThenActions = NULL; + rule->ElseActions = NULL; + rule->priority = 0.0; + pr->rules.LastPremise = NULL; + pr->rules.LastThenAction = NULL; + pr->rules.LastElseAction = NULL; } int newpremise(EN_Project *pr, int logop) @@ -519,7 +534,7 @@ int newpremise(EN_Project *pr, int logop) int i, j, k, m, r, s, v; double x; - Premise *p; + Spremise *p; /* Check for correct number of tokens */ if (par->Ntokens != 5 && par->Ntokens != 6) @@ -638,7 +653,7 @@ int newpremise(EN_Project *pr, int logop) } /* Create new premise structure */ - p = (Premise *)malloc(sizeof(Premise)); + p = (Spremise *)malloc(sizeof(Spremise)); if (p == NULL) return (101); p->object = i; @@ -651,11 +666,11 @@ int newpremise(EN_Project *pr, int logop) /* Add premise to current rule's premise list */ p->next = NULL; - if (rules->Plast == NULL) - rules->Rule[net->Nrules].Pchain = p; + if (rules->LastPremise == NULL) + net->Rule[net->Nrules].Premises = p; else - rules->Plast->next = p; - rules->Plast = p; + rules->LastPremise->next = p; + rules->LastPremise = p; return (0); } @@ -678,7 +693,7 @@ int newaction(EN_Project *pr) int j, k, s; double x; - Action *a; + Saction *a; /* Check for correct number of tokens */ if (par->Ntokens != 6) @@ -691,7 +706,7 @@ int newaction(EN_Project *pr) /*** Updated 9/7/00 ***/ /* Cannot control a CV */ - if (net->Link[j].Type == EN_CVPIPE) + if (net->Link[j].Type == CVPIPE) return (207); /* Find value for status or setting */ @@ -710,12 +725,12 @@ int newaction(EN_Project *pr) /*** Updated 9/7/00 ***/ /* Cannot change setting for a GPV ***/ - if (x != MISSING && net->Link[j].Type == EN_GPV) + if (x != MISSING && net->Link[j].Type == GPV) return (202); /*** Updated 3/1/01 ***/ /* Set status for pipe in case setting was specified */ - if (x != MISSING && net->Link[j].Type == EN_PIPE) { + if (x != MISSING && net->Link[j].Type == PIPE) { if (x == 0.0) s = IS_CLOSED; else @@ -724,7 +739,7 @@ int newaction(EN_Project *pr) } /* Create a new action structure */ - a = (Action *)malloc(sizeof(Action)); + a = (Saction *)malloc(sizeof(Saction)); if (a == NULL) return (101); a->link = j; @@ -734,18 +749,18 @@ int newaction(EN_Project *pr) /* Add action to current rule's action list */ if (rules->RuleState == r_THEN) { a->next = NULL; - if (rules->Tlast == NULL) - rules->Rule[net->Nrules].Tchain = a; + if (rules->LastThenAction == NULL) + net->Rule[net->Nrules].ThenActions = a; else - rules->Tlast->next = a; - rules->Tlast = a; + rules->LastThenAction->next = a; + rules->LastThenAction = a; } else { a->next = NULL; - if (rules->Flast == NULL) - rules->Rule[net->Nrules].Fchain = a; + if (rules->LastElseAction == NULL) + net->Rule[net->Nrules].ElseActions = a; else - rules->Flast->next = a; - rules->Flast = a; + rules->LastElseAction->next = a; + rules->LastElseAction = a; } return (0); } @@ -757,18 +772,54 @@ int newpriority(EN_Project *pr) **--------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - - double x; - if (!getfloat(Tok[1], &x)) - return (202); - rules->Rule[net->Nrules].priority = x; - return (0); + EN_Network *net = &pr->network; + char **Tok = pr->parser.Tok; + double x; + + if (!getfloat(Tok[1], &x)) return (202); + net->Rule[net->Nrules].priority = x; + return 0; } +Spremise *getpremise(Spremise *premises, int i) +/* +**---------------------------------------------------------- +** Return the i-th premise in a rule +**---------------------------------------------------------- +*/ +{ + int count = 0; + Spremise *p; + p = premises; + while (p != NULL) + { + count++; + if (count == i) break; + p = p->next; + } + return p; +} + +Saction *getaction(Saction *actions, int i) +/* +**---------------------------------------------------------- +** Return the i-th action from a rule's action list +**---------------------------------------------------------- +*/ +{ + int count = 0; + Saction *a; + a = actions; + while (a != NULL) + { + count++; + if (count == i) break; + a = a->next; + } + return a; +} + + int evalpremises(EN_Project *pr, int i) /* **---------------------------------------------------------- @@ -776,112 +827,108 @@ int evalpremises(EN_Project *pr, int i) **---------------------------------------------------------- */ { - rules_t *rules = &pr->rules; - - int result; - Premise *p; + EN_Network *net = &pr->network; + int result; + Spremise *p; - result = TRUE; - p = rules->Rule[i].Pchain; - while (p != NULL) { - if (p->logop == r_OR) { - if (result == FALSE) { - result = checkpremise(pr,p); - } - } else { - if (result == FALSE) - return (FALSE); - result = checkpremise(pr,p); + result = TRUE; + p = net->Rule[i].Premises; + while (p != NULL) + { + if (p->logop == r_OR) + { + if (result == FALSE) result = checkpremise(pr, p); + } + else + { + if (result == FALSE) return (FALSE); + result = checkpremise(pr, p); + } + p = p->next; } - p = p->next; - } - return (result); + return result; } -int checkpremise(EN_Project *pr, Premise *p) +int checkpremise(EN_Project *pr, Spremise *p) /* **---------------------------------------------------------- ** Checks if a particular premise is true **---------------------------------------------------------- */ { - if (p->variable == r_TIME || p->variable == r_CLOCKTIME) - return (checktime(pr,p)); - else if (p->status > IS_NUMBER) - return (checkstatus(pr,p)); - else - return (checkvalue(pr,p)); + if (p->variable == r_TIME || + p->variable == r_CLOCKTIME) return (checktime(pr,p)); + else if (p->status > IS_NUMBER) return (checkstatus(pr,p)); + else return (checkvalue(pr,p)); } -int checktime(EN_Project *pr, Premise *p) +int checktime(EN_Project *pr, Spremise *p) /* **------------------------------------------------------------ ** Checks if condition on system time holds **------------------------------------------------------------ */ { - time_options_t *time = &pr->time_options; - rules_t *rules = &pr->rules; - - char flag; - long t1, t2, x; + time_options_t *time = &pr->time_options; + rules_t *rules = &pr->rules; + char flag; + long t1, t2, x; - /* Get start and end of rule evaluation time interval */ - if (p->variable == r_TIME) { - t1 = rules->Time1; - t2 = time->Htime; - } - else if (p->variable == r_CLOCKTIME) { - t1 = (rules->Time1 + time->Tstart) % SECperDAY; - t2 = (time->Htime + time->Tstart) % SECperDAY; - } else - return (0); - - /* Test premise's time */ - x = (long)(p->value); - switch (p->relop) { - /* For inequality, test against current time */ - case LT: - if (t2 >= x) - return (0); - break; - case LE: - if (t2 > x) - return (0); - break; - case GT: - if (t2 <= x) - return (0); - break; - case GE: - if (t2 < x) - return (0); - break; - - /* For equality, test if within interval */ - case EQ: - case NE: - flag = FALSE; - if (t2 < t1) /* E.g., 11:00 am to 1:00 am */ + // Get start and end of rule evaluation time interval + if (p->variable == r_TIME) { - if (x >= t1 || x <= t2) - flag = TRUE; - } else { - if (x >= t1 && x <= t2) - flag = TRUE; + t1 = rules->Time1; + t2 = time->Htime; + } + else if (p->variable == r_CLOCKTIME) + { + t1 = (rules->Time1 + time->Tstart) % SECperDAY; + t2 = (time->Htime + time->Tstart) % SECperDAY; } - if (p->relop == EQ && flag == FALSE) - return (0); - if (p->relop == NE && flag == TRUE) - return (0); - break; - } + else return (0); - /* If we get to here then premise was satisfied */ - return (1); + // Test premise's time + x = (long)(p->value); + switch (p->relop) + { + // For inequality, test against current time + case LT: + if (t2 >= x) return (0); + break; + case LE: + if (t2 > x) return (0); + break; + case GT: + if (t2 <= x) return (0); + break; + case GE: + if (t2 < x) return (0); + break; + + // For equality, test if within interval + case EQ: + case NE: + flag = FALSE; + if (t2 < t1) // E.g., 11:00 am to 1:00 am + { + if (x >= t1 || x <= t2) + flag = TRUE; + } + else + { + if (x >= t1 && x <= t2) + flag = TRUE; + } + if (p->relop == EQ && flag == FALSE) return (0); + if (p->relop == NE && flag == TRUE) return (0); + break; + } + + // If we get to here then premise was satisfied + return 1; } -int checkstatus(EN_Project *pr, Premise *p) +int checkstatus(EN_Project *pr, Spremise *p) /* **------------------------------------------------------------ ** Checks if condition on link status holds @@ -911,7 +958,7 @@ int checkstatus(EN_Project *pr, Premise *p) return (0); } -int checkvalue(EN_Project *pr, Premise *p) +int checkvalue(EN_Project *pr, Spremise *p) /* **---------------------------------------------------------- ** Checks if numerical condition on a variable is true. @@ -963,12 +1010,12 @@ int checkvalue(EN_Project *pr, Premise *p) return (0); x = LinkSetting[i]; switch (Link[i].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: x = x * Ucf[PRESSURE]; break; - case EN_FCV: + case FCV: x = x * Ucf[FLOW]; break; default: @@ -1027,63 +1074,75 @@ int checkvalue(EN_Project *pr, Premise *p) return (1); } -void updateactlist(rules_t *rules, int i, Action *actions) +void updateactionlist(EN_Project *pr, int i, Saction *actions) /* **--------------------------------------------------- ** Adds rule's actions to action list **--------------------------------------------------- */ { - ActItem *item; - Action *a; + rules_t *rules = &pr->rules; + SactionList *actionItem; + Saction *a; - /* Iterate through each action of Rule i */ - a = actions; - while (a != NULL) { - /* Add action to list if not already on it */ - if (!checkaction(rules, i, a)) { - item = (ActItem *)malloc(sizeof(ActItem)); - if (item != NULL) { - item->action = a; - item->ruleindex = i; - item->next = rules->ActList; - rules->ActList = item; - } + // Iterate through each action of Rule i + a = actions; + while (a != NULL) + { + // Add action to list if its link not already on it + if (!onactionlist(pr, i, a)) + { + actionItem = (SactionList *)malloc(sizeof(SactionList)); + if (actionItem != NULL) + { + actionItem->action = a; + actionItem->ruleIndex = i; + actionItem->next = rules->ActionList; + rules->ActionList = actionItem; + } + } + a = a->next; } - a = a->next; - } } -int checkaction(rules_t *rules, int i, Action *a) +int onactionlist(EN_Project *pr, int i, Saction *a) /* -**----------------------------------------------------------- -** Checks if an action is already on the Action List -**----------------------------------------------------------- +**----------------------------------------------------------------------------- +** Checks if action a from rule i can be added to the action list +**----------------------------------------------------------------------------- */ { - int i1, k, k1; - ActItem *item; - Action *a1; + int link, i1; + EN_Network *net = &pr->network; + SactionList *actionItem; + Saction *a1; - /* Search action list for link named in action */ - k = a->link; /* Action applies to link k */ - item = rules->ActList; - while (item != NULL) { - a1 = item->action; - i1 = item->ruleindex; - k1 = a1->link; + // Search action list for link included in action a + link = a->link; + actionItem = pr->rules.ActionList; + while (actionItem != NULL) + { + a1 = actionItem->action; + i1 = actionItem->ruleIndex; - /* If link on list then replace action if rule has higher priority. */ - if (k1 == k) { - if (rules->Rule[i].priority > rules->Rule[i1].priority) { - item->action = a; - item->ruleindex = i; - } - return (1); + // Link appears in list + if (link == a1->link) + { + // Replace it's action with 'a' if rule i has higher priority + if (net->Rule[i].priority > net->Rule[i1].priority) + { + actionItem->action = a; + actionItem->ruleIndex = i; + } + + // Return indicating that 'a' should not be added to action list + return 1; + } + actionItem = actionItem->next; } - item = item->next; - } - return (0); + + // Return indicating that it's ok to add 'a' to the action list + return 0; } int takeactions(EN_Project *pr) @@ -1094,22 +1153,22 @@ int takeactions(EN_Project *pr) */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; report_options_t *rep = &pr->report; - rules_t *rules = &pr->rules; + rules_t *rules = &pr->rules; - Action *a; - ActItem *item; + Saction *a; + SactionList *actionItem; char flag; int k, s, n; double tol = 1.e-3, v, x; n = 0; - item = rules->ActList; - while (item != NULL) { + actionItem = rules->ActionList; + while (actionItem != NULL) { flag = FALSE; - a = item->action; + a = actionItem->action; k = a->link; s = hyd->LinkStatus[k]; v = hyd->LinkSetting[k]; @@ -1130,12 +1189,12 @@ int takeactions(EN_Project *pr) /* Change link's setting */ else if (x != MISSING) { switch (net->Link[k].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: + case PRV: + case PSV: + case PBV: x = x / pr->Ucf[PRESSURE]; break; - case EN_FCV: + case FCV: x = x / pr->Ucf[FLOW]; break; default: @@ -1151,384 +1210,251 @@ int takeactions(EN_Project *pr) if (flag == TRUE) { n++; if (rep->Statflag) - writeruleaction(pr, k, rules->Rule[item->ruleindex].label); + writeruleaction(pr, k, net->Rule[actionItem->ruleIndex].label); } /* Move to next action on list */ - item = item->next; + actionItem = actionItem->next; } return (n); } -void ruleerrmsg(EN_Project *pr, int err) +void ruleerrmsg(EN_Project *pr) /* **----------------------------------------------------------- -** Reports error message +** Report a rule parsing error message **----------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; - int i; - char label[81]; - char fmt[256]; - switch (err) { - case 201: - strcpy(fmt, R_ERR201); - break; - case 202: - strcpy(fmt, R_ERR202); - break; - case 203: - strcpy(fmt, R_ERR203); - break; - case 204: - strcpy(fmt, R_ERR204); - break; + int i; + char label[MAXMSG+1]; + char msg[MAXLINE+1]; - /*** Updated on 9/7/00 ***/ - case 207: - strcpy(fmt, R_ERR207); - break; + // Get text of error message + switch (rules->Errcode) + { + case 201: + strcpy(msg, R_ERR201); + break; + case 202: + strcpy(msg, R_ERR202); + break; + case 203: + strcpy(msg, R_ERR203); + break; + case 204: + strcpy(msg, R_ERR204); + break; + case 207: + strcpy(msg, R_ERR207); + break; + case 221: + strcpy(msg, R_ERR221); + break; + default: + return; + } - case 221: - strcpy(fmt, R_ERR221); - break; - default: - return; - } - if (net->Nrules > 0) { - strcpy(label, t_RULE); - strcat(label, " "); - strcat(label, rules->Rule[net->Nrules].label); - } else - strcpy(label, t_RULES_SECT); - sprintf(pr->Msg, "%s", fmt); - strcat(pr->Msg, label); - strcat(pr->Msg, ":"); - writeline(pr, pr->Msg); - strcpy(fmt, Tok[0]); - for (i = 1; i < par->Ntokens; i++) { - strcat(fmt, " "); - strcat(fmt, Tok[i]); - } - writeline(pr, fmt); + // Get label of rule being parsed + if (net->Nrules > 0) + { + strcpy(label, t_RULE); + strcat(label, " "); + strcat(label, net->Rule[net->Nrules].label); + } + else strcpy(label, t_RULES_SECT); + + // Write rule label and error message to status report + sprintf(pr->Msg, "%s", msg); + strcat(pr->Msg, label); + strcat(pr->Msg, ":"); + writeline(pr, pr->Msg); + + // Write text of rule clause being parsed to status report + strcpy(msg, Tok[0]); + for (i = 1; i < par->Ntokens; i++) + { + strcat(msg, " "); + strcat(msg, Tok[i]); + } + writeline(pr, msg); } -int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx) { - /* - **----------------------------------------------------------- - ** This function writes a specific rule (rule ID, - ** premises, true and false actions and prioriry in the - ** text input file. - ** INPUT: - ** - FILE *f : pointer to the .inp file to be written - ** - int RuleIdx : index of the rule that needs to be written - ** OUTPUT: error code - **----------------------------------------------------------- - */ - EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; - - Slink *Link = net->Link; - Stank *Tank = net->Tank; - Snode *Node = net->Node; - - // int i,j; - Premise *p; - Action *a; - int hours = 0, minutes = 0, seconds = 0; +int writerule(EN_Project *pr, FILE *f, int ruleIndex) +//----------------------------------------------------------------------------- +// Write a rule to an INP file. +//----------------------------------------------------------------------------- +{ + EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + Srule *rule = &net->Rule[ruleIndex]; + Spremise *p; + Saction *a; - // the first condition/premises is different from the others because it starts - // with IF (but it is kept in memory as AND) - p = rules->Rule[RuleIdx].Pchain; - if (p->value == MISSING) { - fprintf(f, "\nIF "); - if ((strncmp(Object[p->object], "NODE", 4) == 0) || - (strncmp(Object[p->object], "Junc", 4) == 0) || - (strncmp(Object[p->object], "Reser", 5) == 0) || - (strncmp(Object[p->object], "Tank", 4) == 0)) { - if (p->index <= net->Njuncs) - fprintf(f, "JUNCTION %s %s %s %s", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Tank[p->index - net->Njuncs].A == 0.0) - fprintf(f, "RESERVOIR %s %s %s %s", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else - fprintf(f, "TANK %s %s %s %s", Node[p->index].ID, Varword[p->variable], - Operator[p->relop], Value[p->status]); - } else { // it is a link - if (Link[p->index].Type == EN_PIPE || Link[p->index].Type == EN_CVPIPE) - fprintf(f, "PIPE %s %s %s %s", Link[p->index].ID, Varword[p->variable], - Operator[p->relop], Value[p->status]); - else if (Link[p->index].Type == EN_PUMP) - fprintf(f, "PUMP %s %s %s %s", Link[p->index].ID, Varword[p->variable], - Operator[p->relop], Value[p->status]); - else - fprintf(f, "VALVE %s %s %s %s", Link[p->index].ID, Varword[p->variable], - Operator[p->relop], Value[p->status]); + // Write each premise clause to the file + p = rule->Premises; + fprintf(f, "\nIF "); + while (p != NULL) + { + writepremise(p, f, net); + p = p->next; + if (p) fprintf(f, "\n%-5s", Ruleword[p->logop]); } - } else { - if (p->variable == r_TIME) { - hours = (int)p->value / 3600; - minutes = (int)((p->value - 3600 * hours) / 60); - seconds = (int)(p->value - 3600 * hours - minutes * 60); - fprintf(f, "\nIF %s %s %s %d:%02d:%02d", Object[p->object], - Varword[p->variable], Operator[p->relop], hours, minutes, - seconds); - } else { - if (p->variable == r_CLOCKTIME) { - hours = (int)p->value / 3600; - minutes = (int)((p->value - 3600 * hours) / 60); - seconds = (int)(p->value - 3600 * hours - minutes * 60); - if (hours < 12) - fprintf(f, "\nIF %s %s %s %d:%02d:%02d AM", Object[p->object], - Varword[p->variable], Operator[p->relop], hours, minutes, - seconds); - else - fprintf(f, "\nIF %s %s %s %d:%02d:%02d PM", Object[p->object], - Varword[p->variable], Operator[p->relop], hours - 12, minutes, - seconds); - } else { - if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) - fprintf(f, "\nIF TANK %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value / 3600.0); - else { - fprintf(f, "\nIF "); - if ((strncmp(Object[p->object], "NODE", 4) == 0) || - (strncmp(Object[p->object], "Junc", 4) == 0) || - (strncmp(Object[p->object], "Reser", 5) == 0) || - (strncmp(Object[p->object], "Tank", 4) == 0)) { - if (p->index <= net->Njuncs) - fprintf(f, "JUNCTION %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else if (Tank[p->index - net->Njuncs].A == 0.0) - fprintf(f, "RESERVOIR %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else - fprintf(f, "TANK %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - } else { // it is a link - if (Link[p->index].Type == EN_PIPE || - Link[p->index].Type == EN_CVPIPE) - fprintf(f, "PIPE %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else if (Link[p->index].Type == EN_PUMP) - fprintf(f, "PUMP %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else - fprintf(f, "VALVE %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - } - } - } + // Write each THEN action clause to the file + a = rule->ThenActions; + if (a) fprintf(f, "\nTHEN "); + while (a != NULL) + { + writeaction(a, f, net); + a = a->next; + if (a) fprintf(f, "\nAND "); } - } - p = p->next; - while (p != NULL) // for all other premises/conditions write the corresponding - // logicOperator - { - if (p->value == MISSING) { - fprintf(f, "\n%s ", Ruleword[p->logop]); - if ((strncmp(Object[p->object], "NODE", 4) == 0) || - (strncmp(Object[p->object], "Junc", 4) == 0) || - (strncmp(Object[p->object], "Reser", 5) == 0) || - (strncmp(Object[p->object], "Tank", 4) == 0)) { - if (p->index <= net->Njuncs) - fprintf(f, "JUNCTION %s %s %s %s", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Tank[p->index - net->Njuncs].A == 0.0) - fprintf(f, "RESERVOIR %s %s %s %s", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else - fprintf(f, "TANK %s %s %s %s", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - } else { // it is a link - if (Link[p->index].Type == EN_PIPE || Link[p->index].Type == EN_CVPIPE) - fprintf(f, "PIPE %s %s %s %s", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Link[p->index].Type == EN_PUMP) - fprintf(f, "PUMP %s %s %s %s", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - else - fprintf(f, "VALVE %s %s %s %s", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], Value[p->status]); - } - } else { - if (p->variable == r_TIME) { - hours = (int)p->value / 3600; - minutes = (int)((p->value - 3600 * hours) / 60); - seconds = (int)(p->value - 3600 * hours - minutes * 60); - fprintf(f, "\n%s %s %s %s %d:%02d:%02d", Ruleword[p->logop], - Object[p->object], Varword[p->variable], Operator[p->relop], - hours, minutes, seconds); - } else { - if (p->variable == r_CLOCKTIME) { - hours = (int)p->value / 3600; - minutes = (int)((p->value - 3600 * hours) / 60); - seconds = (int)(p->value - 3600 * hours - minutes * 60); - - if (hours < 12) - fprintf(f, "\n%s %s %s %s %d:%02d:%02d AM", Ruleword[p->logop], - Object[p->object], Varword[p->variable], Operator[p->relop], - hours, minutes, seconds); - else - fprintf(f, "\n%s %s %s %s %d:%02d:%02d PM", Ruleword[p->logop], - Object[p->object], Varword[p->variable], Operator[p->relop], - hours - 12, minutes, seconds); - } else { - if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) - fprintf(f, "\n%s TANK %s %s %s %s %.4lf", Ruleword[p->logop], - Object[p->object], Node[p->index].ID, Varword[p->variable], - Operator[p->relop], p->value / 3600.0); - else { - fprintf(f, "\n%s ", Ruleword[p->logop]); - if ((strncmp(Object[p->object], "NODE", 4) == 0) || - (strncmp(Object[p->object], "Junc", 4) == 0) || - (strncmp(Object[p->object], "Reser", 5) == 0) || - (strncmp(Object[p->object], "Tank", 4) == 0)) { - if (p->index <= net->Njuncs) - fprintf(f, "JUNCTION %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else if (Tank[p->index - net->Njuncs].A == 0.0) - fprintf(f, "RESERVOIR %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else - fprintf(f, "TANK %s %s %s %.4lf", Node[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - } else { // it is a link - if (Link[p->index].Type == EN_PIPE || - Link[p->index].Type == EN_CVPIPE) - fprintf(f, "PIPE %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else if (Link[p->index].Type == EN_PUMP) - fprintf(f, "PUMP %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - else - fprintf(f, "VALVE %s %s %s %.4lf", Link[p->index].ID, - Varword[p->variable], Operator[p->relop], p->value); - } - } - } - } + // Write each ELSE action clause to the file + a = rule->ElseActions; + if (a) fprintf(f, "\nELSE "); + while (a != NULL) + { + writeaction(a, f, net); + a = a->next; + if (a) fprintf(f, "\nAND "); } - p = p->next; - } - a = rules->Rule[RuleIdx].Tchain; // The first action in hte list of true actions - // starts with THEN - if (a->setting == MISSING) { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nTHEN PIPE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nTHEN PUMP %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); + // Write the rule's priority to the file + if (rule->priority > 0) fprintf(f, "\nPRIORITY %f", rule->priority); + return 0; +} + +void writepremise(Spremise *p, FILE *f, EN_Network *net) +//----------------------------------------------------------------------------- +// Write a rule's premise clause to an INP file. +//----------------------------------------------------------------------------- +{ + char s_obj[20]; + char s_id[MAXID + 1]; + char s_value[20]; + int subtype; + + // Get the type name & ID of object referred to in the premise + if (p->object == r_NODE) + { + subtype = net->Node[p->index].Type; + getobjtxt(r_NODE, subtype, s_obj); + strcpy(s_id, net->Node[p->index].ID); + } + else if (p->object == r_LINK) + { + subtype = net->Link[p->index].Type; + getobjtxt(r_LINK, subtype, s_obj); + strcpy(s_id, net->Link[p->index].ID); + } else - fprintf(f, "\nTHEN VALVE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - } else { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nTHEN PIPE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nTHEN PUMP %s SETTING IS %.4f", Link[a->link].ID, - a->setting); + { + strcpy(s_obj, "SYSTEM"); + strcpy(s_id, ""); + } + + // If premise has no value field, use it's status field as a value + if (p->value == MISSING) strcpy(s_value, Value[p->status]); + + // Otherwise get text of premise's value field else - fprintf(f, "\nTHEN VALVE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - } - - a = a->next; // The other actions in the list of true actions start with AND - while (a != NULL) { - if (a->setting == MISSING) { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else - fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - } else { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else - fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); + { + // For time values convert from seconds to hr:min:sec + switch (p->variable) + { + case r_CLOCKTIME: + case r_DRAINTIME: + case r_FILLTIME: + case r_TIME: + gettimetxt(p->value, s_value); + break; + default: sprintf(s_value, "%.4f", p->value); + } } - a = a->next; - } + // Write the premise clause to the file + fprintf(f, "%s %s %s %s %s", s_obj, s_id, Varword[p->variable], + Operator[p->relop], s_value); +} - a = rules->Rule[RuleIdx].Fchain; // The first action in the list of false actions - // starts with ELSE - if (a != NULL) { - if (a->setting == MISSING) { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nELSE PIPE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nELSE PUMP %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else - fprintf(f, "\nELSE VALVE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - } else { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nELSE PIPE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nELSE PUMP %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else - fprintf(f, "\nELSE VALVE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); + +void writeaction(Saction *a, FILE *f, EN_Network *net) +//----------------------------------------------------------------------------- +// Write a rule's action clause to an INP file. +//----------------------------------------------------------------------------- +{ + char s_id[MAXID + 1]; + char s_obj[20]; + char s_var[20]; + char s_value[20]; + int subtype; + + subtype = net->Link[a->link].Type; + getobjtxt(r_LINK, subtype, s_obj); + strcpy(s_id, net->Link[a->link].ID); + if (a->setting == MISSING) + { + strcpy(s_var, "STATUS"); + strcpy(s_value, Value[a->status]); } - - a = a->next; // The other actions in the list of false actions start with - // AND - while (a != NULL) { - if (a->setting == MISSING) { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - else - fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, - Value[a->status]); - } else { - if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) - fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else if (Link[a->link].Type == EN_PUMP) - fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - else - fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, - a->setting); - } - - a = a->next; + else + { + strcpy(s_var, "SETTING"); + sprintf(s_value, "%.4f", a->setting); } - } - if (rules->Rule[RuleIdx].priority != 0) - fprintf(f, "\nPRIORITY %.4f", rules->Rule[RuleIdx].priority); + fprintf(f, "%s %s %s = %s", s_obj, s_id, s_var, s_value); +} - return (0); +void getobjtxt(int objtype, int subtype, char *objtxt) +//----------------------------------------------------------------------------- +// Retrieve the text label for a specific type of object. +//----------------------------------------------------------------------------- +{ + if (objtype == r_NODE) + { + switch (subtype) + { + case JUNCTION: strcpy(objtxt, "JUNCTION"); break; + case RESERVOIR: strcpy(objtxt, "RESERVOIR"); break; + case TANK: strcpy(objtxt, "TANK"); break; + default: strcpy(objtxt, "NODE"); + } + } + else if (objtype == r_LINK) + { + switch (subtype) + { + case CVPIPE: + case PIPE: strcpy(objtxt, "PIPE"); break; + case PUMP: strcpy(objtxt, "PUMP"); break; + default: strcpy(objtxt, "VALVE"); + } + } + else strcpy(objtxt, "SYSTEM"); +} + +void gettimetxt(double secs, char *timetxt) +//----------------------------------------------------------------------------- +// Convert number of seconds to a text string in hrs:min:sec format. +//----------------------------------------------------------------------------- +{ + int hours = 0, minutes = 0, seconds = 0; + hours = (int)secs / 3600; + if (hours > 24 * 7) sprintf(timetxt, "%.4f", secs / 3600.0); + else + { + minutes = (int)((secs - 3600 * hours) / 60); + seconds = (int)(secs - 3600 * hours - minutes * 60); + sprintf(timetxt, "%d:%02d:%02d", hours, minutes, seconds); + } } /***************** END OF RULES.C ******************/ diff --git a/src/types.h b/src/types.h index 75bcca0..66d895f 100755 --- a/src/types.h +++ b/src/types.h @@ -20,7 +20,7 @@ AUTHOR: L. Rossman #ifndef TYPES_H #define TYPES_H -#include "epanet2.h" +//#include "epanet2.h" #include "hash.h" #include "util/errormanager.h" #include @@ -103,7 +103,7 @@ typedef int INT4; --------------------------------------------------------------------- */ #define MEMCHECK(x) (((x) == NULL) ? 101 : 0 ) -#define FREE(x) if ((x)) free((x)) +#define FREE(x) do { free(x); (x) = NULL; } while(0) /* --------------------------------------------------------------------- @@ -130,25 +130,47 @@ typedef int INT4; */ #define ERRCODE(x) (errcode = ((errcode>100) ? (errcode) : (x))) - - /* ---------------------------------------------- Global Enumeration Types ---------------------------------------------- */ + +typedef enum { + NODE = 0, + LINK = 1 +} ObjectType; + +typedef enum { + JUNCTION = 0, + RESERVOIR = 1, + TANK = 2 +} NodeType; + +typedef enum { + CVPIPE = 0, + PIPE = 1, + PUMP = 2, + PRV = 3, + PSV = 4, + PBV = 5, + FCV = 6, + TCV = 7, + GPV = 8 +} LinkType; + typedef enum { USE, /* use from previous run */ SAVE, /* save after current run */ - SCRATCH -} Hydtype; /* use temporary file */ + SCRATCH /* use temporary file */ +} HydFiletype; typedef enum { NONE, /* no quality analysis */ CHEM, /* analyze a chemical */ AGE, /* analyze water age */ - TRACE -} QualType; /* trace % of flow from a source */ + TRACE /* trace % of flow from a source */ +} QualType; typedef enum { V_CURVE, /* volume curve */ @@ -169,8 +191,8 @@ typedef enum { CONCEN, /* inflow concentration */ MASS, /* mass inflow booster */ SETPOINT, /* setpoint booster */ - FLOWPACED -} SourceType; /* flow paced booster */ + FLOWPACED /* flow paced booster */ +} SourceType; typedef enum { LOWLEVEL, /* act when grade below set level */ @@ -189,14 +211,14 @@ typedef enum { XFCV, /* FCV cannot supply flow */ XPRESSURE, /* valve cannot supply pressure */ FILLING, /* tank filling */ - EMPTYING -} StatType; /* tank emptying */ + EMPTYING /* tank emptying */ +} StatType; typedef enum { HW, /* Hazen-Williams */ DW, /* Darcy-Weisbach */ CM /* Chezy-Manning */ -} FormType; +} HeadLossType; typedef enum { US, /* US */ @@ -398,7 +420,7 @@ typedef struct /* NODE OBJECT */ double C0; /* Initial quality */ double Ke; /* Emitter coeff. */ char Rpt; /* Reporting flag */ - EN_NodeType Type; /* Node Type */ + NodeType Type; /* Node Type */ char Comment[MAXMSG+1]; /* Node Comment */ } Snode; @@ -416,7 +438,7 @@ typedef struct /* LINK OBJECT */ double R; /* Flow resistance */ double Rc; /* Reaction coeff. */ double Qa; // Low flow limit - EN_LinkType Type; /* Link type */ + LinkType Type; // Link type */ StatType Stat; /* Initial status */ char Rpt; /* Reporting flag */ char Comment[MAXMSG+1]; /* Link Comment */ @@ -514,7 +536,7 @@ typedef struct s_Premise /* Rule Premise Clause */ int status; /* Variable's status */ double value; /* Variable's value */ struct s_Premise *next; -} Premise; +} Spremise; typedef struct s_Action /* Rule Action Clause */ { @@ -522,24 +544,23 @@ typedef struct s_Action /* Rule Action Clause */ int status; /* Link's status */ double setting; /* Link's setting */ struct s_Action *next; -} Action; +} Saction; -typedef struct s_aRule /* Control Rule Structure */ +typedef struct /* Control Rule Structure */ { - char label[MAXID+1]; /* Rule character label */ + char label[MAXID+1]; /* Rule label */ double priority; /* Priority level */ - Premise *Pchain; /* Linked list of premises */ - Action *Tchain; /* Linked list of actions if true */ - Action *Fchain; /* Linked list of actions if false */ - //struct s_aRule *next; -} aRule; + Spremise *Premises; /* Linked list of premises */ + Saction *ThenActions; /* Linked list of THEN actions */ + Saction *ElseActions; /* Linked list of ELSE actions */ +} Srule; -typedef struct s_ActItem /* Action list item */ +typedef struct s_ActionItem /* Action list item */ { - int ruleindex; /* Index of rule action belongs to */ - struct s_Action *action; /* An action structure */ - struct s_ActItem *next; -} ActItem; + int ruleIndex; /* Index of rule action belongs to */ + Saction *action; /* An action structure */ + struct s_ActionItem *next; +} SactionList; typedef struct { @@ -707,12 +728,9 @@ typedef struct { char HydFname[MAXFNAME+1], /* Hydraulics file name */ OutFname[MAXFNAME+1], /* Binary output file name */ - TmpFname[MAXFNAME+1], /* Temporary file name */ - TmpDir[MAXFNAME+1], /* Temporary directory name */ Outflag, /* Output file flag */ Hydflag; /* Hydraulics flag */ - long HydOffset, /* Hydraulics file byte offset */ OutOffset1, /* 1st output file byte offset */ @@ -734,18 +752,6 @@ typedef struct { } save_options_t; - -typedef struct { - - aRule *Rule; /* Array of rules */ - ActItem *ActList; /* Linked list of action items */ - int RuleState; /* State of rule interpreter */ - long Time1; /* Start of rule evaluation time interval (sec) */ - Premise *Plast; /* Previous premise clause */ - Action *Tlast; /* Previous true action */ - Action *Flast; /* Previous false action */ -} rules_t; - /* ** NOTE: Hydraulic analysis of the pipe network at a given point in time ** is done by repeatedly solving a linearized version of the @@ -851,6 +857,16 @@ typedef struct { } hydraulics_t; +typedef struct { + SactionList *ActionList; /* Linked list of action items */ + int RuleState; /* State of rule interpreter */ + int Errcode; // Rule parser error code + long Time1; /* Start of rule evaluation time interval (sec) */ + Spremise *LastPremise; /* Previous premise clause */ + Saction *LastThenAction; /* Previous THEN action */ + Saction *LastElseAction; /* Previous ELSE action */ +} rules_t; + typedef struct { int Nnodes, /* Number of network nodes */ Ntanks, /* Number of tanks */ @@ -863,17 +879,18 @@ typedef struct { Nrules, /* Number of control rules */ Npats, /* Number of time patterns */ Ncurves, /* Number of data curves */ - Ncoords; /* Number of Coords */ + Ncoords; /* Number of node coordinates */ - Snode *Node; /* Node data */ - Slink *Link; /* Link data */ - Stank *Tank; /* Tank data */ - Spump *Pump; /* Pump data */ - Svalve *Valve; /* Valve data */ - Spattern *Pattern; /* Time patterns */ - Scurve *Curve; /* Curve data */ - Scoord *Coord; /* Coordinate data */ - Scontrol *Control; /* Control data */ + Snode *Node; /* Node array */ + Slink *Link; /* Link array */ + Stank *Tank; /* Tank array */ + Spump *Pump; /* Pump array */ + Svalve *Valve; /* Valve array */ + Spattern *Pattern; /* Time pattern array */ + Scurve *Curve; /* Data curve array */ + Scoord *Coord; /* Node coordinate array */ + Scontrol *Control; /* Simple controls array */ + Srule *Rule; /* Rule-based controls array */ HashTable *NodeHashTable, *LinkHashTable; /* Hash tables for ID labels */ @@ -896,18 +913,21 @@ typedef struct EN_Project { out_file_t out_files; save_options_t save_options; - double Ucf[MAXVAR]; + double Ucf[MAXVAR]; // Unit conversion factors char - Openflag, /// Toolkit open flag - Warnflag, /// Warning flag - Msg[MAXMSG+1], /// General-purpose string: errors, messages - Title[MAXTITLE][TITLELEN+1],/// Project title - MapFname[MAXFNAME+1]; /// Map file name + Openflag, // Toolkit open flag + Warnflag, // Warning flag + Msg[MAXMSG+1], // General-purpose string: errors, messages + Title[MAXTITLE][TITLELEN+1], // Project title + MapFname[MAXFNAME+1], // Map file name + TmpHydFname[MAXFNAME+1], // Temporary hydraulics file name + TmpOutFname[MAXFNAME+1], // Temporary output file name + TmpStatFname[MAXFNAME+1]; // Temporary statistic file name - error_handle_t* error_handle; //Simple error manager + error_handle_t* error_handle; // Simple error manager - void (* viewprog) (char *); /* Pointer to progress viewing function */ + void (* viewprog) (char *); // Pointer to progress viewing function } EN_Project; diff --git a/tests/test_addrule.cpp b/tests/test_addrule.cpp new file mode 100644 index 0000000..6eeea93 --- /dev/null +++ b/tests/test_addrule.cpp @@ -0,0 +1,109 @@ +// Test of EN_addrule, EN_deletenode & EN_deletelink EPANET API Functions +#define _CRT_SECURE_NO_DEPRECATE + +/* +This is a test for the API functions that adds rules and deletes +nodes and links from a project. Deletion can be conditional on +node or link appearing in any simple or rule-based controls. +*/ + +//#define NO_BOOST + +#ifndef NO_BOOST +#define BOOST_TEST_MODULE "toolkit" +#include +#endif + +#include +#include +#include "epanet2.h" + +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" + +#ifdef NO_BOOST +#define BOOST_REQUIRE(x) (((x)) ? cout << "\nPassed at line " << __LINE__ : cout << "\nFailed at line " << __LINE__ ) +#endif + +using namespace std; + +char R1[] = "RULE 1 \n IF NODE 2 LEVEL < 100 \n THEN LINK 9 STATUS = OPEN"; +char R2[] = "RULE 2\nIF SYSTEM TIME = 4\nTHEN LINK 9 STATUS = CLOSED\nAND LINK 31 STATUS = CLOSED"; +char R3[] = "RULE 3\nIF NODE 23 PRESSURE ABOVE 140\nAND NODE 2 LEVEL > 120\n" + "THEN LINK 113 STATUS = CLOSED\nELSE LINK 22 STATUS = CLOSED"; + +#ifndef NO_BOOST +BOOST_AUTO_TEST_SUITE (test_toolkit) +BOOST_AUTO_TEST_CASE(test_setlinktype) +#else +int main(int argc, char *argv[]) +#endif +{ + int error = 0; + int ruleCount, nP, nTA, nEA; + int link113, node23, link22, pump9_before, pump9_after; + float priority; + + EN_ProjectHandle ph = NULL; + EN_createproject(&ph); + + std::string path_inp = std::string(DATA_PATH_INP); + std::string path_rpt = std::string(DATA_PATH_RPT); + std::string path_out = std::string(DATA_PATH_OUT); + + error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), ""); + BOOST_REQUIRE(error == 0); + + // Add the 3 rules to the project + error = EN_addrule(ph, R1); + BOOST_REQUIRE(error == 0); + error = EN_addrule(ph, R2); + BOOST_REQUIRE(error == 0); + error = EN_addrule(ph, R3); + BOOST_REQUIRE(error == 0); + + // Check that rules were added + error = EN_getcount(ph, EN_RULECOUNT, &ruleCount); + BOOST_REQUIRE(ruleCount == 3); + + // Check the number of clauses in rule 3 + error = EN_getrule(ph, 3, &nP, &nTA, &nEA, &priority); + BOOST_REQUIRE(nP == 2); + BOOST_REQUIRE(nTA == 1); + BOOST_REQUIRE(nTA == 1); + + // Try to delete link 113 conditionally which will fail + // because it's in rule 3 + EN_getlinkindex(ph, "113", &link113); + error = EN_deletelink(ph, link113, EN_CONDITIONAL); + BOOST_REQUIRE(error == 261); + + // Delete node 23 unconditionally which will result in the + // deletion of rule 3 as well as links 22 and 113 + EN_getnodeindex(ph, "23", &node23); + EN_getlinkindex(ph, "22", &link22); + EN_getlinkindex(ph, "9", &pump9_before); + error = EN_deletenode(ph, node23, EN_UNCONDITIONAL); + BOOST_REQUIRE(error == 0); + + // Check that there are now only 2 rules + error = EN_getcount(ph, EN_RULECOUNT, &ruleCount); + BOOST_REQUIRE(ruleCount == 2); + + // Check that link 22 no longer exists + error = EN_getlinkindex(ph, "22", &link22); + BOOST_REQUIRE(error > 0); + + // Check that the index of pump9 has been reduced by 2 + error = EN_getlinkindex(ph, "9", &pump9_after); + BOOST_REQUIRE(pump9_before - pump9_after == 2); + + // Close and delete project + error = EN_close(ph); + BOOST_REQUIRE(error == 0); + EN_deleteproject(&ph); +} +#ifndef NO_BOOST +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/test_setlinktype.cpp b/tests/test_setlinktype.cpp index bf225fe..65293b0 100644 --- a/tests/test_setlinktype.cpp +++ b/tests/test_setlinktype.cpp @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(test_setlinktype) BOOST_REQUIRE(error == 0); error = EN_setlinknodes(ph, p113, n113_2, n113_1); BOOST_REQUIRE(error == 0); - error = EN_setlinktype(ph, &p113, EN_CVPIPE); + error = EN_setlinktype(ph, &p113, EN_CVPIPE, 0); BOOST_REQUIRE(error == 0); // Get index & diameter of pipe 121 connected to node 31 @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(test_setlinktype) BOOST_REQUIRE(error == 0); // Replace it with a PRV - error = EN_setlinktype(ph, &p121, EN_PRV); + error = EN_setlinktype(ph, &p121, EN_PRV, 0); BOOST_REQUIRE(error == 0); // Set diameter & setting of new PRV diff --git a/win_build/WinSDK/epanet2.def b/win_build/WinSDK/epanet2.def index fd4d4a9..44955ec 100644 --- a/win_build/WinSDK/epanet2.def +++ b/win_build/WinSDK/epanet2.def @@ -1,15 +1,38 @@ LIBRARY EPANET2.DLL EXPORTS - ENaddpattern = _ENaddpattern@4 + ENaddcontrol = _ENaddcontrol@24 + ENaddcurve = _ENaddcurve@4 + ENaddlink = _ENaddlink@16 + ENaddnode = _ENaddnode@8 + ENaddpattern = _ENaddpattern@4 + ENaddrule = _ENaddrule@4 ENclose = _ENclose@0 ENcloseH = _ENcloseH@0 - ENcloseQ = _ENcloseQ@0 - ENepanet = _ENepanet@16 + ENcloseQ = _ENcloseQ@0 + ENdeletecontrol = _ENdeletecontrol@4 + ENdeletelink = _ENdeletelink@4 + ENdeletenode = _ENdeletenode@4 + ENdeleterule = _ENdeleterule@4 + ENepanet = _ENepanet@16 + ENgetaveragepatternvalue = _ENgetaveragepatternvalue@8 + ENgetbasedemand = _ENgetbasedemand@12 ENgetcontrol = _ENgetcontrol@24 + ENgetcoord = _ENgetcoord@12 ENgetcount = _ENgetcount@8 + ENgetcurve = _ENgetcurve@20 + ENgetcurveid = _ENgetcurveid@8 + ENgetcurveindex = _ENgetcurveindex@8 + ENgetcurvelen = _ENgetcurvelen@8 + ENgetcurvetype = _ENgetcurvetype@8 + ENgetcurvevalue = _ENgetcurvevalue@16 + ENgetdemandmodel = _ENgetdemandmodel@16 + ENgetdemandname = _ENgetdemandname@12 + ENgetdemandpattern = _ENgetdemandpattern@12 + ENgetelseaction = _ENgetfalseaction@20 ENgeterror = _ENgeterror@12 ENgetflowunits = _ENgetflowunits@4 + ENgetheadcurveindex = _ENgetheadcurveindex@8 ENgetlinkid = _ENgetlinkid@8 ENgetlinkindex = _ENgetlinkindex@8 ENgetlinknodes = _ENgetlinknodes@12 @@ -19,86 +42,69 @@ EXPORTS ENgetnodeindex = _ENgetnodeindex@8 ENgetnodetype = _ENgetnodetype@8 ENgetnodevalue = _ENgetnodevalue@12 + ENgetnumdemands = _ENgetnumdemands@8 ENgetoption = _ENgetoption@8 ENgetpatternid = _ENgetpatternid@8 ENgetpatternindex = _ENgetpatternindex@8 ENgetpatternlen = _ENgetpatternlen@8 ENgetpatternvalue = _ENgetpatternvalue@12 + ENgetpremise = _ENgetpremise@36 + ENgetpumptype = _ENgetpumptype@8 + ENgetqualinfo = _ENgetqualinfo@16 ENgetqualtype = _ENgetqualtype@8 + ENgetrule = _ENgetrule@20 + ENgetruleID = _ENgetruleID@8 + ENgetstatistic = _ENgetstatistic@8 + ENgetthenaction = _ENgettrueaction@20 ENgettimeparam = _ENgettimeparam@8 - ENgetversion = _ENgetversion@4 + ENgetversion = _ENgetversion@4 + ENinit = _ENinit@16 ENinitH = _ENinitH@4 ENinitQ = _ENinitQ@4 ENnextH = _ENnextH@4 - ENnextQ = _ENnextQ@4 + ENnextQ = _ENnextQ@4 ENopen = _ENopen@12 ENopenH = _ENopenH@0 - ENopenQ = _ENopenQ@0 + ENopenQ = _ENopenQ@0 ENreport = _ENreport@0 ENresetreport = _ENresetreport@0 ENrunH = _ENrunH@4 - ENrunQ = _ENrunQ@4 + ENrunQ = _ENrunQ@4 ENsaveH = _ENsaveH@0 ENsavehydfile = _ENsavehydfile@4 ENsaveinpfile = _ENsaveinpfile@4 - ENaddcontrol = _ENaddcontrol@24 + ENsetbasedemand = _ENsetbasedemand@12 ENsetcontrol = _ENsetcontrol@24 - ENsetlinkvalue = _ENsetlinkvalue@12 + ENsetcoord = _ENsetcoord@12 + ENsetcurve = _ENsetcurve@16 + ENsetcurvevalue = _ENsetcurvevalue@16 + ENsetdemandmodel = _ENsetdemandmodel@16 + ENsetdemandname = _ENsetdemandname@12 + ENsetdemandpattern = _ENsetdemandpattern@12 + ENsetelseaction = _ENsetfalseaction@20 + ENsetflowunits = _ENsetflowunits@4 + ENsetheadcurveindex = _ENsetheadcurveindex@8 + ENsetlinkid = _ENsetlinkid@8 + ENsetlinknodes = _ENsetlinknodes@12 + ENsetlinktype = _ENsetlinktype@8 + ENsetlinkvalue = _ENsetlinkvalue@12 + ENsetnodeid = _ENsetnodeid@8 ENsetnodevalue = _ENsetnodevalue@12 ENsetoption = _ENsetoption@8 ENsetpattern = _ENsetpattern@12 ENsetpatternvalue = _ENsetpatternvalue@12 - ENsetqualtype = _ENsetqualtype@16 - ENsetreport = _ENsetreport@4 - ENsetstatusreport = _ENsetstatusreport@4 - ENsettimeparam = _ENsettimeparam@8 - ENsolveH = _ENsolveH@0 - ENsolveQ = _ENsolveQ@0 - ENstepQ = _ENstepQ@4 - ENusehydfile = _ENusehydfile@4 - ENwriteline = _ENwriteline@4 - ENgetnumdemands = _ENgetnumdemands@8 - ENgetbasedemand = _ENgetbasedemand@12 - ENgetdemandpattern = _ENgetdemandpattern@12 - ENgetcurve = _ENgetcurve@20 - ENgetstatistic = _ENgetstatistic@8 - ENgetcoord = _ENgetcoord@12 - ENsetcoord = _ENsetcoord@12 - ENgetqualinfo = _ENgetqualinfo@16 - ENsetbasedemand = _ENsetbasedemand@12 - ENsetdemandpattern = _ENsetdemandpattern@12 - ENgetaveragepatternvalue = _ENgetaveragepatternvalue@8 - ENgetheadcurveindex = _ENgetheadcurveindex@8 - ENsetheadcurveindex = _ENsetheadcurveindex@8 - ENgetpumptype = _ENgetpumptype@8 - ENgetcurveindex = _ENgetcurveindex@8 - ENgetcurveid = _ENgetcurveid@8 - ENgetcurvelen = _ENgetcurvelen@8 - ENgetcurvevalue = _ENgetcurvevalue@16 - ENsetcurvevalue = _ENsetcurvevalue@16 - ENsetcurve = _ENsetcurve@16 - ENaddcurve = _ENaddcurve@4 - ENgetrule = _ENgetrule@20 - ENsetrulepriority = _ENsetrulepriority@8 - ENgetpremise = _ENgetpremise@36 ENsetpremise = _ENsetpremise@36 ENsetpremiseindex = _ENsetpremiseindex@12 ENsetpremisestatus = _ENsetpremisestatus@12 ENsetpremisevalue = _ENsetpremisevalue@12 - ENgettrueaction = _ENgettrueaction@20 - ENsettrueaction = _ENsettrueaction@20 - ENgetfalseaction = _ENgetfalseaction@20 - ENsetfalseaction = _ENsetfalseaction@20 - ENgetruleID = _ENgetruleID@8 - ENsetflowunits = _ENsetflowunits@4 - ENaddnode = _ENaddnode@8 - ENaddlink = _ENaddlink@16 - ENdeletelink = _ENdeletelink@4 - ENdeletenode = _ENdeletenode@4 - ENsetlinktype = _ENsetlinktype@8 - ENgetdemandmodel = _ENgetdemandmodel@16 - ENsetdemandmodel = _ENsetdemandmodel@16 - ENgetcurvetype = _ENgetcurvetype@8 - ENinit = _ENinit@16 - ENgetdemandname = _ENgetdemandname@12 - ENsetdemandname = _ENsetdemandname@12 \ No newline at end of file + ENsetqualtype = _ENsetqualtype@16 + ENsetreport = _ENsetreport@4 + ENsetrulepriority = _ENsetrulepriority@8 + ENsetstatusreport = _ENsetstatusreport@4 + ENsetthenaction = _ENsettrueaction@20 + ENsettimeparam = _ENsettimeparam@8 + ENsolveH = _ENsolveH@0 + ENsolveQ = _ENsolveQ@0 + ENstepQ = _ENstepQ@4 + ENusehydfile = _ENusehydfile@4 + ENwriteline = _ENwriteline@4