Skip to content

Commit 0ff30ff

Browse files
committed
init added link tag functionality
1 parent fb4525b commit 0ff30ff

File tree

3 files changed

+239
-5
lines changed

3 files changed

+239
-5
lines changed

kepconfig/adv_tags/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
specific objects within the Kepware Configuration API
99
"""
1010

11-
from . import adv_tag_group, average_tags, derived_tags, complex_tags, cumulative_tags, min_tags, max_tags
11+
from . import adv_tag_group, average_tags, derived_tags, complex_tags, cumulative_tags, min_tags, max_tags, link_tags
1212
ADV_TAGS_ROOT = '/project/_advancedtags'
1313

1414
def adv_tag_path_split(path: str, *, isItem=False) -> dict:

kepconfig/adv_tags/link_tags.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3+
# See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
r"""`link_tags` exposes an API to allow modifications (add, delete, modify) to
8+
link tag objects within the Kepware Configuration API
9+
"""
10+
11+
from ..connection import server
12+
from ..error import KepError, KepHTTPError
13+
from ..utils import _url_parse_object
14+
from typing import Union
15+
from .. import adv_tags
16+
17+
LINK_TAGS_ROOT = '/link_tags'
18+
19+
def _get_link_tags_url(tag: str = None) -> str:
20+
'''Creates url object for the "link_tags" branch of Kepware's project tree.
21+
22+
Returns the link tag specific url when a value is passed as the tag name.
23+
'''
24+
if tag is None:
25+
return LINK_TAGS_ROOT
26+
else:
27+
return f'{LINK_TAGS_ROOT}/{_url_parse_object(tag)}'
28+
29+
def add_link_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
30+
'''Add `"link_tag"` or multiple `"link_tag"` objects to a specific path in Kepware.
31+
Can be used to pass a list of link tags to be added at one path location.
32+
33+
:param server: instance of the `server` class
34+
:param adv_tag_group_path: path identifying where to add link tag(s). Standard Kepware address decimal
35+
notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
36+
:param DATA: Dict or List of Dicts of the link tag(s) to add
37+
38+
:return: True - If a "HTTP 201 - Created" is received from Kepware server
39+
:return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
40+
link tags added that failed.
41+
42+
:raises KepHTTPError: If urllib provides an HTTPError
43+
:raises KepURLError: If urllib provides an URLError
44+
'''
45+
path_obj = adv_tags.adv_tag_path_split(adv_tag_group_path, isItem=False)
46+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url()
47+
48+
r = server._config_add(url, DATA)
49+
if r.code == 201:
50+
return True
51+
elif r.code == 207:
52+
errors = [item for item in r.payload if item['code'] != 201]
53+
return errors
54+
else:
55+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
56+
57+
def modify_link_tag(server: server, link_tag_path: str, DATA: dict, force: bool = False) -> bool:
58+
'''Modify a `"link_tag"` object and its properties in Kepware.
59+
60+
:param server: instance of the `server` class
61+
:param link_tag_path: path identifying location and link tag to modify. Standard Kepware address decimal
62+
notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
63+
:param DATA: Dict of the `link_tag` properties to be modified
64+
:param force: *(optional)* if True, will force the configuration update to the Kepware server
65+
66+
:return: True - If a "HTTP 200 - OK" is received from Kepware server
67+
68+
:raises KepHTTPError: If urllib provides an HTTPError
69+
:raises KepURLError: If urllib provides an URLError
70+
'''
71+
link_tag_data = server._force_update_check(force, DATA)
72+
path_obj = adv_tags.adv_tag_path_split(link_tag_path, isItem=True)
73+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
74+
75+
r = server._config_update(url, link_tag_data)
76+
if r.code == 200:
77+
return True
78+
else:
79+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
80+
81+
def del_link_tag(server: server, link_tag_path: str) -> bool:
82+
'''Delete `"link_tag"` object at a specific path in Kepware.
83+
84+
:param server: instance of the `server` class
85+
:param link_tag_path: path identifying location and link tag to delete. Standard Kepware address decimal
86+
notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
87+
88+
:return: True - If a "HTTP 200 - OK" is received from Kepware server
89+
90+
:raises KepHTTPError: If urllib provides an HTTPError
91+
:raises KepURLError: If urllib provides an URLError
92+
'''
93+
path_obj = adv_tags.adv_tag_path_split(link_tag_path, isItem=True)
94+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
95+
96+
r = server._config_del(url)
97+
if r.code == 200:
98+
return True
99+
else:
100+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
101+
102+
def get_link_tag(server: server, link_tag_path: str) -> dict:
103+
'''Returns the properties of the `"link_tag"` object at a specific path in Kepware.
104+
105+
:param server: instance of the `server` class
106+
:param link_tag_path: path identifying location and link tag to retrieve. Standard Kepware address decimal
107+
notation string including the link tag such as "_advancedtags.AdvTagGroup1.LinkTag1"
108+
109+
:return: Dict of data for the link tag requested
110+
111+
:raises KepHTTPError: If urllib provides an HTTPError
112+
:raises KepURLError: If urllib provides an URLError
113+
'''
114+
path_obj = adv_tags.adv_tag_path_split(link_tag_path, isItem=True)
115+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url(path_obj['item'])
116+
117+
r = server._config_get(url)
118+
return r.payload
119+
120+
def get_all_link_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
121+
'''Returns the properties of all `"link_tag"` objects at a specific path in Kepware.
122+
123+
:param server: instance of the `server` class
124+
:param adv_tag_group_path: path identifying location to retrieve link tag list. Standard Kepware address decimal
125+
notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
126+
:param options: *(optional)* Dict of parameters to filter, sort or paginate the list of link tags. Options are `filter`,
127+
`sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
128+
129+
:return: List of data for all link tags
130+
131+
:raises KepHTTPError: If urllib provides an HTTPError
132+
:raises KepURLError: If urllib provides an URLError
133+
'''
134+
path_obj = adv_tags.adv_tag_path_split(adv_tag_group_path, isItem=False)
135+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_link_tags_url()
136+
137+
r = server._config_get(url, params=options)
138+
return r.payload

tests/adv_tags_test.py

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import os, sys
1111
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12-
from kepconfig import adv_tags, error, connection
12+
from kepconfig import adv_tags, error, connection, connectivity
1313
import pytest
1414

1515
adv_tag_list_avail = []
@@ -19,6 +19,10 @@
1919
adv_tag_group_child = 'AdvTagGroupChild'
2020
adv_tag_child_group_path = f'_advancedtags.{adv_tag_group_name}.{adv_tag_group_child}'
2121

22+
sim_ch_name = 'SimulatedDevice'
23+
sim_dev_name = 'SimulatedDevice'
24+
sim_tag_name = 'tag1'
25+
2226
avg_tag_name = 'AvgTag1'
2327
avg_tag_data = [
2428
{
@@ -83,6 +87,17 @@
8387
}
8488
]
8589

90+
link_tag_name = 'LinkTag1'
91+
link_tag_data = [
92+
{
93+
"common.ALLTYPES_NAME": link_tag_name,
94+
"common.ALLTYPES_DESCRIPTION": "",
95+
"advanced_tags.ENABLED": True,
96+
"advanced_tags.LINK_INPUT_TAG": "_System._Time_Hour",
97+
"advanced_tags.LINK_OUTPUT_TAG": f"{sim_ch_name}.{sim_dev_name}.{sim_tag_name}",
98+
}
99+
]
100+
86101
def HTTPErrorHandler(err):
87102
if err.__class__ is error.KepHTTPError:
88103
print(err.code)
@@ -101,6 +116,31 @@ def initialize(server: connection.server):
101116
server._config_get(server.url +"/project/_advancedtags")
102117
except Exception as err:
103118
pytest.skip("Advanced Tags is not installed", allow_module_level=True)
119+
120+
# Create simulated device for testing
121+
sim_device = {
122+
"common.ALLTYPES_NAME": sim_ch_name,
123+
"servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator",
124+
"devices": [
125+
{
126+
"common.ALLTYPES_NAME": sim_dev_name,
127+
"servermain.MULTIPLE_TYPES_DEVICE_DRIVER": "Simulator",
128+
"tags": [
129+
{
130+
"common.ALLTYPES_NAME": sim_tag_name,
131+
"servermain.TAG_ADDRESS": "K0"
132+
}
133+
],
134+
}
135+
]
136+
}
137+
try:
138+
connectivity.channel.add_channel(server, sim_device)
139+
except Exception as err:
140+
HTTPErrorHandler(err)
141+
return False
142+
return True
143+
104144

105145
def complete(server: connection.server):
106146
# Remove advanced tag group and all tags created during the test
@@ -126,8 +166,22 @@ def complete(server: connection.server):
126166
for obj in obj_list:
127167
minimum_tag_path = f'_advancedtags.{obj["name"]}'
128168
adv_tags.min_tags.del_minimum_tag(server, minimum_tag_path)
129-
130-
pass
169+
elif key == 'maximum_tags':
170+
for obj in obj_list:
171+
maximum_tag_path = f'_advancedtags.{obj["name"]}'
172+
adv_tags.max_tags.del_maximum_tag(server, maximum_tag_path)
173+
elif key == 'link_tags':
174+
for obj in obj_list:
175+
link_tag_path = f'_advancedtags.{obj["name"]}'
176+
adv_tags.link_tags.del_link_tag(server, link_tag_path)
177+
178+
# Delete all Channels
179+
try:
180+
ch_left = connectivity.channel.get_all_channels(server)
181+
for x in ch_left:
182+
print(connectivity.channel.del_channel(server,x['common.ALLTYPES_NAME']))
183+
except Exception as err:
184+
HTTPErrorHandler(err)
131185

132186
@pytest.fixture(scope="module")
133187
def server(kepware_server):
@@ -136,7 +190,8 @@ def server(kepware_server):
136190
server_type = kepware_server[1]
137191

138192
# Initialize any configuration before testing in module
139-
initialize(server)
193+
if not initialize(server):
194+
pytest.fail("Simulation Device creation failed")
140195

141196
# Everything below yield is run after module tests are completed
142197
yield server
@@ -420,6 +475,47 @@ def test_maximum_tag_del(server):
420475
maximum_tag_path = f'_advancedtags.{adv_tag_group_name}.{maximum_tag_name}'
421476
assert adv_tags.max_tags.del_maximum_tag(server, maximum_tag_path)
422477

478+
def test_link_tag_add(server):
479+
# Add a link tag to the root advanced tag plug-in
480+
assert adv_tags.link_tags.add_link_tag(server, f'_advancedtags', link_tag_data)
481+
482+
testTag = {
483+
"common.ALLTYPES_NAME": "newLinkTag",
484+
"common.ALLTYPES_DESCRIPTION": "",
485+
"advanced_tags.ENABLED": True,
486+
"advanced_tags.LINK_INPUT_TAG": "_System._Time_Hour",
487+
"advanced_tags.LINK_OUTPUT_TAG": f"{sim_ch_name}.{sim_dev_name}.{sim_tag_name}",
488+
}
489+
link_tag_data.append(testTag)
490+
# Add a link tag to the advanced tag group
491+
assert adv_tags.link_tags.add_link_tag(server, f'_advancedtags.{adv_tag_group_name}', link_tag_data)
492+
493+
def test_link_tag_get(server):
494+
# Get the link tag
495+
link_tag_path = f'_advancedtags.{adv_tag_group_name}.{link_tag_name}'
496+
result = adv_tags.link_tags.get_link_tag(server, link_tag_path)
497+
assert type(result) == dict
498+
assert result.get("common.ALLTYPES_NAME") == link_tag_name
499+
500+
def test_link_tag_modify(server):
501+
# Modify the link tag
502+
link_tag_path = f'_advancedtags.{adv_tag_group_name}.{link_tag_name}'
503+
tag_data = {
504+
"common.ALLTYPES_DESCRIPTION": "Modified link tag"
505+
}
506+
assert adv_tags.link_tags.modify_link_tag(server, link_tag_path, tag_data, force=True)
507+
508+
def test_link_tag_get_all(server):
509+
# Get all link tags under the group
510+
result = adv_tags.link_tags.get_all_link_tags(server, f'_advancedtags.{adv_tag_group_name}')
511+
assert type(result) == list
512+
assert any(tag.get("common.ALLTYPES_NAME") == link_tag_name for tag in result)
513+
514+
def test_link_tag_del(server):
515+
# Delete the link tag
516+
link_tag_path = f'_advancedtags.{adv_tag_group_name}.{link_tag_name}'
517+
assert adv_tags.link_tags.del_link_tag(server, link_tag_path)
518+
423519
def test_adv_tag_group_del(server):
424520
# Delete parent advanced tag group
425521
assert adv_tags.adv_tag_group.del_tag_group(server, f'_advancedtags.{adv_tag_group_name}')

0 commit comments

Comments
 (0)