Skip to content

Instantly share code, notes, and snippets.

@nowox
Created November 6, 2018 13:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nowox/7ce93ec0ef68d9a9e7e3b948e68d271c to your computer and use it in GitHub Desktop.
Save nowox/7ce93ec0ef68d9a9e7e3b948e68d271c to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import socket
import subprocess
import sys
import json
import xml.etree.ElementTree as ET
log = logging.getLogger(__name__)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)s - %(message)s')
ch.setFormatter(formatter)
log.addHandler(ch)
log.setLevel(logging.DEBUG)
class EnergyPlusAgent():
def __init__(self, **kwargs):
self.version = 8.8
self.bcvtb_home = '.'
self.model = 'model.idf'
self.weather = 'weather.epw'
self.socketFile = 'socket.cfg'
self.variableFile = 'variables.cfg'
self.time = 0
self.vers = 88
self.flag = 0
self.cwd = '/data/fmu'
self.sent = None
self.rcvd = None
self.socketServer = None
self.simulation = None
self.step = None
self.readVariableFile(self.variableFile)
def setup(self, sender, **kwargs):
log.debug('setup')
def start(self, sender, **kwargs):
self.startSocketServer()
self.startSimulation()
def startSocketServer(self):
self.socketServer = self.SocketServer()
self.socketServer.onRecv = self.recvEnergyPlusMssg
self.socketServer.connect()
def startSimulation(self):
if not self.model:
self.exit('No model specified.')
if not self.weather:
self.exit('No weather specified.')
modelPath = self.model
if (modelPath[0] == '~'):
modelPath = os.path.expanduser(modelPath)
if (modelPath[0] != '/'):
modelPath = os.path.join(self.cwd,modelPath)
weatherPath = self.weather
if (weatherPath[0] == '~'):
weatherPath = os.path.expanduser(weatherPath)
if (weatherPath[0] != '/'):
weatherPath = os.path.join(self.cwd,weatherPath)
modelDir = os.path.dirname(modelPath)
bcvtbDir = self.bcvtb_home
if (bcvtbDir[0] == '~'):
bcvtbDir = os.path.expanduser(bcvtbDir)
if (bcvtbDir[0] != '/'):
bcvtbDir = os.path.join(self.cwd,bcvtbDir)
log.debug('Working in %r', modelDir)
self.writePortFile(os.path.join(modelDir,'socket.cfg'))
if (self.version >= 8.4):
cmdStr = "cd %s; export BCVTB_HOME=\"%s\"; energyplus -w \"%s\" -r \"%s\"" % (modelDir, bcvtbDir, weatherPath, modelPath)
else:
cmdStr = "export BCVTB_HOME=\"%s\"; runenergyplus \"%s\" \"%s\"" % (bcvtbDir, modelPath, weatherPath)
log.debug('Running: %s', cmdStr)
self.simulation = subprocess.Popen(cmdStr, shell=True)
def sendEnergyPlusMssg(self):
if self.socketServer:
self.sent = '%r %r %r 0 0 %r 28.0 1\n' % (self.vers, self.flag, self.ePlusInputs, self.time)
log.info("Sending message to EnergyPlus: %s" % repr(self.sent))
self.socketServer.send(self.sent)
def recvEnergyPlusMssg(self, mssg):
self.rcvd = mssg
self.parseEnergyPlusMssg(mssg)
def parseEnergyPlusMssg(self, mssg):
mssg = str(mssg.rstrip())
log.info('Received message from EnergyPlus: ' + mssg)
arry = mssg.split()
slot = 6
flag = arry[1]
if flag != '0':
if flag == '1':
self.exit('Simulation reached end: ' + flag)
elif flag == '-1':
self.exit('Simulation stopped with unspecified error: ' + flag)
elif flag == '-10':
self.exit('Simulation stopped with error during initialization: ' + flag)
elif flag == '-20':
self.exit('Simulation stopped with error during time integration: ' + flag)
else:
self.exit('Simulation stopped with error code ' + flag)
self.stop()
elif ((int(arry[2]) < self.ePlusOutputs) and (len(arry) < self.ePlusOutputs + 6)):
self.exit('Got message with ' + arry[2] + ' inputs. Expecting ' + str(self.ePlusOutputs) + '.')
else:
data = {'time': 0, 'variables': []}
if float(arry[5]):
self.time = float(arry[5])
data['time'] = self.time
for key in self.outputs:
data['variables'].append({key['name']: float(arry[slot].replace("'",''))})
slot += 1
print(json.dumps(data, indent=2, sort_keys=True))
self.sendEnergyPlusMssg()
def exit(self, mssg):
self.stop()
log.error(mssg)
def stop(self):
if self.socketServer:
self.socketServer.stop()
def writePortFile(self, path):
text = (
'<?xml version="1.0" encoding="ISO-8859-1"?>\n'
'<BCVTB-client>\n'
' <ipc>\n'
' <socket port="%r" hostname="%s"/>\n'
' </ipc>\n'
'</BCVTB-client>') % (self.socketServer.port, self.socketServer.host)
with open(path, "w") as fp:
fp.write(text)
log.debug("Writing socket file '%s'" % text)
log.info("Socket files '%s' created" % path)
def readVariableFile(self, path):
self.outputs = []
self.inputs = []
for variable in ET.parse(path).getroot().findall('variable'):
ep = variable.findall('EnergyPlus')[0]
if variable.get('source') == 'Ptolemy':
self.inputs.append(ep.get('schedule'))
elif variable.get('source') == 'EnergyPlus':
self.outputs.append({'name': ep.get('name'), 'type': ep.get('type')})
else:
raise ValueError('Invalid source name "%s"' % variable.get('source'))
self.ePlusInputs = len(self.inputs)
self.ePlusOutputs = len(self.outputs)
def onUpdateTopicRpc(self, requester_id, topic, value):
self.updateComplete()
def onUpdateComplete(self):
self.sendEnergyPlusMssg()
class SocketServer():
def __init__(self, **kwargs):
self.sock = None
self.size = 4096
self.client = None
self.sent = None
self.rcvd = None
self.host = 'localhost'
self.port = 50051
def onRecv(self, mssg):
log.debug('Received %s' % mssg)
def run(self):
self.listen()
def connect(self):
if self.host is None:
self.host = socket.gethostname()
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.port is None:
self.sock.bind((self.host, 0))
self.port = self.sock.getsockname()[1]
else:
self.sock.bind((self.host, self.port))
log.debug('Bound to %r on %r' % (self.port, self.host))
def send(self, mssg):
self.sent = mssg
if self.client is not None and self.sock is not None:
self.client.send(bytes(self.sent, 'utf-8'))
def recv(self):
if self.client is not None and self.sock is not None:
try:
mssg = self.client.recv(self.size)
except Exception:
log.error('We got an error trying to read a message')
return mssg
def start(self):
log.debug('Starting socket server')
self.run()
def stop(self):
if self.sock != None:
self.sock.close()
def listen(self):
self.sock.listen(10)
log.debug('Server now listening')
self.client, addr = self.sock.accept()
log.debug('Connected with ' + addr[0] + ':' + str(addr[1]))
log.info('Waiting for simulation start states')
mssg = self.recv()
if mssg:
self.rcvd = mssg
self.onRecv(mssg)
log.info('Simulation loop')
while self.sock:
mssg = self.recv()
if mssg:
self.rcvd = mssg
self.onRecv(mssg)
def bcvtb_server():
e = EnergyPlusAgent()
e.startSocketServer()
e.startSimulation()
e.socketServer.listen()
if __name__ == "__main__":
bcvtb_server()
@mehrdadxyz
Copy link

mehrdadxyz commented May 9, 2020

Great job man!

Your code is a light in the darkness for me :)) thanks.
I have one question and one request!

  • Does it work? :)
  • Can you plz briefly explain what does each class (and its methods) do? (sorry man, I'm new to python and object-oriented programming!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment