From 1c3044c9488d83649c354747b06827204ab89905 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sun, 14 Oct 2018 11:05:15 -0400 Subject: [PATCH 01/21] Fix #211 for EN_setreport --- src/epanet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/epanet.c b/src/epanet.c index d021313..f209da7 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1377,9 +1377,10 @@ int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *s) { if (!p->Openflag) return set_error(p->error_handle, 102); - if (strlen(s) > MAXLINE) + if (strlen(s) >= MAXLINE) return set_error(p->error_handle, 250); strcpy(s1, s); + strcat(s1, "\n"); if (setreport(p, s1) > 0) return set_error(p->error_handle, 250); else From 1fce14efb87f19e51bf920121fcb0bc53c62c1ed Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 15 Oct 2018 11:34:25 -0400 Subject: [PATCH 02/21] Increased size of MAXMSG to 255 (#189) --- src/input1.c | 6 +++--- src/input2.c | 2 +- src/output.c | 6 +++--- src/types.h | 5 +++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/input1.c b/src/input1.c index 3eb5585..1e59b80 100644 --- a/src/input1.c +++ b/src/input1.c @@ -104,9 +104,9 @@ void setdefaults(EN_Project *pr) time_options_t *time = &pr->time_options; out_file_t *out = &pr->out_files; - strncpy(pr->Title[0], "", MAXMSG); - strncpy(pr->Title[1], "", MAXMSG); - strncpy(pr->Title[2], "", MAXMSG); + 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); diff --git a/src/input2.c b/src/input2.c index c40d029..717ef81 100644 --- a/src/input2.c +++ b/src/input2.c @@ -292,7 +292,7 @@ int newline(EN_Project *pr, int sect, char *line) n = (int)strlen(line); if (line[n - 1] == 10) line[n - 1] = ' '; - strncpy(pr->Title[par->Ntitle], line, MAXMSG); + strncpy(pr->Title[par->Ntitle], line, TITLELEN); par->Ntitle++; } return (0); diff --git a/src/output.c b/src/output.c index b91ab83..a9ed2a8 100644 --- a/src/output.c +++ b/src/output.c @@ -87,9 +87,9 @@ int savenetdata(EN_Project *pr) fwrite(ibuf, sizeof(INT4), 15, outFile); /* Write string variables to outFile */ - fwrite(pr->Title[0], sizeof(char), MAXMSG + 1, outFile); - fwrite(pr->Title[1], sizeof(char), MAXMSG + 1, outFile); - fwrite(pr->Title[2], sizeof(char), MAXMSG + 1, outFile); + fwrite(pr->Title[0], sizeof(char), TITLELEN + 1, outFile); + fwrite(pr->Title[1], sizeof(char), TITLELEN + 1, outFile); + fwrite(pr->Title[2], sizeof(char), TITLELEN + 1, outFile); fwrite(par->InpFname, sizeof(char), MAXFNAME + 1, outFile); fwrite(rep->Rpt2Fname, sizeof(char), MAXFNAME + 1, outFile); fwrite(qu->ChemName, sizeof(char), MAXID + 1, outFile); diff --git a/src/types.h b/src/types.h index c2350d0..7b9358d 100755 --- a/src/types.h +++ b/src/types.h @@ -48,8 +48,9 @@ typedef int INT4; #define ENGINE_VERSION 201 #define EOFMARK 0x1A /* Use 0x04 for UNIX systems */ #define MAXTITLE 3 /* Max. # title lines */ +#define TITLELEN 79 // Max. # characters in a title line #define MAXID 31 /* Max. # characters in ID name */ -#define MAXMSG 79 /* Max. # characters in message text */ +#define MAXMSG 255 /* Max. # characters in message text */ #define MAXLINE 255 /* Max. # characters read from input line */ #define MAXFNAME 259 /* Max. # characters in file name */ #define MAXTOKS 40 /* Max. items per line of input */ @@ -900,7 +901,7 @@ typedef struct EN_Project { Openflag, /// Toolkit open flag Warnflag, /// Warning flag Msg[MAXMSG+1], /// General-purpose string: errors, messages - Title[MAXTITLE][MAXMSG+1], /// Problem title + Title[MAXTITLE][TITLELEN+1],/// Project title MapFname[MAXFNAME+1]; /// Map file name error_handle_t* error_handle; //Simple error manager From 76419e0b4cacb7a8a2cfe455ee876321aef35ad6 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 17 Oct 2018 09:05:16 -0400 Subject: [PATCH 03/21] Added EN_setnodeid and EN_setlinkid (Issue #306) --- include/epanet2.bas | 2 ++ include/epanet2.h | 18 +++++++++++++ include/epanet2.vb | 2 ++ src/epanet.c | 64 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/include/epanet2.bas b/include/epanet2.bas index cb62b96..c4516f1 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -250,7 +250,9 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Long) As Long Declare Function ENaddcontrol Lib "epanet2.dll" (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 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 + Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long + Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long Declare Function ENaddpattern Lib "epanet2.dll" (ByVal id As String) As Long Declare Function ENsetpattern Lib "epanet2.dll" (ByVal index As Long, F As Any, ByVal N As Long) As Long diff --git a/include/epanet2.h b/include/epanet2.h index 9147912..1593783 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -842,6 +842,14 @@ extern "C" { @return Error code. */ 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. + @param index The index of a node. First node is index 1. + @param newid A string containing the node's new ID name. + @return Error code. + */ + int DLLEXPORT ENsetnodeid(int index, char *newid); /** @brief Set a property value for a node. @@ -852,6 +860,14 @@ extern "C" { @see EN_NodeProperty */ int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); + + /** + @brief Change the ID name for a link. + @param index The index of a link. First link is index 1. + @param newid A string containing the link's new ID name. + @return Error code. + */ + int DLLEXPORT ENsetlinkid(int index, char *newid); /** @brief Set a property value for a link. @@ -1297,7 +1313,9 @@ extern "C" { 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_setlinkid(EN_ProjectHandle ph, int index, char *newid); 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); diff --git a/include/epanet2.vb b/include/epanet2.vb index 0af9f70..5898dfd 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -227,7 +227,9 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENgetversion Lib "epanet2.dll" (ByRef Value As Int32) As Int32 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 + Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 + Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetpattern Lib "epanet2.dll" (ByVal Index as Int32, ByRef F as Single, ByVal N as Int32) as Int32 Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByVal Value As Single) As Int32 diff --git a/src/epanet.c b/src/epanet.c index f209da7..aa2da3b 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -437,10 +437,18 @@ int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, level); } +int DLLEXPORT ENsetnodeid(int index, char *newid) { + return EN_setnodeid(_defaultModel, index, newid); +} + int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) { return EN_setnodevalue(_defaultModel, index, code, v); } +int DLLEXPORT ENsetlinkid(int index, char *newid) { + return EN_setlinkid(_defaultModel, index, newid); +} + int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { return EN_setlinkvalue(_defaultModel, index, code, v); } @@ -2892,6 +2900,34 @@ int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lind return set_error(p->error_handle, 0); } +int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid) +{ + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + size_t n; + + // Check for valid arguments + if (index <= 0 || index > net->Nnodes) + { + return set_error(p->error_handle, 203); + } + n = strlen(newid); + if (n < 1 || n > MAXID) return set_error(p->error_handle, 209); + if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 209); + + // Check if another node with same name exists + if (ENHashTableFind(net->NodeHashTable, newid) > 0) + { + return set_error(p->error_handle, 215); + } + + // Replace the existing node ID with the new value + ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + strncpy(net->Node[index].ID, newid, MAXID); + ENHashTableInsert(net->NodeHashTable, net->Node[index].ID, index); + return set_error(p->error_handle, 0); +} + int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) /*---------------------------------------------------------------- ** Input: index = node index @@ -3158,6 +3194,34 @@ int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_F return set_error(p->error_handle, 0); } +int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) +{ + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + size_t n; + + // Check for valid arguments + if (index <= 0 || index > net->Nlinks) + { + return set_error(p->error_handle, 204); + } + n = strlen(newid); + if (n < 1 || n > MAXID) return set_error(p->error_handle, 211); + if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 211); + + // Check if another link with same name exists + if (ENHashTableFind(net->LinkHashTable, newid) > 0) + { + return set_error(p->error_handle, 215); + } + + // Replace the existing link ID with the new value + ENHashTableDelete(net->LinkHashTable, net->Link[index].ID); + strncpy(net->Link[index].ID, newid, MAXID); + ENHashTableInsert(net->LinkHashTable, net->Link[index].ID, index); + return set_error(p->error_handle, 0); +} + int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) From b3e84e0c42e5b8319680c4bc55c3e5f1a3ba7fe8 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 17 Oct 2018 10:17:00 -0400 Subject: [PATCH 04/21] Added unit test for ENsetnodeid and ENsetlinkid functions --- tests/test_setid.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/test_setid.cpp diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp new file mode 100644 index 0000000..bd50a6e --- /dev/null +++ b/tests/test_setid.cpp @@ -0,0 +1,86 @@ +// +// test_setid.cpp +// + +/* +This is a test for the API functions that change a node or link ID name. +A node and link name are changed, the network is saved, reopened and the new names are checked. +*/ + +#define BOOST_TEST_MODULE "toolkit" +#include + +#include +#include "epanet2.h" + +// NOTE: Project Home needs to be updated to run unit test +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" + +using namespace std; + +BOOST_AUTO_TEST_SUITE (test_toolkit) + +BOOST_AUTO_TEST_CASE(test_setid) +{ + int error = 0; + int index; + char errmsg[256]; + char id[80]; + char newid[80]; + + 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 = ENopen(path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); + BOOST_REQUIRE(error == 0); + + // Test of illegal node name change + error = ENgetnodeid(3, id); + BOOST_REQUIRE(error == 0); + strncpy(newid, "Illegal; node name", 79); + error = ENsetnodeid(3, newid); + BOOST_REQUIRE(error > 0); + + // Test of legal node name change + strncpy(newid, "Node3", 79); + error = ENsetnodeid(3, newid); + BOOST_REQUIRE(error == 0); + + // Test of illegal link name change + error = ENgetlinkid(3, id); + BOOST_REQUIRE(error == 0); + strncpy(newid, "Illegal; link name", 79); + error = ENsetlinkid(3, newid); + BOOST_REQUIRE(error > 0); + + // Test of legal link name change + strncpy(newid, "Link3", 79); + error = ENsetlinkid(3, newid); + BOOST_REQUIRE(error == 0); + + // Save the project + error = ENsaveinpfile("net1_setid.inp"); + BOOST_REQUIRE(error == 0); + error = ENclose(); + BOOST_REQUIRE(error == 0); + + // Re-open the saved project + error = ENopen("net1_setid.inp", path_rpt.c_str(), path_out.c_str()); + BOOST_REQUIRE(error == 0); + + // Check that 3rd node has its new name + error = ENgetnodeindex((char *)"Node3", &index); + BOOST_REQUIRE(error == 0); + BOOST_REQUIRE(index == 3); + + // Check that 3rd link has its new name + error = ENgetlinkindex((char *)"Link3", &index); + BOOST_REQUIRE(error == 0); + BOOST_REQUIRE(index == 3); + ENclose(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 60a79de4a9d8b4a87cbe0648b48bce2da18c5c76 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 18 Oct 2018 10:03:09 -0400 Subject: [PATCH 05/21] Fixed bug in hash.c A bug in the delete function in hash.c was preventing the unit test of the new set ID API functions from running properly. In fixing this bug the entire hash table code was refactored to make it look more like the mempool service routines. Also the need to copy the string passed into the table's insert function was eliminated. --- ReleaseNotes2_2.md | 4 +- src/epanet.c | 67 +++++------ src/hash.c | 269 ++++++++++++++++++++++--------------------- src/hash.h | 24 ++-- src/input2.c | 4 +- src/types.h | 7 +- tests/test_setid.cpp | 59 +++++----- 7 files changed, 212 insertions(+), 222 deletions(-) diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 618499b..2b335f3 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -129,8 +129,6 @@ Both network files are available [here](https://doi.org/10.23719/1375314). ## New API functions |Function|Description| |--|--| -|`ENaddlink`| | -|`ENaddnode`| | |`ENgetcurvetype`| | |`ENgetdemandmodel`|| |`ENsetdemandmodel`|| @@ -156,6 +154,8 @@ Both network files are available [here](https://doi.org/10.23719/1375314). |`ENaddlink`|| |`ENdeletelink`|| |`ENdeletenode`|| +| `ENsetnodeid` || +| `ENsetlinkid` || |`ENgetdemandname`|| |`ENsetdemandname`|| diff --git a/src/epanet.c b/src/epanet.c index aa2da3b..2c9017b 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -89,9 +89,9 @@ This module calls the following functions that reside in other modules: writelogo() writereport() HASH.C - ENHashTablecreate() - ENHashTableFind() - ENHashTableFree() + hashtable_create() + hashtable_find() + hashtable_free() The macro ERRCODE(x) is defined in TYPES.H. It says if the current value of the error code variable (errcode) is not fatal (< 100) then @@ -855,9 +855,8 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) writetime(p, FMT105); } freedata(p); - + out = &p->out_files; - if (out->TmpOutFile != out->OutFile) { if (out->TmpOutFile != NULL) { fclose(out->TmpOutFile); @@ -887,7 +886,7 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) remove(out->HydFname); if (out->Outflag == SCRATCH) remove(out->OutFname); - + p->Openflag = FALSE; p->hydraulics.OpenHflag = FALSE; p->save_options.SaveHflag = FALSE; @@ -2916,15 +2915,15 @@ int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid) if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 209); // Check if another node with same name exists - if (ENHashTableFind(net->NodeHashTable, newid) > 0) + if (hashtable_find(net->NodeHashTable, newid) > 0) { return set_error(p->error_handle, 215); } // Replace the existing node ID with the new value - ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + hashtable_delete(net->NodeHashTable, net->Node[index].ID); strncpy(net->Node[index].ID, newid, MAXID); - ENHashTableInsert(net->NodeHashTable, net->Node[index].ID, index); + hashtable_insert(net->NodeHashTable, net->Node[index].ID, index); return set_error(p->error_handle, 0); } @@ -3210,15 +3209,15 @@ int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 211); // Check if another link with same name exists - if (ENHashTableFind(net->LinkHashTable, newid) > 0) + if (hashtable_find(net->LinkHashTable, newid) > 0) { return set_error(p->error_handle, 215); } // Replace the existing link ID with the new value - ENHashTableDelete(net->LinkHashTable, net->Link[index].ID); + hashtable_delete(net->LinkHashTable, net->Link[index].ID); strncpy(net->Link[index].ID, newid, MAXID); - ENHashTableInsert(net->LinkHashTable, net->Link[index].ID, index); + hashtable_insert(net->LinkHashTable, net->Link[index].ID, index); return set_error(p->error_handle, 0); } @@ -4325,8 +4324,8 @@ int allocdata(EN_Project *p) parser_data_t *par; /* Allocate node & link ID hash tables */ - p->network.NodeHashTable = ENHashTableCreate(); - p->network.LinkHashTable = ENHashTableCreate(); + p->network.NodeHashTable = hashtable_create(); + p->network.LinkHashTable = hashtable_create(); ERRCODE(MEMCHECK(p->network.NodeHashTable)); ERRCODE(MEMCHECK(p->network.LinkHashTable)); @@ -4498,14 +4497,14 @@ void freedata(EN_Project *p) } free(net->Node); } - + /* Free memory for other network objects */ free(net->Link); free(net->Tank); free(net->Pump); free(net->Valve); free(net->Control); - + /* Free memory for time patterns */ if (net->Pattern != NULL) { for (j = 0; j <= par->MaxPats; j++) @@ -4521,20 +4520,19 @@ void freedata(EN_Project *p) } free(net->Curve); } - + /* Free memory for node coordinates */ if (p->parser.Coordflag == TRUE) { free(net->Coord); } - + /* Free memory for rule base (see RULES.C) */ freerules(p); - + /* Free hash table memory */ - if (net->NodeHashTable != NULL) - ENHashTableFree(net->NodeHashTable); - if (net->LinkHashTable != NULL) - ENHashTableFree(net->LinkHashTable); + if (net->NodeHashTable != NULL) hashtable_free(net->NodeHashTable); + + if (net->LinkHashTable != NULL) hashtable_free(net->LinkHashTable); } /* @@ -4637,7 +4635,7 @@ int findnode(EN_Network *n, char *id) **---------------------------------------------------------------- */ { - return (ENHashTableFind(n->NodeHashTable, id)); + return (hashtable_find(n->NodeHashTable, id)); } int findlink(EN_Network *n, char *id) @@ -4649,7 +4647,7 @@ int findlink(EN_Network *n, char *id) **---------------------------------------------------------------- */ { - return (ENHashTableFind(n->LinkHashTable, id)); + return (hashtable_find(n->LinkHashTable, id)); } int findtank(EN_Network *n, int index) @@ -5055,7 +5053,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { // shift rest of Node array for (index = net->Nnodes; index >= net->Njuncs; index--) { - ENHashTableUpdate(net->NodeHashTable, net->Node[index].ID, index + 1); + hashtable_update(net->NodeHashTable, net->Node[index].ID, index + 1); net->Node[index + 1] = net->Node[index]; net->Coord[index + 1] = net->Coord[index]; } @@ -5171,7 +5169,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { coord->Y = 0; /* Insert new node into hash table */ - ENHashTableInsert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ + hashtable_insert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ return set_error(p->error_handle, 0); } @@ -5194,8 +5192,8 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch return set_error(p->error_handle, 215); /* Lookup the from and to nodes */ - N1 = ENHashTableFind(net->NodeHashTable, fromNode); - N2 = ENHashTableFind(net->NodeHashTable, toNode); + N1 = hashtable_find(net->NodeHashTable, fromNode); + N2 = hashtable_find(net->NodeHashTable, toNode); if (N1 == 0 || N2 == 0) { return set_error(p->error_handle, 203); @@ -5277,7 +5275,7 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch link->Rpt = 0; strcpy(link->Comment, ""); - ENHashTableInsert(net->LinkHashTable, link->ID, n); + hashtable_insert(net->LinkHashTable, link->ID, n); return set_error(p->error_handle, 0); } @@ -5300,15 +5298,14 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { link = &net->Link[index]; // remove from hash table - ENHashTableDelete(net->LinkHashTable, link->ID); - ENHashTableFind(net->LinkHashTable, link->ID); + hashtable_delete(net->LinkHashTable, link->ID); // shift link and pump arrays to re-sort link indices net->Nlinks--; for (i = index; i <= net->Nlinks - 1; i++) { net->Link[i] = net->Link[i + 1]; // update hashtable - ENHashTableUpdate(net->LinkHashTable, net->Link[i].ID, i); + hashtable_update(net->LinkHashTable, net->Link[i].ID, i); } for (i = 1; i <= net->Npumps; i++) { if (net->Pump[i].Link > index) { @@ -5361,14 +5358,14 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) { // TODO: check for existing controls/rules that reference this node? // remove from hash table - ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + hashtable_delete(net->NodeHashTable, net->Node[index].ID); // shift node and coord array to remove node for (i = index; i <= net->Nnodes - 1; i++) { net->Node[i] = net->Node[i + 1]; net->Coord[i] = net->Coord[i + 1]; // update hashtable - ENHashTableUpdate(net->NodeHashTable, net->Node[i].ID, i); + hashtable_update(net->NodeHashTable, net->Node[i].ID, i); } // update tank array diff --git a/src/hash.c b/src/hash.c index ac31246..2d007e2 100755 --- a/src/hash.c +++ b/src/hash.c @@ -1,21 +1,20 @@ /*----------------------------------------------------------------------------- ** hash.c ** - ** Implementation of a simple Hash Table for string storage & retrieval + ** Implementation of a simple Hash Table that uses a string pointer + ** as a key and an associated integer as data. ** ** Written by L. Rossman - ** Last Updated on 6/19/03 + ** Last Updated on 10/17/18 ** - ** The hash table data structure (HTable) is defined in "hash.h". ** Interface Functions: - ** HTcreate() - creates a hash table - ** HTinsert() - inserts a string & its index value into a hash table - ** HTfind() - retrieves the index value of a string from a table - ** HTfree() - frees a hash table + ** hashtable_create - creates a hash table + ** hashtable_insert - inserts a string & its data value into a table + ** hashtable_find - retrieves the data value associated with a string + ** hashtable_findkey - retrieves the key associated with a data value + ** hashtable_delete - deletes an entry from a table + ** hashtable_free - frees a hash table ** - ********************************************************************* - ** NOTE: This is a modified version of the original HASH.C module. - ********************************************************************* */ #ifndef __APPLE__ @@ -26,144 +25,148 @@ #include #include "hash.h" -unsigned int _enHash(char *str); -unsigned int _enHash(char *str) -{ - unsigned int hash = 5381; - unsigned int retHash; - int c; - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - retHash = hash % ENHASHTABLEMAXSIZE; - - return retHash; -} +#define HASHTABLEMAXSIZE 128000 -ENHashTable *ENHashTableCreate() +typedef struct DataEntryStruct { - int i; - ENHashTable *ht = (ENHashTable *) calloc(ENHASHTABLEMAXSIZE, sizeof(ENHashTable)); - if (ht != NULL) { - for (i=0; i= ENHASHTABLEMAXSIZE ) { - return(0); - } - entry = (ENHashEntry *) malloc(sizeof(ENHashEntry)); - if (entry == NULL) { - return(0); - } - len = strlen(key) + 1; - entry->key = calloc(len, sizeof(char)); - strncpy(entry->key, key, len); - entry->data = data; - entry->next = ht[i]; - ht[i] = entry; - return(1); -} - -/* Abel Heinsbroek: Added function to update the hash table value for a given key */ -int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data) -{ - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - entry->data = new_data; - return(1); + int i; + HashTable *ht = (HashTable *) calloc(HASHTABLEMAXSIZE, sizeof(HashTable)); + if (ht != NULL) + { + for (i = 0; i < HASHTABLEMAXSIZE; i++) ht[i] = NULL; } - entry = entry->next; - } - return(NOTFOUND); + return ht; } -int ENHashTableDelete(ENHashTable *ht, char *key) { - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - entry = ht[i]; - while (entry != NULL) - { - if (strcmp(entry->key, key) == 0) { - entry->key = ""; - return(1); - } - entry = entry->next; - } - - return(NOTFOUND); -} - -int ENHashTableFind(ENHashTable *ht, char *key) +int hashtable_insert(HashTable *ht, char *key, int data) { - unsigned int i = _enHash(key); - - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - return(entry->data); - } - entry = entry->next; - } - return(NOTFOUND); + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return 0; + entry = (DataEntry *) malloc(sizeof(DataEntry)); + if (entry == NULL) return(0); + entry->key = key; + entry->data = data; + entry->next = ht[i]; + ht[i] = entry; + return 1; } -char *ENHashTableFindKey(ENHashTable *ht, char *key) +int hashtable_update(HashTable *ht, char *key, int new_data) { - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NULL); - } - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - return(entry->key); - } - entry = entry->next; - } - return(NULL); -} - -void ENHashTableFree(ENHashTable *ht) -{ - ENHashEntry *entry, *nextentry; - int i; - for (i=0; i= HASHTABLEMAXSIZE ) return NOTFOUND; entry = ht[i]; while (entry != NULL) { - nextentry = entry->next; - free(entry->key); - free(entry); - entry = nextentry; + if ( strcmp(entry->key, key) == 0 ) + { + entry->data = new_data; + return 1; + } + entry = entry->next; } - } - free(ht); + return NOTFOUND; +} + +int hashtable_delete(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry, *preventry; + + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; + + // Check if first entry in bucket i to be deleted + entry = ht[i]; + if (strcmp(entry->key, key) == 0) + { + ht[i] = entry->next; + free(entry); + return 1; + } + + // Check remaining entries in bucket i + preventry = ht[i]; + entry = ht[i]->next; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { + preventry->next = entry->next; + free(entry); + return 1; + } + preventry = entry; + entry = entry->next; + } + return NOTFOUND; +} + +int hashtable_find(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; + entry = ht[i]; + while (entry != NULL) + { + if ( strcmp(entry->key, key) == 0 ) + { + return entry->data; + } + entry = entry->next; + } + return NOTFOUND; +} + +char *hashtable_findkey(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NULL; + entry = ht[i]; + while (entry != NULL) + { + if ( strcmp(entry->key, key) == 0 ) return entry->key; + entry = entry->next; + } + return NULL; +} + +void hashtable_free(HashTable *ht) +{ + DataEntry *entry, *nextentry; + int i; + for (i = 0; i < HASHTABLEMAXSIZE; i++) + { + entry = ht[i]; + while (entry != NULL) + { + nextentry = entry->next; + free(entry); + entry = nextentry; + } + } + free(ht); } diff --git a/src/hash.h b/src/hash.h index 1285c11..612b8d3 100755 --- a/src/hash.h +++ b/src/hash.h @@ -7,24 +7,16 @@ #ifndef HASH_H #define HASH_H -#define ENHASHTABLEMAXSIZE 128000 #define NOTFOUND 0 -typedef struct HTentryStruct -{ - char *key; - int data; - struct HTentryStruct *next; -} ENHashEntry; +typedef struct DataEntryStruct *HashTable; -typedef ENHashEntry *ENHashTable; +HashTable *hashtable_create(void); +int hashtable_insert(HashTable *, char *, int); +int hashtable_find(HashTable *, char *); +char *hashtable_findkey(HashTable *, char *); +void hashtable_free(HashTable *); +int hashtable_update(HashTable *ht, char *key, int new_data); +int hashtable_delete(HashTable *ht, char *key); -ENHashTable *ENHashTableCreate(void); -int ENHashTableInsert(ENHashTable *, char *, int); -int ENHashTableFind(ENHashTable *, char *); -char *ENHashTableFindKey(ENHashTable *, char *); -void ENHashTableFree(ENHashTable *); -int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data); -int ENHashTableDelete(ENHashTable *ht, char *key); - #endif diff --git a/src/input2.c b/src/input2.c index 717ef81..d141d72 100644 --- a/src/input2.c +++ b/src/input2.c @@ -472,7 +472,7 @@ int addnodeID(EN_Network *net, int n, char *id) return (0); /* see EPANET.C */ } strncpy(net->Node[n].ID, id, MAXID); - ENHashTableInsert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ + hashtable_insert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ return (1); } @@ -490,7 +490,7 @@ int addlinkID(EN_Network *net, int n, char *id) return (0); /* see EPANET.C */ } strncpy(net->Link[n].ID, id, MAXID); - ENHashTableInsert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ + hashtable_insert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ return (1); } diff --git a/src/types.h b/src/types.h index 7b9358d..1984b28 100755 --- a/src/types.h +++ b/src/types.h @@ -874,9 +874,10 @@ typedef struct { Scurve *Curve; /* Curve data */ Scoord *Coord; /* Coordinate data */ Scontrol *Control; /* Control data */ - ENHashTable *NodeHashTable, - *LinkHashTable; /* Hash tables for ID labels */ - Padjlist *Adjlist; /* Node adjacency lists */ + HashTable + *NodeHashTable, + *LinkHashTable; /* Hash tables for ID labels */ + Padjlist *Adjlist; /* Node adjacency lists */ } EN_Network; diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp index bd50a6e..5a03b01 100644 --- a/tests/test_setid.cpp +++ b/tests/test_setid.cpp @@ -1,6 +1,4 @@ -// -// test_setid.cpp -// +// Test of ENsetid EPANET API Function /* This is a test for the API functions that change a node or link ID name. @@ -10,77 +8,76 @@ A node and link name are changed, the network is saved, reopened and the new nam #define BOOST_TEST_MODULE "toolkit" #include -#include +#include #include "epanet2.h" -// NOTE: Project Home needs to be updated to run unit test -#define DATA_PATH_INP "./net1.inp" -#define DATA_PATH_RPT "./test.rpt" -#define DATA_PATH_OUT "./test.out" +#define DATA_PATH_INP "net1.inp" +#define DATA_PATH_RPT "test.rpt" +#define DATA_PATH_OUT "" using namespace std; BOOST_AUTO_TEST_SUITE (test_toolkit) BOOST_AUTO_TEST_CASE(test_setid) +{ + +int main(int argc, char *argv[]) { int error = 0; int index; - char errmsg[256]; - char id[80]; - char newid[80]; + string newid; 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 = ENopen(path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); + error = ENopen(path_inp.c_str(), path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Test of illegal node name change - error = ENgetnodeid(3, id); - BOOST_REQUIRE(error == 0); - strncpy(newid, "Illegal; node name", 79); - error = ENsetnodeid(3, newid); + newid = "Illegal; node name"; + error = ENsetnodeid(3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal node name change - strncpy(newid, "Node3", 79); - error = ENsetnodeid(3, newid); + newid = "Node3"; + error = ENsetnodeid(3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); - + // Test of illegal link name change - error = ENgetlinkid(3, id); - BOOST_REQUIRE(error == 0); - strncpy(newid, "Illegal; link name", 79); - error = ENsetlinkid(3, newid); + newid = "Illegal; link name"; + error = ENsetlinkid(3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal link name change - strncpy(newid, "Link3", 79); - error = ENsetlinkid(3, newid); + newid = "Link3"; + error = ENsetlinkid(3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); // Save the project error = ENsaveinpfile("net1_setid.inp"); BOOST_REQUIRE(error == 0); + error = ENclose(); BOOST_REQUIRE(error == 0); - + // Re-open the saved project - error = ENopen("net1_setid.inp", path_rpt.c_str(), path_out.c_str()); + error = ENopen("net1_setid.inp", path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Check that 3rd node has its new name - error = ENgetnodeindex((char *)"Node3", &index); + error = ENgetnodeindex("Node3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); // Check that 3rd link has its new name - error = ENgetlinkindex((char *)"Link3", &index); + error = ENgetlinkindex("Link3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); - ENclose(); + + error = ENclose(); + BOOST_REQUIRE(error == 0); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() From 0041772aa0cd4ed25126bbd8eedaddcf8b8e51d6 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 18 Oct 2018 10:16:47 -0400 Subject: [PATCH 06/21] Corrected test_setid unit test code --- tests/test_setid.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp index 5a03b01..2b3693e 100644 --- a/tests/test_setid.cpp +++ b/tests/test_setid.cpp @@ -20,9 +20,6 @@ using namespace std; BOOST_AUTO_TEST_SUITE (test_toolkit) BOOST_AUTO_TEST_CASE(test_setid) -{ - -int main(int argc, char *argv[]) { int error = 0; int index; From f7346cef5febab6b3e32846ad0f7de6407b7246f Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Fri, 19 Oct 2018 10:36:39 -0400 Subject: [PATCH 07/21] Modified re-factored version of hash.c --- src/epanet.c | 1 - src/hash.c | 43 ++++++++++++++++++++++++------------------- tests/test_setid.cpp | 35 +++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 2c9017b..1229139 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -850,7 +850,6 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) out_file_t *out; EN_Project *p = (EN_Project*)ph; - if (p->Openflag) { writetime(p, FMT105); } diff --git a/src/hash.c b/src/hash.c index 2d007e2..99c4cb4 100755 --- a/src/hash.c +++ b/src/hash.c @@ -1,11 +1,11 @@ /*----------------------------------------------------------------------------- ** hash.c ** - ** Implementation of a simple Hash Table that uses a string pointer - ** as a key and an associated integer as data. + ** Implementation of a simple Hash Table that uses a string as a key + ** and an associated integer as data. ** ** Written by L. Rossman - ** Last Updated on 10/17/18 + ** Last Updated on 10/19/18 ** ** Interface Functions: ** hashtable_create - creates a hash table @@ -47,6 +47,14 @@ unsigned int gethash(char *str) return retHash; } +char *dupstr(const char *s) +{ + size_t size = strlen(s) + 1; + char *p = malloc(size); + if (p) memcpy(p, s, size); + return p; +} + HashTable *hashtable_create() { int i; @@ -65,7 +73,7 @@ int hashtable_insert(HashTable *ht, char *key, int data) if ( i >= HASHTABLEMAXSIZE ) return 0; entry = (DataEntry *) malloc(sizeof(DataEntry)); if (entry == NULL) return(0); - entry->key = key; + entry->key = dupstr(key); entry->data = data; entry->next = ht[i]; ht[i] = entry; @@ -76,6 +84,7 @@ int hashtable_update(HashTable *ht, char *key, int new_data) { unsigned int i = gethash(key); DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; entry = ht[i]; while (entry != NULL) @@ -97,23 +106,15 @@ int hashtable_delete(HashTable *ht, char *key) if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; - // Check if first entry in bucket i to be deleted + preventry = NULL; entry = ht[i]; - if (strcmp(entry->key, key) == 0) - { - ht[i] = entry->next; - free(entry); - return 1; - } - - // Check remaining entries in bucket i - preventry = ht[i]; - entry = ht[i]->next; while (entry != NULL) { if (strcmp(entry->key, key) == 0) { - preventry->next = entry->next; + if (preventry == NULL) ht[i] = entry->next; + else preventry->next = entry->next; + free(entry->key); free(entry); return 1; } @@ -127,6 +128,7 @@ int hashtable_find(HashTable *ht, char *key) { unsigned int i = gethash(key); DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; entry = ht[i]; while (entry != NULL) @@ -158,15 +160,18 @@ void hashtable_free(HashTable *ht) { DataEntry *entry, *nextentry; int i; + for (i = 0; i < HASHTABLEMAXSIZE; i++) { entry = ht[i]; while (entry != NULL) { - nextentry = entry->next; - free(entry); - entry = nextentry; + nextentry = entry->next; + free(entry->key); + free(entry); + entry = nextentry; } + ht[i] = NULL; } free(ht); } diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp index 2b3693e..ee9e1da 100644 --- a/tests/test_setid.cpp +++ b/tests/test_setid.cpp @@ -1,4 +1,5 @@ // Test of ENsetid EPANET API Function +#define _CRT_SECURE_NO_DEPRECATE /* This is a test for the API functions that change a node or link ID name. @@ -11,9 +12,9 @@ A node and link name are changed, the network is saved, reopened and the new nam #include #include "epanet2.h" -#define DATA_PATH_INP "net1.inp" -#define DATA_PATH_RPT "test.rpt" -#define DATA_PATH_OUT "" +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" using namespace std; @@ -24,57 +25,63 @@ BOOST_AUTO_TEST_CASE(test_setid) int error = 0; int index; string newid; + + 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 = ENopen(path_inp.c_str(), path_rpt.c_str(), ""); + error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Test of illegal node name change newid = "Illegal; node name"; - error = ENsetnodeid(3, (char *)newid.c_str()); + error = EN_setnodeid(ph, 3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal node name change newid = "Node3"; - error = ENsetnodeid(3, (char *)newid.c_str()); + error = EN_setnodeid(ph, 3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); // Test of illegal link name change newid = "Illegal; link name"; - error = ENsetlinkid(3, (char *)newid.c_str()); + error = EN_setlinkid(ph, 3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal link name change newid = "Link3"; - error = ENsetlinkid(3, (char *)newid.c_str()); + error = EN_setlinkid(ph, 3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); // Save the project - error = ENsaveinpfile("net1_setid.inp"); + error = EN_saveinpfile(ph, "net1_setid.inp"); BOOST_REQUIRE(error == 0); - error = ENclose(); + error = EN_close(ph); BOOST_REQUIRE(error == 0); + EN_deleteproject(&ph); // Re-open the saved project - error = ENopen("net1_setid.inp", path_rpt.c_str(), ""); + EN_createproject(&ph); + error = EN_open(ph, "net1_setid.inp", path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Check that 3rd node has its new name - error = ENgetnodeindex("Node3", &index); + error = EN_getnodeindex(ph, "Node3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); // Check that 3rd link has its new name - error = ENgetlinkindex("Link3", &index); + error = EN_getlinkindex(ph, "Link3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); - error = ENclose(); + error = EN_close(ph); BOOST_REQUIRE(error == 0); + EN_deleteproject(&ph); } BOOST_AUTO_TEST_SUITE_END() From 69388024fbe7ea609902715f3ad99d97448e5746 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Tue, 23 Oct 2018 16:00:14 -0400 Subject: [PATCH 08/21] Fixed bug in EN_deletelink (#312) --- src/epanet.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 1229139..00fe533 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -5280,10 +5280,11 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { int i; + int pumpindex; + int valveindex; + EN_LinkType linkType; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; Slink *link; @@ -5293,14 +5294,12 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { return set_error(p->error_handle, 203); EN_getlinktype(p, index, &linkType); - link = &net->Link[index]; // remove from hash table hashtable_delete(net->LinkHashTable, link->ID); // shift link and pump arrays to re-sort link indices - net->Nlinks--; for (i = index; i <= net->Nlinks - 1; i++) { net->Link[i] = net->Link[i + 1]; // update hashtable @@ -5316,25 +5315,27 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { net->Valve[i].Link -= 1; } } - - // update pumps + // remove any pump associated with the deleted link if (linkType == EN_PUMP) { - int pumpindex = findpump(net,index); + pumpindex = findpump(net,index); for (i = pumpindex; i <= net->Npumps - 1; i++) { net->Pump[i] = net->Pump[i + 1]; } net->Npumps--; } - // update valves + // remove any valve associated with the deleted link if (linkType > EN_PUMP) { - int valveindex = findvalve(net,index); + valveindex = findvalve(net,index); for (i = valveindex; i <= net->Nvalves - 1; i++) { net->Valve[i] = net->Valve[i + 1]; } net->Nvalves--; } + + // reduce total link count + net->Nlinks--; return set_error(p->error_handle, 0); } From 27c21e499e76ff9e4c308837e99e3f320366a8d3 Mon Sep 17 00:00:00 2001 From: Marios Kyriakou Date: Thu, 25 Oct 2018 13:55:03 +0300 Subject: [PATCH 09/21] Update ReleaseNotes2_2.md 1. Update description of new API functions. 2. Update API extensions --- ReleaseNotes2_2.md | 69 +++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 2b335f3..10ca70a 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -129,35 +129,35 @@ Both network files are available [here](https://doi.org/10.23719/1375314). ## New API functions |Function|Description| |--|--| -|`ENgetcurvetype`| | -|`ENgetdemandmodel`|| -|`ENsetdemandmodel`|| -|`ENsetflowunits`|| -|`ENaddcontrol`|| -|`ENsetdemandpattern`|| -|`ENgetrule`|| -|`ENsetrulepriority`|| -|`ENgetpremise`|| -|`ENsetpremise`|| -|`ENsetpremiseindex`|| -|`ENsetpremisestatus`|| -|`ENsetpremisevalue`|| -|`ENgettrueaction`|| -|`ENsettrueaction`|| -|`ENgetfalseaction`|| -|`ENsetfalseaction`|| -|`ENgetruleID`|| -|`ENinit`|| -|`ENsetheadcurveindex`|| -|`ENsetlinktype`|| -|`ENaddnode`|| -|`ENaddlink`|| -|`ENdeletelink`|| -|`ENdeletenode`|| -| `ENsetnodeid` || -| `ENsetlinkid` || -|`ENgetdemandname`|| -|`ENsetdemandname`|| +|`ENgetcurvetype`|Get the type of a curve| +|`ENgetdemandmodel`|Retrieves the type of demand model in use and its parameters| +|`ENsetdemandmodel`|Sets the type of demand model to use and its parameters| +|`ENsetflowunits`|Sets the flow units| +|`ENaddcontrol`|Specify parameters to add a new simple control| +|`ENsetdemandpattern`|Sets the index of the demand pattern assigned to a node for a category index| +|`ENgetrule`|Gets the number of premises, true actions, and false actions and the priority of an existing rule-based control| +|`ENsetrulepriority`|Sets the priority of the existing rule-based control| +|`ENgetpremise`|Gets the components of a premise/condition in an existing rule-based control| +|`ENsetpremise`|Sets the components of a premise/condition in an existing rule-based control| +|`ENsetpremiseindex`|Sets the index of an object in a premise of an existing rule-based control| +|`ENsetpremisestatus`|Sets the status in a premise of an existing rule-based control| +|`ENsetpremisevalue`|Sets the value in a premise of an existing rule-based control| +|`ENgettrueaction`|Gets the components of a true-action in an existing rule-based control| +|`ENsettrueaction`|Sets the components of a true-action in an existing rule-based control| +|`ENgetfalseaction`|Gets the components of a false-action in an existing rule-based control| +|`ENsetfalseaction`|Sets the components of a false-action in an existing rule-based control| +|`ENgetruleID`|Returns the ID of a rule| +|`ENinit`|Initializes an EPANET session| +|`ENsetheadcurveindex`|Sets the curve id for a specified pump index| +|`ENsetlinktype`|Set the link type code for a specified link| +|`ENaddnode`|Adds a new node| +|`ENaddlink`|Adds a new link| +|`ENdeletelink`|Deletes a link| +|`ENdeletenode`|Deletes a node| +| `ENsetnodeid` |Change the ID name for a node| +| `ENsetlinkid` |Change the ID name for a link| +|`ENgetdemandname`|Sets the node's demand name for a category| +|`ENsetdemandname`|Sets the node's demand name for a category| ## API Extensions (additional definitions) ### Link value types: @@ -174,10 +174,14 @@ Both network files are available [here](https://doi.org/10.23719/1375314). - `EN_HW` - `EN_DW` - `EN_CM` -### Misc. options: +### Option types: - `EN_HEADERROR` - `EN_FLOWCHANGE` - `EN_DEMANDDEFPAT` + - `EN_HEADLOSSFORM` +### Time statistic types: + - `EN_MAXHEADERROR` + - `EN_MAXFLOWCHANGE` - `EN_MASSBALANCE` ### Curve types: - `EN_V_CURVE` @@ -185,7 +189,10 @@ Both network files are available [here](https://doi.org/10.23719/1375314). - `EN_E_CURVE` - `EN_H_CURVE` - `EN_G_CURVE` - +### Demand model types: + - `EN_DDA` + - `EN_PDA` + ## Authors contributing to this release: - List item From 39fcfa2162cad97381b6902b14dc699dfe1bf8e4 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Thu, 25 Oct 2018 09:39:28 -0400 Subject: [PATCH 10/21] Fixed bug setting pump curve params (#314) and in setting demand categories -Split off calc. of a pump's curve coeffs. from getpumpparams to updatepumpparams. - Fixed problem with initializing node demand category when default demand pattern set before node is created. - Increased MAXLINE to 1024 to accomodate increase in MAXMSG. - Replaced EN_geterror calls in input2.c with geterrmsg (it's bad form to call API functions in the body of the library) --- src/epanet.c | 6 +- src/funcs.h | 1 + src/input1.c | 1 + src/input2.c | 215 +++++++++++++++++++++++++++------------------------ src/types.h | 2 +- 5 files changed, 120 insertions(+), 105 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 00fe533..d4c004f 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -3835,6 +3835,7 @@ int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v) for (demand = node->D; demand != NULL; demand = demand->next) { if (demand->Pat == tmpPat) { demand->Pat = (int)value; + strcpy(demand->Name, ""); } } } @@ -3973,7 +3974,7 @@ int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int index, int curveinde pump->Ptype = NOCURVE; pump->Hcurve = curveindex; // update pump parameters - getpumpparams(p); + updatepumpparams(p, pIdx); // convert units if (pump->Ptype == POWER_FUNC) { pump->H0 /= Ucf[HEAD]; @@ -4871,8 +4872,6 @@ int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx const int Nnodes = net->Nnodes; const int Njuncs = net->Njuncs; - double *Ucf = pr->Ucf; - Pdemand d; int n = 1; /* Check for valid arguments */ @@ -5047,6 +5046,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); demand->Base = 0.0; demand->Pat = hyd->DefPat; // Use default pattern + strcpy(demand->Name, ""); demand->next = NULL; node->D = demand; diff --git a/src/funcs.h b/src/funcs.h index 8d267f9..b0b027f 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -75,6 +75,7 @@ int addcurve(parser_data_t *par, char *); /* Adds curve to data base STmplist *findID(char *, STmplist *); /* Locates ID on linked list */ int unlinked(EN_Project *pr); /* Checks for unlinked nodes */ int getpumpparams(EN_Project *pr); /* Computes pump curve coeffs.*/ +int updatepumpparams(EN_Project *pr, int); // Updates pump curve coeffs. int getpatterns(EN_Project *pr); /* Gets pattern data from list*/ int getcurves(EN_Project *pr); /* Gets curve data from list */ int findmatch(char *, char *[]); /* Finds keyword in line */ diff --git a/src/input1.c b/src/input1.c index 1e59b80..89a4e1d 100644 --- a/src/input1.c +++ b/src/input1.c @@ -356,6 +356,7 @@ void adjustdata(EN_Project *pr) for (demand = node->D; demand != NULL; demand = demand->next) { if (demand->Pat == 0) { demand->Pat = hyd->DefPat; + strcpy(demand->Name, ""); } } } diff --git a/src/input2.c b/src/input2.c index d141d72..374ca87 100644 --- a/src/input2.c +++ b/src/input2.c @@ -40,10 +40,10 @@ The following utility functions are all called from INPUT3.C #define MAXERRS 10 /* Max. input errors reported */ - /* Defined in enumstxt.h in EPANET.C */ extern char *SectTxt[]; /* Input section keywords */ + int netsize(EN_Project *pr) /* **-------------------------------------------------------------- @@ -206,9 +206,10 @@ 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]); +// 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); errsum++; @@ -363,101 +364,121 @@ int getpumpparams(EN_Project *pr) **------------------------------------------------------------- ** Input: none ** Output: returns error code -** Purpose: computes & checks pump curve parameters +** Purpose: computes pump curve coefficients for all pumps **-------------------------------------------------------------- */ { - int i, j = 0, k, m, n = 0; - double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; - char errMsg[MAXMSG+1]; - Spump *pump; - Slink *link; - Scurve *curve; - - EN_Network *net = &pr->network; - - for (i = 1; i <= net->Npumps; i++) { - pump = &net->Pump[i]; - k = pump->Link; - link = &net->Link[k]; - if (pump->Ptype == CONST_HP) { /* Constant Hp pump */ - pump->H0 = 0.0; - pump->R = -8.814 * link->Km; - pump->N = -1.0; - pump->Hmax = BIG; /* No head limit */ - pump->Qmax = BIG; /* No flow limit */ - pump->Q0 = 1.0; /* Init. flow = 1 cfs */ - continue; - } - else if (pump->Ptype == NOCURVE) { /* Pump curve specified */ - j = pump->Hcurve; /* Get index of head curve */ - if (j == 0) { /* Error: No head curve */ - EN_geterror(226, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); - } - curve = &net->Curve[j]; - curve->Type = P_CURVE; - n = curve->Npts; - if (n == 1) { /* Only a single h-q point supplied so use generic */ - pump->Ptype = POWER_FUNC; /* power function curve. */ - q1 = curve->X[0]; - h1 = curve->Y[0]; - h0 = 1.33334 * h1; - q2 = 2.0 * q1; - h2 = 0.0; - } else if (n == 3 && curve->X[0] == 0.0) /* 3 h-q points supplied with */ - { /* shutoff head so use fitted */ - pump->Ptype = POWER_FUNC; /* power function curve. */ - h0 = curve->Y[0]; - q1 = curve->X[1]; - h1 = curve->Y[1]; - q2 = curve->X[2]; - h2 = curve->Y[2]; - } - else { // use a custom curve, referenced by ID - pump->Ptype = CUSTOM; /* Else use custom pump curve.*/ - // at this point, j is set to that curve's index. - } - - /* Compute shape factors & limits of power function pump curves */ - if (pump->Ptype == POWER_FUNC) { - if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) { /* Error: Invalid curve */ - EN_geterror(227, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); - } else { - pump->H0 = -a; - pump->R = -b; - pump->N = c; - pump->Q0 = q1; - pump->Qmax = pow((-a / b), (1.0 / c)); - pump->Hmax = h0; + int i, k, errcode = 0; + EN_Network *net = &pr->network; + + for (i = 1; i <= net->Npumps; i++) + { + errcode = updatepumpparams(pr, i); + if (errcode) + { + k = net->Pump[i].Link; + sprintf(pr->Msg, "%s link: %s", geterrmsg(errcode, pr->Msg), + net->Link[k].ID); + writeline(pr, pr->Msg); + return 200; } - } + } + return 0; +} + +int updatepumpparams(EN_Project *pr, int pumpindex) +/* +**------------------------------------------------------------- +** Input: pumpindex = index of a pump +** Output: returns error code +** Purpose: computes & checks a pump's head curve coefficients +**-------------------------------------------------------------- +*/ +{ + int m; + int curveindex; + int npts = 0; + int errcode = 0; + double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; + + EN_Network *net = &pr->network; + Spump *pump; + Scurve *curve; + + pump = &net->Pump[pumpindex]; + if (pump->Ptype == CONST_HP) // Constant Hp pump + { + pump->H0 = 0.0; + pump->R = -8.814 * net->Link[pump->Link].Km; + pump->N = -1.0; + pump->Hmax = BIG; // No head limit + pump->Qmax = BIG; // No flow limit + pump->Q0 = 1.0; // Init. flow = 1 cfs + return errcode; } - /* Assign limits to custom pump curves */ - if (pump->Ptype == CUSTOM) { - curve = &net->Curve[j]; - for (m = 1; m < n; m++) { - if (curve->Y[m] >= curve->Y[m - 1]) { /* Error: Invalid curve */ - EN_geterror(227, errMsg, MAXMSG); - sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); - writeline(pr, pr->Msg); - return (200); + else if (pump->Ptype == NOCURVE) // Pump curve specified + { + curveindex = pump->Hcurve; + if (curveindex == 0) return 226; + curve = &net->Curve[curveindex]; + curve->Type = P_CURVE; + npts = curve->Npts; + + // Generic power function curve + if (npts == 1) + { + pump->Ptype = POWER_FUNC; + q1 = curve->X[0]; + h1 = curve->Y[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + + // 3 point curve with shutoff head + else if (npts == 3 && curve->X[0] == 0.0) + { + pump->Ptype = POWER_FUNC; + h0 = curve->Y[0]; + q1 = curve->X[1]; + h1 = curve->Y[1]; + q2 = curve->X[2]; + h2 = curve->Y[2]; + } + + // Custom pump curve + else + { + pump->Ptype = CUSTOM; + for (m = 1; m < npts; m++) + { + if (curve->Y[m] >= curve->Y[m - 1]) return 227; + } + pump->Qmax = curve->X[npts - 1]; + pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; + pump->Hmax = curve->Y[0]; + } + + // Compute shape factors & limits of power function curves + if (pump->Ptype == POWER_FUNC) + { + if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) return 227; + else + { + pump->H0 = -a; + pump->R = -b; + pump->N = c; + pump->Q0 = q1; + pump->Qmax = pow((-a / b), (1.0 / c)); + pump->Hmax = h0; + } } - } - pump->Qmax = curve->X[n - 1]; - pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; - pump->Hmax = curve->Y[0]; } - } /* Next pump */ - return (0); + return 0; } + int addnodeID(EN_Network *net, int n, char *id) /* **------------------------------------------------------------- @@ -603,7 +624,6 @@ int unlinked(EN_Project *pr) EN_Network *net = &pr->network; int *marked; int i, err, errcode; - char errMsg[MAXMSG+1]; errcode = 0; err = 0; @@ -621,8 +641,7 @@ int unlinked(EN_Project *pr) if (marked[i] == 0) /* If not marked then error */ { err++; - EN_geterror(233, errMsg, MAXMSG); - sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[i].ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(233, pr->Msg), net->Node[i].ID); writeline(pr, pr->Msg); } if (err >= MAXERRS) @@ -732,9 +751,7 @@ int getcurves(EN_Project *pr) /* Check that curve has data points */ if (curve->Npts <= 0) { - char errMsg[MAXMSG+1]; - EN_geterror(230, errMsg, MAXMSG); - sprintf(pr->Msg, "%s curve: %s", errMsg, curve->ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); writeline(pr, pr->Msg); return (200); } @@ -754,9 +771,7 @@ int getcurves(EN_Project *pr) /* Check that x data is in ascending order */ if (fx->value >= x) { - char errMsg[MAXMSG+1]; - EN_geterror(230, errMsg, MAXMSG); - sprintf(pr->Msg, "%s node: %s", errMsg, curve->ID); + sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); writeline(pr, pr->Msg); return (200); } @@ -1012,10 +1027,8 @@ void inperrmsg(EN_Project *pr, int err, int sect, char *line) char errStr[MAXMSG + 1]; char id[MAXMSG + 1]; - EN_geterror(err, errStr, MAXMSG); - /* get text for error message */ - sprintf(pr->Msg, "%s - section: %s", errStr, SectTxt[sect]); + sprintf(pr->Msg, "%s - section: %s", geterrmsg(err, errStr), SectTxt[sect]); // append ID? /* Retrieve ID label of object with input error */ diff --git a/src/types.h b/src/types.h index 1984b28..58f5137 100755 --- a/src/types.h +++ b/src/types.h @@ -51,7 +51,7 @@ typedef int INT4; #define TITLELEN 79 // Max. # characters in a title line #define MAXID 31 /* Max. # characters in ID name */ #define MAXMSG 255 /* Max. # characters in message text */ -#define MAXLINE 255 /* Max. # characters read from input line */ +#define MAXLINE 1024 /* Max. # characters read from input line */ #define MAXFNAME 259 /* Max. # characters in file name */ #define MAXTOKS 40 /* Max. items per line of input */ #define TZERO 1.E-4 /* Zero time tolerance */ From e9161adbe621799a2a717cceae1c6035821dcb85 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 08:48:41 -0400 Subject: [PATCH 11/21] Defined EN_MAXID & EN_MAXMSG limits in API header --- include/epanet2.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/epanet2.h b/include/epanet2.h index 1593783..842875c 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -67,6 +67,9 @@ // --- Define the EPANET toolkit constants +#define EN_MAXID 31 /**< Max. # characters in ID name */ +#define EN_MAXMSG 255 /**< Max. # characters in message text */ + /// Node property codes typedef enum { EN_ELEVATION = 0, /**< Node Elevation */ From 8286918b94e2c073d5ec88b1dbe2bfd27aab04b3 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 09:05:30 -0400 Subject: [PATCH 12/21] Removed unused runconcurrent() function --- src/epanet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index d4c004f..b26c7b6 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -143,7 +143,7 @@ void errorLookup(int errcode, char *errmsg, int len); LEGACY (v <= 2.1) API: uses global project variable *****************************************************************/ - +/* int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *reportfile, const char *outputfile, void(*pviewprog)(char *)) { @@ -180,7 +180,7 @@ int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *report return errcode; } - +*/ /*------------------------------------------------------------------------ ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file From 9d0b738e771efbd87def518ece8f734c83fe4918 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 09:36:21 -0400 Subject: [PATCH 13/21] Removed writecon function and all calls to it --- src/epanet.c | 58 ++++++++++++++++++++++++++-------------------------- src/funcs.h | 2 +- src/report.c | 12 +++++------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index b26c7b6..e8b5eb9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -792,8 +792,8 @@ int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const writelogo(p); /* Find network size & allocate memory for data */ - writecon(FMT02); - writewin(p->viewprog, FMT100); +//// writecon(FMT02); +//// writewin(p->viewprog, FMT100); ERRCODE(netsize(p)); ERRCODE(allocdata(p)); @@ -920,7 +920,7 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) if (!errcode) { /* Initialize hydraulics */ errcode = EN_initH(ph, EN_SAVE); - writecon(FMT14); +//// writecon(FMT14); /* Analyze each hydraulic period */ if (!errcode) @@ -932,7 +932,7 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - writecon(p->Msg); +//// writecon(p->Msg); sprintf(p->Msg, FMT101, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -941,14 +941,14 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) ERRCODE(EN_runH(ph, &t)); ERRCODE(EN_nextH(ph, &tstep)); /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); +//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } /* Close hydraulics solver */ /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); +//// writecon("\b\b\b\b\b\b\b\b "); EN_closeH(ph); errcode = MAX(errcode, p->Warnflag); @@ -1190,12 +1190,12 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { if (!errcode) { /* Initialize WQ */ errcode = EN_initQ(ph, EN_SAVE); - if (p->quality.Qualflag) - writecon(FMT15); - else { - writecon(FMT16); - writewin(p->viewprog, FMT103); - } +//// if (p->quality.Qualflag) +//// writecon(FMT15); +//// else { +//// writecon(FMT16); + if (!p->quality.Qualflag) writewin(p->viewprog, FMT103); +//// } /* Analyze each hydraulic period */ if (!errcode) @@ -1207,7 +1207,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - writecon(p->Msg); +//// writecon(p->Msg); if (p->quality.Qualflag) { sprintf(p->Msg, FMT102, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -1219,7 +1219,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { ERRCODE(EN_nextQ(ph, &tstep)); /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); +//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } @@ -1227,7 +1227,7 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { /* Close WQ solver */ /*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); +//// writecon("\b\b\b\b\b\b\b\b "); EN_closeQ(ph); return set_error(p->error_handle, errcode); } @@ -4064,20 +4064,20 @@ int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) /* Check that file names are not identical */ if (strcomp(f1, f2) || strcomp(f1, f3) || (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { - writecon(FMT04); +//// writecon(FMT04); return 301; } /* Attempt to open input and report files */ if ((par->InFile = fopen(f1, "rt")) == NULL) { - writecon(FMT05); - writecon(f1); +//// writecon(FMT05); +//// writecon(f1); return 302; } if (strlen(f2) == 0) rep->RptFile = stdout; else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { - writecon(FMT06); +//// writecon(FMT06); return 303; } @@ -4208,7 +4208,7 @@ int openoutfile(EN_Project *p) if (out->Outflag == SAVE) { if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { - writecon(FMT07); +//// writecon(FMT07); errcode = 304; } } @@ -4218,7 +4218,7 @@ int openoutfile(EN_Project *p) getTmpName(p, out->OutFname); if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { - writecon(FMT08); +//// writecon(FMT08); errcode = 304; } } @@ -4733,25 +4733,25 @@ void errmsg(EN_Project *p, int errcode) { if (errcode == 309) /* Report file write error - */ { /* Do not write msg to file. */ - writecon("\n "); - writecon(geterrmsg(errcode,p->Msg)); +//// writecon("\n "); +//// writecon(geterrmsg(errcode,p->Msg)); } else if (p->report.RptFile != NULL && p->report.Messageflag) { writeline(p, geterrmsg(errcode,p->Msg)); } } -void writecon(const char *s) +//void writecon(const char *s) /*---------------------------------------------------------------- ** Input: text string ** Output: none ** Purpose: writes string of characters to console **---------------------------------------------------------------- */ -{ - - fprintf(stdout, "%s", s); - fflush(stdout); -} +//{ +// +// fprintf(stdout, "%s", s); +// fflush(stdout); +//} void writewin(void (*vp)(char *), char *s) /*---------------------------------------------------------------- diff --git a/src/funcs.h b/src/funcs.h index b0b027f..082b576 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -52,7 +52,7 @@ int findvalve(EN_Network *n, int); /* Find valve index from node int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ void errmsg(EN_Project *p, int); /* Reports program error */ -void writecon(const char *); /* Writes text to console */ +////void writecon(const char *); /* Writes text to console */ void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ /* ------- INPUT1.C --------------------*/ diff --git a/src/report.c b/src/report.c index 058ab22..b0ee1b3 100644 --- a/src/report.c +++ b/src/report.c @@ -82,8 +82,8 @@ int writereport(EN_Project *pr) /* write formatted output to primary report file. */ rep->Fprinterr = FALSE; if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { - writecon(FMT17); - writecon(rep->Rpt1Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -95,8 +95,8 @@ int writereport(EN_Project *pr) /* If secondary report file has same name as either input */ /* or primary report file then use primary report file. */ if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { - writecon(FMT17); - writecon(rep->Rpt1Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -117,8 +117,8 @@ int writereport(EN_Project *pr) /* Write full formatted report to file */ else { rep->Rptflag = 1; - writecon(FMT17); - writecon(rep->Rpt2Fname); +//// writecon(FMT17); +//// writecon(rep->Rpt2Fname); writelogo(pr); if (rep->Summaryflag) writesummary(pr); From 320dec3ff7a2172a6857b8b7114725a9a4331d0c Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 11:00:00 -0400 Subject: [PATCH 14/21] Replaced main() for runnable version of the library --- run/main.c | 143 ++++++++++++++++++++++----------------------------- src/epanet.c | 99 ++++------------------------------- src/funcs.h | 1 - src/report.c | 6 --- src/text.h | 14 ++--- 5 files changed, 76 insertions(+), 187 deletions(-) diff --git a/run/main.c b/run/main.c index 5d811ee..00bc4ee 100644 --- a/run/main.c +++ b/run/main.c @@ -1,33 +1,26 @@ -#include -#include -#include "epanet2.h" - -#define MAXMSG 255 /* Max. # characters in message text */ -#define MAXWARNCODE 99 -/* text copied here, no more need of include "text.h" */ -#define FMT01 "\nEPANET Version %d.%d.%d\n" -#define FMT03 "\nUsage:\n %s []\n" -#define FMT09 "\n\nEPANET completed.\n" -#define FMT10 "\nEPANET completed. There are warnings.\n" -#define FMT11 "\nEPANET completed. There are errors.\n" - - -void writeConsole(char *s); - - /* ---------------------------------------------------------------- - Entry point used to compile a stand-alone executable. +Command line executable for the EPANET water distribution system +analysis program using the EPANET API library. ---------------------------------------------------------------- */ +#include +#include "epanet2.h" -int main(int argc, char *argv[]) +// Function for writing progress messages to the console +void writeConsole(char *s) +{ + fprintf(stdout, "\r%s", s); + fflush(stdout); +} + +int main(int argc, char *argv[]) /*-------------------------------------------------------------- ** Input: argc = number of command line arguments ** *argv[] = array of command line arguments ** Output: none - ** Purpose: main program segment + ** Purpose: main program stub for command line EPANET ** ** Command line for stand-alone operation is: ** progname f1 f2 f3 @@ -38,70 +31,56 @@ int main(int argc, char *argv[]) **-------------------------------------------------------------- */ { - char *f1,*f2,*f3; - char blank[] = ""; - char errmsg[MAXMSG+1]=""; - int errcode; - int version; - int major; - int minor; - int patch; + char *f1,*f2,*f3; + char blank[] = ""; + int errcode, version, major, minor, patch; + EN_ProjectHandle ph; + + // Check for proper number of command line arguments + if (argc < 2) + { + printf( + "\nUsage:\n %s []\n", + argv[0]); + return 0; + } - /* get version from DLL and trasform in Major.Minor.Patch format - instead of hardcoded version */ - ENgetversion(&version); - major= version/10000; - minor= (version%10000)/100; - patch= version%100; - printf(FMT01, major, minor, patch); + // Get version number and display in Major.Minor.Patch format + ENgetversion(&version); + major = version/10000; + minor = (version%10000)/100; + patch = version%100; + printf("\n... Running EPANET Version %d.%d.%d\n", major, minor, patch); - /* Check for proper number of command line arguments */ - if (argc < 2) { - printf(FMT03, argv[0]); - return(1); - } + // Assign pointers to file names + f1 = argv[1]; + if (argc > 2) f2 = argv[2]; // set rptfile name + else f2 = blank; // use stdout for rptfile + if (argc > 3) f3 = argv[3]; // set binary output file name + else f3 = blank; // no binary output file - /* set inputfile name */ - f1 = argv[1]; - if (argc > 2) { - /* set rptfile name */ - f2 = argv[2]; - } - else { - /* use stdout for rptfile */ - f2 = blank; - } - if (argc > 3) { - /* set binary output file name */ - f3 = argv[3]; - } - else { - /* NO binary output*/ - f3 = blank; - } + // Create a project, run it, and delete it + EN_createproject(&ph); + errcode = EN_runproject(ph, f1, f2, f3, &writeConsole); + EN_deleteproject(&ph); - /* Call the main control function */ - errcode = ENepanet(f1,f2,f3,NULL); + // Blank out the last progress message + printf("\r "); - /* Error/Warning check */ - if (errcode == 0) { - /* no errors */ - printf(FMT09); - return(0); - } - else { - if (errcode > MAXWARNCODE) printf("\n Fatal Error: "); - ENgeterror(errcode, errmsg, MAXMSG); - printf("%s\n", errmsg); - if (errcode > MAXWARNCODE) { - // error // - printf(FMT11); - return(errcode); - } - else { - // warning // - printf(FMT10); - return(0); - } - } -} /* End of main */ + // Report run's status + if (errcode == 0) + { + printf("\n... EPANET ran successfully.\n"); + } + else if (errcode < 100) + { + printf( + "\n... EPANET ran with warnings - see the Status Report.\n"); + } + else + { + printf( + "\n... EPANET failed with ERROR %d - see the Status Report\n", errcode); + } + return errcode; +} diff --git a/src/epanet.c b/src/epanet.c index e8b5eb9..74ca6ce 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -143,44 +143,7 @@ void errorLookup(int errcode, char *errmsg, int len); LEGACY (v <= 2.1) API: uses global project variable *****************************************************************/ -/* -int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *reportfile, - const char *outputfile, void(*pviewprog)(char *)) -{ - long t, tstep_h, tstep_q; - int errcode = 0; - EN_Project *p = NULL; - - - ERRCODE(EN_open(ph, inputfile, reportfile, outputfile)); - p = (EN_Project*)(ph); - p->viewprog = pviewprog; - - ERRCODE(EN_openH(ph)); - ERRCODE(EN_initH(ph, EN_SAVE)); - - ERRCODE(EN_openQ(ph)); - ERRCODE(EN_initQ(ph, EN_SAVE)); - - do { - ERRCODE(EN_runH(ph, &t)); - ERRCODE(EN_runQ(ph, &t)); - - ERRCODE(EN_nextH(ph, &tstep_h)); - ERRCODE(EN_nextQ(ph, &tstep_q)); - - } while (tstep_h > 0); - - ERRCODE(EN_closeH(ph)); - ERRCODE(EN_closeQ(ph)); - - ERRCODE(EN_report(ph)); - ERRCODE(EN_close(ph)); - - return errcode; -} -*/ /*------------------------------------------------------------------------ ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file @@ -201,14 +164,17 @@ int runconcurrent(EN_ProjectHandle ph, const char *inputfile, const char *report 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; ERRCODE(EN_createproject(&_defaultModel)); ERRCODE(EN_runproject(_defaultModel, f1, f2, f3, pviewprog)); + if (errcode < 100) warncode = errcode; ERRCODE(EN_deleteproject(&_defaultModel)); + if (warncode) errcode = MAX(errcode, warncode); return (errcode); } @@ -682,7 +648,8 @@ int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, const char *f2, ERRCODE(EN_report(ph)); EN_close(ph); - + + if (p->Warnflag) errcode = MAX(errcode, p->Warnflag); return errcode; } @@ -792,8 +759,7 @@ int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const writelogo(p); /* Find network size & allocate memory for data */ -//// writecon(FMT02); -//// writewin(p->viewprog, FMT100); + writewin(p->viewprog, FMT100); ERRCODE(netsize(p)); ERRCODE(allocdata(p)); @@ -920,19 +886,14 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) if (!errcode) { /* Initialize hydraulics */ errcode = EN_initH(ph, EN_SAVE); -//// writecon(FMT14); /* Analyze each hydraulic period */ if (!errcode) do { /* Display progress message */ - - /*** Updated 6/24/02 ***/ sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - -//// writecon(p->Msg); sprintf(p->Msg, FMT101, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -940,16 +901,10 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) tstep = 0; ERRCODE(EN_runH(ph, &t)); ERRCODE(EN_nextH(ph, &tstep)); - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b\b\b"); } while (tstep > 0); } /* Close hydraulics solver */ - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b "); - EN_closeH(ph); errcode = MAX(errcode, p->Warnflag); @@ -1190,24 +1145,15 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { if (!errcode) { /* Initialize WQ */ errcode = EN_initQ(ph, EN_SAVE); -//// if (p->quality.Qualflag) -//// writecon(FMT15); -//// else { -//// writecon(FMT16); - if (!p->quality.Qualflag) writewin(p->viewprog, FMT103); -//// } + if (!p->quality.Qualflag) writewin(p->viewprog, FMT106); /* Analyze each hydraulic period */ if (!errcode) do { /* Display progress message */ - - /*** Updated 6/24/02 ***/ sprintf(p->Msg, "%-10s", clocktime(p->report.Atime, p->time_options.Htime)); - -//// writecon(p->Msg); if (p->quality.Qualflag) { sprintf(p->Msg, FMT102, p->report.Atime); writewin(p->viewprog, p->Msg); @@ -1217,17 +1163,10 @@ int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { tstep = 0; ERRCODE(EN_runQ(ph, &t)); ERRCODE(EN_nextQ(ph, &tstep)); - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b\b\b"); - } while (tstep > 0); } /* Close WQ solver */ - - /*** Updated 6/24/02 ***/ -//// writecon("\b\b\b\b\b\b\b\b "); EN_closeQ(ph); return set_error(p->error_handle, errcode); } @@ -1355,6 +1294,7 @@ int DLLEXPORT EN_report(EN_ProjectHandle ph) { /* Check if results saved to binary output file */ if (!p->save_options.SaveQflag) return set_error(p->error_handle, 106); + writewin(p->viewprog, FMT103); errcode = writereport(p); if (errcode) errmsg(p, errcode); @@ -4064,20 +4004,16 @@ int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) /* Check that file names are not identical */ if (strcomp(f1, f2) || strcomp(f1, f3) || (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { -//// writecon(FMT04); return 301; } /* Attempt to open input and report files */ if ((par->InFile = fopen(f1, "rt")) == NULL) { -//// writecon(FMT05); -//// writecon(f1); return 302; } if (strlen(f2) == 0) rep->RptFile = stdout; else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { -//// writecon(FMT06); return 303; } @@ -4208,7 +4144,6 @@ int openoutfile(EN_Project *p) if (out->Outflag == SAVE) { if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { -//// writecon(FMT07); errcode = 304; } } @@ -4218,7 +4153,6 @@ int openoutfile(EN_Project *p) getTmpName(p, out->OutFname); if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { -//// writecon(FMT08); errcode = 304; } } @@ -4715,6 +4649,7 @@ char *geterrmsg(int errcode, char *msg) { switch (errcode) { /* Warnings */ #define DAT(code,enumer,string) case code: strcpy(msg, string); break; +//#define DAT(code,enumer,string) case code: sprintf(msg, "Error %d: %s", code, string); break; #include "errors.dat" #undef DAT default: @@ -4733,26 +4668,12 @@ void errmsg(EN_Project *p, int errcode) { if (errcode == 309) /* Report file write error - */ { /* Do not write msg to file. */ -//// writecon("\n "); -//// writecon(geterrmsg(errcode,p->Msg)); + } else if (p->report.RptFile != NULL && p->report.Messageflag) { writeline(p, geterrmsg(errcode,p->Msg)); } } -//void writecon(const char *s) -/*---------------------------------------------------------------- -** Input: text string -** Output: none -** Purpose: writes string of characters to console -**---------------------------------------------------------------- -*/ -//{ -// -// fprintf(stdout, "%s", s); -// fflush(stdout); -//} - void writewin(void (*vp)(char *), char *s) /*---------------------------------------------------------------- ** Input: text string diff --git a/src/funcs.h b/src/funcs.h index 082b576..bf1d913 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -52,7 +52,6 @@ int findvalve(EN_Network *n, int); /* Find valve index from node int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ void errmsg(EN_Project *p, int); /* Reports program error */ -////void writecon(const char *); /* Writes text to console */ void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ /* ------- INPUT1.C --------------------*/ diff --git a/src/report.c b/src/report.c index b0ee1b3..cbb0e11 100644 --- a/src/report.c +++ b/src/report.c @@ -82,8 +82,6 @@ int writereport(EN_Project *pr) /* write formatted output to primary report file. */ rep->Fprinterr = FALSE; if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { -//// writecon(FMT17); -//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -95,8 +93,6 @@ int writereport(EN_Project *pr) /* If secondary report file has same name as either input */ /* or primary report file then use primary report file. */ if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { -//// writecon(FMT17); -//// writecon(rep->Rpt1Fname); if (rep->Energyflag) writeenergy(pr); errcode = writeresults(pr); @@ -117,8 +113,6 @@ int writereport(EN_Project *pr) /* Write full formatted report to file */ else { rep->Rptflag = 1; -//// writecon(FMT17); -//// writecon(rep->Rpt2Fname); writelogo(pr); if (rep->Summaryflag) writesummary(pr); diff --git a/src/text.h b/src/text.h index 656b3c2..b428be1 100755 --- a/src/text.h +++ b/src/text.h @@ -449,19 +449,15 @@ AUTHOR: L. Rossman #define FMT82 "\n\f\n Page %-d %60.60s\n" /* ------------------- Progress Messages ---------------------- */ -#define FMT100 "Retrieving network data..." -#define FMT101 "Computing hydraulics at hour %s" -#define FMT102 "Computing water quality at hour %s" -#define FMT103 "Saving results to file..." +#define FMT100 " Retrieving network data ... " +#define FMT101 " Computing hydraulics at hour %-10s " +#define FMT102 " Computing water quality at hour %-10s " +#define FMT103 " Writing output report ... " +#define FMT106 " Transferring results to file ... " #define FMT104 "Analysis begun %s" #define FMT105 "Analysis ended %s" /*------------------- Error Messages --------------------*/ - - - - - #define R_ERR201 "Input Error 201: syntax error in following line of " #define R_ERR202 "Input Error 202: illegal numeric value in following line of " #define R_ERR203 "Input Error 203: undefined node in following line of " From 27cc088c4a9089f604e992db6317adaf44603a6b Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sat, 27 Oct 2018 11:31:06 -0400 Subject: [PATCH 15/21] Reverted back to old main() Trying to see why new version of main causes some CI reg tests to fail. --- run/main.c | 143 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/run/main.c b/run/main.c index 00bc4ee..5d811ee 100644 --- a/run/main.c +++ b/run/main.c @@ -1,26 +1,33 @@ +#include +#include +#include "epanet2.h" + +#define MAXMSG 255 /* Max. # characters in message text */ +#define MAXWARNCODE 99 +/* text copied here, no more need of include "text.h" */ +#define FMT01 "\nEPANET Version %d.%d.%d\n" +#define FMT03 "\nUsage:\n %s []\n" +#define FMT09 "\n\nEPANET completed.\n" +#define FMT10 "\nEPANET completed. There are warnings.\n" +#define FMT11 "\nEPANET completed. There are errors.\n" + + +void writeConsole(char *s); + + /* ---------------------------------------------------------------- -Command line executable for the EPANET water distribution system -analysis program using the EPANET API library. + Entry point used to compile a stand-alone executable. ---------------------------------------------------------------- */ -#include -#include "epanet2.h" -// Function for writing progress messages to the console -void writeConsole(char *s) -{ - fprintf(stdout, "\r%s", s); - fflush(stdout); -} - -int main(int argc, char *argv[]) +int main(int argc, char *argv[]) /*-------------------------------------------------------------- ** Input: argc = number of command line arguments ** *argv[] = array of command line arguments ** Output: none - ** Purpose: main program stub for command line EPANET + ** Purpose: main program segment ** ** Command line for stand-alone operation is: ** progname f1 f2 f3 @@ -31,56 +38,70 @@ int main(int argc, char *argv[]) **-------------------------------------------------------------- */ { - char *f1,*f2,*f3; - char blank[] = ""; - int errcode, version, major, minor, patch; - EN_ProjectHandle ph; - - // Check for proper number of command line arguments - if (argc < 2) - { - printf( - "\nUsage:\n %s []\n", - argv[0]); - return 0; - } + char *f1,*f2,*f3; + char blank[] = ""; + char errmsg[MAXMSG+1]=""; + int errcode; + int version; + int major; + int minor; + int patch; - // Get version number and display in Major.Minor.Patch format - ENgetversion(&version); - major = version/10000; - minor = (version%10000)/100; - patch = version%100; - printf("\n... Running EPANET Version %d.%d.%d\n", major, minor, patch); + /* get version from DLL and trasform in Major.Minor.Patch format + instead of hardcoded version */ + ENgetversion(&version); + major= version/10000; + minor= (version%10000)/100; + patch= version%100; + printf(FMT01, major, minor, patch); - // Assign pointers to file names - f1 = argv[1]; - if (argc > 2) f2 = argv[2]; // set rptfile name - else f2 = blank; // use stdout for rptfile - if (argc > 3) f3 = argv[3]; // set binary output file name - else f3 = blank; // no binary output file + /* Check for proper number of command line arguments */ + if (argc < 2) { + printf(FMT03, argv[0]); + return(1); + } - // Create a project, run it, and delete it - EN_createproject(&ph); - errcode = EN_runproject(ph, f1, f2, f3, &writeConsole); - EN_deleteproject(&ph); + /* set inputfile name */ + f1 = argv[1]; + if (argc > 2) { + /* set rptfile name */ + f2 = argv[2]; + } + else { + /* use stdout for rptfile */ + f2 = blank; + } + if (argc > 3) { + /* set binary output file name */ + f3 = argv[3]; + } + else { + /* NO binary output*/ + f3 = blank; + } - // Blank out the last progress message - printf("\r "); + /* Call the main control function */ + errcode = ENepanet(f1,f2,f3,NULL); - // Report run's status - if (errcode == 0) - { - printf("\n... EPANET ran successfully.\n"); - } - else if (errcode < 100) - { - printf( - "\n... EPANET ran with warnings - see the Status Report.\n"); - } - else - { - printf( - "\n... EPANET failed with ERROR %d - see the Status Report\n", errcode); - } - return errcode; -} + /* Error/Warning check */ + if (errcode == 0) { + /* no errors */ + printf(FMT09); + return(0); + } + else { + if (errcode > MAXWARNCODE) printf("\n Fatal Error: "); + ENgeterror(errcode, errmsg, MAXMSG); + printf("%s\n", errmsg); + if (errcode > MAXWARNCODE) { + // error // + printf(FMT11); + return(errcode); + } + else { + // warning // + printf(FMT10); + return(0); + } + } +} /* End of main */ From 8514929622454f0ab8659f83aaef859b232db6a4 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sun, 28 Oct 2018 16:58:43 -0400 Subject: [PATCH 16/21] Rewrite of EN_setlinktype function (#305) - Complete rewrite of EN_setlinktype with link index argument passed by reference - New unit test of EN_setlinktype added - New function EN_setlinknodes added --- ReleaseNotes2_2.md | 3 +- include/epanet2.bas | 3 +- include/epanet2.h | 20 ++++++-- include/epanet2.vb | 2 + src/epanet.c | 99 ++++++++++++++++++++++++++++++++---- tests/test_setlinktype.cpp | 100 +++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 tests/test_setlinktype.cpp diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 2b335f3..89adf5d 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -29,7 +29,7 @@ int runEpanet(char *finp, char *frpt) if (!err) err = EN_solveH(ph); if (!err) err = EN_report(ph); EN_close(ph); - EN_deleteproject(ph); + EN_deleteproject(&ph); return err; } ``` @@ -149,6 +149,7 @@ Both network files are available [here](https://doi.org/10.23719/1375314). |`ENgetruleID`|| |`ENinit`|| |`ENsetheadcurveindex`|| +|`ENsetlinknodes`|| |`ENsetlinktype`|| |`ENaddnode`|| |`ENaddlink`|| diff --git a/include/epanet2.bas b/include/epanet2.bas index c4516f1..8d29b59 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -288,7 +288,8 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Long, ByVal HeadlossFormula As Long) As Long Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, ByVal curveIndex As Long) As Long - Declare Function ENsetlinktype Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long) As Long + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Long, ByVal node1 As Long, ByVal node2 As Long) As Long + Declare Function ENsetlinktype Lib "epanet2.dll" (index As Long, ByVal code As Long) As Long Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Long) As Long Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Long, ByVal fromNode As String, ByVal toNode As String) As Long Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long diff --git a/include/epanet2.h b/include/epanet2.h index 842875c..2601335 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -743,12 +743,12 @@ extern "C" { /** @brief Set the link type code for a specified link - @param id The id of a link - @param type The 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. @return Error code @see EN_LinkType */ - int DLLEXPORT ENsetlinktype(char *id, EN_LinkType type); + int DLLEXPORT ENsetlinktype(int *index, EN_LinkType code); /** @brief Get the indexes of a link's start- and end-nodes. @@ -872,6 +872,16 @@ extern "C" { */ int DLLEXPORT ENsetlinkid(int index, char *newid); + /** + @brief Set the indexes of a link's start- and end-nodes. + @param index The index of a link (first link is index 1) + @param node1 The index of the link's start node (first node is index 1). + @param node2 The index of the link's end node (first node is index 1). + @return Error code + @see ENsetnodeid, ENsetlinkid + */ + int DLLEXPORT ENsetlinknodes(int index, int node1, int node2); + /** @brief Set a property value for a link. @param index The index of a link. First link is index 1. @@ -1064,7 +1074,6 @@ extern "C" { @see ENgetcurveindex ENsetcurve */ 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. @@ -1303,7 +1312,6 @@ extern "C" { 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_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType type); 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); @@ -1319,6 +1327,8 @@ extern "C" { 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_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_addpattern(EN_ProjectHandle ph, char *id); int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int len); diff --git a/include/epanet2.vb b/include/epanet2.vb index 5898dfd..2c6d5f3 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -230,6 +230,8 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Int32, ByVal node1 As Int32, ByVal node2 As Int32) As Int32 + Declare Function ENsetlinktype Lib "epanet2.dll" (ByRef index As Int32, ByVal code As Int32) As Int32 Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetpattern Lib "epanet2.dll" (ByVal Index as Int32, ByRef F as Single, ByVal N as Int32) as Int32 Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByVal Value As Single) As Int32 diff --git a/src/epanet.c b/src/epanet.c index 74ca6ce..3fc97d9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -415,6 +415,14 @@ int DLLEXPORT ENsetlinkid(int index, char *newid) { return EN_setlinkid(_defaultModel, index, newid); } +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 ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { return EN_setlinkvalue(_defaultModel, index, code, v); } @@ -567,10 +575,6 @@ int DLLEXPORT ENgetruleID(int indexRule, char* id){ return EN_getruleID(_defaultModel, indexRule, id); } -int DLLEXPORT ENsetlinktype(char *id, EN_LinkType toType) { - return EN_setlinktype(_defaultModel, id, toType); -} - int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) { return EN_addnode(_defaultModel, id, nodeType); } @@ -3160,6 +3164,37 @@ int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) return set_error(p->error_handle, 0); } +int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int node2) +{ + int type; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + + // Check that nodes exist + if (node1 < 0 || node1 > net->Nnodes) return set_error(p->error_handle, 203); + if (node2 < 0 || node2 > net->Nnodes) return set_error(p->error_handle, 203); + + // Check for illegal valve connection + type = net->Link[index].Type; + if (type == EN_PRV || type == EN_PSV || type == EN_FCV) + { + // Can't be connected to a fixed grade node + if (node1 > net->Njuncs || + node2 > net->Njuncs) return set_error(p->error_handle, 219); + + // Can't be connected to another pressure/flow control valve + if (!valvecheck(p, type, node1, node2)) + { + return set_error(p->error_handle, 220); + } + } + + // Assign new end nodes to link + net->Link[index].N1 = node1; + net->Link[index].N2 = node2; + return set_error(p->error_handle, 0); +} + int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) @@ -4809,7 +4844,7 @@ int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx } int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int patIndex) { - + EN_Project *pr = (EN_Project*)ph; EN_Network *net = &pr->network; @@ -4884,7 +4919,52 @@ 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, char *id, EN_LinkType toType) { +int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) { + + int i = *index, n1, n2; + char id[MAXID+1]; + char id1[MAXID+1]; + char id2[MAXID+1]; + int errcode; + EN_LinkType oldtype; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + + if (!p->Openflag) return set_error(p->error_handle, 102); + if (type < 0 || type > EN_GPV) return set_error(p->error_handle, 211); + + // Check if a link with the id exists + if (i <= 0 || i > net->Nlinks) return set_error(p->error_handle, 204); + + // Get the current type of the link + EN_getlinktype(p, i, &oldtype); + if (oldtype == type) return set_error(p->error_handle, 0); + + // Pipe changing from or to having a check valve + if (oldtype <= EN_PIPE && type <= EN_PIPE) + { + net->Link[i].Type = type; + if (type == EN_CVPIPE) net->Link[i].Stat = OPEN; + return set_error(p->error_handle, 0); + } + + // Get ID's of link & its end nodes + EN_getlinkid(ph, i, id); + EN_getlinknodes(ph, i, &n1, &n2); + EN_getnodeid(ph, n1, id1); + EN_getnodeid(ph, n2, id2); + + // Delete the original link + EN_deletelink(ph, i); + + // Create a new link of new type and old id + errcode = EN_addlink(ph, id, type, id1, id2); + + // Find the index of this new link + EN_getlinkindex(ph, id, index); + return set_error(p->error_handle, errcode); + + /*********************************************** int i; EN_LinkType fromType; @@ -4895,16 +4975,16 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) if (!p->Openflag) return set_error(p->error_handle, 102); - /* Check if a link with the id exists */ + // Check if a link with the id exists if (EN_getlinkindex(p, id, &i) != 0) return set_error(p->error_handle, 215); - /* Get the current type of the link */ + // Get the current type of the link EN_getlinktype(p, i, &fromType); if (fromType == toType) return set_error(p->error_handle, 0); - /* Change link from Pipe */ + // Change link from Pipe if (toType <= EN_PIPE) { net->Npipes++; } else if (toType == EN_PUMP) { @@ -4921,6 +5001,7 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, char *id, EN_LinkType toType) net->Npumps--; } return set_error(p->error_handle, 0); +**********************************************/ } int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { diff --git a/tests/test_setlinktype.cpp b/tests/test_setlinktype.cpp new file mode 100644 index 0000000..bf225fe --- /dev/null +++ b/tests/test_setlinktype.cpp @@ -0,0 +1,100 @@ +// Test of ENsetlinktype EPANET API Function +#define _CRT_SECURE_NO_DEPRECATE + +/* +This is a test for the API function that changes a link's type. +Two links in Net1.inp are changed: Pipe 113 is reversed with a CV added +and Pipe 121 is changed to a 100 psi PRV. After running the revised model, +at hour 0 the flow in Pipe 113 should be zero and the pressure at node 31 +of the PRV 121 should be 100. +*/ + +#define BOOST_TEST_MODULE "toolkit" +#include + +#include +#include "epanet2.h" + +#define DATA_PATH_INP "./net1.inp" +#define DATA_PATH_RPT "./test.rpt" +#define DATA_PATH_OUT "./test.out" + +using namespace std; + +BOOST_AUTO_TEST_SUITE (test_toolkit) + +BOOST_AUTO_TEST_CASE(test_setlinktype) +{ + int error = 0; + int p113, n31, p121, n113_1, n113_2; + float q113 = 0.0f, p31 = 0.0f, diam; + + 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); + + // Change duration to 0 + error = EN_settimeparam(ph, EN_DURATION, 0); + BOOST_REQUIRE(error == 0); + + // Get indexes of pipe 113 and node 31 + error = EN_getlinkindex(ph, (char *)"113", &p113); + BOOST_REQUIRE(error == 0); + error = EN_getnodeindex(ph, (char *)"31", &n31); + BOOST_REQUIRE(error == 0); + + // Reverse pipe 113 and give it a check valve + error = EN_getlinknodes(ph, p113, &n113_1, &n113_2); + BOOST_REQUIRE(error == 0); + error = EN_setlinknodes(ph, p113, n113_2, n113_1); + BOOST_REQUIRE(error == 0); + error = EN_setlinktype(ph, &p113, EN_CVPIPE); + BOOST_REQUIRE(error == 0); + + // Get index & diameter of pipe 121 connected to node 31 + error = EN_getlinkindex(ph, (char *)"121", &p121); + BOOST_REQUIRE(error == 0); + error = EN_getlinkvalue(ph, p121, EN_DIAMETER, &diam); + BOOST_REQUIRE(error == 0); + + // Replace it with a PRV + error = EN_setlinktype(ph, &p121, EN_PRV); + BOOST_REQUIRE(error == 0); + + // Set diameter & setting of new PRV + error = EN_setlinkvalue(ph, p121, EN_INITSETTING, 100); + BOOST_REQUIRE(error == 0); + error = EN_setlinkvalue(ph, p121, EN_DIAMETER, diam); + BOOST_REQUIRE(error == 0); + + // Solve for hydraulics + error = EN_solveH(ph); + BOOST_REQUIRE(error == 0); + + // Get flow in link 113 and pressure at node 31 + error = EN_getlinkvalue(ph, p113, EN_FLOW, &q113); + BOOST_REQUIRE(error == 0); + error = EN_getnodevalue(ph, n31, EN_PRESSURE, &p31); + BOOST_REQUIRE(error == 0); + + // Require that link 113 flow be 0 + q113 = fabs(q113); + BOOST_REQUIRE(q113 < 0.001); + + // Require that node 31 pressure be 100 + p31 = fabs(p31 - 100.0f); + BOOST_REQUIRE(p31 < 0.001); + + // Close and delete project + error = EN_close(ph); + BOOST_REQUIRE(error == 0); + EN_deleteproject(&ph); +} + +BOOST_AUTO_TEST_SUITE_END() From c2b16a64663608ab3127812f10140c369dce3bd2 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Sun, 28 Oct 2018 20:07:54 -0400 Subject: [PATCH 17/21] Deleted previous, commented-out version of EN_setlinktype --- src/epanet.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index 3fc97d9..50618cc 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -4963,45 +4963,6 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) // Find the index of this new link EN_getlinkindex(ph, id, index); return set_error(p->error_handle, errcode); - - /*********************************************** - int i; - EN_LinkType fromType; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - if (!p->Openflag) - return set_error(p->error_handle, 102); - - // Check if a link with the id exists - if (EN_getlinkindex(p, id, &i) != 0) - return set_error(p->error_handle, 215); - - // Get the current type of the link - EN_getlinktype(p, i, &fromType); - if (fromType == toType) - return set_error(p->error_handle, 0); - - // Change link from Pipe - if (toType <= EN_PIPE) { - net->Npipes++; - } else if (toType == EN_PUMP) { - net->Npumps++; - net->Pump[net->Npumps].Link = i; - } else { - net->Nvalves++; - net->Valve[net->Nvalves].Link = i; - } - - if (fromType <= EN_PIPE) { - net->Npipes--; - } else if (fromType == EN_PUMP) { - net->Npumps--; - } - return set_error(p->error_handle, 0); -**********************************************/ } int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { From a86ba29cb64dce3c0322b60308661df06b830301 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 29 Oct 2018 09:06:08 -0400 Subject: [PATCH 18/21] Restored epanet_export.h to \include folder --- include/epanet_export.h | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 include/epanet_export.h diff --git a/include/epanet_export.h b/include/epanet_export.h new file mode 100644 index 0000000..72113e6 --- /dev/null +++ b/include/epanet_export.h @@ -0,0 +1,42 @@ + +#ifndef DLLEXPORT_H +#define DLLEXPORT_H + +#ifdef SHARED_EXPORTS_BUILT_AS_STATIC +# define DLLEXPORT +# define EPANET_NO_EXPORT +#else +# ifndef DLLEXPORT +# ifdef epanet_EXPORTS + /* We are building this library */ +# define DLLEXPORT __declspec(dllexport) +# else + /* We are using this library */ +# define DLLEXPORT __declspec(dllimport) +# endif +# endif + +# ifndef EPANET_NO_EXPORT +# define EPANET_NO_EXPORT +# endif +#endif + +#ifndef EPANET_DEPRECATED +# define EPANET_DEPRECATED __declspec(deprecated) +#endif + +#ifndef EPANET_DEPRECATED_EXPORT +# define EPANET_DEPRECATED_EXPORT DLLEXPORT EPANET_DEPRECATED +#endif + +#ifndef EPANET_DEPRECATED_NO_EXPORT +# define EPANET_DEPRECATED_NO_EXPORT EPANET_NO_EXPORT EPANET_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef EPANET_NO_DEPRECATED +# define EPANET_NO_DEPRECATED +# endif +#endif + +#endif /* DLLEXPORT_H */ From 4394c53f9bf86d9307a7aceca95c5152386d861f Mon Sep 17 00:00:00 2001 From: Elad Salomons Date: Mon, 29 Oct 2018 17:20:45 +0200 Subject: [PATCH 19/21] Add ENsetlinknodes to DEF file and add a function check --- src/epanet.c | 3 +++ win_build/WinSDK/epanet2.def | 1 + 2 files changed, 4 insertions(+) diff --git a/src/epanet.c b/src/epanet.c index 50618cc..8d72f4a 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -3169,6 +3169,9 @@ int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int nod int type; EN_Project *p = (EN_Project*)ph; EN_Network *net = &p->network; + + // Check that end and start nodes are not the same + if (node1 == node2) return set_error(p->error_handle, 222); // Check that nodes exist if (node1 < 0 || node1 > net->Nnodes) return set_error(p->error_handle, 203); diff --git a/win_build/WinSDK/epanet2.def b/win_build/WinSDK/epanet2.def index fd4d4a9..7935207 100644 --- a/win_build/WinSDK/epanet2.def +++ b/win_build/WinSDK/epanet2.def @@ -13,6 +13,7 @@ EXPORTS ENgetlinkid = _ENgetlinkid@8 ENgetlinkindex = _ENgetlinkindex@8 ENgetlinknodes = _ENgetlinknodes@12 + ENsetlinknodes = _ENsetlinknodes@12 ENgetlinktype = _ENgetlinktype@8 ENgetlinkvalue = _ENgetlinkvalue@12 ENgetnodeid = _ENgetnodeid@8 From 5ddb54ab9069fb4ba1f5adfa4cb50d7a0e96f640 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 29 Oct 2018 11:44:25 -0400 Subject: [PATCH 20/21] Deleted include\epanet_export.h It gets generated through the cmake build process. --- include/epanet_export.h | 42 ----------------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 include/epanet_export.h diff --git a/include/epanet_export.h b/include/epanet_export.h deleted file mode 100644 index 72113e6..0000000 --- a/include/epanet_export.h +++ /dev/null @@ -1,42 +0,0 @@ - -#ifndef DLLEXPORT_H -#define DLLEXPORT_H - -#ifdef SHARED_EXPORTS_BUILT_AS_STATIC -# define DLLEXPORT -# define EPANET_NO_EXPORT -#else -# ifndef DLLEXPORT -# ifdef epanet_EXPORTS - /* We are building this library */ -# define DLLEXPORT __declspec(dllexport) -# else - /* We are using this library */ -# define DLLEXPORT __declspec(dllimport) -# endif -# endif - -# ifndef EPANET_NO_EXPORT -# define EPANET_NO_EXPORT -# endif -#endif - -#ifndef EPANET_DEPRECATED -# define EPANET_DEPRECATED __declspec(deprecated) -#endif - -#ifndef EPANET_DEPRECATED_EXPORT -# define EPANET_DEPRECATED_EXPORT DLLEXPORT EPANET_DEPRECATED -#endif - -#ifndef EPANET_DEPRECATED_NO_EXPORT -# define EPANET_DEPRECATED_NO_EXPORT EPANET_NO_EXPORT EPANET_DEPRECATED -#endif - -#if 0 /* DEFINE_NO_DEPRECATED */ -# ifndef EPANET_NO_DEPRECATED -# define EPANET_NO_DEPRECATED -# endif -#endif - -#endif /* DLLEXPORT_H */ From ee335ab0778bf2103297f7e3ebdaaee502fac22e Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Wed, 31 Oct 2018 11:32:01 -0400 Subject: [PATCH 21/21] Fixes bug #172 (adjust controls & rules when node/link added or deleted) - Also adds new API function EN_deletecontrol - Updates and re-arranges entries in the VBA and VB.Net headers --- include/epanet2.bas | 140 +++++++++------- include/epanet2.h | 8 + include/epanet2.vb | 122 +++++++++----- src/epanet.c | 401 +++++++++++++++++++++++--------------------- src/funcs.h | 2 + src/rules.c | 191 +++++++++++++++++---- src/types.h | 2 +- 7 files changed, 535 insertions(+), 331 deletions(-) diff --git a/include/epanet2.bas b/include/epanet2.bas index 8d29b59..e3681ac 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -175,11 +175,15 @@ Public Const EN_G_CURVE = 4 ' General\default curve 'These are the external functions that comprise the DLL +'System Functions + Declare Function ENgetversion Lib "epanet2.dll" (value As Long) As Long Declare Function ENepanet Lib "epanet2.dll" (ByVal F1 As String, ByVal F2 As String, ByVal F3 As String, ByVal F4 As Any) As Long + Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Long, ByVal HeadlossFormula As Long) As Long Declare Function ENopen Lib "epanet2.dll" (ByVal F1 As String, ByVal F2 As String, ByVal F3 As String) As Long Declare Function ENsaveinpfile Lib "epanet2.dll" (ByVal F As String) As Long Declare Function ENclose Lib "epanet2.dll" () As Long +'Hydraulic Analysis Functions Declare Function ENsolveH Lib "epanet2.dll" () As Long Declare Function ENsaveH Lib "epanet2.dll" () As Long Declare Function ENopenH Lib "epanet2.dll" () As Long @@ -190,6 +194,7 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENsavehydfile Lib "epanet2.dll" (ByVal F As String) As Long Declare Function ENusehydfile Lib "epanet2.dll" (ByVal F As String) As Long +'Water Quality Analysis Functions Declare Function ENsolveQ Lib "epanet2.dll" () As Long Declare Function ENopenQ Lib "epanet2.dll" () As Long Declare Function ENinitQ Lib "epanet2.dll" (ByVal SaveFlag As Long) As Long @@ -198,82 +203,97 @@ Public Const EN_G_CURVE = 4 ' General\default curve Declare Function ENstepQ Lib "epanet2.dll" (Tleft As Long) As Long Declare Function ENcloseQ Lib "epanet2.dll" () As Long +'Reporting Functions Declare Function ENwriteline Lib "epanet2.dll" (ByVal S As String) As Long Declare Function ENreport Lib "epanet2.dll" () As Long Declare Function ENresetreport Lib "epanet2.dll" () As Long Declare Function ENsetreport Lib "epanet2.dll" (ByVal S As String) As Long - - Declare Function ENgetcontrol Lib "epanet2.dll" (ByVal Cindex As Long, Ctype As Long, Lindex As Long, setting As Single, Nindex As Long, Level As Single) As Long + 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 + +'Analysis Options Functions Declare Function ENgetoption Lib "epanet2.dll" (ByVal code As Long, value As Single) As Long - Declare Function ENgettimeparam Lib "epanet2.dll" (ByVal code As Long, value As Long) As Long + Declare Function ENsetoption Lib "epanet2.dll" (ByVal code As Long, ByVal value As Single) As Long Declare Function ENgetflowunits Lib "epanet2.dll" (code As Long) As Long + Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Long) As Long + Declare Function ENgettimeparam Lib "epanet2.dll" (ByVal code As Long, value As Long) As Long + Declare Function ENsettimeparam Lib "epanet2.dll" (ByVal code As Long, ByVal value As Long) As Long + Declare Function ENgetqualtype Lib "epanet2.dll" (QualCode As Long, TraceNode As Long) As Long + Declare Function ENgetqualinfo Lib "epanet2.dll" (QualCode As Long, ByVal ChemName As String, ByVal ChemUnits As String, TraceNode As Long) As Long + Declare Function ENsetqualtype Lib "epanet2.dll" (ByVal QualCode As Long, ByVal ChemName As String, ByVal ChemUnits As String, ByVal TraceNode As String) As Long + +'Basic Node Functions + Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Long) As Long + Declare Function ENdeletenode Lib "epanet2.dll" (ByVal linkIndex As Long) As Long + Declare Function ENgetnodeindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long + Declare Function ENgetnodeid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long + Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long + Declare Function ENgetnodetype Lib "epanet2.dll" (ByVal index As Long, code As Long) As Long + Declare Function ENgetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, value As Single) As Long + Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long + Declare Function ENgetcoord Lib "epanet2.dll" (ByVal index As Long, X As Single, Y As Single) As Long + Declare Function ENsetcoord Lib "epanet2.dll" (ByVal index As Long, ByVal X As Single, ByVal Y As Single) As Long + +'Nodal Demand Functions + Declare Function ENgetdemandmodel Lib "epanet2.dll" (mtype As Long, pmin As Single, preq As Single, pexp As Single) As Long + Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal mtype As Long, ByVal pmin As Single, ByVal preq As Single, ByVal pexp As Single) As Long + Declare Function ENgetnumdemands Lib "epanet2.dll" (ByVal index As Long, numDemands As Long) As Long + Declare Function ENgetbasedemand Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, value As Single) As Long + Declare Function ENsetbasedemand Lib "epanet2.dll" (ByVal Index As Long, ByVal DemandIndex As Long, ByVal BaseDemand As Single) As Long + Declare Function ENgetdemandpattern Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, PatIndex As Long) As Long + Declare Function ENsetdemandpattern Lib "epanet2.dll" (ByVal Index As Long, ByVal DemandIndex As Long, ByVal PatIndex As Long) As Long + Declare Function ENgetdemandname Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, ByVal demandName As String) As Long + Declare Function ENsetdemandname Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal DemandIndex As Long, ByVal demandName As String) As Long + +'Basic Link Functions + Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Long, ByVal fromNode As String, ByVal toNode As String) As Long + Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long + Declare Function ENgetlinkindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long + Declare Function ENgetlinkid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long + Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long + Declare Function ENgetlinktype Lib "epanet2.dll" (ByVal index As Long, code As Long) As Long + Declare Function ENsetlinktype Lib "epanet2.dll" (index As Long, ByVal code As Long) As Long + Declare Function ENgetlinknodes Lib "epanet2.dll" (ByVal index As Long, Node1 As Long, Node2 As Long) As Long + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Long, ByVal node1 As Long, ByVal node2 As Long) As Long + Declare Function ENgetlinkvalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, value As Single) As Long + Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long + +'Pump Functions + Declare Function ENgetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, curveIndex As Long) As Long + Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, ByVal curveIndex As Long) As Long + Declare Function ENgetpumptype Lib "epanet2.dll" (ByVal index As Long, PumpType As Long) As Long + +'Time Pattern Functions + Declare Function ENaddpattern Lib "epanet2.dll" (ByVal id As String) As Long Declare Function ENgetpatternindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long Declare Function ENgetpatternid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long Declare Function ENgetpatternlen Lib "epanet2.dll" (ByVal index As Long, L As Long) As Long Declare Function ENgetpatternvalue Lib "epanet2.dll" (ByVal index As Long, ByVal Period As Long, value As Single) As Long - Declare Function ENgetaveragepatternvalue Lib "epanet2.dll" (ByVal index As Long, value As Single) As Long - Declare Function ENgetqualtype Lib "epanet2.dll" (QualCode As Long, TraceNode 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, value As Single) As Long - - Declare Function ENgetnodeindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long - Declare Function ENgetnodeid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long - Declare Function ENgetnodetype Lib "epanet2.dll" (ByVal index As Long, code As Long) As Long - Declare Function ENgetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, value As Single) As Long - Declare Function ENgetcoord Lib "epanet2.dll" (ByVal index As Long, X As Single, Y As Single) As Long - Declare Function ENsetcoord Lib "epanet2.dll" (ByVal index As Long, ByVal X As Single, ByVal Y As Single) As Long - - Declare Function ENgetnumdemands Lib "epanet2.dll" (ByVal index As Long, numDemands As Long) As Long - Declare Function ENgetbasedemand Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, value As Single) As Long - Declare Function ENgetdemandpattern Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, PatIndex As Long) As Long - - Declare Function ENgetdemandname Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, ByVal demandName As String) As Long - Declare Function ENsetdemandname Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal DemandIndex As Long, ByVal demandName As String) As Long - - Declare Function ENgetlinkindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long - Declare Function ENgetlinkid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long - Declare Function ENgetlinktype Lib "epanet2.dll" (ByVal index As Long, code As Long) As Long - Declare Function ENgetlinknodes Lib "epanet2.dll" (ByVal index As Long, Node1 As Long, Node2 As Long) As Long - Declare Function ENgetlinkvalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, value As Single) As Long - - Declare Function ENgetcurve Lib "epanet2.dll" (ByVal curveIndex As Long, ByVal CurveID As String, nValues As Long, xValues As Any, yValues As Any) As Long - Declare Function ENgetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, curveIndex As Long) As Long - Declare Function ENgetpumptype Lib "epanet2.dll" (ByVal index As Long, PumpType As Long) As Long - Declare Function ENgetcurvetype Lib "epanet2.dll" (ByVal curveIndex As Long, CurveType As Long) As Long - - Declare Function ENgetversion Lib "epanet2.dll" (value As Long) As Long - - Declare Function ENgetdemandmodel Lib "epanet2.dll" (mtype As Long, pmin As Single, preq As Single, pexp As Single) As Long - Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal mtype As Long, ByVal pmin As Single, ByVal preq As Single, ByVal pexp As Single) As Long - - Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Long) As Long - Declare Function ENaddcontrol Lib "epanet2.dll" (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 - 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 - Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long - Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long - Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Long, ByVal newid As String) As Long - Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long, ByVal value As Single) As Long - Declare Function ENaddpattern Lib "epanet2.dll" (ByVal id As String) As Long - Declare Function ENsetpattern Lib "epanet2.dll" (ByVal index As Long, F As Any, ByVal N As Long) As Long Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal index As Long, ByVal Period As Long, ByVal value As Single) As Long - Declare Function ENsettimeparam Lib "epanet2.dll" (ByVal code As Long, ByVal value As Long) As Long - Declare Function ENsetoption Lib "epanet2.dll" (ByVal code As Long, ByVal value As Single) As Long - Declare Function ENsetstatusreport Lib "epanet2.dll" (ByVal code As Long) As Long - Declare Function ENsetqualtype Lib "epanet2.dll" (ByVal QualCode As Long, ByVal ChemName As String, ByVal ChemUnits As String, ByVal TraceNode As String) As Long - Declare Function ENgetqualinfo Lib "epanet2.dll" (QualCode As Long, ByVal ChemName As String, ByVal ChemUnits As String, TraceNode As Long) As Long - Declare Function ENsetbasedemand Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal DemandIndex As Long, ByVal BaseDemand As Single) As Long - Declare Function ENsetdemandpattern Lib "epanet2.dll" (ByVal index As Long, ByVal DemandIndex As Long, ByVal PatIndex As Long) As Long + Declare Function ENgetaveragepatternvalue Lib "epanet2.dll" (ByVal index As Long, value As Single) As Long + Declare Function ENsetpattern Lib "epanet2.dll" (ByVal index As Long, F As Any, ByVal N As Long) As Long +'Data Curve Functions + Declare Function ENaddcurve Lib "epanet2.dll" (ByVal id As String) As Long Declare Function ENgetcurveindex Lib "epanet2.dll" (ByVal id As String, index As Long) As Long Declare Function ENgetcurveid Lib "epanet2.dll" (ByVal index As Long, ByVal id As String) As Long Declare Function ENgetcurvelen Lib "epanet2.dll" (ByVal index As Long, L As Long) As Long + Declare Function ENgetcurvetype Lib "epanet2.dll" (ByVal curveIndex As Long, CurveType As Long) As Long Declare Function ENgetcurvevalue Lib "epanet2.dll" (ByVal index As Long, ByVal Ptn As Long, X As Single, Y As Single) As Long Declare Function ENsetcurvevalue Lib "epanet2.dll" (ByVal index As Long, ByVal Ptn As Long, ByVal X As Single, ByVal Y As Single) As Long + Declare Function ENgetcurve Lib "epanet2.dll" (ByVal curveIndex As Long, ByVal CurveID As String, nValues As Long, xValues As Any, yValues As Any) As Long Declare Function ENsetcurve Lib "epanet2.dll" (ByVal index As Long, X As Any, Y As Any, ByVal N As Long) As Long - Declare Function ENaddcurve Lib "epanet2.dll" (ByVal id As String) As Long +'Simple Control Functions + Declare Function ENaddcontrol Lib "epanet2.dll" (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 + Declare Function ENdeletecontrol Lib "epanet2.dll" (ByVal Cindex as Long) As Long + Declare Function ENgetcontrol Lib "epanet2.dll" (ByVal Cindex As Long, Ctype As Long, Lindex As Long, setting As Single, Nindex As Long, Level As Single) As Long + 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 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 Declare Function ENgetpremise Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexPremise As Long, logop As Long, object As Long, indexObj As Long, variable As Long, relop As Long, status As Long, value As Single) As Long Declare Function ENsetpremise Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexPremise As Long, ByVal logop As Long, ByVal object As Long, ByVal indexObj As Long, ByVal variable As Long, ByVal relop As Long, ByVal status As Long, ByVal value As Single) As Long @@ -284,13 +304,3 @@ Public Const EN_G_CURVE = 4 ' General\default curve 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 ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Long, ByVal id As String) As Long - - Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Long, ByVal HeadlossFormula As Long) As Long - Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, ByVal curveIndex As Long) As Long - Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Long, ByVal node1 As Long, ByVal node2 As Long) As Long - Declare Function ENsetlinktype Lib "epanet2.dll" (index As Long, ByVal code As Long) As Long - Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Long) As Long - Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Long, ByVal fromNode As String, ByVal toNode As String) As Long - Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long - Declare Function ENdeletenode Lib "epanet2.dll" (ByVal linkIndex As Long) As Long diff --git a/include/epanet2.h b/include/epanet2.h index 2601335..6ddfd6b 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -833,6 +833,13 @@ extern "C" { @return Error code. */ 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 + @param cindex The index of the control. First control is index 1. + @return Error code. + */ + int DLLEXPORT ENdeletecontrol(int cindex); /** @brief Specify parameters to define a simple control @@ -1371,6 +1378,7 @@ extern "C" { 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); #if defined(__cplusplus) } diff --git a/include/epanet2.vb b/include/epanet2.vb index 2c6d5f3..0c812a3 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -39,6 +39,7 @@ Public Const EN_MIXFRACTION = 22 Public Const EN_TANK_KBULK = 23 Public Const EN_TANKVOLUME = 24 'ES +Public Const EN_MAXVOLUME = 25 Public Const EN_DIAMETER = 0 ' Link parameters Public Const EN_LENGTH = 1 @@ -60,6 +61,9 @@ Public Const EN_EFFICIENCY = 16 Public Const EN_HEADCURVE = 17 Public Const EN_EFFICIENCYCURVE = 18 Public Const EN_PRICEPATTERN = 19 +Public Const EN_STATE = 20 +Public Const EN_CONST_POWER = 21 +Public Const EN_SPEED = 22 Public Const EN_DURATION = 0 ' Time parameters Public Const EN_HYDSTEP = 1 @@ -115,6 +119,10 @@ Public Const EN_MASS = 1 Public Const EN_SETPOINT = 2 Public Const EN_FLOWPACED = 3 +Public Const EN_HW = 0 ' Head loss formula +Public Const EN_DW = 1 +Public Const EN_CM = 2 + Public Const EN_CFS = 0 ' Flow units types Public Const EN_GPM = 1 Public Const EN_MGD = 2 @@ -126,6 +134,9 @@ Public Const EN_MLD = 7 Public Const EN_CMH = 8 Public Const EN_CMD = 9 +Public Const EN_DDA = 0 ' Demand driven analysis +Public Const EN_PDA = 1 ' Pressure driven analysis + Public Const EN_TRIALS = 0 ' Misc. options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 @@ -159,13 +170,23 @@ Public Const EN_CONST_HP = 0 ' constant horsepower Public Const EN_POWER_FUNC = 1 ' power function Public Const EN_CUSTOM = 2 ' user-defined custom curve +Public Const EN_V_CURVE = 0 ' volume curve +Public Const EN_P_CURVE = 1 ' pump curve +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 + 'These are the external functions that comprise the DLL +'System Functions + Declare Function ENgetversion Lib "epanet2.dll" (ByRef Value As Int32) As Int32 Declare Function ENepanet Lib "epanet2.dll" (ByVal F1 As String, ByVal F2 As String, ByVal F3 As String, ByVal F4 As String) As Int32 + Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Int32, ByVal HeadlossFormula As Int32) As Int32 Declare Function ENopen Lib "epanet2.dll" (ByVal F1 As String, ByVal F2 As String, ByVal F3 As String) As Int32 Declare Function ENsaveinpfile Lib "epanet2.dll" (ByVal F As String) As Int32 Declare Function ENclose Lib "epanet2.dll" () As Int32 +'Hydraulic Analysis Functions Declare Function ENsolveH Lib "epanet2.dll" () As Int32 Declare Function ENsaveH Lib "epanet2.dll" () As Int32 Declare Function ENopenH Lib "epanet2.dll" () As Int32 @@ -176,6 +197,7 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENsavehydfile Lib "epanet2.dll" (ByVal F As String) As Int32 Declare Function ENusehydfile Lib "epanet2.dll" (ByVal F As String) As Int32 +'Water Quality Analysis Functions Declare Function ENsolveQ Lib "epanet2.dll" () As Int32 Declare Function ENopenQ Lib "epanet2.dll" () As Int32 Declare Function ENinitQ Lib "epanet2.dll" (ByVal SaveFlag As Int32) As Int32 @@ -184,75 +206,98 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENstepQ Lib "epanet2.dll" (ByRef Tleft As Int32) As Int32 Declare Function ENcloseQ Lib "epanet2.dll" () As Int32 +'Reporting Functions Declare Function ENwriteline Lib "epanet2.dll" (ByVal S As String) As Int32 Declare Function ENreport Lib "epanet2.dll" () As Int32 Declare Function ENresetreport Lib "epanet2.dll" () As Int32 Declare Function ENsetreport Lib "epanet2.dll" (ByVal S As String) As Int32 - - Declare Function ENgetcontrol Lib "epanet2.dll" (ByVal Cindex As Int32, ByRef CtlType As Int32, ByRef Lindex As Int32, ByRef Setting As Single, ByRef Nindex As Int32, ByRef Level As Single) As Int32 + Declare Function ENsetstatusreport Lib "epanet2.dll" (ByVal Code As Int32) As Int32 Declare Function ENgetcount Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Int32) As Int32 - Declare Function ENgetoption Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Single) As Int32 - Declare Function ENgettimeparam Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Int32) As Int32 - Declare Function ENgetflowunits Lib "epanet2.dll" (ByRef Code As Int32) As Int32 - Declare Function ENgetpatternindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 - Declare Function ENgetpatternid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 - Declare Function ENgetpatternlen Lib "epanet2.dll" (ByVal Index As Int32, ByRef L As Int32) As Int32 - Declare Function ENgetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByRef Value As Single) As Int32 - Declare Function ENgetqualtype Lib "epanet2.dll" (ByRef QualCode As Int32, ByRef TraceNode As Int32) As Int32 - Declare Function ENgeterror Lib "epanet2.dll" (ByVal ErrCode As Int32, ByVal ErrMsg As StringBuilder, ByVal N As Int32) + Declare Function ENgeterror Lib "epanet2.dll" (ByVal ErrCode As Int32, ByVal ErrMsg As StringBuilder, ByVal N As Int32) As Int32 + Declare Function ENgetstatistic Lib "epanet2.dll" (ByVal code As Int32, ByRef value As Single) As Int32 +'Analysis Options Functions + Declare Function ENgetoption Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Single) As Int32 + Declare Function ENsetoption Lib "epanet2.dll" (ByVal Code As Int32, ByVal Value As Single) As Int32 + Declare Function ENgetflowunits Lib "epanet2.dll" (ByRef Code As Int32) As Int32 + Declare Function ENsetflowunits Lib "epanet2.dll" (ByVal code As Int32) As Int32 + Declare Function ENgettimeparam Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Int32) As Int32 + Declare Function ENsettimeparam Lib "epanet2.dll" (ByVal Code As Int32, ByVal Value As Int32) As Int32 + Declare Function ENgetqualtype Lib "epanet2.dll" (ByRef QualCode As Int32, ByRef TraceNode As Int32) As Int32 + Declare Function ENgetqualinfo Lib "epanet2.dll" (ByRef QualCode As Int32, ByVal ChemName As String, ByVal ChemUnits As String, ByRef TraceNode As Int32) As Int32 + Declare Function ENsetqualtype Lib "epanet2.dll" (ByVal QualCode As Int32, ByVal ChemName As String, ByVal ChemUnits As String, ByVal TraceNode As String) As Int32 + +'Basic Node Functions + Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Int32) As Int32 + Declare Function ENdeletenode Lib "epanet2.dll" (ByVal linkIndex As Long) As Long Declare Function ENgetnodeindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 Declare Function ENgetnodeid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 + Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 Declare Function ENgetnodetype Lib "epanet2.dll" (ByVal Index As Int32, ByRef Code As Int32) As Int32 Declare Function ENgetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByRef Value As Single) As Int32 + Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENgetcoord Lib "epanet2.dll" (ByVal Index As Int32, ByRef X As Single, ByRef Y As Single) As Int32 Declare Function ENsetcoord Lib "epanet2.dll" (ByVal Index As Int32, ByVal X As Single, ByVal Y As Single) As Int32 +'Nodal Demand Functions + Declare Function ENgetdemandmodel Lib "epanet2.dll" (ByRef mtype As Int32, ByRef pmin As Single, ByRef preq As Single, ByRef pexp As Single) As Int32 + Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal mtype As Int32, ByVal pmin As Single, ByVal preq As Single, ByVal pexp As Single) As Int32 Declare Function ENgetnumdemands Lib "epanet2.dll" (ByVal Index As Int32, ByRef numDemands As Int32) As Int32 'ES Declare Function ENgetbasedemand Lib "epanet2.dll" (ByVal Index As Int32, ByVal DemandIndex As Int32, ByRef BaseDemand As Single) As Int32 'ES Declare Function ENsetbasedemand Lib "epanet2.dll" (ByVal Index As Int32, ByVal DemandIndex As Int32, ByVal BaseDemand As Single) As Int32 'ES Declare Function ENgetdemandpattern Lib "epanet2.dll" (ByVal Index As Int32, ByVal DemandIndex As Int32, ByRef PatIndex As Int32) As Int32 'ES - Declare Function ENsetdemandpattern Lib "epanet2.dll" (ByVal Index As Int32, ByVal DemandIndex As Int32, ByVal PatIndex As Int32) As Int32 'ES + Declare Function ENsetdemandpattern Lib "epanet2.dll" (ByVal index As Int32, ByVal DemandIndex As Int32, ByVal PatIndex As Int32) As Int32 + Declare Function ENgetdemandname Lib "epanet2.dll" (ByVal index As Int32, ByVal DemandIndex As Int32, ByVal demandName As String) As Int32 + Declare Function ENsetdemandname Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal DemandIndex As Int32, ByVal demandName As String) As Int32 +'Basic Link Functions + Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Int32, ByVal fromNode As String, ByVal toNode As String) As Int32 + Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long Declare Function ENgetlinkindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 Declare Function ENgetlinkid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 - Declare Function ENgetlinktype Lib "epanet2.dll" (ByVal Index As Int32, ByRef Code As Int32) As Int32 - Declare Function ENgetlinknodes Lib "epanet2.dll" (ByVal Index As Int32, ByRef Node1 As Int32, ByRef Node2 As Int32) As Int32 - Declare Function ENgetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByRef Value As Single) As Int32 - - Declare Function ENgetcurve Lib "epanet2.dll" (ByVal CurveIndex As Int32, ByRef nValues As Int32, ByRef xValues As Single, ByRef yValues As Single) As Int32 'ES - Declare Function ENgetheadcurveindex Lib "epanet2.dll" (ByVal Index As Int32, ByVal CurveIndex As int32) As Int32 'ES - Declare Function ENgetpumptype Lib "epanet2.dll" (ByVal Index As Int32, ByRef PumpType As Int32) As Int32 'ES - - Declare Function ENgetversion Lib "epanet2.dll" (ByRef Value As Int32) As Int32 - - 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 - Declare Function ENsetnodeid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 - Declare Function ENsetnodevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetlinkid Lib "epanet2.dll" (ByVal index As Int32, ByVal newid As String) As Int32 - Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Int32, ByVal node1 As Int32, ByVal node2 As Int32) As Int32 + Declare Function ENgetlinktype Lib "epanet2.dll" (ByVal Index As Int32, ByRef Code As Int32) As Int32 Declare Function ENsetlinktype Lib "epanet2.dll" (ByRef index As Int32, ByVal code As Int32) As Int32 + Declare Function ENgetlinknodes Lib "epanet2.dll" (ByVal Index As Int32, ByRef Node1 As Int32, ByRef Node2 As Int32) As Int32 + Declare Function ENsetlinknodes Lib "epanet2.dll" (ByVal index As Int32, ByVal node1 As Int32, ByVal node2 As Int32) As Int32 + Declare Function ENgetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByRef Value As Single) As Int32 Declare Function ENsetlinkvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Code As Int32, ByVal Value As Single) As Int32 - Declare Function ENsetpattern Lib "epanet2.dll" (ByVal Index as Int32, ByRef F as Single, ByVal N as Int32) as Int32 - Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByVal Value As Single) As Int32 - Declare Function ENsettimeparam Lib "epanet2.dll" (ByVal Code As Int32, ByVal Value As Int32) As Int32 - Declare Function ENsetoption Lib "epanet2.dll" (ByVal Code As Int32, ByVal Value As Single) As Int32 - Declare Function ENsetstatusreport Lib "epanet2.dll" (ByVal Code As Int32) As Int32 - Declare Function ENsetqualtype Lib "epanet2.dll" (ByVal QualCode As Int32, ByVal ChemName As String, ByVal ChemUnits As String, ByVal TraceNode As String) As Int32 - + +'Pump Functions + Declare Function ENgetheadcurveindex Lib "epanet2.dll" (ByVal Index As Int32, ByVal CurveIndex As int32) As Int32 'ES + Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Int32, ByVal curveIndex As Int32) As Int32 + Declare Function ENgetpumptype Lib "epanet2.dll" (ByVal Index As Int32, ByRef PumpType As Int32) As Int32 'ES + +'Time Pattern Functions Declare Function ENaddpattern Lib "epanet2.dll" (ByVal ID As String) As Int32 - + Declare Function ENgetpatternindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 + Declare Function ENgetpatternid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 + Declare Function ENgetpatternlen Lib "epanet2.dll" (ByVal Index As Int32, ByRef L As Int32) As Int32 + Declare Function ENgetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByRef Value As Single) As Int32 + Declare Function ENsetpatternvalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Period As Int32, ByVal Value As Single) As Int32 + Declare Function ENgetaveragepatternvalue Lib "epanet2.dll" (ByVal index As Int32, ByRef value As Single) As Int32 + Declare Function ENsetpattern Lib "epanet2.dll" (ByVal Index as Int32, ByRef F as Single, ByVal N as Int32) as Int32 + +'Data Curve Functions + Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Int32 Declare Function ENgetcurveindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 Declare Function ENgetcurveid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 Declare Function ENgetcurvelen Lib "epanet2.dll" (ByVal Index As Int32, ByRef L As Int32) As Int32 + Declare Function ENgetcurvetype Lib "epanet2.dll" (ByVal curveIndex As Int32, ByRef CurveType As Int32) As Int32 Declare Function ENgetcurvevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Pnt As Int32, ByRef X As Single, ByRef Y As Single) As Int32 Declare Function ENsetcurvevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Pnt As Int32, ByVal X As Single, ByVal Y As Single) As Int32 + Declare Function ENgetcurve Lib "epanet2.dll" (ByVal CurveIndex As Int32, ByRef nValues As Int32, ByRef xValues As Single, ByRef yValues As Single) As Int32 'ES Declare Function ENsetcurve Lib "epanet2.dll" (ByVal Index as Int32, ByRef X as Single, ByRef Y as Single, ByVal N as Int32) as Int32 - Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Int32 -Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Long +'Simple Control Functions + Declare Function ENaddcontrol Lib "epanet2.dll" (ByRef Cindex As Int32, ByVal Ctype As Int32, ByVal Lindex As Int32, ByVal setting As Single, ByVal Nindex As Int32, ByVal Level As Single) As Int32 + Declare Function ENdeletecontrol Lib "epanet2.dll" (ByVal Cindex as Int32) As Int32 + Declare Function ENgetcontrol Lib "epanet2.dll" (ByVal Cindex As Int32, ByRef CtlType As Int32, ByRef Lindex As Int32, ByRef Setting As Single, ByRef Nindex As Int32, ByRef Level As Single) As Int32 + 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 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 Declare Function ENgetpremise Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexPremise As Int32, ByRef logop As Int32, ByRef object As Int32, ByRef indexObj As Int32, ByRef variable As Int32, ByRef relop As Int32, ByRef status As Int32, ByRef value As Single) As Int32 Declare Function ENsetpremise Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal indexPremise As Int32, ByVal logop As Int32, ByVal object As Int32, ByVal indexObj As Int32, ByVal variable As Int32, ByVal relop As Int32, ByVal status As Int32, ByVal value As Single) As Int32 @@ -261,10 +306,7 @@ Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Long 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 - Decalre 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 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 ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal id As StringBuilder) As Int32 - - End Module diff --git a/src/epanet.c b/src/epanet.c index 3fc97d9..407328a 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -138,6 +138,7 @@ void *_defaultModel; // Local functions void errorLookup(int errcode, char *errmsg, int len); + /**************************************************************** LEGACY (v <= 2.1) API: uses global project variable @@ -403,6 +404,11 @@ int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, level); } +int DLLEXPORT ENdeletecontrol(int cindex) { + return EN_deletecontrol(_defaultModel, cindex); +} + + int DLLEXPORT ENsetnodeid(int index, char *newid) { return EN_setnodeid(_defaultModel, index, newid); } @@ -4963,45 +4969,6 @@ int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type) // Find the index of this new link EN_getlinkindex(ph, id, index); return set_error(p->error_handle, errcode); - - /*********************************************** - int i; - EN_LinkType fromType; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - if (!p->Openflag) - return set_error(p->error_handle, 102); - - // Check if a link with the id exists - if (EN_getlinkindex(p, id, &i) != 0) - return set_error(p->error_handle, 215); - - // Get the current type of the link - EN_getlinktype(p, i, &fromType); - if (fromType == toType) - return set_error(p->error_handle, 0); - - // Change link from Pipe - if (toType <= EN_PIPE) { - net->Npipes++; - } else if (toType == EN_PUMP) { - net->Npumps++; - net->Pump[net->Npumps].Link = i; - } else { - net->Nvalves++; - net->Valve[net->Nvalves].Link = i; - } - - if (fromType <= EN_PIPE) { - net->Npipes--; - } else if (fromType == EN_PUMP) { - net->Npumps--; - } - return set_error(p->error_handle, 0); -**********************************************/ } int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { @@ -5010,7 +4977,6 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { struct Sdemand *demand; EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; hydraulics_t *hyd = &p->hydraulics; quality_t *qu = &p->quality; @@ -5018,9 +4984,6 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { Snode *node; Scoord *coord; Scontrol *control; -// rules_t *rule; - Premise *pchain, *pnext; - /* Check if a node with same id already exists */ if (!p->Openflag) @@ -5039,6 +5002,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { qu->NodeQual = (double *)realloc(qu->NodeQual, (net->Nnodes + 2) * sizeof(double)); hyd->NodeHead = (double *)realloc(hyd->NodeHead, (net->Nnodes + 2) * sizeof(double)); + // Actions taken when a new Junction is added if (nodeType == EN_JUNCTION) { net->Njuncs++; nIdx = net->Njuncs; @@ -5082,43 +5046,10 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { } } - // shift indices of Rules for tanks/reservoirs - for (index = 1; index <= net->Nrules; ++index) { - pchain = (&p->rules)->Rule[i].Pchain; - while (pchain != NULL) { - pnext = pchain->next; - // object types are: (duplicated here from rules.c --> TODO: move these to external definition? -// enum Objects { -// r_JUNC, -// r_RESERV, -// r_TANK, -// r_PIPE, -// r_PUMP, -// r_VALVE, -// r_NODE, -// r_LINK, -// r_SYSTEM -// }; - - // if object is a node - switch (pchain->object) { - case 0: // junc - case 1: // reservoir - case 2: // tank - case 6: // node - // if the junction needs to be re-indexed: - if (pchain->index > net->Njuncs) { - pchain->index += 1; - } - break; - default: - break; - } - // next premise in the chain - pchain = pnext; - } - } - + // adjust indices of tanks/reservoirs in Rule premises (see RULES.C) + adjusttankrules(p); + + // Actions taken when a new Tank/Reservoir is added } else { nIdx = net->Nnodes+1; node = &net->Node[nIdx]; @@ -5280,138 +5211,232 @@ 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 i; - int pumpindex; - int valveindex; +int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) +/*---------------------------------------------------------------- +** Input: index = index of the control +** Output: none +** Returns: error code +** Purpose: deletes a link from a project. +**---------------------------------------------------------------- +*/ +{ + int i; + int pumpindex; + int valveindex; - EN_LinkType linkType; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - Slink *link; + EN_LinkType linkType; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + Slink *link; - if (!p->Openflag) - return set_error(p->error_handle, 102); - if (index <= 0 || index > net->Nlinks) - return set_error(p->error_handle, 203); + // 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); - EN_getlinktype(p, index, &linkType); - link = &net->Link[index]; + // Get references to the link and its type + link = &net->Link[index]; + EN_getlinktype(p, index, &linkType); - // remove from hash table - hashtable_delete(net->LinkHashTable, link->ID); + // Remove link from hash table + hashtable_delete(net->LinkHashTable, link->ID); - // shift link and pump arrays to re-sort link indices - for (i = index; i <= net->Nlinks - 1; i++) { - net->Link[i] = net->Link[i + 1]; - // update hashtable - hashtable_update(net->LinkHashTable, net->Link[i].ID, i); - } - for (i = 1; i <= net->Npumps; i++) { - if (net->Pump[i].Link > index) { - net->Pump[i].Link -= 1; + // Shift position of higher entries in Link array down one + for (i = index; i <= net->Nlinks - 1; i++) + { + net->Link[i] = net->Link[i + 1]; + // ... update hashtable + hashtable_update(net->LinkHashTable, net->Link[i].ID, i); } - } - for (i = 1; i <= net->Nvalves; i++) { - if (net->Valve[i].Link > index) { - net->Valve[i].Link -= 1; - } - } - // remove any pump associated with the deleted link - if (linkType == EN_PUMP) { - pumpindex = findpump(net,index); - for (i = pumpindex; i <= net->Npumps - 1; i++) { - net->Pump[i] = net->Pump[i + 1]; + // Adjust references to higher numbered links for pumps & valves + for (i = 1; i <= net->Npumps; i++) + { + if (net->Pump[i].Link > index) net->Pump[i].Link -= 1; + } + for (i = 1; i <= net->Nvalves; i++) + { + if (net->Valve[i].Link > index) net->Valve[i].Link -= 1; + } + + // Delete any pump associated with the deleted link + if (linkType == EN_PUMP) + { + pumpindex = findpump(net,index); + for (i = pumpindex; i <= net->Npumps - 1; i++) + { + net->Pump[i] = net->Pump[i + 1]; + } + net->Npumps--; } - net->Npumps--; - } - // remove any valve associated with the deleted link - if (linkType > EN_PUMP) { - valveindex = findvalve(net,index); - for (i = valveindex; i <= net->Nvalves - 1; i++) { - net->Valve[i] = net->Valve[i + 1]; + // Delete any valve (linkType > EN_PUMP) associated with the deleted link + if (linkType > EN_PUMP) + { + valveindex = findvalve(net,index); + for (i = valveindex; i <= net->Nvalves - 1; i++) + { + net->Valve[i] = net->Valve[i + 1]; + } + net->Nvalves--; } - net->Nvalves--; - } - - // reduce total link count - net->Nlinks--; - return set_error(p->error_handle, 0); + // Delete any control containing the link + for (i = net->Ncontrols; i >= 1; i--) + { + if (net->Control[i].Link == index) EN_deletecontrol(ph, i); + } + + // Adjust higher numbered link indices in remaining controls + for (i = 1; i <= net->Ncontrols; i++) + { + if (net->Control[i].Link > index) net->Control[i].Link--; + } + + // Make necessary adjustments to rule-based controls (r_LINK = 7) + adjustrules(p, 7, index); // see RULES.C + + // Reduce link count by one + net->Nlinks--; + 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) +/*---------------------------------------------------------------- +** Input: index = index of the control +** Output: none +** Returns: error code +** Purpose: deletes a node from a project. +**---------------------------------------------------------------- +*/ +{ + int i, nodeType, tankindex; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + Snode *node; + Pdemand demand, nextdemand; + Psource source; - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - int i, nodeType; - - if (!p->Openflag) - return set_error(p->error_handle, 102); - if (index <= 0 || index > net->Nnodes) - return set_error(p->error_handle, 203); - - EN_getnodetype(p, index, &nodeType); - - // TODO: check for existing controls/rules that reference this node? - - // remove from hash table - hashtable_delete(net->NodeHashTable, net->Node[index].ID); - - // shift node and coord array to remove node - for (i = index; i <= net->Nnodes - 1; i++) { - net->Node[i] = net->Node[i + 1]; - net->Coord[i] = net->Coord[i + 1]; - // update hashtable - hashtable_update(net->NodeHashTable, net->Node[i].ID, i); - } - - // update tank array - if (nodeType != EN_JUNCTION) { - int tankindex = findtank(net, index); - for (i = tankindex; i <= net->Ntanks - 1; i++) { - net->Tank[i] = net->Tank[i + 1]; + // Check that node exists + if (!p->Openflag) return set_error(p->error_handle, 102); + if (index <= 0 || index > net->Nnodes) + { + return set_error(p->error_handle, 203); } - } - // update tank node indices - for (i = 1; i <= net->Ntanks; i++) { - if (net->Tank[i].Node > index) { - net->Tank[i].Node -= 1; + // Get a reference to the node & its type + node = &net->Node[index]; + EN_getnodetype(ph, index, &nodeType); + + // Remove node from hash table + hashtable_delete(net->NodeHashTable, node->ID); + + // Free memory allocated to node's demands & WQ source + demand = node->D; + while (demand != NULL) + { + nextdemand = demand->next; + free(demand); + demand = nextdemand; } - } - - // gather a list of link ids to remove in reverse order, - // so that re-shuffling doesn't destroy the indexing - for (i = net->Nlinks; i >= 1; i--) { - if (net->Link[i].N1 == index || net->Link[i].N2 == index) { - EN_deletelink(p,i); + source = node->S; + if (source != NULL) free(source); + + // Shift position of higher entries in Node 7 Cord 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 + hashtable_update(net->NodeHashTable, net->Node[i].ID, i); } - } - for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].N1 > index) { - net->Link[i].N1 -= 1; + // Remove references to demands & source in last (inactive) Node array entry + net->Node[net->Nnodes].D = NULL; + net->Node[net->Nnodes].S = NULL; + + // If deleted node is a tank, remove it from the Tank array + if (nodeType != EN_JUNCTION) + { + tankindex = findtank(net, index); + for (i = tankindex; i <= net->Ntanks - 1; i++) + { + net->Tank[i] = net->Tank[i + 1]; + } } - if (net->Link[i].N2 > index) { - net->Link[i].N2 -= 1; + + // Shift higher node indices in Tank array down one + for (i = 1; i <= net->Ntanks; i++) + { + if (net->Tank[i].Node > index) net->Tank[i].Node -= 1; } - } - // update counters - if (nodeType == EN_JUNCTION) { - net->Njuncs--; - } else { - net->Ntanks--; - } + // Delete any links connected to the deleted node + // (Process links in reverse order to maintain their indexing) + for (i = net->Nlinks; i >= 1; i--) + { + if (net->Link[i].N1 == index || + net->Link[i].N2 == index) EN_deletelink(ph, i); + } - net->Nnodes--; + // Adjust indices of all link end nodes + for (i = 1; i <= net->Nlinks; i++) + { + if (net->Link[i].N1 > index) net->Link[i].N1 -= 1; + if (net->Link[i].N2 > index) net->Link[i].N2 -= 1; + } - return set_error(p->error_handle, 0); + // Delete any control containing the node + for (i = net->Ncontrols; i >= 1; i--) + { + if (net->Control[i].Node == index) EN_deletecontrol(ph, i); + } + + // Adjust higher numbered link indices in remaining controls + for (i = 1; i <= net->Ncontrols; i++) + { + if (net->Control[i].Node > index) net->Control[i].Node--; + } + + // 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; + } + + // Reduce counts of node types + if (nodeType == EN_JUNCTION) net->Njuncs--; + else net->Ntanks--; + net->Nnodes--; + return set_error(p->error_handle, 0); +} + +int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index) +/*---------------------------------------------------------------- +** Input: index = index of the control +** Output: none +** Returns: error code +** Purpose: deletes a simple control from a project. +**---------------------------------------------------------------- +*/ +{ + int i; + EN_Project *p = (EN_Project*)ph; + EN_Network *net = &p->network; + + if (index <= 0 || index > net->Ncontrols) + { + return set_error(p->error_handle, 241); + } + for (i = index; i <= net->Ncontrols - 1; i++) + { + net->Control[i] = net->Control[i + 1]; + } + net->Ncontrols--; + 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) diff --git a/src/funcs.h b/src/funcs.h index bf1d913..7390f5b 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -121,6 +121,8 @@ void changestatus(EN_Network *net, int, StatType, void initrules(rules_t *rules); /* 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 */ diff --git a/src/rules.c b/src/rules.c index b8d0518..84dbe68 100644 --- a/src/rules.c +++ b/src/rules.c @@ -90,29 +90,24 @@ char *Operator[] = {"=", "<>", "<=", ">=", "<", ">", enum Values { IS_NUMBER, IS_OPEN, IS_CLOSED, IS_ACTIVE }; char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE, NULL}; -/* External variables declared in INPUT2.C */ +// Local Functions +static void newrule(EN_Project *pr); +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 int takeactions(EN_Project *pr); +static void clearactlist(rules_t *rules); +static void ruleerrmsg(EN_Project *pr, int); +static void clearrule(EN_Project *pr, int); +static void deleterule(EN_Project *pr, int); -/* -** Local function prototypes are defined here and not in FUNCS.H -** because some of them utilize the Premise and Action structures -** defined locally in this module. -*/ -void newrule(EN_Project *pr); -int newpremise(EN_Project *pr, int); -int newaction(EN_Project *pr); -int newpriority(EN_Project *pr); -int evalpremises(EN_Project *pr, int); -void updateactlist(rules_t *rules, int, Action *); -int checkaction(rules_t *rules, int, Action *); -int checkpremise(EN_Project *pr, Premise *); -int checktime(EN_Project *pr, Premise *); -int checkstatus(EN_Project *pr, Premise *); -int checkvalue(EN_Project *pr, Premise *); -int takeactions(EN_Project *pr); -void clearactlist(rules_t *rules); -void clearrules(EN_Project *pr); -void ruleerrmsg(EN_Project *pr, int); -//int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx); void initrules(rules_t *rules) /* @@ -166,8 +161,9 @@ void freerules(EN_Project *pr) **-------------------------------------------------------------- */ { - clearrules(pr); - free(pr->rules.Rule); + int i; + for (i = 1; i <= pr->network.Nrules; i++) clearrule(pr, i); + free(pr->rules.Rule); } int checkrules(EN_Project *pr, long dt) @@ -217,7 +213,7 @@ int ruledata(EN_Project *pr) **-------------------------------------------------------------- ** Parses a line from [RULES] section of input. ** Called by newline() in INPUT2.C module. -** Tok[] is global array of tokens parsed from input line. +** Tok[] is an array of tokens parsed from input line. **-------------------------------------------------------------- */ { @@ -305,6 +301,132 @@ int ruledata(EN_Project *pr) return (err); } + +void adjustrules(EN_Project *pr, int objtype, int index) +/* +**----------------------------------------------------------- +** Adjusts rules when a specific node or link is deleted. +** Called by EN_deletenode & EN_deletelink in EPANET.C. +**----------------------------------------------------------- +*/ +{ + int i, delete; + EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + Premise *p; + Action *a; + + // Delete rules that refer to objtype and index + for (i = net->Nrules; i >= 1; i--) + { + delete = FALSE; + p = rules->Rule[i].Pchain; + while (p != NULL && !delete) + { + if (objtype == p->object && p->index == index) delete = TRUE; + p = p->next; + } + if (objtype == r_LINK) + { + a = rules->Rule[i].Tchain; + while (a != NULL && !delete) + { + if (a->link == index) delete = TRUE; + a = a->next; + } + a = rules->Rule[i].Fchain; + while (a != NULL && !delete) + { + if (a->link == index) delete = TRUE; + a = a->next; + } + } + if (delete) deleterule(pr, i); + } + + // Adjust all higher object indices to reflect deletion of object index + for (i = 1; i <= net->Nrules; i++) + { + p = rules->Rule[i].Pchain; + while (p != NULL) + { + if (objtype == p->object && p->index > index) p->index--; + p = p->next; + } + if (objtype == r_LINK) + { + a = rules->Rule[i].Tchain; + while (a != NULL) + { + if (a->link > index) a->link--; + a = a->next; + } + a = rules->Rule[i].Fchain; + while (a != NULL) + { + if (a->link > index) a->link--; + a = a->next; + } + } + } +} + +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) +/* +**----------------------------------------------------------- +** Deletes a specific rule +**----------------------------------------------------------- +*/ +{ + int i; + EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + aRule *lastRule; + + // Free memory allocated to rule's premises & actions + clearrule(pr, index); + + // Shift position of higher indexed rules down one + for (i = index; i <= net->Nrules - 1; i++) + { + rules->Rule[i] = rules->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; + + // Reduce active rule count by one + net->Nrules--; +} + void clearactlist(rules_t *rules) /* **---------------------------------------------------------- @@ -322,23 +444,19 @@ void clearactlist(rules_t *rules) } } -void clearrules(EN_Project *pr) +void clearrule(EN_Project *pr, int i) /* **----------------------------------------------------------- -** Clears memory used for premises & actions for all rules +** Clears memory used by a rule for premises & actions **----------------------------------------------------------- */ { - EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + Premise *p; + Premise *pnext; + Action *a; + Action *anext; - rules_t *rules = &pr->rules; - - Premise *p; - Premise *pnext; - Action *a; - Action *anext; - int i; - for (i = 1; i <= net->Nrules; i++) { p = rules->Rule[i].Pchain; while (p != NULL) { pnext = p->next; @@ -357,7 +475,6 @@ void clearrules(EN_Project *pr) free(a); a = anext; } - } } void newrule(EN_Project *pr) diff --git a/src/types.h b/src/types.h index 58f5137..75bcca0 100755 --- a/src/types.h +++ b/src/types.h @@ -531,7 +531,7 @@ typedef struct s_aRule /* Control Rule Structure */ 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; + //struct s_aRule *next; } aRule; typedef struct s_ActItem /* Action list item */