SolarManager unter Versionsverwaltung
Erster Stand der Hintergrundprozesse, die auf der Synology unter /volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins Web-Verzeichnis. Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot), zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat. Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py, gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte. Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein pip, sie muessen neben den Skripten liegen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
import sunspec2.mdef as mdef
|
||||
import json
|
||||
import copy
|
||||
import pytest
|
||||
|
||||
|
||||
def test_to_int():
|
||||
assert mdef.to_int('4') == 4
|
||||
assert isinstance(mdef.to_int('4'), int)
|
||||
assert isinstance(mdef.to_int(4.0), int)
|
||||
|
||||
|
||||
def test_to_str():
|
||||
assert mdef.to_str(4) == '4'
|
||||
assert isinstance(mdef.to_str('4'), str)
|
||||
|
||||
|
||||
def test_to_float():
|
||||
assert mdef.to_float('4') == 4.0
|
||||
assert isinstance(mdef.to_float('4'), float)
|
||||
assert mdef.to_float('z') is None
|
||||
|
||||
|
||||
def test_to_number_type():
|
||||
assert mdef.to_number_type('4') == 4
|
||||
assert mdef.to_number_type('4.0') == 4.0
|
||||
assert mdef.to_number_type('z') == 'z'
|
||||
|
||||
|
||||
def test_validate_find_point():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
assert mdef.validate_find_point(model_json['group'], 'ID') == model_json['group']['points'][0]
|
||||
assert mdef.validate_find_point(model_json['group'], 'abc') is None
|
||||
|
||||
|
||||
def test_validate_attrs():
|
||||
with open('./sunspec2/models/json/model_701.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
# model
|
||||
assert mdef.validate_attrs(model_json, mdef.model_attr) == ''
|
||||
|
||||
model_unexp_attr_err = copy.deepcopy(model_json)
|
||||
model_unexp_attr_err['abc'] = 'def'
|
||||
assert mdef.validate_attrs(model_unexp_attr_err, mdef.model_attr)[0:37] == 'Unexpected model definition attribute'
|
||||
|
||||
model_unexp_type_err = copy.deepcopy(model_json)
|
||||
model_unexp_type_err['id'] = '701'
|
||||
assert mdef.validate_attrs(model_unexp_type_err, mdef.model_attr)[0:15] == 'Unexpected type'
|
||||
|
||||
model_attr_missing = copy.deepcopy(model_json)
|
||||
del model_attr_missing['id']
|
||||
assert mdef.validate_attrs(model_attr_missing, mdef.model_attr)[0:27] == 'Mandatory attribute missing'
|
||||
|
||||
# group
|
||||
assert mdef.validate_attrs(model_json['group'], mdef.group_attr) == ''
|
||||
group_unexp_attr_err = copy.deepcopy(model_json)['group']
|
||||
group_unexp_attr_err['abc'] = 'def'
|
||||
assert mdef.validate_attrs(group_unexp_attr_err, mdef.group_attr)[0:37] == 'Unexpected model definition attribute'
|
||||
|
||||
group_unexp_type_err = copy.deepcopy(model_json)['group']
|
||||
group_unexp_type_err['name'] = 1
|
||||
assert mdef.validate_attrs(group_unexp_type_err, mdef.group_attr)[0:15] == 'Unexpected type'
|
||||
|
||||
group_attr_missing = copy.deepcopy(model_json)['group']
|
||||
del group_attr_missing['name']
|
||||
assert mdef.validate_attrs(group_attr_missing, mdef.group_attr)[0:27] == 'Mandatory attribute missing'
|
||||
|
||||
# point
|
||||
assert mdef.validate_attrs(model_json['group']['points'][0], mdef.point_attr) == ''
|
||||
|
||||
point_unexp_attr_err = copy.deepcopy(model_json)['group']['points'][0]
|
||||
point_unexp_attr_err['abc'] = 'def'
|
||||
assert mdef.validate_attrs(point_unexp_attr_err, mdef.point_attr)[0:37] == 'Unexpected model definition attribute'
|
||||
|
||||
point_unexp_type_err = copy.deepcopy(model_json)['group']['points'][0]
|
||||
point_unexp_type_err['name'] = 1
|
||||
assert mdef.validate_attrs(point_unexp_type_err, mdef.point_attr)[0:15] == 'Unexpected type'
|
||||
|
||||
point_unexp_value_err = copy.deepcopy(model_json)['group']['points'][1]
|
||||
point_unexp_value_err['access'] = 'z'
|
||||
assert mdef.validate_attrs(point_unexp_value_err, mdef.point_attr)[0:16] == 'Unexpected value'
|
||||
|
||||
point_attr_missing = copy.deepcopy(model_json)['group']['points'][0]
|
||||
del point_attr_missing['name']
|
||||
assert mdef.validate_attrs(point_attr_missing, mdef.point_attr)[0:27] == 'Mandatory attribute missing'
|
||||
|
||||
# symbol
|
||||
assert mdef.validate_attrs(model_json['group']['points'][2]['symbols'][0], mdef.symbol_attr) == ''
|
||||
|
||||
symbol_unexp_attr_err = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
|
||||
symbol_unexp_attr_err['abc'] = 'def'
|
||||
assert mdef.validate_attrs(symbol_unexp_attr_err, mdef.symbol_attr)[0:37] == 'Unexpected model definition attribute'
|
||||
|
||||
symbol_unexp_type_err = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
|
||||
symbol_unexp_type_err['name'] = 1
|
||||
assert mdef.validate_attrs(symbol_unexp_type_err, mdef.symbol_attr)[0:15] == 'Unexpected type'
|
||||
|
||||
symbol_attr_missing = copy.deepcopy(model_json)['group']['points'][2]['symbols'][0]
|
||||
del symbol_attr_missing['name']
|
||||
assert mdef.validate_attrs(symbol_attr_missing, mdef.symbol_attr)[0:27] == 'Mandatory attribute missing'
|
||||
|
||||
|
||||
def test_validate_group_point_dup():
|
||||
with open('./sunspec2/models/json/model_704.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
assert mdef.validate_group_point_dup(model_json['group']) == ''
|
||||
|
||||
dup_group_id_model = copy.deepcopy(model_json)
|
||||
dup_group_id_group = dup_group_id_model['group']
|
||||
dup_group_id_group['groups'][0]['name'] = 'PFWInjRvrt'
|
||||
assert mdef.validate_group_point_dup(dup_group_id_group)[0:18] == 'Duplicate group id'
|
||||
|
||||
dup_group_point_id_model = copy.deepcopy(model_json)
|
||||
dup_group_point_id_group = dup_group_point_id_model['group']
|
||||
dup_group_point_id_group['groups'][0]['name'] = 'PFWInjEna'
|
||||
assert mdef.validate_group_point_dup(dup_group_point_id_group)[0:28] == 'Duplicate group and point id'
|
||||
|
||||
mand_attr_miss_model = copy.deepcopy(model_json)
|
||||
mand_attr_miss_group = mand_attr_miss_model['group']
|
||||
del mand_attr_miss_group['groups'][0]['name']
|
||||
assert mdef.validate_group_point_dup(mand_attr_miss_group)[0:32] == 'Mandatory name attribute missing'
|
||||
|
||||
dup_point_id_model = copy.deepcopy(model_json)
|
||||
dup_point_id_group = dup_point_id_model['group']
|
||||
dup_point_id_group['points'][1]['name'] = 'ID'
|
||||
assert mdef.validate_group_point_dup(dup_point_id_group)[0:30] == 'Duplicate point id ID in group'
|
||||
|
||||
mand_attr_miss_point_model = copy.deepcopy(model_json)
|
||||
mand_attr_miss_point_group = mand_attr_miss_point_model['group']
|
||||
del mand_attr_miss_point_group['points'][1]['name']
|
||||
assert mdef.validate_group_point_dup(mand_attr_miss_point_group)[0:55] == 'Mandatory attribute missing in point ' \
|
||||
'definition element'
|
||||
|
||||
|
||||
def test_validate_symbols():
|
||||
symbols = [
|
||||
{'name': 'CAT_A', 'value': 1},
|
||||
{'name': 'CAT_B', 'value': 2}
|
||||
]
|
||||
assert mdef.validate_symbols(symbols, mdef.symbol_attr) == ''
|
||||
|
||||
|
||||
def test_validate_sf():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
model_point = model_json['group']['points'][2]
|
||||
model_group = model_json['group']
|
||||
model_group_arr = [model_group, model_group]
|
||||
assert mdef.validate_sf(model_point, 'W_SF', model_group_arr) == ''
|
||||
|
||||
not_sf_type_model = copy.deepcopy(model_json)
|
||||
not_sf_type_point = not_sf_type_model['group']['points'][2]
|
||||
not_sf_type_group = not_sf_type_model['group']
|
||||
not_sf_type_group_arr = [not_sf_type_group, not_sf_type_group]
|
||||
for point in not_sf_type_model['group']['points']:
|
||||
if point['name'] == 'W_SF':
|
||||
point['type'] = 'abc'
|
||||
assert mdef.validate_sf(not_sf_type_point, 'W_SF', not_sf_type_group_arr)[0:60] == 'Scale factor W_SF for point ' \
|
||||
'WMaxRtg is not scale factor ' \
|
||||
'type'
|
||||
|
||||
sf_not_found_model = copy.deepcopy(model_json)
|
||||
sf_not_found_point = sf_not_found_model['group']['points'][2]
|
||||
sf_not_found_group = sf_not_found_model['group']
|
||||
sf_not_found_group_arr = [sf_not_found_group, sf_not_found_group]
|
||||
assert mdef.validate_sf(sf_not_found_point, 'ABC', sf_not_found_group_arr)[0:44] == 'Scale factor ABC for point ' \
|
||||
'WMaxRtg not found'
|
||||
|
||||
sf_out_range_model = copy.deepcopy(model_json)
|
||||
sf_out_range_point = sf_out_range_model['group']['points'][2]
|
||||
sf_out_range_group = sf_out_range_model['group']
|
||||
sf_out_range_group_arr = [sf_out_range_group, sf_out_range_group]
|
||||
assert mdef.validate_sf(sf_out_range_point, 11, sf_out_range_group_arr)[0:46] == 'Scale factor 11 for point ' \
|
||||
'WMaxRtg out of range'
|
||||
|
||||
sf_invalid_type_model = copy.deepcopy(model_json)
|
||||
sf_invalid_type_point = sf_invalid_type_model['group']['points'][2]
|
||||
sf_invalid_type_group = sf_invalid_type_model['group']
|
||||
sf_invalid_type_group_arr = [sf_invalid_type_group, sf_invalid_type_group]
|
||||
assert mdef.validate_sf(sf_invalid_type_point, 4.0, sf_invalid_type_group_arr)[0:51] == 'Scale factor 4.0 for' \
|
||||
' point WMaxRtg has ' \
|
||||
'invalid type'
|
||||
|
||||
|
||||
def test_validate_point_def():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
model_group = model_json['group']
|
||||
group = model_json['group']
|
||||
point = model_json['group']['points'][0]
|
||||
assert mdef.validate_point_def(point, model_group, group) == ''
|
||||
|
||||
unk_point_type_model = copy.deepcopy(model_json)
|
||||
unk_point_type_model_group = unk_point_type_model['group']
|
||||
unk_point_type_group = unk_point_type_model['group']
|
||||
unk_point_type_point = unk_point_type_model['group']['points'][0]
|
||||
unk_point_type_point['type'] = 'abc'
|
||||
assert mdef.validate_point_def(unk_point_type_point, unk_point_type_model_group,
|
||||
unk_point_type_group)[0:35] == 'Unknown point type abc for point ID'
|
||||
|
||||
dup_symbol_model = copy.deepcopy(model_json)
|
||||
dup_symbol_model_group = dup_symbol_model['group']
|
||||
dup_symbol_group = dup_symbol_model['group']
|
||||
dup_symbol_point = dup_symbol_model['group']['points'][21]
|
||||
dup_symbol_point['symbols'][0]['name'] = 'CAT_B'
|
||||
assert mdef.validate_point_def(dup_symbol_point, dup_symbol_model_group,
|
||||
dup_symbol_group)[0:19] == 'Duplicate symbol id'
|
||||
|
||||
mand_attr_missing = copy.deepcopy(model_json)
|
||||
mand_attr_missing_model_group = mand_attr_missing['group']
|
||||
mand_attr_missing_group = mand_attr_missing['group']
|
||||
mand_attr_missing_point = mand_attr_missing['group']['points'][0]
|
||||
del mand_attr_missing_point['name']
|
||||
assert mdef.validate_point_def(mand_attr_missing_point, mand_attr_missing_model_group,
|
||||
mand_attr_missing_group)[0:27] == 'Mandatory attribute missing'
|
||||
|
||||
|
||||
def test_validate_group_def():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
assert mdef.validate_group_def(model_json['group'], model_json['group']) == ''
|
||||
|
||||
|
||||
def test_validate_model_group_def():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
assert mdef.validate_model_group_def(model_json, model_json['group']) == ''
|
||||
|
||||
missing_id_model = copy.deepcopy(model_json)
|
||||
missing_id_group = missing_id_model['group']
|
||||
missing_id_group['points'][0]['name'] = 'abc'
|
||||
assert mdef.validate_model_group_def(missing_id_model, missing_id_group)[0:41] == 'First point in top-level' \
|
||||
' group must be ID'
|
||||
|
||||
wrong_model_id_model = copy.deepcopy(model_json)
|
||||
wrong_model_id_group = wrong_model_id_model['group']
|
||||
wrong_model_id_group['points'][0]['value'] = 0
|
||||
assert mdef.validate_model_group_def(wrong_model_id_model, wrong_model_id_group)[0:42] == 'Model ID does not ' \
|
||||
'match top-level group ID'
|
||||
|
||||
missing_len_model = copy.deepcopy(model_json)
|
||||
missing_len_group = missing_len_model['group']
|
||||
missing_len_group['points'][1]['name'] = 'abc'
|
||||
assert mdef.validate_model_group_def(missing_len_model, missing_len_group)[0:41] == 'Second point in top-level ' \
|
||||
'group must be L'
|
||||
|
||||
missing_two_p_model = copy.deepcopy(model_json)
|
||||
missing_two_p_group = missing_two_p_model['group']
|
||||
missing_two_p_point = missing_two_p_group['points'][0]
|
||||
del missing_two_p_group['points']
|
||||
missing_two_p_group['points'] = [missing_two_p_point]
|
||||
assert mdef.validate_model_group_def(missing_two_p_model, missing_two_p_group)[0:48] == 'Top-level group must' \
|
||||
' contain at least two ' \
|
||||
'points'
|
||||
|
||||
missing_p_def_model = copy.deepcopy(model_json)
|
||||
missing_p_def_group = missing_p_def_model['group']
|
||||
del missing_p_def_group['points']
|
||||
assert mdef.validate_model_group_def(missing_p_def_model, missing_p_def_group)[0:41] == 'Top-level group' \
|
||||
' missing point definitions'
|
||||
|
||||
|
||||
def test_validate_model_def():
|
||||
with open('./sunspec2/models/json/model_702.json') as f:
|
||||
model_json = json.load(f)
|
||||
|
||||
assert mdef.validate_model_def(model_json) == ''
|
||||
|
||||
|
||||
def test_from_json_str():
|
||||
with open('./sunspec2/models/json/model_63001.json') as f:
|
||||
model_json = json.load(f)
|
||||
model_json_str = json.dumps(model_json)
|
||||
assert isinstance(mdef.from_json_str(model_json_str), dict)
|
||||
|
||||
|
||||
def test_from_json_file():
|
||||
assert isinstance(mdef.from_json_file('./sunspec2/models/json/model_63001.json'), dict)
|
||||
|
||||
|
||||
def test_to_json_str():
|
||||
with open('./sunspec2/models/json/model_63001.json') as f:
|
||||
model_json = json.load(f)
|
||||
assert isinstance(mdef.to_json_str(model_json), str)
|
||||
|
||||
|
||||
def test_to_json_filename():
|
||||
assert mdef.to_json_filename('63001') == 'model_63001.json'
|
||||
|
||||
|
||||
def test_to_json_file(tmp_path):
|
||||
with open('./sunspec2/models/json/model_63001.json') as f:
|
||||
model_json = json.load(f)
|
||||
mdef.to_json_file(model_json, filedir=tmp_path)
|
||||
|
||||
with open(tmp_path / 'model_63001.json') as f:
|
||||
model_json = json.load(f)
|
||||
assert isinstance(model_json, dict)
|
||||
|
||||
|
||||
def test_model_filename_to_id():
|
||||
assert mdef.model_filename_to_id('model_00077.json') == 77
|
||||
with pytest.raises(Exception) as exc:
|
||||
mdef.model_filename_to_id('model_abc.json')
|
||||
assert 'Error extracting model id from filename' in str(exc.value)
|
||||
Reference in New Issue
Block a user