From 05ce4b8413ec9a94f86affc0d91a4c25c2c9cbc9 Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Mon, 12 Aug 2024 12:58:38 +0200 Subject: [PATCH 1/6] Active reactive with phase number --- primestg/report/base.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/primestg/report/base.py b/primestg/report/base.py index 021f5a9..d2472c2 100644 --- a/primestg/report/base.py +++ b/primestg/report/base.py @@ -201,6 +201,25 @@ def active_reactive(self, measure, measure_type): 'r4': float(measure.get('R4{}'.format(measure_type))), } + def active_reactive_with_phase(self, measure, phase_num): + """ + Get the active and reactive measures. + + :param measure: an lxml.objectify.StringElement representing a set of \ + measures + :param phase_num: the phase number of measure, added at the end of the \ + name of each measure (1,2,3) + :return: a dict with the active and reactive phase measures + """ + return { + 'ai{}'.format(phase_num): float(measure.get('AI{}'.format(phase_num))), + 'ae{}'.format(phase_num): float(measure.get('AE{}'.format(phase_num))), + 'r1{}'.format(phase_num): float(measure.get('R1{}'.format(phase_num))), + 'r2{}'.format(phase_num): float(measure.get('R2{}'.format(phase_num))), + 'r3{}'.format(phase_num): float(measure.get('R3{}'.format(phase_num))), + 'r4{}'.format(phase_num): float(measure.get('R4{}'.format(phase_num))), + } + class MeasureAverageVoltageAndCurrent(Measure): """ From 6d6ff94bff99a11f539f636aace0a74c34e0019f Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Mon, 12 Aug 2024 13:02:47 +0200 Subject: [PATCH 2/6] Implement S53 classes --- primestg/report/reports.py | 158 +++++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 23 deletions(-) diff --git a/primestg/report/reports.py b/primestg/report/reports.py index e509a73..e8e7c7a 100644 --- a/primestg/report/reports.py +++ b/primestg/report/reports.py @@ -7,8 +7,10 @@ from primestg.message import MessageS from primestg.utils import octet2name, octet2number -SUPPORTED_REPORTS = ['S02', 'S04', 'S05', 'S06', 'S09', 'S12', 'S13', 'S14', 'S15', - 'S17', 'S18', 'S23', 'S24', 'S27', 'S42', 'S52'] +CNC_REPORTS = ['S02', 'S04', 'S05', 'S06', 'S09', 'S12', 'S13', 'S14', 'S15', + 'S17', 'S18', 'S23', 'S24', 'S27', 'S42'] +RTU_REPORTS = ['S52', 'S53'] +SUPPORTED_REPORTS = CNC_REPORTS + RTU_REPORTS def is_supported(report_code): @@ -291,6 +293,35 @@ def values(self): return values +class OperationS42(Operation): + """ + Class for a set of measures of report S42. + """ + + @property + def values(self): + """ + Set of measures of report S42. + :return: a dict with a set of measures of report S42 + """ + values = [] + try: + common_values = { + "Fh": self._get_timestamp('Fh'), + "Operation": self.objectified.get('Operation'), + "obis": self.objectified.get('obis'), + "class": self.objectified.get('class'), + "element": self.objectified.get('element'), + "data": self.objectified.get('data'), + "result": self.objectified.get('result'), + } + values.append(common_values) + except Exception as e: + values.append(['ERROR: Thrown exception: {}'.format(e)]) + self._warnings.append('ERROR: Thrown exception: {}'.format(e)) + return values + + class MeasureS52(MeasureActiveReactiveFloat): """ Class for a set of measures of report S52. @@ -318,33 +349,33 @@ def values(self): return [values] -class OperationS42(Operation): +class MeasureS53(MeasureActiveReactiveFloat): """ - Class for a set of measures of report S52. + Class for a set of measures of report S53. """ @property def values(self): """ - Set of measures of report S42. - :return: a dict with a set of measures of report S42 + Set of measures of report S53. + + :return: a dict with a set of measures of report S53 """ - values = [] try: - common_values = { - "Fh": self._get_timestamp('Fh'), - "Operation": self.objectified.get('Operation'), - "obis": self.objectified.get('obis'), - "class": self.objectified.get('class'), - "element": self.objectified.get('element'), - "data": self.objectified.get('data'), - "result": self.objectified.get('result'), - } - values.append(common_values) + values = None + for phase in range(1, 4): # Phase 1..3 + values = self.active_reactive_with_phase(self.objectified, phase) + values.update( + { + 'timestamp': self._get_timestamp('Fh'), + 'bc': self.objectified.get('Bc') + } + ) except Exception as e: - values.append(['ERROR: Thrown exception: {}'.format(e)]) self._warnings.append('ERROR: Thrown exception: {}'.format(e)) - return values + return [] + + return [values] class MeasureEvents(Measure): @@ -1022,6 +1053,43 @@ def values(self): return values +class LineSupervisorS53(LineSupervisorDetails): + """ + Class for a line supervisor of report S53. + """ + + @property + def report_type(self): + """ + The type of report for report S53. + + :return: a string with 'S53' + """ + return 'S53' + + @property + def measure_class(self): + """ + The class used to instance measure sets for report S53. + + :return: a class to instance measure sets of report S53 + """ + return MeasureS53 + + @property + def values(self): + """ + Values of measure sets of this line supervisor of report that need the name of the remote terminal unit + and the line supervisor + + :return: a list with the values of the measure sets + """ + values = super(LineSupervisorS53, self).values + for value in values: + value['magn'] = self.magnitude + return values + + class MeterS01(MeterWithMagnitude): """ Class for a meter of report S01. @@ -2078,7 +2146,7 @@ class RemoteTerminalUnitS52(RemoteTerminalUnitDetails): def __init__(self, objectified_rt_unit, report_version, request_id): """ - Create a RemoteTerminalUnit object for the report S62. + Create a RemoteTerminalUnit object for the report S52. :param objectified_rt_unit: an lxml.objectify.StringElement \ representing a line supervisor @@ -2101,12 +2169,48 @@ def line_supervisor_class(self): @property def report_type(self): """ - The type of report for report S64. + The type of report for report S52. :return: a string with 'S52' """ return 'S52' +class RemoteTerminalUnitS53(RemoteTerminalUnitDetails): + """ + Class for a remote terminal unit of report S53. + """ + + def __init__(self, objectified_rt_unit, report_version, request_id): + """ + Create a RemoteTerminalUnit object for the report S53. + + :param objectified_rt_unit: an lxml.objectify.StringElement \ + representing a line supervisor + :param report_version: a string with the version of report + :return: a LineSupervisor object + """ + super(RemoteTerminalUnitS53, self).__init__(objectified_rt_unit) + self.report_version = report_version + self.request_id = request_id + + @property + def line_supervisor_class(self): + """ + The class used to instance line supervisors for report S53. + + :return: a class to instance line supervisors of report S53 + """ + return LineSupervisorS53 + + @property + def report_type(self): + """ + The type of report for report S53. + :return: a string with 'S53' + """ + return 'S53' + + class Report(object): """ Report class to process MessageS @@ -2295,7 +2399,15 @@ def get_rt_unit(self, objectified_rt_unit): self.report_version, self.request_id ], - } + }, + 'S53': { + 'class': RemoteTerminalUnitS53, + 'args': [ + objectified_rt_unit, + self.report_version, + self.request_id + ], + }, } if self.report_type not in report_type_class: @@ -2337,7 +2449,7 @@ def values(self): :return: a list with the values of the whole report """ values = [] - if self.report_type == 'S52': + if self.report_type == RTU_REPORTS: for rt_unit in self.rt_units: values.extend(rt_unit.values) else: From 8911557107a506e7343f6256832d8ef9aa5f2263 Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Mon, 12 Aug 2024 14:06:21 +0200 Subject: [PATCH 3/6] Add S53 example and spec file --- spec/Report_S53_spec.py | 53 +++++++++++++++++++ .../ORMR208231717738_0_S53_1_20240624050827 | 19 +++++++ 2 files changed, 72 insertions(+) create mode 100644 spec/Report_S53_spec.py create mode 100644 spec/data/ORMR208231717738_0_S53_1_20240624050827 diff --git a/spec/Report_S53_spec.py b/spec/Report_S53_spec.py new file mode 100644 index 0000000..c0e35c5 --- /dev/null +++ b/spec/Report_S53_spec.py @@ -0,0 +1,53 @@ +from expects import expect, equal +from primestg.report import Report + + +with description('Report S53 example'): + with before.all: + + self.data_filename = 'spec/data/ORMR208231717738_0_S53_1_20240624050827' + + self.report = {} + with open(self.data_filename) as data_file: + self.report = Report(data_file) + + with it('generates expected results for a value of the first line supervisor of ' + 'first remote terminal unit'): + + expected_first_value = dict( + ai1=2978.0, + ai2=4171.0, + ai3=2601.0, + ae1=0.0, + ae2=0.0, + ae3=0.0, + r11=225.0, + r12=973.0, + r13=201.0, + r21=0.0, + r22=0.0, + r23=0.0, + r31=0.0, + r32=0.0, + r33=0.0, + r41=0.0, + r42=0.0, + r43=0.0, + bc='00', + timestamp='2024-06-24 05:00:00', + rt_unit_name='ORMR208231717738', + name='ORML208231065728', + magn=1 + ) + + rt_unit = list(self.report.rt_units)[0] + line_supervisor = list(rt_unit.line_supervisors)[0] + values = line_supervisor.values + + first_value_first_line_supervisor = {} + for x in values: + if x['timestamp'] == expected_first_value['timestamp']: + first_value_first_line_supervisor = x + + expect(first_value_first_line_supervisor)\ + .to(equal(expected_first_value)) diff --git a/spec/data/ORMR208231717738_0_S53_1_20240624050827 b/spec/data/ORMR208231717738_0_S53_1_20240624050827 new file mode 100644 index 0000000..4b4d34d --- /dev/null +++ b/spec/data/ORMR208231717738_0_S53_1_20240624050827 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + From b82385d2387063aacbdab3e32661f8334e0ddd7b Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Mon, 12 Aug 2024 17:16:03 +0200 Subject: [PATCH 4/6] Fix values S53 --- primestg/report/reports.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/primestg/report/reports.py b/primestg/report/reports.py index e8e7c7a..cded79e 100644 --- a/primestg/report/reports.py +++ b/primestg/report/reports.py @@ -362,15 +362,15 @@ def values(self): :return: a dict with a set of measures of report S53 """ try: - values = None + values = {} for phase in range(1, 4): # Phase 1..3 - values = self.active_reactive_with_phase(self.objectified, phase) - values.update( - { - 'timestamp': self._get_timestamp('Fh'), - 'bc': self.objectified.get('Bc') - } - ) + values.update(self.active_reactive_with_phase(self.objectified, phase)) + values.update( + { + 'timestamp': self._get_timestamp('Fh'), + 'bc': self.objectified.get('Bc') + } + ) except Exception as e: self._warnings.append('ERROR: Thrown exception: {}'.format(e)) return [] From 7c47cbe39d6049ff94956a6448c08fe8711ae8b7 Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Tue, 17 Sep 2024 16:53:25 +0200 Subject: [PATCH 5/6] Delete S53 --- primestg/report/reports.py | 119 +----------------- spec/Report_S53_spec.py | 53 -------- .../ORMR208231717738_0_S53_1_20240624050827 | 19 --- 3 files changed, 4 insertions(+), 187 deletions(-) delete mode 100644 spec/Report_S53_spec.py delete mode 100644 spec/data/ORMR208231717738_0_S53_1_20240624050827 diff --git a/primestg/report/reports.py b/primestg/report/reports.py index cded79e..a107ecc 100644 --- a/primestg/report/reports.py +++ b/primestg/report/reports.py @@ -7,10 +7,8 @@ from primestg.message import MessageS from primestg.utils import octet2name, octet2number -CNC_REPORTS = ['S02', 'S04', 'S05', 'S06', 'S09', 'S12', 'S13', 'S14', 'S15', - 'S17', 'S18', 'S23', 'S24', 'S27', 'S42'] -RTU_REPORTS = ['S52', 'S53'] -SUPPORTED_REPORTS = CNC_REPORTS + RTU_REPORTS +SUPPORTED_REPORTS = ['S02', 'S04', 'S05', 'S06', 'S09', 'S12', 'S13', 'S14', 'S15', + 'S17', 'S18', 'S23', 'S24', 'S27', 'S42', 'S52'] def is_supported(report_code): @@ -349,35 +347,6 @@ def values(self): return [values] -class MeasureS53(MeasureActiveReactiveFloat): - """ - Class for a set of measures of report S53. - """ - - @property - def values(self): - """ - Set of measures of report S53. - - :return: a dict with a set of measures of report S53 - """ - try: - values = {} - for phase in range(1, 4): # Phase 1..3 - values.update(self.active_reactive_with_phase(self.objectified, phase)) - values.update( - { - 'timestamp': self._get_timestamp('Fh'), - 'bc': self.objectified.get('Bc') - } - ) - except Exception as e: - self._warnings.append('ERROR: Thrown exception: {}'.format(e)) - return [] - - return [values] - - class MeasureEvents(Measure): """ Class for a set of measures of report S09. @@ -1053,42 +1022,6 @@ def values(self): return values -class LineSupervisorS53(LineSupervisorDetails): - """ - Class for a line supervisor of report S53. - """ - - @property - def report_type(self): - """ - The type of report for report S53. - - :return: a string with 'S53' - """ - return 'S53' - - @property - def measure_class(self): - """ - The class used to instance measure sets for report S53. - - :return: a class to instance measure sets of report S53 - """ - return MeasureS53 - - @property - def values(self): - """ - Values of measure sets of this line supervisor of report that need the name of the remote terminal unit - and the line supervisor - - :return: a list with the values of the measure sets - """ - values = super(LineSupervisorS53, self).values - for value in values: - value['magn'] = self.magnitude - return values - class MeterS01(MeterWithMagnitude): """ @@ -2175,42 +2108,6 @@ def report_type(self): return 'S52' -class RemoteTerminalUnitS53(RemoteTerminalUnitDetails): - """ - Class for a remote terminal unit of report S53. - """ - - def __init__(self, objectified_rt_unit, report_version, request_id): - """ - Create a RemoteTerminalUnit object for the report S53. - - :param objectified_rt_unit: an lxml.objectify.StringElement \ - representing a line supervisor - :param report_version: a string with the version of report - :return: a LineSupervisor object - """ - super(RemoteTerminalUnitS53, self).__init__(objectified_rt_unit) - self.report_version = report_version - self.request_id = request_id - - @property - def line_supervisor_class(self): - """ - The class used to instance line supervisors for report S53. - - :return: a class to instance line supervisors of report S53 - """ - return LineSupervisorS53 - - @property - def report_type(self): - """ - The type of report for report S53. - :return: a string with 'S53' - """ - return 'S53' - - class Report(object): """ Report class to process MessageS @@ -2399,15 +2296,7 @@ def get_rt_unit(self, objectified_rt_unit): self.report_version, self.request_id ], - }, - 'S53': { - 'class': RemoteTerminalUnitS53, - 'args': [ - objectified_rt_unit, - self.report_version, - self.request_id - ], - }, + } } if self.report_type not in report_type_class: @@ -2449,7 +2338,7 @@ def values(self): :return: a list with the values of the whole report """ values = [] - if self.report_type == RTU_REPORTS: + if self.report_type == 'S52': for rt_unit in self.rt_units: values.extend(rt_unit.values) else: diff --git a/spec/Report_S53_spec.py b/spec/Report_S53_spec.py deleted file mode 100644 index c0e35c5..0000000 --- a/spec/Report_S53_spec.py +++ /dev/null @@ -1,53 +0,0 @@ -from expects import expect, equal -from primestg.report import Report - - -with description('Report S53 example'): - with before.all: - - self.data_filename = 'spec/data/ORMR208231717738_0_S53_1_20240624050827' - - self.report = {} - with open(self.data_filename) as data_file: - self.report = Report(data_file) - - with it('generates expected results for a value of the first line supervisor of ' - 'first remote terminal unit'): - - expected_first_value = dict( - ai1=2978.0, - ai2=4171.0, - ai3=2601.0, - ae1=0.0, - ae2=0.0, - ae3=0.0, - r11=225.0, - r12=973.0, - r13=201.0, - r21=0.0, - r22=0.0, - r23=0.0, - r31=0.0, - r32=0.0, - r33=0.0, - r41=0.0, - r42=0.0, - r43=0.0, - bc='00', - timestamp='2024-06-24 05:00:00', - rt_unit_name='ORMR208231717738', - name='ORML208231065728', - magn=1 - ) - - rt_unit = list(self.report.rt_units)[0] - line_supervisor = list(rt_unit.line_supervisors)[0] - values = line_supervisor.values - - first_value_first_line_supervisor = {} - for x in values: - if x['timestamp'] == expected_first_value['timestamp']: - first_value_first_line_supervisor = x - - expect(first_value_first_line_supervisor)\ - .to(equal(expected_first_value)) diff --git a/spec/data/ORMR208231717738_0_S53_1_20240624050827 b/spec/data/ORMR208231717738_0_S53_1_20240624050827 deleted file mode 100644 index 4b4d34d..0000000 --- a/spec/data/ORMR208231717738_0_S53_1_20240624050827 +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - From 961ffbf8db78acbf76949bdba2860e52e08fd736 Mon Sep 17 00:00:00 2001 From: markusrc11 Date: Tue, 17 Sep 2024 16:57:22 +0200 Subject: [PATCH 6/6] misc --- primestg/report/reports.py | 1 - 1 file changed, 1 deletion(-) diff --git a/primestg/report/reports.py b/primestg/report/reports.py index a107ecc..8e05517 100644 --- a/primestg/report/reports.py +++ b/primestg/report/reports.py @@ -1022,7 +1022,6 @@ def values(self): return values - class MeterS01(MeterWithMagnitude): """ Class for a meter of report S01.