Skip to content

Commit

Permalink
Merge pull request #98 from sunspec/development
Browse files Browse the repository at this point in the history
pysunspec2 updates
  • Loading branch information
Kudrat9 authored Nov 18, 2024
2 parents 05bb2bb + 46b4eaa commit 5de1e03
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 17 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
All Rights Reserved
"""

from distutils.core import setup
from setuptools import setup

setup(
name='pysunspec2',
Expand Down
12 changes: 9 additions & 3 deletions sunspec2/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ModelError(Exception):

model_defs_path = ['.', models_dir]
model_path_options = ['.', 'json', 'smdx']

model_defs_cache = {}

def get_model_defs_path():
return model_defs_path
Expand Down Expand Up @@ -72,6 +72,10 @@ def get_model_def(model_id, mapping=True):
except:
raise mdef.ModelDefinitionError('Invalid model id: %s' % model_id)

global model_defs_cache
if (model_id, mapping) in model_defs_cache:
return model_defs_cache[(model_id, mapping)].copy()

model_def_file_json = mdef.to_json_filename(model_id)
model_def_file_smdx = smdx.to_smdx_filename(model_id)
model_def = None
Expand All @@ -98,6 +102,7 @@ def get_model_def(model_id, mapping=True):
if model_def is not None:
if mapping:
add_mappings(model_def[mdef.GROUP])
model_defs_cache[(model_id, mapping)] = model_def.copy()
return model_def
raise mdef.ModelDefinitionError('Model definition not found for model %s' % model_id)

Expand Down Expand Up @@ -549,7 +554,7 @@ def _init_repeating_group(self, gdef=None, model_offset=None, data=None, data_of
# compute count based on model len if present, otherwise allocate when set
model_len = self.model.len
if model_len:
gdata = self._group_data(data=data, name=gdef[mdef.NAME])
gdata = self._group_data(data=data, name=gdef[mdef.NAME], index=0)
g = self.group_class(gdef=gdef, model=self.model, model_offset=model_offset, data=gdata,
data_offset=data_offset, index=1)
group_points_len = g.points_len
Expand All @@ -569,7 +574,8 @@ def _init_repeating_group(self, gdef=None, model_offset=None, data=None, data_of
model_offset += g.len
data_offset += g.len
for i in range(count - 1):
g = self.group_class(gdef=gdef, model=self.model, model_offset=model_offset, data=data,
gdata = self._group_data(data=data, index=(i+1))
g = self.group_class(gdef=gdef, model=self.model, model_offset=model_offset, data=gdata,
data_offset=data_offset, index=i+2)
model_offset += g.len
data_offset += g.len
Expand Down
2 changes: 1 addition & 1 deletion sunspec2/file/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, model_id=None, model_addr=0, model_len=0, model_def=None, dat
self.add_error(str(e))

FileClientGroup.__init__(self, gdef=gdef, model=self, model_offset=0, group_len=self.model_len, data=data,
data_offset=0, group_class=group_class)
data_offset=0, group_class=group_class, point_class=point_class)

def add_error(self, error_info):
self.error_info = '%s%s\n' % (self.error_info, error_info)
Expand Down
4 changes: 2 additions & 2 deletions sunspec2/modbus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ def __init__(self, slave_id=1, ipaddr='127.0.0.1', ipport=502, timeout=None, ctx
if self.client is None:
raise SunSpecModbusClientError('No modbus tcp client set for device')

def connect(self):
self.client.connect()
def connect(self, timeout=None):
self.client.connect(timeout)

def disconnect(self):
self.client.disconnect()
Expand Down
41 changes: 31 additions & 10 deletions sunspec2/smdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ def from_smdx(element):
fixed_def[mdef.DESCRIPTION] = a.text
elif a.tag == SMDX_NOTES and a.text:
fixed_def[mdef.DETAIL] = a.text

# Assign point info to point definitions
for p in e.findall(SMDX_POINT):
pid = p.attrib.get(SMDX_ATTR_ID)
label = desc = notes = None
Expand All @@ -266,23 +268,42 @@ def from_smdx(element):
elif a.tag == SMDX_NOTES and a.text:
notes = a.text

point_def = fixed_points_map.get(pid)
if point_def is not None:
if label:
point_def[mdef.LABEL] = label
if desc:
point_def[mdef.DESCRIPTION] = desc
if notes:
point_def[mdef.DETAIL] = notes
point_def = repeating_points_map.get(pid)
if point_def is not None:
for points_map in [fixed_points_map, repeating_points_map]:
point_def = points_map.get(pid)
if point_def is None:
continue

if label:
point_def[mdef.LABEL] = label
if desc:
point_def[mdef.DESCRIPTION] = desc
if notes:
point_def[mdef.DETAIL] = notes

# Assign symbol info to the point's symbol definitions
for s in p.findall(SMDX_SYMBOL):
sid = s.attrib.get(SMDX_ATTR_ID)
s_label = s_desc = s_notes = None
for a in s.findall('*'):
if a.tag == SMDX_LABEL and a.text:
s_label = a.text
elif a.tag == SMDX_DESCRIPTION and a.text:
s_desc = a.text
elif a.tag == SMDX_NOTES and a.text:
s_notes = a.text

for s in point_def.get(mdef.SYMBOLS):
if s[mdef.NAME] != sid:
continue

if s_label:
s[mdef.LABEL] = s_label
if s_desc:
s[mdef.DESCRIPTION] = s_desc
if s_notes:
s[mdef.DETAIL] = s_notes
break

model_def = {'id': mid, 'group': fixed_def}
return model_def

Expand Down
213 changes: 213 additions & 0 deletions sunspec2/tests/test_data/inverter_123.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
{
"name": null,
"did": "fa0a9f2d-d503-470e-8ce8-ec9c51428829",
"models": [
{
"ID": 1,
"L": 66,
"Mn": "Device Manufacturer",
"Md": "Inverter 123",
"Opt": null,
"Vr": "v0.0.1",
"SN": "9999abcd",
"DA": null,
"Pad": 32768
},
{
"ID": 129,
"L": 210,
"ActCrv": 1,
"ModEna": 0,
"WinTms": null,
"RvrtTms": null,
"RmpTms": null,
"NCrv": 4,
"NPt": 10,
"Tms_SF": -2,
"V_SF": -1,
"Pad": 32768,
"curve": [
{
"ActPt": 4,
"Tms1": 200,
"V1": 880,
"Tms2": 71,
"V2": 650,
"Tms3": 20,
"V3": 450,
"Tms4": 0,
"V4": 300,
"Tms5": 0,
"V5": 0,
"Tms6": 0,
"V6": 0,
"Tms7": 0,
"V7": 0,
"Tms8": 0,
"V8": 0,
"Tms9": 0,
"V9": 0,
"Tms10": 0,
"V10": 0,
"Tms11": null,
"V11": null,
"Tms12": null,
"V12": null,
"Tms13": null,
"V13": null,
"Tms14": null,
"V14": null,
"Tms15": null,
"V15": null,
"Tms16": null,
"V16": null,
"Tms17": null,
"V17": null,
"Tms18": null,
"V18": null,
"Tms19": null,
"V19": null,
"Tms20": null,
"V20": null,
"CrvNam": null,
"ReadOnly": 1
},
{
"ActPt": 0,
"Tms1": 0,
"V1": 0,
"Tms2": 0,
"V2": 0,
"Tms3": 0,
"V3": 0,
"Tms4": 0,
"V4": 0,
"Tms5": 0,
"V5": 0,
"Tms6": 0,
"V6": 0,
"Tms7": 0,
"V7": 0,
"Tms8": 0,
"V8": 0,
"Tms9": 0,
"V9": 0,
"Tms10": 0,
"V10": 0,
"Tms11": null,
"V11": null,
"Tms12": null,
"V12": null,
"Tms13": null,
"V13": null,
"Tms14": null,
"V14": null,
"Tms15": null,
"V15": null,
"Tms16": null,
"V16": null,
"Tms17": null,
"V17": null,
"Tms18": null,
"V18": null,
"Tms19": null,
"V19": null,
"Tms20": null,
"V20": null,
"CrvNam": null,
"ReadOnly": 0
},
{
"ActPt": 0,
"Tms1": 0,
"V1": 0,
"Tms2": 0,
"V2": 0,
"Tms3": 0,
"V3": 0,
"Tms4": 0,
"V4": 0,
"Tms5": 0,
"V5": 0,
"Tms6": 0,
"V6": 0,
"Tms7": 0,
"V7": 0,
"Tms8": 0,
"V8": 0,
"Tms9": 0,
"V9": 0,
"Tms10": 0,
"V10": 0,
"Tms11": null,
"V11": null,
"Tms12": null,
"V12": null,
"Tms13": null,
"V13": null,
"Tms14": null,
"V14": null,
"Tms15": null,
"V15": null,
"Tms16": null,
"V16": null,
"Tms17": null,
"V17": null,
"Tms18": null,
"V18": null,
"Tms19": null,
"V19": null,
"Tms20": null,
"V20": null,
"CrvNam": null,
"ReadOnly": 0
},
{
"ActPt": 0,
"Tms1": 0,
"V1": 0,
"Tms2": 0,
"V2": 0,
"Tms3": 0,
"V3": 0,
"Tms4": 0,
"V4": 0,
"Tms5": 0,
"V5": 0,
"Tms6": 0,
"V6": 0,
"Tms7": 0,
"V7": 0,
"Tms8": 0,
"V8": 0,
"Tms9": 0,
"V9": 0,
"Tms10": 0,
"V10": 0,
"Tms11": null,
"V11": null,
"Tms12": null,
"V12": null,
"Tms13": null,
"V13": null,
"Tms14": null,
"V14": null,
"Tms15": null,
"V15": null,
"Tms16": null,
"V16": null,
"Tms17": null,
"V17": null,
"Tms18": null,
"V18": null,
"Tms19": null,
"V19": null,
"Tms20": null,
"V20": null,
"CrvNam": null,
"ReadOnly": 0
}
]
}
]
}
7 changes: 7 additions & 0 deletions sunspec2/tests/test_file_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2847,6 +2847,13 @@ def test_scan(self):
assert d.common
assert d.DERMeasureAC

def test_repeating_point(self):
d = file_client.FileClientDevice('sunspec2/tests/test_data/inverter_123.json')
d.scan()
assert d.models[129][-1].curve[0].Tms1.value == 200
assert d.models[129][-1].curve[1].Tms1.value == 0
assert d.models[129][-1].curve[2].Tms11.value is None

def test_get_text(self, model_705_data):
d = file_client.FileClientDevice()
m = file_client.FileClientModel(705, data=model_705_data)
Expand Down
17 changes: 17 additions & 0 deletions sunspec2/tests/test_modbus_modbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,20 @@ def test_write(self, monkeypatch):
check_req = b"\x00\x00\x00\x00\x00'\x01\x10\x9ct\x00\x10 sn-000\x00\x00\x00\x00\x00\x00\x00" \
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
assert c.socket.request[0] == check_req

def test_write_over_max_size(self, monkeypatch):
c = modbus_client.ModbusClientTCP()
monkeypatch.setattr(socket, 'socket', MockSocket.mock_socket)
c.connect()
data_to_write = bytearray((c.max_write_count+1)*2)
data_to_write[:6] = b'sn-000'

buffer = [b'\x00\x00\x00\x00\x00\x06\x01\x10\x9ct\x00\x7b',
b'\x00\x00\x00\x00\x00\x06\x01\x10\x9c\xef\x00\x01']
c.socket._set_buffer(buffer)
c.write(40052, data_to_write)

check_req0 = b"\x00\x00\x00\x00\x00\xfd\x01" + b"\x10\x9ct\x00{\xf6" + data_to_write[:(c.max_write_count*2)]
check_req1 = b"\x00\x00\x00\x00\x00\x09\x01" + b"\x10\x9c\xef\x00\x01\x02\x00\x00"
assert c.socket.request[0] == check_req0
assert c.socket.request[1] == check_req1
Loading

0 comments on commit 5de1e03

Please sign in to comment.