From 44ad6e841a25d9c577781cfca8d1b8f3b68cd434 Mon Sep 17 00:00:00 2001 From: Bryant McDonnell Date: Thu, 10 Dec 2015 20:54:57 -0500 Subject: [PATCH] Adding Python wrapper for outputapi --- tools/outputapi/ENOutputWrapper.py | 1 + tools/outputapi/_ENOutputToolkit.py | 1 + tools/outputapi/outputapi.c | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 tools/outputapi/ENOutputWrapper.py create mode 100644 tools/outputapi/_ENOutputToolkit.py diff --git a/tools/outputapi/ENOutputWrapper.py b/tools/outputapi/ENOutputWrapper.py new file mode 100644 index 0000000..31b14f5 --- /dev/null +++ b/tools/outputapi/ENOutputWrapper.py @@ -0,0 +1 @@ +''' Wrapper for EPANET Output API. Author: Bryant E. McDonnell Date: 12/7/2015 Language: Anglais ''' from ctypes import * from _ENOutputToolkit import * #Used just to pull the Pointer of the ENResultsAPI struct class _Opaque(Structure): ''' Used soley for passing the pointer to the enrapi struct to API ''' pass class OutputObject: def __init__(self, dllLoc): ''' Instantiate python Wrapper Object and build Wrapper functions. ''' try: self.DLL = CDLL(dllLoc) except: raise Exception('Failed to Open Linked Library') ###ENResultsAPI* DLLEXPORT ENR_alloc(void); self._enrapiFunc = self.DLL.ENR_alloc self._enrapiFunc.restype = POINTER(_Opaque) ###int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path); self._OpenFunc = self.DLL.ENR_open self._OpenFunc.argtypes = [POINTER(_Opaque),POINTER(c_char)] self._OpenFunc.restype = c_int ###int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count); self._GetNetSize = self.DLL.ENR_getNetSize self._GetNetSize.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._GetNetSize.restype = c_int ###int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag); self._GetUnits = self.DLL.ENR_getUnits self._GetUnits.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._GetUnits.restype = c_int ###int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time) self._getTimes = self.DLL.ENR_getTimes self._getTimes.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._getTimes.restype = c_int ###float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, ### int seriesLength, int* length, int* errcode); self._newOutValueSeries = self.DLL.ENR_newOutValueSeries self._newOutValueSeries.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueSeries.restype = POINTER(c_float) ###float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, ### ENR_ElementType type, int* length, int* errcode); self._newOutValueArray = self.DLL.ENR_newOutValueArray self._newOutValueArray.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueArray.restype = POINTER(c_float) ###int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, ### int timeIndex, int length, float* outValueSeries, int* len); self._getNodeSeries = self.DLL.ENR_getNodeSeries self._getNodeSeries.argtypes = [POINTER(_Opaque), c_int, c_int, c_int, c_int, POINTER(c_float)] self._getNodeSeries.restype = c_int ###int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, ### int timeIndex, int length, float* outValueSeries); self._getLinkSeries = self.DLL.ENR_getLinkSeries self._getLinkSeries.argtypes = [POINTER(_Opaque), c_int, c_int, c_int, c_int, POINTER(c_float)] self._getLinkSeries.restype = c_int ###int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_NodeAttribute attr, float* outValueArray); self._getNodeAttribute = self.DLL.ENR_getNodeAttribute self._getNodeAttribute.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_float)] self._getNodeAttribute.restype = c_int ###int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_LinkAttribute attr, float* outValueArray); self._getLinkAttribute = self.DLL.ENR_getLinkAttribute self._getLinkAttribute.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_float)] self._getLinkAttribute.restype = c_int ###int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, ### float* outValueArray); self._getNodeResult = self.DLL.ENR_getNodeResult self._getNodeResult.argtypes = [POINTER(_Opaque), c_int, c_int,\ POINTER(c_float)] self._getNodeResult.restype = c_int ###int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, ### float* outValueArray); self._getLinkResult = self.DLL.ENR_getLinkResult self._getLinkResult.argtypes = [POINTER(_Opaque), c_int, c_int,\ POINTER(c_float)] self._getLinkResult.restype = c_int ###int DLLEXPORT ENR_free(float *array); self._free = self.DLL.ENR_free self._free.argtypes = [POINTER(c_float)] self._free.restype = c_int ###int DLLEXPORT ENR_close(ENResultsAPI* enrapi); self._CloseOut = self.DLL.ENR_close self._CloseOut.argtypes = [POINTER(_Opaque)] self._CloseOut.restype = c_int ###int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n); self._RetErrMessage = self.DLL.ENR_errMessage self._RetErrMessage.argtypes = [c_int, POINTER(c_char_p), c_int] self._RetErrMessage.restype = c_int def OpenOutputFile(self, binfile): ''' 1) Initializes the opaque pointer to enrapi struct. 2) Opens the output file. ''' self.enrapi = self._enrapiFunc() ret = self._OpenFunc(self.enrapi, binfile) if ret != 0: self.CloseOutputFile(self.enrapi) raise Exception('Failed to open OutputFile') ##Update Function at a later date ## def RaiseError(self, ErrNo): ## ErMsg = c_char_p(256) ## ## self._RetErrMessage(ErrNo , ErMsg, 256) ## ## print ErMsg def get_Units(self): ''' Purpose: Returns pressure and flow units ''' unit = c_int() self._GetUnits(self.enrapi, ENR_flowUnits, unit) self.flowUnits = unit.value unit = c_int() self._GetUnits(self.enrapi, ENR_pressUnits, unit) self.pressUnits = unit.value def get_NetSize(self): ''' Populates object attributes with the water object counts ''' count = c_int() self._GetNetSize(self.enrapi, ENR_nodeCount, byref(count)) self.nodeCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_tankCount, byref(count)) self.tankCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_linkCount, byref(count)) self.linkCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_pumpCount, byref(count)) self.pumpCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_valveCount, byref(count)) self.valveCount = count.value def get_Times(self): ''' Purpose: Returns report and simulation time related parameters. ''' temp = c_int() self._getTimes(self.enrapi, ENR_reportStart, byref(temp)) self.reportStart = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_reportStep, byref(temp)) self.reportStep = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_simDuration, byref(temp)) self.simDuration = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_numPeriods, byref(temp)) self.numPeriods = temp.value def get_NodeSeries(self, NodeInd, NodeAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getNodeSeries(self.enrapi, NodeInd, NodeAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] self._free(SeriesPtr) return BldArray def get_LinkSeries(self, LinkInd, LinkAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getLinkSeries(self.enrapi, LinkInd, LinkAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] ret = self._free(SeriesPtr) return BldArray def get_NodeAttribute(self, NodeAttr, TimeInd): ''' Purpose: For all nodes at given time, get a particular attribute ''' if not hasattr(self, 'nodeCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeAttribute(self.enrapi, TimeInd, NodeAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkAttribute(self, LinkAttr, TimeInd): ''' Purpose: For all links at given time, get a particular attribute ''' if not hasattr(self, 'linkCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkAttribute(self.enrapi, TimeInd, LinkAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_NodeResult(self, NodeInd, TimeInd): ''' Purpose: For a node at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeResult(self.enrapi, TimeInd, NodeInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkResult(self, LinkInd, TimeInd): ''' Purpose: For a link at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkResult(self.enrapi, TimeInd, LinkInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def CloseOutputFile(self): ''' Call to close binary file. ''' ret = self._CloseOut(self.enrapi) if ret != 0: raise Exception('Failed to Close *.out file') if __name__ in "__main__": dllLoc = 'outputAPI.dll' binfile = 'C:\\PROJECTCODE\\EPANEToutputAPI\\Net3.out' print("Testing Wrapper...") print("Test Network 3...") print("-->Instantiating DLL Object / Opening DLL") self = OutputObject(dllLoc) print("...Opened DLL") print("\n-->Reading Binary File") self.OpenOutputFile(binfile) print("...Opened Bin File") print("\n-->get_NodeSeries") LENTest = -1 demands = self.get_NodeSeries(0,ENR_demand, 0, LENTest) head = self.get_NodeSeries(0,ENR_head, 0, LENTest) pressure = self.get_NodeSeries(0,ENR_pressure, 0, LENTest) quality = self.get_NodeSeries(0,ENR_quality, 0, LENTest) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demands): print ind, val, head[ind], pressure[ind], quality[ind] print("\n-->get_LinkSeries") flow = self.get_LinkSeries(0,ENR_flow) velocity =self.get_LinkSeries(0,ENR_velocity) headloss =self.get_LinkSeries(0,ENR_headloss) avgQuality=self.get_LinkSeries(0,ENR_avgQuality) status=self.get_LinkSeries(0,ENR_status) setting=self.get_LinkSeries(0,ENR_setting) rxRate=self.get_LinkSeries(0,ENR_rxRate) frctnFctr =self.get_LinkSeries(0,ENT_frctnFctr) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow): print ind, val, velocity[ind],headloss[ind],avgQuality[ind],status[ind],setting[ind],rxRate[ind],frctnFctr[ind] print("\n-->get_NodeAttribute") demand_1 = self.get_NodeAttribute(ENR_demand,0) head_1 = self.get_NodeAttribute(ENR_head,0) pressure_1 = self.get_NodeAttribute(ENR_pressure,0) quality_1 = self.get_NodeAttribute(ENR_quality,0) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demand_1): print ind, val, head_1[ind], pressure_1[ind], quality_1[ind] print("\n-->get_LinkAttribute") flow1 = self.get_LinkAttribute(ENR_flow,0) velocity1 =self.get_LinkAttribute(ENR_velocity,0) headloss1 =self.get_LinkAttribute(ENR_headloss,0) avgQuality1=self.get_LinkAttribute(ENR_avgQuality,0) status1=self.get_LinkAttribute(ENR_status,0) setting1=self.get_LinkAttribute(ENR_setting,0) rxRate1=self.get_LinkAttribute(ENR_rxRate,0) frctnFctr1 =self.get_LinkAttribute(ENT_frctnFctr,0) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow1): print ind, val, velocity1[ind],headloss1[ind],avgQuality1[ind],status1[ind],setting1[ind],rxRate1[ind],frctnFctr1[ind] print("\n-->Object Counts") print("Nodes") print(self.nodeCount) print("Nodes") print(self.nodeCount) print("Tanks") print(self.tankCount) print("Links") print(self.linkCount) print("Pumps") print(self.pumpCount) print("Values") print(self.valveCount) print("\n-->get_NodeResult") print(self.get_NodeResult(96,24)) print("\n-->get_LinkResult") print(self.get_LinkResult(0,0)) print("\n-->get_Units") self.get_Units() print("...flow unit code") print(self.flowUnits) print("...pressure unit code") print(self.pressUnits) print("\n-->Closing Binary File") self.CloseOutputFile() print("...Closed Binary File") \ No newline at end of file diff --git a/tools/outputapi/_ENOutputToolkit.py b/tools/outputapi/_ENOutputToolkit.py new file mode 100644 index 0000000..7687828 --- /dev/null +++ b/tools/outputapi/_ENOutputToolkit.py @@ -0,0 +1 @@ +##ENR_ElementType; ENR_node = 1 ENR_link = 2 ##ENR_ApiFunction; ENR_getSeries = 1 ENR_getAttribute = 2 ENR_getResult = 3 ##ENR_ElementCount; ENR_nodeCount = 1 ENR_tankCount = 2 ENR_linkCount = 3 ENR_pumpCount = 4 ENR_valveCount = 5 ##ENR_Unit; ENR_flowUnits = 1 ENR_pressUnits = 2 ##ENR_Time; ENR_reportStart = 1 ENR_reportStep = 2 ENR_simDuration = 3 ENR_numPeriods = 4 ##ENR_NodeAttribute; ENR_demand = 0 ENR_head = 1 ENR_pressure = 2 ENR_quality = 3 ##ENR_LinkAttribute; ENR_flow = 0 ENR_velocity = 1 ENR_headloss = 2 ENR_avgQuality = 3 ENR_status = 4 ENR_setting = 5 ENR_rxRate = 6 ENT_frctnFctr = 7 \ No newline at end of file diff --git a/tools/outputapi/outputapi.c b/tools/outputapi/outputapi.c index 2daac29..3c99440 100644 --- a/tools/outputapi/outputapi.c +++ b/tools/outputapi/outputapi.c @@ -36,7 +36,7 @@ struct ENResultsAPI { bool isOpened; // current state (CLOSED = 0, OPEN = 1) FILE *file; // FILE structure pointer - INT4 nodeCount, tankCount, linkCount, pumpCount; + INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount; INT4 reportStart, reportStep, simDuration, nPeriods; INT4 flowFlag, pressFlag; @@ -141,6 +141,7 @@ int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* c case ENR_tankCount: *count = enrapi->tankCount; break; case ENR_linkCount: *count = enrapi->linkCount; break; case ENR_pumpCount: *count = enrapi->pumpCount; break; + case ENR_valveCount: *count = enrapi->valveCount; break; default: return 421; } return 0; @@ -274,8 +275,8 @@ int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAtt if (outValueSeries == NULL) return 411; // loop over and build time series - for (k = 1; k <= seriesLength; k++) - outValueSeries[k] = getNodeValue(enrapi, seriesStart + k, + for (k = 0; k <= seriesLength; k++) + outValueSeries[k] = getNodeValue(enrapi, seriesStart + 1 + k, nodeIndex, attr); return 0; @@ -300,8 +301,8 @@ int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAtt if (outValueSeries == NULL) return 411; // loop over and build time series - for (k = 1; k <= seriesLength; k++) - outValueSeries[k] = getLinkValue(enrapi, seriesStart + k, + for (k = 0; k <= seriesLength; k++) + outValueSeries[k] = getLinkValue(enrapi, seriesStart +1 + k, linkIndex, attr); return 0; @@ -324,7 +325,7 @@ int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, if (outValueArray == NULL) return 411; // calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod; + offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod; // add offset for node and attribute offset += (attr*enrapi->nodeCount)*RECORDSIZE; @@ -350,7 +351,7 @@ int DLLEXPORT ENR_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, if (outValueArray == NULL) return 411; // calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod + offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod + (NNODERESULTS*enrapi->nodeCount)*RECORDSIZE; // add offset for link and attribute offset += (attr*enrapi->linkCount)*RECORDSIZE; @@ -377,7 +378,7 @@ int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeInd if (outValueArray == NULL) return 411; for (j = 0; j < NNODERESULTS; j++) - outValueArray[j] = getNodeValue(enrapi, timeIndex, nodeIndex, j); + outValueArray[j] = getNodeValue(enrapi, timeIndex + 1, nodeIndex, j); return 0; } @@ -398,7 +399,7 @@ int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkInd if (outValueArray == NULL) return 411; for (j = 0; j < NLINKRESULTS; j++) - outValueArray[j] = getLinkValue(enrapi, timeIndex, linkIndex, j); + outValueArray[j] = getLinkValue(enrapi, timeIndex + 1, linkIndex, j); return 0; }