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+
8+ # r""":mod:`exchange` exposes an API to allow modifications (add, delete, modify) to
9+ # exchange objects for EGD devices within the Kepware Configuration API
10+ # """
11+
12+ import kepconfig
13+ from typing import Union
14+ from .. import egd as EGD , channel , device
15+
16+ CONSUMER_ROOT = '/consumer_exchange_groups/consumer exchanges/consumer_exchanges'
17+ PRODUCER_ROOT = '/producer_exchange_groups/producer exchanges/producer_exchanges'
18+
19+ def _create_url (device_path , ex_type , exchange_name = None ):
20+ '''Creates url object for the "exchange" branch of Kepware's project tree. Used
21+ to build a part of Kepware Configuration API URL structure
22+
23+ Returns the exchange specific url when a value is passed as the exchange name.
24+ '''
25+ path_obj = kepconfig .path_split (device_path )
26+ device_root = channel ._create_url (path_obj ['channel' ]) + device ._create_url (path_obj ['device' ])
27+
28+ if exchange_name == None :
29+ if ex_type == EGD .CONSUMER_EXCHANGE :
30+ return device_root + CONSUMER_ROOT
31+ else :
32+ return device_root + PRODUCER_ROOT
33+ else :
34+ if ex_type == EGD .CONSUMER_EXCHANGE :
35+ return '{}{}/{}' .format (device_root ,CONSUMER_ROOT ,exchange_name )
36+ else :
37+ return '{}{}/{}' .format (device_root ,PRODUCER_ROOT ,exchange_name )
38+
39+ def add_exchange (server , device_path , ex_type , DATA ) -> Union [bool , list ]:
40+ '''Add a "exchange" or multiple "exchange" objects to Kepware. Can be used to pass children of a exchange object
41+ such as ranges. This allows you to create a exchange and ranges for the exchange all in one function, if desired.
42+
43+ Additionally it can be used to pass a list of exchanges and it's children to be added all at once.
44+
45+ INPUTS:
46+
47+ "server" - instance of the "server" class
48+
49+ "device_path" - path to exchanges. Standard Kepware address decimal
50+ notation string such as "channel1.device1"
51+
52+ "type" - type of exchange either consumer or producer
53+
54+ "DATA" - properly JSON object (dict) of the exchange and it's children
55+ expected by Kepware Configuration API
56+
57+ RETURNS:
58+
59+ True - If a "HTTP 201 - Created" is received from Kepware
60+
61+ List - If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
62+ exchanges added that failed.
63+
64+ False - If a non-expected "2xx successful" code is returned
65+
66+
67+ EXCEPTIONS:
68+
69+ KepHTTPError - If urllib provides an HTTPError
70+ KepURLError - If urllib provides an URLError
71+ '''
72+
73+ r = server ._config_add (server .url + _create_url (device_path , ex_type ), DATA )
74+ if r .code == 201 : return True
75+ elif r .code == 207 :
76+ errors = []
77+ for item in r .payload :
78+ if item ['code' ] != 201 :
79+ errors .append (item )
80+ return errors
81+ else : return False
82+
83+ def del_exchange (server , device_path , ex_type , exchange_name ) -> bool :
84+ '''Delete a "exchange" object in Kepware. This will delete all children as well
85+
86+ INPUTS:
87+
88+ "server" - instance of the "server" class
89+
90+ "device_path" - path to exchanges. Standard Kepware address decimal
91+ notation string such as "channel1.device1"
92+
93+ "ex_type" - type of exchange either consumer or producer
94+
95+ "exchange_name" - name of exchange
96+
97+ RETURNS:
98+
99+ True - If a "HTTP 200 - OK" is received from Kepware
100+
101+ EXCEPTIONS:
102+
103+ KepHTTPError - If urllib provides an HTTPError
104+ KepURLError - If urllib provides an URLError
105+ '''
106+
107+ r = server ._config_del (server .url + _create_url (device_path , ex_type , exchange_name ))
108+ if r .code == 200 : return True
109+ else : return False
110+
111+ def modify_exchange (server , device_path , ex_type , DATA , exchange_name = None , force = False ) -> bool :
112+ '''Modify a exchange object and it's properties in Kepware. If a "exchange_name" is not provided as an input,
113+ you need to identify the exchange in the 'common.ALLTYPES_NAME' property field in the "DATA". It will
114+ assume that is the exchange that is to be modified.
115+
116+ INPUTS:
117+
118+ "server" - instance of the "server" class
119+
120+ "device_path" - path to exchanges. Standard Kepware address decimal
121+ notation string such as "channel1.device1"
122+
123+ "ex_type" - type of exchange either consumer or producer
124+
125+ "DATA" - properly JSON object (dict) of the exchange properties to be modified.
126+
127+ "exchange_name" (optional) - name of exchange to modify. Only needed if not existing in "DATA"
128+
129+ "force" (optional) - if True, will force the configuration update to the Kepware server
130+
131+ RETURNS:
132+
133+ True - If a "HTTP 200 - OK" is received from Kepware
134+
135+ EXCEPTIONS:
136+
137+ KepHTTPError - If urllib provides an HTTPError
138+ KepURLError - If urllib provides an URLError
139+ '''
140+
141+ exchange_data = server ._force_update_check (force , DATA )
142+ if exchange_name == None :
143+ try :
144+ r = server ._config_update (server .url + _create_url (device_path , ex_type , exchange_data ['common.ALLTYPES_NAME' ]), exchange_data )
145+ if r .code == 200 : return True
146+ else : return False
147+ except KeyError as err :
148+ print ('Error: No exchange identified in DATA | Key Error: {}' .format (err ))
149+ return False
150+ # except Exception as e:
151+ # return 'Error: Error with {}: {}'.format(inspect.currentframe().f_code.co_name, str(e))
152+ else :
153+ r = server ._config_update (server .url + _create_url (device_path , ex_type , exchange_name ), exchange_data )
154+ if r .code == 200 : return True
155+ else : return False
156+
157+ def get_exchange (server , device_path , ex_type , exchange_name = None ) -> Union [dict , list ]:
158+ '''Returns the properties of the exchange object or a list of all exchanges and their
159+ properties for the type input. Returned object is JSON.
160+
161+ INPUTS:
162+
163+ "server" - instance of the "server" class
164+
165+ "device_path" - path to exchanges. Standard Kepware address decimal
166+ notation string such as "channel1.device1"
167+
168+ "ex_type" - type of exchange either consumer or producer
169+
170+ "exchange_name" - name of exchange
171+
172+ RETURNS:
173+
174+ JSON - data for the exchange requested or a list of exchanges and their properties
175+
176+ EXCEPTIONS:
177+
178+ KepHTTPError - If urllib provides an HTTPError
179+ KepURLError - If urllib provides an URLError
180+ '''
181+ if exchange_name == None :
182+ r = server ._config_get (server .url + _create_url (device_path , ex_type ))
183+ else :
184+ r = server ._config_get (server .url + _create_url (device_path , ex_type , exchange_name ))
185+ return r .payload
186+
187+ def get_all_exchanges (server , device_path ):
188+ '''Returns list of all exchange objects and their properties. Returned object is JSON list.
189+
190+ INPUTS:
191+
192+ "server" - instance of the "server" class
193+
194+ "device_path" - path to exchanges. Standard Kepware address decimal
195+ notation string such as "channel1.device1"
196+
197+ RETURNS:
198+
199+ List - [list of consumer exchanges, list of producer exchanges] - list of lists for all
200+ exchanges for the device
201+
202+ EXCEPTIONS:
203+
204+ KepHTTPError - If urllib provides an HTTPError
205+ KepURLError - If urllib provides an URLError
206+ '''
207+ exchange_list = []
208+ exchange_list .append (get_exchange (server , device_path , EGD .CONSUMER_EXCHANGE ))
209+ exchange_list .append (get_exchange (server , device_path , EGD .PRODUCER_EXCHANGE ))
210+ return exchange_list
0 commit comments