Skip to content

Commit

Permalink
Merge pull request #1 from sguduguntla/master
Browse files Browse the repository at this point in the history
Updated Indoor_data and outdoor_temperature service to pymortar
  • Loading branch information
daniellengyel authored Mar 17, 2019
2 parents 4180dc4 + 3bdfea4 commit 3fad448
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 170 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.linting.pylintEnabled": true
}
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions services/indoor_data_historical/req.txt
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ xbos_services_utils2 <= 0.0.10dev
pytz == 2018.3
xbos==0.0.35
bw2python==0.6.1
pymortar>=1.0.0
rfc3339==6.0

234 changes: 129 additions & 105 deletions services/indoor_data_historical/server.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,120 +3,142 @@
from concurrent import futures
import time
import grpc
import pymortar
import indoor_temperature_action_pb2
import indoor_temperature_action_pb2_grpc
from xbos.services import mdal
from bw2python.client import Client
from xbos.services.hod import HodClient

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

# getting the utils file here
import os, sys
import xbos_services_utils2 as utils
import datetime
from datetime import datetime
from rfc3339 import rfc3339
import pytz

def _get_raw_actions(building, zone, mdal_client, hod_client, start, end, window_size):
def _get_raw_actions(building, zone, pymortar_client, start, end, window_size):
"""
TODO how to deal with windows in which two different actions are performed in given zone.
Note: GETS THE MAX ACTION IN GIVEN INTERVAL.
:param building:
:param zone:
:param mdal_client:
:param hod_client:
:param start: datetime, timezone aware
:param end: datetime, timezoneaware
:param window_size: string with [s, m, h, d] calssified in the end. e.g. "1s" for one second.
:param pymortar_client:
:param start: datetime, timezone aware, rfc3339
:param end: datetime, timezone aware, rfc3339
:param window_size: string with [s, m, h, d] classified in the end. e.g. "1s" for one second.
:return:
"""

# following query is for the whole building.
hod_thermostat_action_query = """SELECT ?zone ?uuid FROM %s WHERE {
?tstat rdf:type brick:Thermostat .
?tstat bf:hasLocation/bf:isPartOf ?location_zone .
?location_zone rdf:type brick:HVAC_Zone .
?tstat bf:controls ?RTU .
?RTU rdf:type brick:RTU .
?RTU bf:feeds ?zone.
?zone rdf:type brick:HVAC_Zone .
?tstat bf:hasPoint ?status_point .
?status_point rdf:type brick:Thermostat_Status .
?status_point bf:uuid ?uuid.
};""" % building

action_query_data = hod_client.do_query(hod_thermostat_action_query)["Rows"]
action_zone_uuid = {row["?zone"]: row["?uuid"] for row in action_query_data}[zone]

# get the data for the thermostats for queried zone.
mdal_query = {
'Composition': [action_zone_uuid],
'Selectors': [mdal.MAX],
'Time': {'T0': utils.datetime_to_mdal_string(start),
'T1': utils.datetime_to_mdal_string(end),
'WindowSize': window_size,
'Aligned': True}}

print(mdal_query)

mdal_action_data = utils.get_mdal_data(mdal_client, mdal_query).squeeze()
mdal_action_data.name = zone + "_raw_indoor_actions"
return mdal_action_data, None


def _get_raw_indoor_temperatures(building, zone, mdal_client, hod_client, start, end, window_size):
thermostat_action_query = """SELECT ?tstat ?zone ?status_point WHERE {
?tstat rdf:type brick:Thermostat .
?tstat bf:controls/bf:feeds ?zone .
?tstat bf:hasPoint ?status_point .
?status_point rdf:type brick:Thermostat_Status .
};"""

# resp = pymortar_client.qualify([thermostat_action_query]) Needed to get list of all sites

thermostat_action_view = pymortar.View(
name="thermostat_action_view",
sites=[building],
definition=thermostat_action_query,
)

thermostat_action_stream = pymortar.DataFrame(
name="thermostat_action",
aggregation=pymortar.MAX,
window=window_size,
timeseries=[
pymortar.Timeseries(
view="thermostat_action_view",
dataVars=["?status_point"],
)
]
)

request = pymortar.FetchRequest(
sites=[building],
views=[
thermostat_action_view
],
dataFrames=[
thermostat_action_stream
],
time=pymortar.TimeParams(
start=rfc3339(start),
end=rfc3339(end),
)
)

thermostat_action_data = pymortar_client.fetch(request)["thermostat_action"]

if thermostat_action_data is None:
return None, "did not fetch data from pymortar with query: %s" % thermostat_action_query

return thermostat_action_data, None

def _get_raw_indoor_temperatures(building, zone, pymortar_client, start, end, window_size):
"""
:param building:
:param zone:
:param mdal_client:
:param hod_client:
:param start: datetime, timezone aware
:param end: datetime, timezoneaware
:param pymortar_client:
:param start: datetime, timezone aware, rfc3339
:param end: datetime, timezone aware, rfc3339
:param window_size:
:return:
"""

# following query is for the whole building.
hod_temperature_query = """SELECT ?zone ?uuid FROM %s WHERE {
?tstat rdf:type brick:Thermostat .
?tstat bf:hasLocation/bf:isPartOf ?location_zone .
?location_zone rdf:type brick:HVAC_Zone .
?tstat bf:controls ?RTU .
?RTU rdf:type brick:RTU .
?RTU bf:feeds ?zone.
?zone rdf:type brick:HVAC_Zone .
?tstat bf:hasPoint ?thermostat_point .
?thermostat_point rdf:type brick:Temperature_Sensor .
?thermostat_point bf:uuid ?uuid.
};""" % building



temperature_query_data = hod_client.do_query(hod_temperature_query)["Rows"]
temperature_zone_uuid = {row["?zone"]: row["?uuid"] for row in temperature_query_data}[zone]

# get the data for the thermostats for each zone.
mdal_query = {
'Composition': [temperature_zone_uuid],
'Selectors': [mdal.MEAN],
'Time': {'T0': utils.datetime_to_mdal_string(start),
'T1': utils.datetime_to_mdal_string(end),
'WindowSize': window_size,
'Aligned': True}}

print(mdal_query)

mdal_temperature_data = utils.get_mdal_data(mdal_client, mdal_query).squeeze()
mdal_temperature_data.name = zone + "_raw_indoor_temperatures"
return mdal_temperature_data, None
temperature_query = """SELECT ?tstat ?zone ?temp WHERE {
?tstat rdf:type brick:Thermostat .
?tstat bf:controls/bf:feeds ?zone .
?tstat bf:hasPoint ?temp .
?temp rdf:type brick:Temperature_Sensor .
};"""

# resp = pymortar_client.qualify([temperature_query]) Needed to get list of all sites

temperature_view = pymortar.View(
name="temperature_view",
sites=[building],
definition=temperature_query,
)

temperature_stream = pymortar.DataFrame(
name="temperature",
aggregation=pymortar.MEAN,
window=window_size,
timeseries=[
pymortar.Timeseries(
view="temperature_view",
dataVars=["?temp"],
)
]
)

request = pymortar.FetchRequest(
sites=[building],
views=[
temperature_view
],
dataFrames=[
temperature_stream
],
time=pymortar.TimeParams(
start=rfc3339(start),
end=rfc3339(end),
)
)

temperature_data = pymortar_client.fetch(request)["temperature"]

if temperature_data is None:
return None, "did not fetch data from pymortar with query: %s" % temperature_query

return temperature_data, None


# TODO Make sure we don't include NONE values in the returned points.
def get_raw_indoor_temperatures(request, mdal_client, hod_client):
def get_raw_indoor_temperatures(request, pymortar_client):
"""Returns temperatures for the given request or None."""
print("received request:", request.building, request.zone, request.start, request.end, request.window)
duration = utils.get_window_in_sec(request.window)
duration = get_window_in_sec(request.window)

unit = "F" # we will keep the outside temperature in fahrenheit for now.

Expand All @@ -133,12 +155,12 @@ def get_raw_indoor_temperatures(request, mdal_client, hod_client):
if request.start + (duration * 1e9) > request.end:
return None, "invalid request, start date + window is greater than end date"

start_datetime = datetime.datetime.utcfromtimestamp(
start_datetime = datetime.utcfromtimestamp(
float(request.start / 1e9)).replace(tzinfo=pytz.utc)
end_datetime = datetime.datetime.utcfromtimestamp(float(request.end / 1e9)).replace(
end_datetime = datetime.utcfromtimestamp(float(request.end / 1e9)).replace(
tzinfo=pytz.utc)

raw_indoor_temperature_data, err = _get_raw_indoor_temperatures(request.building, request.zone, mdal_client, hod_client,
raw_indoor_temperature_data, err = _get_raw_indoor_temperatures(request.building, request.zone, pymortar_client,
start_datetime,
end_datetime,
request.window)
Expand All @@ -153,10 +175,10 @@ def get_raw_indoor_temperatures(request, mdal_client, hod_client):
return indoor_temperature_action_pb2.RawTemperatureReply(temperatures=temperatures), None


def get_raw_actions(request, mdal_client, hod_client):
def get_raw_actions(request, pymortar_client):
"""Returns actions for the given request or None."""
print("received request:", request.building, request.zone, request.start, request.end, request.window)
duration = utils.get_window_in_sec(request.window)
duration = get_window_in_sec(request.window)

request_length = [len(request.building), len(request.zone), request.start, request.end,
duration]
Expand All @@ -172,13 +194,12 @@ def get_raw_actions(request, mdal_client, hod_client):
if request.start + (duration * 1e9) > request.end:
return None, "invalid request, start date + window is greater than end date"

start_datetime = datetime.datetime.utcfromtimestamp(
float(request.start / 1e9)).replace(tzinfo=pytz.utc)
end_datetime = datetime.datetime.utcfromtimestamp(float(request.end / 1e9)).replace(
start_datetime = datetime.utcfromtimestamp(float(request.start / 1e9)).replace(tzinfo=pytz.utc)
end_datetime = datetime.utcfromtimestamp(float(request.end / 1e9)).replace(
tzinfo=pytz.utc)


raw_action_data, err = _get_raw_actions(request.building, request.zone, mdal_client, hod_client,
raw_action_data, err = _get_raw_actions(request.building, request.zone, pymortar_client,
start_datetime,
end_datetime,
request.window)
Expand All @@ -192,22 +213,26 @@ def get_raw_actions(request, mdal_client, hod_client):

return indoor_temperature_action_pb2.RawActionReply(actions=actions), None

def get_window_in_sec(s):
"""Returns number of seconds in a given duration or zero if it fails.
Supported durations are seconds (s), minutes (m), hours (h), and days(d)."""
seconds_per_unit = {"s": 1, "m": 60, "h": 3600, "d": 86400}
try:
return int(float(s[:-1])) * seconds_per_unit[s[-1]]
except:
return 0

class IndoorTemperatureActionServicer(indoor_temperature_action_pb2_grpc.IndoorTemperatureActionServicer):
def __init__(self):
self.bw_client = Client()
self.bw_client.setEntityFromEnviron()
self.bw_client.overrideAutoChainTo(True)
self.hod_client = HodClient("xbos/hod", self.bw_client)
self.mdal_client = mdal.MDALClient("xbos/mdal")
self.pymortar_client = pymortar.Client()

def GetRawTemperatures(self, request, context):
"""A simple RPC.
Sends the indoor temperature for a given HVAC zone, within a timeframe (start, end), and a requested window
An error is returned if there are no temperatures for the given request
"""
raw_temperatures, error = get_raw_indoor_temperatures(request, self.mdal_client, self.hod_client)
raw_temperatures, error = get_raw_indoor_temperatures(request, self.pymortar_client)
if raw_temperatures is None:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
context.set_details(error)
Expand All @@ -221,7 +246,7 @@ def GetRawActions(self, request, context):
Sends the indoor action for a given HVAC Zone, within a timeframe (start, end), and a requested window
An error is returned if there are no actions for the given request
"""
raw_actions, error = get_raw_actions(request, self.mdal_client, self.hod_client)
raw_actions, error = get_raw_actions(request, self.pymortar_client)
if raw_actions is None:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
context.set_details(error)
Expand All @@ -243,5 +268,4 @@ def serve():


if __name__ == '__main__':
serve()

serve()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"eyJraWQiOiJGUlJXZXU4TzlIUVwvdHluNXJBbkw0STNBc05ZWUR0VUo5akVMOEsrcXRWST0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI3NmJjZGZmMC1jNTE5LTRjZGQtYWZjMy03NjRlMzg4NzY4ODAiLCJldmVudF9pZCI6ImVkNjJkMWYyLTQ3NDYtMTFlOS1hZWFiLWVkYjUxOGRhYWU4OSIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1NTI2NzA1NTYsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX2RENk1IWnBkTiIsImV4cCI6MTU1MjY3NDE1NiwiaWF0IjoxNTUyNjcwNTU2LCJqdGkiOiIzMzMyMjNjNy1iNGI3LTQ1ZDgtODg3OC1iY2ZkOWUwMWIzODQiLCJjbGllbnRfaWQiOiIzOXNlaWNqbDJpMXNldGJ1MHVyMDc1bjFicCIsInVzZXJuYW1lIjoic2d1ZHVndW50bGEifQ.oxKmqqXMufixs0HJ0fh5j2VZA0yyZ_PBpNwy__JdRZ2j2tKUMQIy_vVUMvMmJz0c8hhMrATMNFnqAjyaE39oqDGhxOqg34OQU0hUf6kbk8y2oI0n1dWSh11lEVM-htyIZR_wFcBDCEpGYvzbvKZrCdNphARlrCmxgtlFeJKAbqB39sQKxfog_dEc23pWZQgOYTe4131YhrlYKWLrjXPhdKwlYfeKYvgqn9cTv42azucSUw0qyfYzzp1cqwv38Her1Pa1CgWImuY1dzzaFPDQ17wjmkbxzVI_4cIP-68FKU3A4yL4Rho06bLaOtZfNs3hn190_L0015Uqgm9iVlWhHQ"
Binary file not shown.
Binary file not shown.
10 changes: 5 additions & 5 deletions services/outdoor_temperature_historical/req.txt
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
grpcio==1.12.1
grpcio-tools==1.12.1
PyYAML==3.12
xbos_services_utils2 <= 0.0.10dev
pytz == 2018.3
xbos==0.0.35
bw2python==0.6.1
pathlib == 1.0.1
pytz==2018.3
xbos==0.0.35pi
pymortar>=1.0.0
rfc3339==6.0


Loading

0 comments on commit 3fad448

Please sign in to comment.