Bladeren bron

Further refinements to report data structures

Stan Janssen 5 jaren geleden
bovenliggende
commit
ec5c161878

+ 59 - 3
pyopenadr/enums.py

@@ -1,8 +1,64 @@
 # Some handy enums to use in your messages
 
-class READING_TYPE:
+class Enum(type):
+    def __getitem__(self, item):
+        return getattr(self, item)
 
-class REPORT_TYPE:
+    @property
+    def members(self):
+        return sorted([item for item in list(set(dir(self)) - set(dir(Enum))) if not item.startswith("_")])
 
-class REPORT_NAME:
+    @property
+    def values(self):
+        return [self[item] for item in self.members]
 
+class READING_TYPE(metaclass=Enum):
+    DIRECT_READ = "Direct Read"
+    NET = "Net"
+    ALLOCATED = "Allocated"
+    ESTIMATED = "Estimated"
+    SUMMED = "Summed"
+    DERIVED = "Derived"
+    MEAN = "Mean"
+    PEAK = "Peak"
+    HYBRID = "Hybrid"
+    CONTRACT = "Contract"
+    PROJECTED = "Projected"
+    X_RMS = "x-RMS"
+    X_NOT_APPLICABLE = "x-notApplicable"
+
+class REPORT_TYPE(metaclass=Enum):
+    READING = "reading"
+    USAGE = "usage"
+    DEMAND = "demand"
+    SET_POINT = "setPoint"
+    DELTA_USAGE = "deltaUsage"
+    DELTA_SET_POINT = "deltaSetPoint"
+    DELTA_DEMAND = "deltaDemand"
+    BASELINE = "baseline"
+    DEVIATION = "deviation"
+    AVG_USAGE = "avgUsage"
+    AVG_DEMAND = "avgDemand"
+    OPERATING_STATE = "operatingState"
+    UP_REGULATION_CAPACITY_AVAILABLE = "upRegulationCapacityAvailable"
+    DOWN_REGULATION_CAPACITY_AVAILABLE = "downRegulationCapacityAvailable"
+    REGULATION_SETPOINT = "regulationSetpoint"
+    STORED_ENERGY = "storedEnergy"
+    TARGET_ENERGY_STORAGE = "targetEnergyStorage"
+    AVAILABLE_ENERGY_STORAGE = "availableEnergyStorage"
+    PRICE = "price"
+    LEVEL = "level"
+    POWER_FACTOR = "powerFactor"
+    PERCENT_USAGE = "percentUsage"
+    PERCENT_DEMAND = "percentDemand"
+    X_RESOURCE_STATUS = "x-resourceStatus"
+
+class REPORT_NAME(metaclass=Enum):
+    METADATA_HISTORY_USAGE = "METADATA_HISTORY_USAGE"
+    HISTORY_USAGE = "METADATA_HISTORY_USAGE"
+    METADATA_HISTORY_GREENBUTTON = "METADATA_HISTORY_GREENBUTTON"
+    HISTORY_GREENBUTTON = "HISTORY_GREENBUTTON"
+    METADATA_TELEMETRY_USAGE = "METADATA_TELEMETRY_USAGE"
+    TELEMETRY_USAGE = "TELEMETRY_USAGE"
+    METADATA_TELEMETRY_STATUS = "METADATA_TELEMETRY_STATUS"
+    TELEMETRY_STATUS = "TELEMETRY_STATUS"

+ 11 - 1
pyopenadr/templates/oadrRegisterReport.xml

@@ -4,7 +4,17 @@
     <oadrRegisterReport ei:schemaVersion="2.0b" xmlns:ei="http://docs.oasis-open.org/ns/energyinterop/201110">
       <requestID xmlns="http://docs.oasis-open.org/ns/energyinterop/201110/payloads">{{ request_id }}</requestID>
 {% for report in reports %}
-      <oadrReport>
+      <oadrReport xmlns:xcal="urn:ietf:params:xml:ns:icalendar-2.0" xmlns:strm="urn:ietf:params:xml:ns:icalendar-2.0:stream">
+        {% if report.dtstart %}
+        <xcal:dtstart>
+          <xcal:date-time>{{ report.duration|timedeltaformat }}</xcal:date-time>
+        </xcal:dtstart>
+        {% endif %}
+        {% if report.duration %}
+        <xcal:duration>
+          <duration>{{ report.duration|timedeltaformat }}</duration>
+        </xcal:duration>
+        {% endif %}
         <ei:eiReportID>{{ report.report_id }}</ei:eiReportID>
     {% for report_description in report.report_descriptions %}
         {% include 'parts/oadrReportDescription.xml' %}

+ 1 - 3
pyopenadr/templates/parts/oadrReportDescription.xml

@@ -83,9 +83,7 @@
   </ei:reportDataSource>
   {% endif %}
   <ei:reportType>{{ report_description.report_type }}</ei:reportType>
-  <!--           {% if report_description.item_base %}
-  <emix:itemBase>{{ report_description.item_base }}</emix:itemBase>
-  {% endif %} -->
+  {% include 'parts/reportDescriptionEmix.xml' %}
   <ei:readingType>{{ report_description.reading_type }}</ei:readingType>
   {% if report_description.market_context %}
   <emix:marketContext>{{ report_description.market_context }}</emix:marketContext>

+ 82 - 0
pyopenadr/templates/parts/reportDescriptionEmix.xml

@@ -0,0 +1,82 @@
+  {% if report_description.power_real %}
+  <powerReal xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.power_real.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.power_real.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.power_real.si_scale_code }}</scale:siScaleCode>
+    <powerAttributes>
+      <hertz>{{ report_description.power_real.power_attributes.hertz }}</hertz>
+      <voltage>{{ report_description.power_real.power_attributes.voltage }}</voltage>
+      <ac>{{ report_description.power_real.power_attributes.ac|booleanformat }}</ac>
+    </powerAttributes>
+  </powerReal>
+  {% endif %}
+
+  {% if report_description.power_apparent %}
+  <powerApparent xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.power_apparent.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.power_apparent.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.power_apparent.si_scale_code }}</scale:siScaleCode>
+    <powerAttributes>
+      <hertz>{{ report_description.power_apparent.power_attributes.hertz }}</hertz>
+      <voltage>{{ report_description.power_apparent.power_attributes.voltage }}</voltage>
+      <ac>{{ report_description.power_apparent.power_attributes.ac|booleanformat }}</ac>
+    </powerAttributes>
+  </powerApparent>
+  {% endif %}
+
+  {% if report_description.power_reactive %}
+  <powerReactive xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.power_reactive.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.power_reactive.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.power_reactive.si_scale_code }}</scale:siScaleCode>
+    <powerAttributes>
+      <hertz>{{ report_description.power_reactive.power_attributes.hertz }}</hertz>
+      <voltage>{{ report_description.power_reactive.power_attributes.voltage }}</voltage>
+      <ac>{{ report_description.power_reactive.power_attributes.ac|booleanformat }}</ac>
+    </powerAttributes>
+  </powerReactive>
+  {% endif %}
+
+  {% if report_description.energy_real %}
+  <energyReal xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.energy_real.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.energy_real.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.energy_real.si_scale_code }}</scale:siScaleCode>
+  </energyReal>
+  {% endif %}
+
+  {% if report_description.energy_apparent %}
+  <energyApparent xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.energy_apparent.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.energy_apparent.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.energy_apparent.si_scale_code }}</scale:siScaleCode>
+  </energyApparent>
+  {% endif %}
+
+  {% if report_description.energy_reactive %}
+  <energyReactive xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.energy_reactive.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.energy_reactive.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.energy_reactive.si_scale_code }}</scale:siScaleCode>
+  </energyReactive>
+  {% endif %}
+
+  {% if report_description.energy_quantity %}
+  <energyQuantity xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <quantity>{{ report_description.energy_quantity.quantity }}</quantity>
+    <energyItem xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+      <itemDescription>{{ report_description.energy_quantity.energy_item.item_description }}</itemDescription>
+      <itemUnits>{{ report_description.energy_quantity.energy_item.item_units }}</itemUnits>
+      <scale:siScaleCode>{{ report_description.energy_quantity.energy_item.si_scale_code }}</scale:siScaleCode>
+    </energyItem>
+  </energyQuantity>
+  {% endif %}
+
+  {% if report_description.voltage %}
+  <voltage xmlns="http://docs.oasis-open.org/ns/emix/2011/06/power" xmlns:scale="http://docs.oasis-open.org/ns/emix/2011/06/siscale">
+    <itemDescription>{{ report_description.voltage.item_description }}</itemDescription>
+    <itemUnits>{{ report_description.voltage.item_units }}</itemUnits>
+    <scale:siScaleCode>{{ report_description.voltage.si_scale_code }}</scale:siScaleCode>
+  </voltage>
+  {% endif %}
+

+ 3 - 1
pyopenadr/utils.py

@@ -305,7 +305,9 @@ NAMESPACES = {
     'urn:ietf:params:xml:ns:icalendar-2.0': None,
     'http://docs.oasis-open.org/ns/energyinterop/201110/payloads': None,
     'http://docs.oasis-open.org/ns/emix/2011/06': None,
-    'urn:ietf:params:xml:ns:icalendar-2.0:stream': None
+    'urn:ietf:params:xml:ns:icalendar-2.0:stream': None,
+    'http://docs.oasis-open.org/ns/emix/2011/06/power': None,
+    'http://docs.oasis-open.org/ns/emix/2011/06/siscale': None
 }
 
 TEMPLATES.filters['datetimeformat'] = datetimeformat

+ 97 - 5
test/test_message_conversion.py

@@ -1,4 +1,5 @@
 from pyopenadr.utils import create_message, parse_message, generate_id
+from pyopenadr import enums
 from pprint import pprint
 from termcolor import colored
 from datetime import datetime, timezone, timedelta
@@ -47,7 +48,7 @@ def create_dummy_event(ven_id):
 
 def test_message(message_type, **data):
     message = create_message(message_type, **data)
-    #print(message)
+    # print(message)
     parsed = parse_message(message)[1]
 
     if parsed == data:
@@ -142,8 +143,8 @@ test_message('oadrRequestReregistration', ven_id='123ABC')
 test_message('oadrRegisterReport', request_id=generate_id(), reports=[{'report_id': generate_id(),
                                                                        'report_descriptions': [{
                                                                             'r_id': generate_id(),
-                                                                            'report_subject': {'ven_id': '123ABC'},
-                                                                            'report_data_source': {'ven_id': '123ABC'},
+                                                                            'report_subjects': [{'ven_id': '123ABC'}],
+                                                                            'report_data_sources': [{'ven_id': '123ABC'}],
                                                                             'report_type': 'reading',
                                                                             'reading_type': 'Direct Read',
                                                                             'market_context': 'http://localhost',
@@ -154,8 +155,99 @@ test_message('oadrRegisterReport', request_id=generate_id(), reports=[{'report_i
                                                                        'created_date_time': datetime.now(timezone.utc)}],
                                                         ven_id='123ABC',
                                                         report_request_id=generate_id())
+test_message('oadrRegisterReport', **{'request_id': '8a4f859883', 'reports': [{'report_id': generate_id(),
+                                                                               'duration': timedelta(seconds=7200),
+                                                                               'report_descriptions': [{'r_id': 'resource1_status',
+                                                                                                        'report_data_sources': [{'resource_id': 'resource1'}],
+                                                                                                        'report_type': 'x-resourceStatus',
+                                                                                                        'reading_type': 'x-notApplicable',
+                                                                                                        'market_context': 'http://MarketContext1',
+                                                                                                        'sampling_rate': {'min_period': timedelta(seconds=60), 'max_period': timedelta(seconds=60), 'on_change': False}}],
+                                                                                'report_request_id': '0',
+                                                                                'report_specifier_id': '789ed6cd4e_telemetry_status',
+                                                                                'report_name': 'METADATA_TELEMETRY_STATUS',
+                                                                                'created_date_time': datetime(2019, 11, 20, 15, 4, 52, 638621, tzinfo=timezone.utc)},
+                                                                               {'report_id': generate_id(),
+                                                                                'duration': timedelta(seconds=7200),
+                                                                                'report_descriptions': [{'r_id': 'resource1_energy',
+                                                                                                         'report_data_sources': [{'resource_id': 'resource1'}],
+                                                                                                         'report_type': 'usage',
+                                                                                                         'energy_real': {'item_description': 'RealEnergy',
+                                                                                                                         'item_units': 'Wh',
+                                                                                                                         'si_scale_code': 'n'},
+                                                                                                         'reading_type': 'Direct Read',
+                                                                                                         'market_context': 'http://MarketContext1',
+                                                                                                         'sampling_rate': {'min_period': timedelta(seconds=60), 'max_period': timedelta(seconds=60), 'on_change': False}},
+                                                                                                        {'r_id': 'resource1_power',
+                                                                                                         'report_data_sources': [{'resource_id': 'resource1'}],
+                                                                                                         'report_type': 'usage',
+                                                                                                         'power_real': {'item_description': 'RealPower',
+                                                                                                                        'item_units': 'W',
+                                                                                                                        'si_scale_code': 'n',
+                                                                                                                        'power_attributes': {'hertz': 60, 'voltage': 110, 'ac': False}},
+                                                                                                          'reading_type': 'Direct Read',
+                                                                                                          'market_context': 'http://MarketContext1',
+                                                                                                          'sampling_rate': {'min_period': timedelta(seconds=60), 'max_period': timedelta(seconds=60), 'on_change': False}}],
+                                                                                'report_request_id': '0',
+                                                                                'report_specifier_id': '789ed6cd4e_telemetry_usage',
+                                                                                'report_name': 'METADATA_TELEMETRY_USAGE',
+                                                                                'created_date_time': datetime(2019, 11, 20, 15, 4, 52, 638621, tzinfo=timezone.utc)},
+                                                                               {'report_id': generate_id(),
+                                                                                'duration': timedelta(seconds=7200),
+                                                                                'report_descriptions': [{'r_id': 'resource1_energy',
+                                                                                                         'report_data_sources': [{'resource_id': 'resource1'}],
+                                                                                                         'report_type': 'usage',
+                                                                                                         'energy_real': {'item_description': 'RealEnergy',
+                                                                                                                         'item_units': 'Wh',
+                                                                                                                         'si_scale_code': 'n'},
+                                                                                                         'reading_type': 'Direct Read',
+                                                                                                         'market_context': 'http://MarketContext1',
+                                                                                                         'sampling_rate': {'min_period': timedelta(seconds=60), 'max_period': timedelta(seconds=60), 'on_change': False}},
+                                                                                                        {'r_id': 'resource1_power',
+                                                                                                         'report_data_sources': [{'resource_id': 'resource1'}],
+                                                                                                         'report_type': 'usage',
+                                                                                                         'power_real': {'item_description': 'RealPower',
+                                                                                                                        'item_units': 'W', 'si_scale_code': 'n',
+                                                                                                                        'power_attributes': {'hertz': 60, 'voltage': 110, 'ac': False}},
+                                                                                                         'reading_type': 'Direct Read',
+                                                                                                         'market_context': 'http://MarketContext1',
+                                                                                                         'sampling_rate': {'min_period': timedelta(seconds=60), 'max_period': timedelta(seconds=60), 'on_change': False}}],
+                                                                                'report_request_id': '0',
+                                                                                'report_specifier_id': '789ed6cd4e_history_usage',
+                                                                                'report_name': 'METADATA_HISTORY_USAGE',
+                                                                                'created_date_time': datetime(2019, 11, 20, 15, 4, 52, 638621, tzinfo=timezone.utc)}], 'ven_id': 's3cc244ee6'})
 test_message('oadrResponse', response={'response_code': 200, 'response_description': 'OK', 'request_id': generate_id()}, ven_id='123ABC')
 test_message('oadrResponse', response={'response_code': 200, 'response_description': 'OK', 'request_id': None}, ven_id='123ABC')
-#test_message('oadrUpdatedReport')
-#test_message('oadrUpdateReport')
+test_message('oadrUpdatedReport', response={'response_code': 200, 'response_description': 'OK', 'request_id': generate_id()}, ven_id='123ABC', cancel_report={'request_id': generate_id(), 'report_request_id': [generate_id(), generate_id(), generate_id()], 'report_to_follow': False, 'ven_id': '123ABC'})
+test_message('oadrUpdateReport', request_id=generate_id(), reports=[{'report_id': generate_id(),
+                                                                                  'report_name': enums.REPORT_NAME.values[0],
+                                                                                  'created_date_time': datetime.now(timezone.utc),
+                                                                                  'report_request_id': generate_id(),
+                                                                                  'report_specifier_id': generate_id(),
+                                                                                 'report_descriptions': [{'r_id': generate_id(),
+                                                                                                          'report_subjects': [{'ven_id': '123ABC'}, {'ven_id': 'DEF456'}],
+                                                                                                          'report_data_sources': [{'ven_id': '123ABC'}],
+                                                                                                          'report_type': enums.REPORT_TYPE.values[0],
+                                                                                                          'reading_type': enums.READING_TYPE.values[0],
+                                                                                                          'market_context': 'http://localhost',
+                                                                                                          'sampling_rate': {'min_period': timedelta(minutes=1),
+                                                                                                                            'max_period': timedelta(minutes=2),
+                                                                                                                            'on_change': False}}]}], ven_id='123ABC')
+# for report_name in enums.REPORT_NAME.values:
+#     for reading_type in enums.READING_TYPE.values:
+#         for report_type in enums.REPORT_TYPE.values:
+#             test_message('oadrUpdateReport', request_id=generate_id(), reports=[{'report_id': generate_id(),
+#                                                                                   'report_name': report_name,
+#                                                                                   'created_date_time': datetime.now(timezone.utc),
+#                                                                                   'report_request_id': generate_id(),
+#                                                                                   'report_specifier_id': generate_id(),
+#                                                                                   'report_descriptions': [{'r_id': generate_id(),
+#                                                                                                            'report_subjects': [{'ven_id': '123ABC'}, {'ven_id': 'DEF456'}],
+#                                                                                                            'report_data_sources': [{'ven_id': '123ABC'}],
+#                                                                                                            'report_type': report_type,
+#                                                                                                            'reading_type': reading_type,
+#                                                                                                            'market_context': 'http://localhost',
+#                                                                                                            'sampling_rate': {'min_period': timedelta(minutes=1),
+#                                                                                                                              'max_period': timedelta(minutes=2),
+#                                                                                                                              'on_change': False}}]}], ven_id='123ABC')
 

File diff suppressed because it is too large
+ 3 - 2
test/test_schema.py


Some files were not shown because too many files changed in this diff