diff --git a/include/epanet2.h b/include/epanet2.h index 3779d66..99c0ad0 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -72,9 +72,9 @@ extern "C" { int DLLEXPORT ENopen(const char *inpFile, const char *rptFile, const char *outFile); - + int DLLEXPORT ENgettitle(char *line1, char *line2, char *line3); - + int DLLEXPORT ENsettitle(char *line1, char *line2, char *line3); int DLLEXPORT ENgetcomment(int object, int index, char *comment); @@ -156,9 +156,13 @@ extern "C" { int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); int DLLEXPORT ENgetstatistic(int type, EN_API_FLOAT_TYPE* value); - + int DLLEXPORT ENgetresultindex(int type, int index, int *value); + int DLLEXPORT ENsetreportcallback(void (*callback)(void *userData, void *EN_projectHandle, char*)); + int DLLEXPORT ENsetreportcallbackuserdata(void *userData); + + /******************************************************************** Analysis Options Functions @@ -235,7 +239,7 @@ extern "C" { char *demandPattern, char *demandName); int DLLEXPORT ENdeletedemand(int nodeIndex, int demandIndex); - + int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands); int DLLEXPORT ENgetdemandindex(int nodeIndex, char *demandName, int *demandIndex); @@ -285,13 +289,13 @@ extern "C" { int DLLEXPORT ENsetpipedata(int index, EN_API_FLOAT_TYPE length, EN_API_FLOAT_TYPE diam, EN_API_FLOAT_TYPE rough, EN_API_FLOAT_TYPE mloss); - + int DLLEXPORT ENgetvertexcount(int index, int *count); - + int DLLEXPORT ENgetvertex(int index, int vertex, double *x, double *y); - + int DLLEXPORT ENsetvertex(int index, int vertex, double x, double y); - + int DLLEXPORT ENsetvertices(int index, double *x, double *y, int count); /******************************************************************** @@ -351,7 +355,7 @@ extern "C" { int DLLEXPORT ENgetcurvelen(int index, int *len); int DLLEXPORT ENgetcurvetype(int index, int *type); - + int DLLEXPORT ENsetcurvetype(int index, int type); int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, @@ -425,7 +429,7 @@ extern "C" { int DLLEXPORT ENsetelseaction(int ruleIndex, int actionIndex, int linkIndex, int status, EN_API_FLOAT_TYPE setting); - + int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); #if defined(__cplusplus) diff --git a/include/epanet2_2.h b/include/epanet2_2.h index 18ef127..852eab2 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -517,6 +517,18 @@ typedef struct Project *EN_Project; ********************************************************************/ + /** + @brief Set a user-supplied callback function for reporting + @param ph an EPANET project handle. + @param callback a function pointer with declared signature, which gets called by EPANET for reporting. + @return an error code. + @details The report callback function must have the signature specified - void(void* userData, EN_Project, char*) - + use the userData parameter to pass any client context necessary (a context pointer or wrapper object perhaps). + Leave un-set or set the report callback to NULL to revert to EPANET's default behavior. + **/ + int DLLEXPORT EN_setreportcallback(EN_Project ph, void (*callback)(void *userData, void *EN_projectHandle, char*)); + int DLLEXPORT EN_setreportcallbackuserdata(EN_Project ph, void *userData); + /** @brief Writes a line of text to a project's report file. @param ph an EPANET project handle. diff --git a/src/epanet.c b/src/epanet.c index 1635b36..feb8c4e 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -841,6 +841,19 @@ int DLLEXPORT EN_closeQ(EN_Project p) ********************************************************************/ + + int DLLEXPORT EN_setreportcallback(EN_Project p, void (*callback)(void*,void*,char*)) + { + p->report.reportCallback = callback; + return 0; + } + + int DLLEXPORT EN_setreportcallbackuserdata(EN_Project p, void *userData) + { + p->report.reportCallbackUserData = userData; + return 0; + } + int DLLEXPORT EN_writeline(EN_Project p, char *line) /*---------------------------------------------------------------- ** Input: line = line of text @@ -1774,9 +1787,9 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index) // Check if a node with same id already exists if (EN_getnodeindex(p, id, &i) == 0) return 215; - + // Check for valid node type - if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251; + if (nodeType < EN_JUNCTION || nodeType > EN_TANK) return 251; // Grow node-related arrays to accomodate the new node size = (net->Nnodes + 2) * sizeof(Snode); @@ -1797,7 +1810,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index) hashtable_update(net->NodeHashTable, net->Node[i].ID, i + 1); net->Node[i + 1] = net->Node[i]; } - + // set index of new Junction node net->Njuncs++; nIdx = net->Njuncs; @@ -2254,20 +2267,20 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val if (Node[index].Type != TANK) return 0; v = Tank[index - nJuncs].CanOverflow; break; - + case EN_DEMANDDEFICIT: if (index > nJuncs) return 0; // After an analysis, DemandFlow contains node's required demand // while NodeDemand contains delivered demand + emitter flow if (hyd->DemandFlow[index] < 0.0) return 0; - v = (hyd->DemandFlow[index] - + v = (hyd->DemandFlow[index] - (hyd->NodeDemand[index] - hyd->EmitterFlow[index])) * Ucf[FLOW]; break; - + case EN_NODE_INCONTROL: v = (double)incontrols(p, NODE, index); break; - + default: return 251; } @@ -2483,7 +2496,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); // new min. volume Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume - Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area + Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area (curve->X[n] - curve->X[0]); break; @@ -3801,7 +3814,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val v = net->Valve[findvalve(&p->network, index)].Curve; } break; - + case EN_GPV_CURVE: if (Link[index].Type == GPV) { @@ -3812,7 +3825,7 @@ int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, int property, double *val case EN_LINK_INCONTROL: v = (double)incontrols(p, LINK, index); break; - + default: return 251; } @@ -4029,7 +4042,7 @@ int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int property, double valu net->Valve[findvalve(&p->network, index)].Curve = curveIndex; } break; - + case EN_GPV_CURVE: if (Link[index].Type == GPV) { @@ -4095,20 +4108,20 @@ int DLLEXPORT EN_getvertexcount(EN_Project p, int index, int *count) */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists *count = 0; if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Set count to number of vertices vertices = Link[index].Vertices; if (vertices) *count = vertices->Npts; return 0; -} +} int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, double *y) /*---------------------------------------------------------------- @@ -4122,22 +4135,22 @@ int DLLEXPORT EN_getvertex(EN_Project p, int index, int vertex, double *x, doubl */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists *x = MISSING; *y = MISSING; if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Check that vertex exists vertices = Link[index].Vertices; if (vertices == NULL) return 255; if (vertex <= 0 || vertex > vertices->Npts) return 255; *x = vertices->X[vertex - 1]; - *y = vertices->Y[vertex - 1]; + *y = vertices->Y[vertex - 1]; return 0; } @@ -4153,23 +4166,23 @@ int DLLEXPORT EN_setvertex(EN_Project p, int index, int vertex, double x, double */ { Network *net = &p->network; - + Slink *Link = net->Link; Pvertices vertices; - + // Check that link exists if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; - + // Check that vertex exists vertices = Link[index].Vertices; if (vertices == NULL) return 255; if (vertex <= 0 || vertex > vertices->Npts) return 255; vertices->X[vertex - 1] = x; - vertices->Y[vertex - 1] = y; + vertices->Y[vertex - 1] = y; return 0; } - + int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int count) /*---------------------------------------------------------------- ** Input: index = link index @@ -4182,11 +4195,11 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int */ { Network *net = &p->network; - + Slink *link; int i; int err = 0; - + // Check that link exists if (!p->Openflag) return 102; if (index <= 0 || index > net->Nlinks) return 204; @@ -4194,7 +4207,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int // Delete existing set of vertices freelinkvertices(link); - + // Add each new vertex to the link for (i = 0; i < count; i++) { @@ -4203,7 +4216,7 @@ int DLLEXPORT EN_setvertices(EN_Project p, int index, double *x, double *y, int } if (err) freelinkvertices(link); return err; -} +} /******************************************************************** @@ -4287,16 +4300,16 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex) pump = &p->network.Pump[pumpIndex]; oldCurveIndex = pump->Hcurve; newCurveType = p->network.Curve[curveIndex].Type; - + // Assign the new curve to the pump pump->Ptype = NOCURVE; pump->Hcurve = curveIndex; if (curveIndex == 0) return 0; - + // Update the pump's head curve parameters (which also changes // the new curve's Type to PUMP_CURVE) err = updatepumpparams(p, pumpIndex); - + // If the parameter updating failed (new curve was not a valid pump curve) // restore the pump's original curve and its parameters if (err > 0) @@ -4306,8 +4319,8 @@ int DLLEXPORT EN_setheadcurveindex(EN_Project p, int linkIndex, int curveIndex) pump->Hcurve = oldCurveIndex; if (oldCurveIndex == 0) return err; updatepumpparams(p, pumpIndex); - } - + } + // Convert the units of the updated pump parameters to feet and cfs if (pump->Ptype == POWER_FUNC) { @@ -4796,7 +4809,7 @@ int DLLEXPORT EN_setcurvetype(EN_Project p, int index, int type) Network *net = &p->network; if (!p->Openflag) return 102; if (index < 1 || index > net->Ncurves) return 206; - if (type < 0 || type > EN_GENERIC_CURVE) return 251; + if (type < 0 || type > EN_GENERIC_CURVE) return 251; net->Curve[index].Type = type; return 0; } @@ -4870,7 +4883,7 @@ int DLLEXPORT EN_setcurvevalue(EN_Project p, int curveIndex, int pointIndex, // Insert new point into curve curve->X[n] = x; curve->Y[n] = y; - + // Adjust parameters for pumps using curve as a head curve return adjustpumpparams(p, curveIndex); } @@ -4944,7 +4957,7 @@ int DLLEXPORT EN_setcurve(EN_Project p, int index, double *xValues, curve->X[j] = xValues[j]; curve->Y[j] = yValues[j]; } - + // Adjust parameters for pumps using curve as a head curve return adjustpumpparams(p, index); } diff --git a/src/epanet2.c b/src/epanet2.c index 1706936..5618550 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -212,6 +212,16 @@ int DLLEXPORT ENsetstatusreport(int level) return EN_setstatusreport(_defaultProject, level); } +int DLLEXPORT ENsetreportcallback(void (*callback)(void *userData, void *EN_projectHandle, char*)) +{ + return EN_setreportcallback(_defaultProject, callback); +} + +int DLLEXPORT ENsetreportcallbackuserdata(void *userData) +{ + return EN_setreportcallbackuserdata(_defaultProject, userData); +} + int DLLEXPORT ENgetversion(int *version) { return EN_getversion(version); } int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen) @@ -516,7 +526,7 @@ int DLLEXPORT ENgetvertexcount(int index, int *count) { return EN_getvertexcount(_defaultProject, index, count); } - + int DLLEXPORT ENgetvertex(int index, int vertex, double *x, double *y) { return EN_getvertex(_defaultProject, index, vertex, x, y); @@ -530,7 +540,7 @@ int DLLEXPORT ENsetvertex(int index, int vertex, double x, double y) int DLLEXPORT ENsetvertices(int index, double *x, double *y, int count) { return EN_setvertices(_defaultProject, index, x, y, count); -} +} /******************************************************************** @@ -714,10 +724,10 @@ int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *xValues, double *xx = NULL; double *yy = NULL; int i, errcode = 0; - + if (xValues == NULL || yValues == NULL) return 206; if (nPoints < 1) return 202; - + xx = (double *)calloc(nPoints, sizeof(double)); yy = (double *)calloc(nPoints, sizeof(double)); if (xx && yy) diff --git a/src/project.c b/src/project.c index 4b8a971..2aefa21 100644 --- a/src/project.c +++ b/src/project.c @@ -281,6 +281,8 @@ void initpointers(Project *pr) pr->hydraul.smatrix.NZSUB = NULL; pr->hydraul.smatrix.LNZ = NULL; + pr->report.reportCallback = NULL; + initrules(pr); } diff --git a/src/report.c b/src/report.c index 8fc6d16..2922332 100644 --- a/src/report.c +++ b/src/report.c @@ -885,6 +885,12 @@ void writeline(Project *pr, char *s) **-------------------------------------------------------------- */ { + if (pr->report.reportCallback != NULL) + { + pr->report.reportCallback(pr->report.reportCallbackUserData, pr, s); + return; + } + Report *rpt = &pr->report; if (rpt->RptFile == NULL) return; diff --git a/src/types.h b/src/types.h index 281e012..e719c6b 100755 --- a/src/types.h +++ b/src/types.h @@ -632,7 +632,10 @@ typedef struct { Rpt2Fname[MAXFNAME+1], // Secondary report file name DateStamp[26]; // Current date & time - SField Field[MAXVAR]; // Output reporting fields + SField Field[MAXVAR]; // Output reporting fields + + void (*reportCallback)(void*,void*,char*); // user-supplied reporting callback + void *reportCallbackUserData; // user-supplied reporting context } Report;