Replace fixed-sized comment strings with dynamic strings

This commit is contained in:
Lew Rossman
2019-03-17 19:54:51 -04:00
parent c26775314c
commit 18f65eb8b0
17 changed files with 494 additions and 85 deletions

View File

@@ -5,7 +5,7 @@ Attribute VB_Name = "Module1"
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL)
'Last updated on 02/28/2019
'Last updated on 03/17/2019
' These are codes used by the DLL functions
Public Const EN_ELEVATION = 0 ' Node parameters
@@ -83,6 +83,13 @@ Public Const EN_MAXHEADERROR = 2
Public Const EN_MAXFLOWCHANGE = 3
Public Const EN_MASSBALANCE = 4
Public Const EN_NODE = 0 ' Component types
Public Const EN_LINK = 1
Public Const EN_TIMEPAT = 2
Public Const EN_CURVE = 3
Public Const EN_CONTROL = 4
Public Const EN_RULE = 5
Public Const EN_NODECOUNT = 0 ' Component counts
Public Const EN_TANKCOUNT = 1
Public Const EN_LINKCOUNT = 2

View File

@@ -21,6 +21,7 @@ EXPORTS
ENepanet = _ENepanet@16
ENgetaveragepatternvalue = _ENgetaveragepatternvalue@8
ENgetbasedemand = _ENgetbasedemand@12
ENgetcomment = _ENgetcomment@12
ENgetcontrol = _ENgetcontrol@24
ENgetcoord = _ENgetcoord@12
ENgetcount = _ENgetcount@8
@@ -80,6 +81,7 @@ EXPORTS
ENsavehydfile = _ENsavehydfile@4
ENsaveinpfile = _ENsaveinpfile@4
ENsetbasedemand = _ENsetbasedemand@12
ENsetcomment = _ENsetcomment@12
ENsetcontrol = _ENsetcontrol@24
ENsetcoord = _ENsetcoord@20
ENsetcurve = _ENsetcurve@16

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 02/28/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -77,6 +77,10 @@ extern "C" {
int DLLEXPORT ENsettitle(char *line1, char *line2, char *line3);
int DLLEXPORT ENgetcomment(int object, int index, char *comment);
int DLLEXPORT ENsetcomment(int object, int index, char *comment);
int DLLEXPORT ENgetcount(int object, int *count);
int DLLEXPORT ENsaveinpfile(const char *filename);

View File

@@ -4,7 +4,7 @@
'Declarations of functions in the EPANET PROGRAMMERs TOOLKIT
'(EPANET2.DLL) for use with VB.Net.
'Last updated on 02/28/2019
'Last updated on 03/17/2019
Imports System.Runtime.InteropServices
Imports System.Text
@@ -88,6 +88,13 @@ Public Const EN_MAXHEADERROR = 2
Public Const EN_MAXFLOWCHANGE = 3
Public Const EN_MASSBALANCE = 4
Public Const EN_NODE = 0 ' Component types
Public Const EN_LINK = 1
Public Const EN_TIMEPAT = 2
Public Const EN_CURVE = 3
Public Const EN_CONTROL = 4
Public Const EN_RULE = 5
Public Const EN_NODECOUNT = 0 'Component counts
Public Const EN_TANKCOUNT = 1
Public Const EN_LINKCOUNT = 2

View File

@@ -11,7 +11,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 02/08/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -146,6 +146,26 @@ typedef struct Project *EN_Project;
*/
int DLLEXPORT EN_settitle(EN_Project ph, char *line1, char *line2, char *line3);
/**
@brief Retrieves a descriptive comment assigned to a Node, Link, Pattern or Curve.
@param ph an EPANET project handle.
@param object a type of object (either EN_NODE, EN_LINK, EN_TIMEPAT or EN_CURVE)
@param index the object's index starting from 1
@param[out] comment the comment string assigned to the object
@return an error code
*/
int DLLEXPORT EN_getcomment(EN_Project ph, int object, int index, char *comment);
/**
@brief Assigns a descriptive comment to a Node, Link, Pattern or Curve.
@param ph an EPANET project handle.
@param object a type of object (either EN_NODE, EN_LINK, EN_TIMEPAT or EN_CURVE)
@param index the object's index starting from 1
@param[out] comment the comment string assigned to the object
@return an error code
*/
int DLLEXPORT EN_setcomment(EN_Project ph, int object, int index, char *comment);
/**
@brief Retrieves the number of objects of a given type in a project.
@param ph an EPANET project handle.

View File

@@ -9,7 +9,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 01/14/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -128,6 +128,19 @@ typedef enum {
EN_MASSBALANCE = 4 //!< Cumulative water quality mass balance ratio
} EN_AnalysisStatistic;
/// Types of network objects
/**
A network model is composed of these types of objects.
*/
typedef enum {
EN_NODE = 0, //!< Nodes
EN_LINK = 1, //!< Links
EN_TIMEPAT = 2, //!< Time patterns
EN_CURVE = 3, //!< Data curves
EN_CONTROL = 4, //!< Simple controls
EN_RULE = 5 //!< Control rules
} EN_ObjectType;
/// Types of objects to count
/**
These options tell @ref EN_getcount which type of object to count.

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 03/08/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -275,6 +275,32 @@ int DLLEXPORT EN_settitle(EN_Project p, char *line1, char *line2, char *line3)
return 0;
}
int DLLEXPORT EN_getcomment(EN_Project p, int object, int index, char *comment)
/*----------------------------------------------------------------
** Input: object = a type of object (see EN_ObjectType)
** index = the object's index
** Output: comment = the object's descriptive comment
** Returns: error code
** Purpose: Retrieves an object's descriptive comment
**----------------------------------------------------------------
*/
{
return getcomment(&p->network, object, index, comment);
}
int DLLEXPORT EN_setcomment(EN_Project p, int object, int index, char *comment)
/*----------------------------------------------------------------
** Input: object = a type of object (see EN_ObjectType)
** index = the object's index
** comment = a descriptive comment to assign
** Returns: error code
** Purpose: Assigns a descriptive comment to an object
**----------------------------------------------------------------
*/
{
return setcomment(&p->network, object, index, comment);
}
int DLLEXPORT EN_getcount(EN_Project p, int object, int *count)
/*----------------------------------------------------------------
** Input: object = type of object to count (see EN_CountType)
@@ -1208,7 +1234,7 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value)
if (demand->Pat == tmpPat)
{
demand->Pat = pat;
strcpy(demand->Name, "");
demand->Name = xstrcpy(&demand->Name, "", MAXMSG);
}
}
}
@@ -1668,7 +1694,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType)
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand));
demand->Base = 0.0;
demand->Pat = hyd->DefPat; // Use default pattern
strcpy(demand->Name, "");
demand->Name = NULL;
demand->next = NULL;
node->D = demand;
@@ -1743,7 +1769,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType)
node->Rpt = 0;
node->X = MISSING;
node->Y = MISSING;
strcpy(node->Comment, "");
node->Comment = NULL;
// Insert new node into hash table
hashtable_insert(net->NodeHashTable, node->ID, nIdx);
@@ -1769,7 +1795,6 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
int i, nodeType, tankindex;
Snode *node;
Pdemand demand, nextdemand;
Psource source;
// Cannot modify network structure while solvers are active
if (!p->Openflag) return 102;
@@ -1801,16 +1826,17 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
// Remove node from its hash table
hashtable_delete(net->NodeHashTable, node->ID);
// Free memory allocated to node's demands & WQ source
// Free memory allocated to node's demands, WQ source & comment
demand = node->D;
while (demand != NULL)
{
nextdemand = demand->next;
free(demand->Name);
free(demand);
demand = nextdemand;
}
source = node->S;
if (source != NULL) free(source);
free(node->S);
free(node->Comment);
// Shift position of higher entries in Node & Coord arrays down one
for (i = index; i <= net->Nnodes - 1; i++)
@@ -1823,6 +1849,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
// Remove references to demands & source in last (inactive) Node array entry
net->Node[net->Nnodes].D = NULL;
net->Node[net->Nnodes].S = NULL;
net->Node[net->Nnodes].Comment = NULL;
// If deleted node is a tank, remove it from the Tank array
if (nodeType != EN_JUNCTION)
@@ -2522,7 +2549,6 @@ int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
return 0;
}
int DLLEXPORT EN_getcoord(EN_Project p, int index, double *x, double *y)
/*----------------------------------------------------------------
** Input: index = node index
@@ -2752,7 +2778,7 @@ int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
for (d = p->network.Node[nodeIndex].D;
n < demandIndex && d->next != NULL; d = d->next) n++;
if (n != demandIndex) return 253;
strncpy(d->Name, demandName, MAXMSG);
d->Name = xstrcpy(&d->Name, demandName, MAXMSG);
return 0;
}
@@ -2944,7 +2970,7 @@ int DLLEXPORT EN_addlink(EN_Project p, char *id, int linkType,
link->R = 0;
link->Rc = 0;
link->Rpt = 0;
strcpy(link->Comment, "");
link->Comment = NULL;
hashtable_insert(net->LinkHashTable, link->ID, n);
return 0;
@@ -2993,6 +3019,10 @@ int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode)
// Remove link from its hash table
hashtable_delete(net->LinkHashTable, link->ID);
// Remove link's comment
free(net->Link[index].Comment);
net->Link[net->Nlinks].Comment = NULL;
// Shift position of higher entries in Link array down one
for (i = index; i <= net->Nlinks - 1; i++)
{
@@ -3864,6 +3894,7 @@ int DLLEXPORT EN_addpattern(EN_Project p, char *id)
// Assign properties to the new pattern
pat = &net->Pattern[n];
strcpy(pat->ID, id);
pat->Comment = NULL;
pat->Length = 1;
pat->F = (double *)calloc(1, sizeof(double));
if (pat->F == NULL) err = 1;
@@ -3924,6 +3955,7 @@ int DLLEXPORT EN_deletepattern(EN_Project p, int index)
// Free the pattern's factor array
FREE(net->Pattern[index].F);
FREE(net->Pattern[index].Comment);
// Shift the entries in the network's Pattern array
for (i = index; i < net->Npats; i++) net->Pattern[i] = net->Pattern[i+1];
@@ -4141,6 +4173,7 @@ int DLLEXPORT EN_addcurve(EN_Project p, char *id)
// Set the properties of the new curve
curve = &net->Curve[n];
strcpy(curve->ID, id);
curve->Comment = NULL;
curve->Npts = 1;
curve->Type = GENERIC_CURVE;
curve->X = (double *)calloc(1, sizeof(double));
@@ -4194,6 +4227,7 @@ int DLLEXPORT EN_deletecurve(EN_Project p, int index)
// Free the curve's data arrays
FREE(net->Curve[index].X);
FREE(net->Curve[index].Y);
FREE(net->Curve[index].Comment);
// Shift the entries in the network's Curve array
for (i = index; i < net->Ncurves; i++) net->Curve[i] = net->Curve[i + 1];
@@ -5092,7 +5126,6 @@ int DLLEXPORT EN_getelseaction(EN_Project p, int ruleIndex, int actionIndex,
**----------------------------------------------------------------
*/
{
Saction *actions;
Saction *action;

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 02/08/2019
Last Updated: 03/17/2019
******************************************************************************
*/
#ifndef __APPLE__
@@ -112,6 +112,16 @@ int DLLEXPORT ENsettitle(char *line1, char *line2, char *line3)
return EN_settitle(_defaultProject, line1, line2, line3) ;
}
int DLLEXPORT ENgetcomment(int object, int index, char *comment)
{
return EN_getcomment(_defaultProject, object, index, comment);
}
int DLLEXPORT ENsetcomment(int object, int index, char *comment)
{
return EN_setcomment(_defaultProject, object, index, comment);
}
int DLLEXPORT ENgetcount(int object, int *count)
{
return EN_getcount(_defaultProject, object, count);

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 02/08/2019
Last Updated: 03/17/2019
******************************************************************************
*/
#ifndef FUNCS_H
@@ -39,7 +39,11 @@ int findpump(Network *, int);
void adjustpatterns(Network *, int);
void adjustcurves(Network *, int);
int getcomment(Network *, int, int, char *);
int setcomment(Network *, int, int, const char *);
char *getTmpName(char *);
char *xstrcpy(char **, const char *, const size_t n);
int strcomp(const char *, const char *);
double interp(int, double [], double [], double);
char *geterrmsg(int, char *);

View File

@@ -7,7 +7,7 @@ Description: saves network data to an EPANET formatted text file
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 03/09/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -84,7 +84,7 @@ void saveauxdata(Project *pr, FILE *f)
case _LABELS:
case _BACKDROP:
case _TAGS:
fprintf(f, "%s", line);
fprintf(f, "\n%s", line);
}
}
}
@@ -161,8 +161,8 @@ int saveinpfile(Project *pr, const char *fname)
for (i = 1; i <= net->Njuncs; i++)
{
node = &net->Node[i];
fprintf(f, "\n %-31s %12.4f ;%s", node->ID, node->El * pr->Ucf[ELEV],
node->Comment);
fprintf(f, "\n %-31s %12.4f", node->ID, node->El * pr->Ucf[ELEV]);
if (node->Comment) fprintf(f, " ;%s", node->Comment);
}
// Write [RESERVOIRS] section
@@ -175,9 +175,10 @@ int saveinpfile(Project *pr, const char *fname)
{
node = &net->Node[tank->Node];
sprintf(s, " %-31s %12.4f", node->ID, node->El * pr->Ucf[ELEV]);
if ((j = tank->Pat) > 0) sprintf(s1, " %-31s", net->Pattern[j].ID);
else strcpy(s1, "");
fprintf(f, "\n%s %s ;%s", s, s1, node->Comment);
if ((j = tank->Pat) > 0) sprintf(s1, " %s", net->Pattern[j].ID);
else strcpy(s1, " ");
fprintf(f, "\n%s %-31s", s, s1);
if (node->Comment) fprintf(f, " ;%s", node->Comment);
}
}
@@ -197,9 +198,10 @@ int saveinpfile(Project *pr, const char *fname)
(tank->Hmax - node->El) * pr->Ucf[ELEV],
sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV],
tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]);
if ((j = tank->Vcurve) > 0) sprintf(s1, "%-31s", net->Curve[j].ID);
else strcpy(s1, "");
fprintf(f, "\n%s %s ;%s", s, s1, node->Comment);
if ((j = tank->Vcurve) > 0) sprintf(s1, "%s", net->Curve[j].ID);
else strcpy(s1, " ");
fprintf(f, "\n%s %-31s", s, s1);
if (node->Comment) fprintf(f, " ;%s", node->Comment);
}
}
@@ -216,17 +218,15 @@ int saveinpfile(Project *pr, const char *fname)
if (hyd->Formflag == DW) kc = kc * pr->Ucf[ELEV] * 1000.0;
km = link->Km * SQR(d) * SQR(d) / 0.02517;
sprintf(s, " %-31s %-31s %-31s %12.4f %12.4f", link->ID,
net->Node[link->N1].ID, net->Node[link->N2].ID,
link->Len * pr->Ucf[LENGTH], d * pr->Ucf[DIAM]);
if (hyd->Formflag == DW) sprintf(s1, "%12.4f %12.4f", kc, km);
else sprintf(s1, "%12.4f %12.4f", kc, km);
sprintf(s, " %-31s %-31s %-31s %12.4f %12.4f %12.4f %12.4f",
link->ID, net->Node[link->N1].ID, net->Node[link->N2].ID,
link->Len * pr->Ucf[LENGTH], d * pr->Ucf[DIAM], kc, km);
if (link->Type == CVPIPE) sprintf(s2, "CV");
else if (link->Status == CLOSED) sprintf(s2, "CLOSED");
else strcpy(s2, "");
fprintf(f, "\n%s %s %s ;%s", s, s1, s2, link->Comment);
else strcpy(s2, " ");
fprintf(f, "\n%s %-6s", s, s2);
if (link->Comment) fprintf(f, " ;%s", link->Comment);
}
}
@@ -275,7 +275,9 @@ int saveinpfile(Project *pr, const char *fname)
strcat(s, s1);
}
fprintf(f, "\n%s ;%s", s, link->Comment);
fprintf(f, "\n%s", s);
if (link->Comment) fprintf(f, " ;%s", link->Comment);
}
// Write [VALVES] section
@@ -316,7 +318,8 @@ int saveinpfile(Project *pr, const char *fname)
sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km);
}
else sprintf(s1, "%12.4f %12.4f", kc, km);
fprintf(f, "\n%s %s ;%s", s, s1, link->Comment);
fprintf(f, "\n%s %s", s, s1);
if (link->Comment) fprintf(f, " ;%s", link->Comment);
}
// Write [DEMANDS] section
@@ -329,9 +332,10 @@ int saveinpfile(Project *pr, const char *fname)
for (demand = node->D; demand != NULL; demand = demand->next)
{
sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base);
if ((j = demand->Pat) > 0) sprintf(s1, " %s", net->Pattern[j].ID);
else strcpy(s1, "");
fprintf(f, "\n%s %s ;%s", s, s1, demand->Name);
if ((j = demand->Pat) > 0) sprintf(s1, " %-31s", net->Pattern[j].ID);
else strcpy(s1, " ");
fprintf(f, "\n%s %-31s", s, s1);
if (demand->Name) fprintf(f, " ;%s", demand->Name);
}
}
@@ -392,6 +396,7 @@ int saveinpfile(Project *pr, const char *fname)
fprintf(f, s_PATTERNS);
for (i = 1; i <= net->Npats; i++)
{
if (net->Pattern[i].Comment) fprintf(f, "\n;%s", net->Pattern[i].Comment);
for (j = 0; j < net->Pattern[i].Length; j++)
{
if (j % 6 == 0) fprintf(f, "\n %-31s", net->Pattern[i].ID);
@@ -404,11 +409,11 @@ int saveinpfile(Project *pr, const char *fname)
fprintf(f, s_CURVES);
for (i = 1; i <= net->Ncurves; i++)
{
if (net->Curve[i].Comment) fprintf(f, "\n;%s", net->Curve[i].Comment);
for (j = 0; j < net->Curve[i].Npts; j++)
{
curve = &net->Curve[i];
fprintf(f, "\n %-31s %12.4f %12.4f", curve->ID, curve->X[j],
curve->Y[j]);
fprintf(f, "\n %-31s %12.4f %12.4f", curve->ID, curve->X[j], curve->Y[j]);
}
}
@@ -776,7 +781,6 @@ int saveinpfile(Project *pr, const char *fname)
}
else fprintf(f, "\n %-20sNO",field->Name);
}
fprintf(f, "\n\n");
// Write [COORDINATES] section
fprintf(f, "\n\n");
@@ -787,9 +791,9 @@ int saveinpfile(Project *pr, const char *fname)
if (node->X == MISSING || node->Y == MISSING) continue;
fprintf(f, "\n %-31s %14.6f %14.6f", node->ID, node->X, node->Y);
}
fprintf(f, "\n\n");
// Save auxilary data to new input file
fprintf(f, "\n");
saveauxdata(pr, f);
// Close the new input file

View File

@@ -7,7 +7,7 @@ Description: retrieves network data from an EPANET input file
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 12/15/2018
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -334,7 +334,7 @@ void adjustdata(Project *pr)
if (demand->Pat == 0)
{
demand->Pat = hyd->DefPat;
strcpy(demand->Name, "");
xstrcpy(&demand->Name, "", MAXMSG);
}
}
}

View File

@@ -7,7 +7,7 @@ Description: reads and interprets network data from an EPANET input file
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 01/01/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -166,6 +166,7 @@ int readdata(Project *pr)
net->Npats = parser->MaxPats;
parser->PrevPat = NULL;
parser->PrevCurve = NULL;
parser->LineComment[0] = '\0';
sect = -1;
errsum = 0;
@@ -175,19 +176,31 @@ int readdata(Project *pr)
{
// Make copy of line and scan for tokens
strcpy(wline, line);
parser->Ntokens = gettokens(wline, parser->Tok, MAXTOKS,
parser->Comment);
parser->Ntokens = gettokens(wline, parser->Tok, MAXTOKS, parser->Comment);
// Skip blank lines and comments
// Skip blank lines and those filled with a comment
parser->ErrTok = -1;
if (parser->Ntokens == 0) continue;
if (*parser->Tok[0] == ';') continue;
if (parser->Ntokens == 0)
{
// Store full line comment for Patterns and Curves
if (sect == _PATTERNS || sect == _CURVES)
{
strncpy(parser->LineComment, parser->Comment, MAXMSG);
}
continue;
}
// Apply full line comment for Patterns and Curves
if (sect == _PATTERNS || sect == _CURVES)
{
strcpy(parser->Comment, parser->LineComment);
}
parser->LineComment[0] = '\0';
// Check if max. line length exceeded
if (strlen(line) >= MAXLINE)
{
sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg),
SectTxt[sect]);
sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg), SectTxt[sect]);
writeline(pr, pr->Msg);
writeline(pr, line);
errsum++;

View File

@@ -7,7 +7,7 @@ Description: parses network data from a line of an EPANET input file
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 01/01/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -115,15 +115,14 @@ int juncdata(Project *pr)
node->Ke = 0.0;
node->Rpt = 0;
node->Type = JUNCTION;
strcpy(node->Comment, parser->Comment);
node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
// create a demand record, even if no demand is specified here.
demand = (struct Sdemand *) malloc(sizeof(struct Sdemand));
if (demand == NULL) return 101;
demand->Base = y;
demand->Pat = p;
strncpy(demand->Name, "", MAXMSG);
demand->Name = NULL;
demand->next = NULL;
node->D = demand;
hyd->NodeDemand[njuncs] = y;
@@ -224,7 +223,7 @@ int tankdata(Project *pr)
node->S = NULL;
node->Ke = 0.0;
node->Type = (diam == 0) ? RESERVOIR : TANK;
strcpy(node->Comment, parser->Comment);
node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
tank->Node = i;
tank->H0 = initlevel;
tank->Hmin = minlevel;
@@ -329,7 +328,7 @@ int pipedata(Project *pr)
link->Type = type;
link->Status = status;
link->Rpt = 0;
strcpy(link->Comment, parser->Comment);
link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
return 0;
}
@@ -392,7 +391,7 @@ int pumpdata(Project *pr)
link->Type = PUMP;
link->Status = OPEN;
link->Rpt = 0;
strcpy(link->Comment, parser->Comment);
link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
pump->Link = net->Nlinks;
pump->Ptype = NOCURVE; // NOCURVE is a placeholder
pump->Hcurve = 0;
@@ -535,7 +534,7 @@ int valvedata(Project *pr)
link->Type = type;
link->Status = status;
link->Rpt = 0;
strcpy(link->Comment, parser->Comment);
link->Comment = xstrcpy(&link->Comment, parser->Comment, MAXMSG);
net->Valve[net->Nvalves].Link = net->Nlinks;
return 0;
}
@@ -559,6 +558,7 @@ int patterndata(Project *pr)
double x;
SFloatlist *f;
STmplist *p;
Spattern *pattern;
n = parser->Ntokens - 1;
if (n < 1) return 201;
@@ -566,8 +566,13 @@ int patterndata(Project *pr)
// Check for a new pattern
if (parser->PrevPat != NULL &&
strcmp(parser->Tok[0], parser->PrevPat->ID) == 0) p = parser->PrevPat;
else p = getlistitem(parser->Tok[0], parser->Patlist);
else
{
p = getlistitem(parser->Tok[0], parser->Patlist);
if (p == NULL) return setError(parser, 0, 205);
pattern = &(net->Pattern[p->i]);
pattern->Comment = xstrcpy(&pattern->Comment, parser->Comment, MAXMSG);
}
// Add parsed multipliers to the pattern
for (i = 1; i <= n; i++)
@@ -606,13 +611,19 @@ int curvedata(Project *pr)
double x, y;
SFloatlist *fx, *fy;
STmplist *c;
Scurve *curve;
// Check for valid curve ID
if (parser->Ntokens < 3) return 201;
if (parser->PrevCurve != NULL &&
strcmp(parser->Tok[0], parser->PrevCurve->ID) == 0) c = parser->PrevCurve;
else c = getlistitem(parser->Tok[0], parser->Curvelist);
else
{
c = getlistitem(parser->Tok[0], parser->Curvelist);
if (c == NULL) return setError(parser, 0, 206);
curve = &(net->Curve[c->i]);
curve->Comment = xstrcpy(&curve->Comment, parser->Comment, MAXMSG);
}
// Check for valid data
if (!getfloat(parser->Tok[1], &x)) return setError(parser, 1, 202);
@@ -731,7 +742,7 @@ int demanddata(Project *pr)
// with what is specified in this section
demand->Base = y;
demand->Pat = p;
strncpy(demand->Name, parser->Comment, MAXMSG);
demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXMSG);
hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category.
}
@@ -744,7 +755,7 @@ int demanddata(Project *pr)
if (demand == NULL) return 101;
demand->Base = y;
demand->Pat = p;
strncpy(demand->Name, parser->Comment, MAXMSG);
demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXMSG);
demand->next = NULL;
cur_demand->next = demand;
}

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 03/05/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -370,6 +370,7 @@ int allocdata(Project *pr)
{
pr->network.Pattern[n].Length = 0;
pr->network.Pattern[n].F = NULL;
pr->network.Pattern[n].Comment = NULL;
}
for (n = 0; n <= pr->parser.MaxCurves; n++)
{
@@ -377,10 +378,17 @@ int allocdata(Project *pr)
pr->network.Curve[n].Type = GENERIC_CURVE;
pr->network.Curve[n].X = NULL;
pr->network.Curve[n].Y = NULL;
pr->network.Curve[n].Comment = NULL;
}
for (n = 0; n <= pr->parser.MaxNodes; n++)
{
pr->network.Node[n].D = NULL; // node demand
pr->network.Node[n].S = NULL; // node source
pr->network.Node[n].Comment = NULL;
}
for (n = 0; n <= pr->parser.MaxLinks; n++)
{
pr->network.Link[n].Comment = NULL;
}
}
@@ -459,18 +467,29 @@ void freedata(Project *pr)
while (demand != NULL)
{
nextdemand = demand->next;
free(demand->Name);
free(demand);
demand = nextdemand;
}
// Free memory used for WQ source data
source = pr->network.Node[j].S;
if (source != NULL) free(source);
free(source);
free(pr->network.Node[j].Comment);
}
free(pr->network.Node);
}
// Free memory for other network objects
// Free memory for link data
if (pr->network.Link != NULL)
{
for (j = 0; j <= pr->parser.MaxLinks; j++)
{
free(pr->network.Link[j].Comment);
}
}
free(pr->network.Link);
// Free memory for other network objects
free(pr->network.Tank);
free(pr->network.Pump);
free(pr->network.Valve);
@@ -479,7 +498,11 @@ void freedata(Project *pr)
// Free memory for time patterns
if (pr->network.Pattern != NULL)
{
for (j = 0; j <= pr->parser.MaxPats; j++) free(pr->network.Pattern[j].F);
for (j = 0; j <= pr->parser.MaxPats; j++)
{
free(pr->network.Pattern[j].F);
free(pr->network.Pattern[j].Comment);
}
free(pr->network.Pattern);
}
@@ -490,6 +513,7 @@ void freedata(Project *pr)
{
free(pr->network.Curve[j].X);
free(pr->network.Curve[j].Y);
free(pr->network.Curve[j].Comment);
}
free(pr->network.Curve);
}
@@ -878,6 +902,87 @@ void adjustcurves(Network *network, int index)
}
}
int getcomment(Network *network, int object, int index, char *comment)
//----------------------------------------------------------------
// Input: object = a type of network object
// index = index of the specified object
// comment = the object's comment string
// Output: error code
// Purpose: gets the comment string assigned to an object.
//----------------------------------------------------------------
{
char *currentcomment;
// Get pointer to specified object's comment
switch (object)
{
case NODE:
if (index < 1 || index > network->Nnodes) return 251;
currentcomment = network->Node[index].Comment;
break;
case LINK:
if (index < 1 || index > network->Nlinks) return 251;
currentcomment = network->Link[index].Comment;
break;
case TIMEPAT:
if (index < 1 || index > network->Npats) return 251;
currentcomment = network->Pattern[index].Comment;
break;
case CURVE:
if (index < 1 || index > network->Ncurves) return 251;
currentcomment = network->Curve[index].Comment;
break;
default:
strcpy(comment, "");
return 251;
}
// Copy the object's comment to the returned string
if (currentcomment) strcpy(comment, currentcomment);
else comment[0] = '\0';
return 0;
}
int setcomment(Network *network, int object, int index, const char *newcomment)
//----------------------------------------------------------------
// Input: object = a type of network object
// index = index of the specified object
// newcomment = new comment string
// Output: error code
// Purpose: sets the comment string of an object.
//----------------------------------------------------------------
{
char *comment;
switch (object)
{
case NODE:
if (index < 1 || index > network->Nnodes) return 251;
comment = network->Node[index].Comment;
network->Node[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
return 0;
case LINK:
if (index < 1 || index > network->Nlinks) return 251;
comment = network->Link[index].Comment;
network->Link[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
return 0;
case TIMEPAT:
if (index < 1 || index > network->Npats) return 251;
comment = network->Pattern[index].Comment;
network->Pattern[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
return 0;
case CURVE:
if (index < 1 || index > network->Ncurves) return 251;
comment = network->Curve[index].Comment;
network->Curve[index].Comment = xstrcpy(&comment, newcomment, MAXMSG);
return 0;
default: return 251;
}
}
char *getTmpName(char *fname)
//----------------------------------------------------------------
@@ -911,6 +1016,42 @@ char *getTmpName(char *fname)
return fname;
}
char *xstrcpy(char **s1, const char *s2, const size_t n)
//----------------------------------------------------------------
// Input: s1 = destination string
// s2 = source string
// n = maximum size of strings
// Output: none
// Purpose: like strcpy except for dynamic strings.
//----------------------------------------------------------------
{
size_t n1 = 0, n2;
// Source string is empty -- free destination string
if (s2 == NULL || strlen(s2) == 0)
{
free(*s1);
return NULL;
}
// Source string not empty -- overwrite destination string
else
{
// See if size of destination string needs to grow
if (*s1) n1 = strlen(*s1);
if ((n2 = strlen(s2)) > n) n2 = n;
if (n2 > n1)
{
free(*s1);
*s1 = (char *)malloc((n2 + 1) * sizeof(char));
}
// Copy the new comment string into the existing one
if (*s1) strcpy(*s1, s2);
return *s1;
}
}
int strcomp(const char *s1, const char *s2)
/*---------------------------------------------------------------
** Input: s1 = character string

View File

@@ -7,7 +7,7 @@
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 01/01/2019
Last Updated: 03/17/2019
******************************************************************************
*/
@@ -118,11 +118,15 @@ typedef int INT4;
----------------------------------------------
Enumerated Data Types
----------------------------------------------
*/
*/
typedef enum {
NODE,
LINK
LINK,
TIMEPAT,
CURVE,
CONTROL,
RULE
} ObjectType;
typedef enum {
@@ -333,6 +337,7 @@ typedef struct Tmplist STmplist; // Pointer to temporary list of objects
typedef struct // Time Pattern Object
{
char ID[MAXID+1]; // pattern ID
char *Comment; // pattern comment
int Length; // pattern length
double *F; // pattern factors
} Spattern;
@@ -340,6 +345,7 @@ typedef struct // Time Pattern Object
typedef struct // Curve Object
{
char ID[MAXID+1]; // curve ID
char *Comment; // curve comment
CurveType Type; // curve type
int Npts; // number of points
double *X; // x-values
@@ -350,7 +356,7 @@ struct Sdemand // Demand List Item
{
double Base; // baseline demand
int Pat; // pattern index
char Name[MAXMSG+1]; // demand category name
char *Name; // demand category name
struct Sdemand *next; // next demand list item
};
typedef struct Sdemand *Pdemand; // Pointer to demand list
@@ -386,7 +392,7 @@ typedef struct // Node Object
double Ke; // emitter coeff.
int Rpt; // reporting flag
NodeType Type; // node type
char Comment[MAXMSG+1]; // node comment
char *Comment; // node comment
} Snode;
typedef struct // Link Object
@@ -406,7 +412,7 @@ typedef struct // Link Object
LinkType Type; // link type
StatusType Status; // initial status
int Rpt; // reporting flag
char Comment[MAXMSG+1]; // link Comment
char *Comment; // link comment
} Slink;
typedef struct // Tank Object
@@ -544,10 +550,11 @@ typedef struct {
FILE *InFile; // Input file handle
char
DefPatID[MAXID+1], // Default demand pattern ID
InpFname[MAXFNAME+1], // Input file name
DefPatID[MAXID + 1], // Default demand pattern ID
InpFname[MAXFNAME + 1], // Input file name
*Tok[MAXTOKS], // Array of token strings
Comment[MAXMSG+1]; // Comment text
Comment[MAXMSG + 1], // Comment text
LineComment[MAXMSG + 1]; // Full line comment
int
MaxNodes, // Node count from input file */

131
tests/test_comments.cpp Normal file
View File

@@ -0,0 +1,131 @@
// Test of EPANET's Comment Handling Functions
//
// This is a test of the API functions EN_getcomment and EN_setcomment
//
#define _CRT_SECURE_NO_DEPRECATE
//#define NO_BOOST
#ifndef NO_BOOST
#define BOOST_TEST_MODULE "toolkit"
#include <boost/test/included/unit_test.hpp>
#endif
#include <iostream>
#include <string>
#include "epanet2_2.h"
#define DATA_PATH_INP "./net1.inp"
#define DATA_PATH_RPT "./test.rpt"
#define DATA_PATH_OUT "./test.out"
#define DATA_PATH_TMP "./tmp.inp"
#ifdef NO_BOOST
#define BOOST_REQUIRE(x) (((x)) ? cout << "\nPassed at line " << __LINE__ : cout << "\nFailed at line " << __LINE__ )
#endif
using namespace std;
int checkComments(EN_Project ph)
{
int index;
char comment[EN_MAXMSG + 1];
EN_getnodeindex(ph, (char *)"11", &index);
EN_getcomment(ph, EN_NODE, index, comment);
if (strcmp(comment, (char *)"J11") != 0) return 0;
EN_getnodeindex(ph, (char *)"23", &index);
EN_getcomment(ph, EN_NODE, index, comment);
if (strcmp(comment, (char *)"Junc23") != 0) return 0;
EN_getlinkindex(ph, (char *)"11", &index);
EN_getcomment(ph, EN_LINK, index, comment);
if (strcmp(comment, (char *)"P11") != 0) return 0;
EN_getlinkindex(ph, (char *)"9", &index);
EN_getcomment(ph, EN_LINK, index, comment);
if (strcmp(comment, (char *)"Pump9") != 0) return 0;
EN_getpatternindex(ph, (char *)"1", &index);
EN_getcomment(ph, EN_TIMEPAT, index, comment);
if (strcmp(comment, (char *)"Time Pattern 1") != 0) return 0;
EN_getcurveindex(ph, (char *)"1", &index);
EN_getcomment(ph, EN_CURVE, index, comment);
if (strcmp(comment, (char *)"Curve 1") != 0) return 0;
return 1;
}
#ifndef NO_BOOST
BOOST_AUTO_TEST_SUITE (test_toolkit)
BOOST_AUTO_TEST_CASE(test_setlinktype)
{
#else
int main(int argc, char *argv[])
{
#endif
int error = 0;
int index;
char comment[EN_MAXMSG+1];
// Create & load a project
EN_Project ph = NULL;
EN_createproject(&ph);
std::string path_inp = string(DATA_PATH_INP);
std::string path_rpt = string(DATA_PATH_RPT);
std::string path_out = string(DATA_PATH_OUT);
error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), "");
BOOST_REQUIRE(error == 0);
// Add comments to selected objects
EN_getnodeindex(ph, (char *)"11", &index);
EN_setcomment(ph, EN_NODE, index, (char *)"J11");
EN_getnodeindex(ph, (char *)"23", &index);
EN_setcomment(ph, EN_NODE, index, (char *)"Junc23");
EN_getlinkindex(ph, (char *)"11", &index);
EN_setcomment(ph, EN_LINK, index, (char *)"P11");
EN_getlinkindex(ph, (char *)"9", &index);
EN_setcomment(ph, EN_LINK, index, (char *)"Pump9");
EN_getpatternindex(ph, (char *)"1", &index);
EN_setcomment(ph, EN_TIMEPAT, index, (char *)"Time Pattern 1");
EN_getcurveindex(ph, (char *)"1", &index);
EN_setcomment(ph, EN_CURVE, index, (char *)"Curve 1");
// Retrieve comments and test their values
BOOST_REQUIRE(checkComments(ph) == 1);
// Replace short comment with longer one and vice versa
EN_getnodeindex(ph, (char *)"11", &index);
EN_setcomment(ph, EN_NODE, index, (char *)"Junction11");
EN_getcomment(ph, EN_NODE, index, comment);
BOOST_REQUIRE(strcmp(comment, (char *)"Junction11") == 0);
EN_setcomment(ph, EN_NODE, index, (char *)"J11");
EN_getcomment(ph, EN_NODE, index, comment);
BOOST_REQUIRE(strcmp(comment, (char *)"J11") == 0);
// Save & re-open project
string path_tmp = string(DATA_PATH_TMP);
EN_saveinpfile(ph, path_tmp.c_str());
EN_close(ph);
error = EN_open(ph, path_tmp.c_str(), path_rpt.c_str(), "");
BOOST_REQUIRE(error == 0);
// Check that comments were saved & read correctly
BOOST_REQUIRE(checkComments(ph) == 1);
remove(path_tmp.c_str());
// Close project
EN_close(ph);
EN_deleteproject(&ph);
#ifdef NO_BOOST
return 0;
#endif
}
#ifndef NO_BOOST
BOOST_AUTO_TEST_SUITE_END()
#endif

View File

@@ -21,6 +21,7 @@ EXPORTS
ENepanet = _ENepanet@16
ENgetaveragepatternvalue = _ENgetaveragepatternvalue@8
ENgetbasedemand = _ENgetbasedemand@12
ENgetcomment = _ENgetcomment@12
ENgetcontrol = _ENgetcontrol@24
ENgetcoord = _ENgetcoord@12
ENgetcount = _ENgetcount@8
@@ -80,6 +81,7 @@ EXPORTS
ENsavehydfile = _ENsavehydfile@4
ENsaveinpfile = _ENsaveinpfile@4
ENsetbasedemand = _ENsetbasedemand@12
ENsetcomment = _ENsetcomment@12
ENsetcontrol = _ENsetcontrol@24
ENsetcoord = _ENsetcoord@20
ENsetcurve = _ENsetcurve@16