gridappsd-python 2024.1.4a0__tar.gz → 2024.4.0a0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/PKG-INFO +9 -10
  2. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/README.md +8 -9
  3. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/__init__.py +0 -2
  4. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/__main__.py +5 -3
  5. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/app_registration.py +8 -12
  6. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/difference_builder.py +1 -1
  7. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/goss.py +35 -22
  8. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/gridappsd.py +29 -30
  9. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/houses.py +7 -17
  10. gridappsd_python-2024.4.0a0/gridappsd/json_extension.py +123 -0
  11. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/loghandler.py +6 -5
  12. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/register_app.py +5 -4
  13. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/simulation.py +21 -29
  14. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/timeseries.py +34 -34
  15. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/topics.py +25 -15
  16. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/utils.py +2 -4
  17. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/pyproject.toml +2 -2
  18. gridappsd_python-2024.1.4a0/gridappsd/json_extension.py +0 -55
  19. {gridappsd_python-2024.1.4a0 → gridappsd_python-2024.4.0a0}/gridappsd/docker_handler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gridappsd-python
3
- Version: 2024.1.4a0
3
+ Version: 2024.4.0a0
4
4
  Summary: A GridAPPS-D Python Adapter
5
5
  Home-page: https://gridappsd.readthedocs.io
6
6
  License: BSD-3-Clause
@@ -203,9 +203,9 @@ integration tests available to be run. In order to execute these, you must have
203
203
  as above with dev-dependencies.
204
204
 
205
205
  During the testing phase the docker containers required for the tests are downloaded from
206
- dockerhub and started. By default the `develop` tag is used to test the library using pytest.
206
+ dockerhub and started. By default the `develop` tag is used to test the library using pytest.
207
207
  One can customize the docker image tag by setting the environmental
208
- variable `GRIDAPPSD_TAG_ENV` either by `export GRIDAPPSD_TAG_ENV=other_tag` or by executing
208
+ variable `GRIDAPPSD_TAG_ENV` either by `export GRIDAPPSD_TAG_ENV=other_tag` or by executing
209
209
  pytest with the following:
210
210
 
211
211
  ```shell script
@@ -215,7 +215,7 @@ pytest with the following:
215
215
  export GRIDAPPSD_TAG_ENV=other_tag
216
216
  pytest
217
217
 
218
- # Tests also require the username and password to be avaialable as environmental variables
218
+ # Tests also require the username and password to be avaialable as environmental variables
219
219
  # in order for them to properly run these tests
220
220
  export GRIDAPPSD_USER=user
221
221
  export GRIDAPPSD_PASSWORD=pass
@@ -225,18 +225,18 @@ pytest
225
225
 
226
226
  ***NOTE: the first running the tests will download all of the docker images associated with the
227
227
  [GOSS-GridAPPS-D](http://github.com/GRIDAPPSD/GOSS-GridAPPS-D) repository. This process may take some time.***
228
-
228
+
229
229
  ### Running tests created in a new project
230
230
 
231
231
  The `gridappsd-python` library exposes a testing environment through the `gridappsd.docker_handler` module. Including the following
232
232
  `conftest.py` in the root of your base test directory allows tests to reference these. Using these fixtures will start all of the
233
- base containers required for `gridappsd` to run.
233
+ base containers required for `gridappsd` to run.
234
234
 
235
235
  ```python
236
236
 
237
237
  # conftest.py
238
- # Create a conftest.py file in the root of the tests directory to enable usage throughout the tests directory and below.
239
- #
238
+ # Create a conftest.py file in the root of the tests directory to enable usage throughout the tests directory and below.
239
+ #
240
240
  # Tested project structure an layout
241
241
  #
242
242
  # project-folder\
@@ -328,7 +328,7 @@ Using the above fixtures from inside a test module and test function looks like
328
328
 
329
329
  ```python
330
330
 
331
- # Example test function using the gridappsd_client fixture
331
+ # Example test function using the gridappsd_client fixture
332
332
 
333
333
  @mock.patch.dict(os.environ, {"GRIDAPPSD_APPLICATION_ID": "helics_goss_bridge.py"})
334
334
  def test_gridappsd_status(gridappsd_client):
@@ -351,4 +351,3 @@ def test_gridappsd_status(gridappsd_client):
351
351
  assert gappsd.get_application_status() == ProcessStatusEnum.COMPLETE.value
352
352
  ```
353
353
 
354
-
@@ -178,9 +178,9 @@ integration tests available to be run. In order to execute these, you must have
178
178
  as above with dev-dependencies.
179
179
 
180
180
  During the testing phase the docker containers required for the tests are downloaded from
181
- dockerhub and started. By default the `develop` tag is used to test the library using pytest.
181
+ dockerhub and started. By default the `develop` tag is used to test the library using pytest.
182
182
  One can customize the docker image tag by setting the environmental
183
- variable `GRIDAPPSD_TAG_ENV` either by `export GRIDAPPSD_TAG_ENV=other_tag` or by executing
183
+ variable `GRIDAPPSD_TAG_ENV` either by `export GRIDAPPSD_TAG_ENV=other_tag` or by executing
184
184
  pytest with the following:
185
185
 
186
186
  ```shell script
@@ -190,7 +190,7 @@ pytest with the following:
190
190
  export GRIDAPPSD_TAG_ENV=other_tag
191
191
  pytest
192
192
 
193
- # Tests also require the username and password to be avaialable as environmental variables
193
+ # Tests also require the username and password to be avaialable as environmental variables
194
194
  # in order for them to properly run these tests
195
195
  export GRIDAPPSD_USER=user
196
196
  export GRIDAPPSD_PASSWORD=pass
@@ -200,18 +200,18 @@ pytest
200
200
 
201
201
  ***NOTE: the first running the tests will download all of the docker images associated with the
202
202
  [GOSS-GridAPPS-D](http://github.com/GRIDAPPSD/GOSS-GridAPPS-D) repository. This process may take some time.***
203
-
203
+
204
204
  ### Running tests created in a new project
205
205
 
206
206
  The `gridappsd-python` library exposes a testing environment through the `gridappsd.docker_handler` module. Including the following
207
207
  `conftest.py` in the root of your base test directory allows tests to reference these. Using these fixtures will start all of the
208
- base containers required for `gridappsd` to run.
208
+ base containers required for `gridappsd` to run.
209
209
 
210
210
  ```python
211
211
 
212
212
  # conftest.py
213
- # Create a conftest.py file in the root of the tests directory to enable usage throughout the tests directory and below.
214
- #
213
+ # Create a conftest.py file in the root of the tests directory to enable usage throughout the tests directory and below.
214
+ #
215
215
  # Tested project structure an layout
216
216
  #
217
217
  # project-folder\
@@ -303,7 +303,7 @@ Using the above fixtures from inside a test module and test function looks like
303
303
 
304
304
  ```python
305
305
 
306
- # Example test function using the gridappsd_client fixture
306
+ # Example test function using the gridappsd_client fixture
307
307
 
308
308
  @mock.patch.dict(os.environ, {"GRIDAPPSD_APPLICATION_ID": "helics_goss_bridge.py"})
309
309
  def test_gridappsd_status(gridappsd_client):
@@ -325,4 +325,3 @@ def test_gridappsd_status(gridappsd_client):
325
325
  assert gappsd.get_service_status() == ProcessStatusEnum.COMPLETE.value
326
326
  assert gappsd.get_application_status() == ProcessStatusEnum.COMPLETE.value
327
327
  ```
328
-
@@ -54,5 +54,3 @@ from gridappsd.gridappsd import GridAPPSD
54
54
  from gridappsd.difference_builder import DifferenceBuilder
55
55
  from gridappsd.app_registration import ApplicationController
56
56
  import gridappsd.json_extension as json
57
-
58
-
@@ -49,19 +49,21 @@ from gridappsd import GridAPPSD
49
49
 
50
50
  assert sys.version_info >= (3, 6), "Minimum version is python 3.6"
51
51
 
52
- logging.basicConfig(stream=sys.stdout, level=logging.INFO,
52
+ logging.basicConfig(stream=sys.stdout,
53
+ level=logging.INFO,
53
54
  format="'%(asctime)s: %(name)-20s - %(levelname)-6s - %(message)s")
54
55
 
55
56
  logging.getLogger('stomp.py').setLevel(logging.WARNING)
56
57
  _log = logging.getLogger("gridappsd.__main__")
57
58
 
58
-
59
59
  if __name__ == '__main__':
60
60
 
61
61
  parser = ArgumentParser()
62
62
 
63
63
  group = parser.add_mutually_exclusive_group(required=True)
64
- group.add_argument("-s", "--run-simulation", type=argparse.FileType('r'),
64
+ group.add_argument("-s",
65
+ "--run-simulation",
66
+ type=argparse.FileType('r'),
65
67
  help="Start running a simulation from a passed simulation file.")
66
68
 
67
69
  opts = parser.parse_args()
@@ -13,8 +13,8 @@ import shlex
13
13
  import sys
14
14
  import os
15
15
 
16
- from . gridappsd import GridAPPSD
17
- from . topics import REQUEST_REGISTER_APP
16
+ from .gridappsd import GridAPPSD
17
+ from .topics import REQUEST_REGISTER_APP
18
18
  from . import utils, json_extension as json
19
19
 
20
20
  _log = logging.getLogger(__name__)
@@ -26,6 +26,7 @@ if os.name == 'posix':
26
26
 
27
27
 
28
28
  class Job(threading.Thread):
29
+
29
30
  def __init__(self, args, out=sys.stdout, err=sys.stderr):
30
31
  threading.Thread.__init__(self)
31
32
  _log.debug("Creating job")
@@ -42,10 +43,7 @@ class Job(threading.Thread):
42
43
  self.running = True
43
44
  os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'RUNNING'
44
45
 
45
- p = subprocess.Popen(args=self._args,
46
- shell=False,
47
- stdout=self._out,
48
- stderr=self._err)
46
+ p = subprocess.Popen(args=self._args, shell=False, stdout=self._out, stderr=self._err)
49
47
 
50
48
  # Loop while process is executing
51
49
  while p.poll() is None and self.running:
@@ -112,13 +110,10 @@ class ApplicationController(object):
112
110
  return self._heartbeat_thread is not None
113
111
 
114
112
  def register_app(self, end_callback):
115
- print("Sending {}\n\tto {}".format(self._configDict,
116
- REQUEST_REGISTER_APP))
113
+ print("Sending {}\n\tto {}".format(self._configDict, REQUEST_REGISTER_APP))
117
114
  self._gapd.get_logger().debug("Started App Registration")
118
115
 
119
- response = self._gapd.get_response(REQUEST_REGISTER_APP,
120
- self._configDict,
121
- 60)
116
+ response = self._gapd.get_response(REQUEST_REGISTER_APP, self._configDict, 60)
122
117
  if 'message' in response:
123
118
  _log.error("An error regisering the application occured")
124
119
  _log.error(response.get('message'))
@@ -156,7 +151,8 @@ class ApplicationController(object):
156
151
  # print("Seanding heartbeat {} {}".format(self._heartbeat_topic, self._application_id))
157
152
  # print("Heartbeat period: {}".format(self._heartbeat_period))
158
153
  self._gapd.send(self._heartbeat_topic, self._application_id)
159
- time.sleep(self._heartbeat_period - ((time.time() - starttime) % self._heartbeat_period))
154
+ time.sleep(self._heartbeat_period -
155
+ ((time.time() - starttime) % self._heartbeat_period))
160
156
  except:
161
157
  error_callback()
162
158
 
@@ -70,7 +70,7 @@ class DifferenceBuilder(object):
70
70
  self._forward = []
71
71
  self._reverse = []
72
72
 
73
- def get_message(self, epoch = None):
73
+ def get_message(self, epoch=None):
74
74
  if epoch is None:
75
75
  epoch = calendar.timegm(time.gmtime())
76
76
  msg = dict(command="update",
@@ -37,7 +37,6 @@
37
37
  # PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the
38
38
  # UNITED STATES DEPARTMENT OF ENERGY under Contract DE-AC05-76RL01830
39
39
  # -------------------------------------------------------------------------------
40
-
41
40
  """
42
41
  Created on March 1, 2018
43
42
 
@@ -81,10 +80,14 @@ class GOSS(object):
81
80
  """ Base class providing connections to a GOSS instance via stomp protocol
82
81
  """
83
82
 
84
- def __init__(self, username=None, password=None,
85
- stomp_address='localhost', stomp_port='61613',
83
+ def __init__(self,
84
+ username=None,
85
+ password=None,
86
+ stomp_address='localhost',
87
+ stomp_port='61613',
86
88
  attempt_connection=True,
87
- override_threading=None, stomp_log_level=logging.WARNING,
89
+ override_threading=None,
90
+ stomp_log_level=logging.WARNING,
88
91
  goss_log_level=logging.INFO):
89
92
 
90
93
  logging.getLogger('stomp.py').setLevel(stomp_log_level)
@@ -146,27 +149,31 @@ class GOSS(object):
146
149
  if isinstance(message, list) or isinstance(message, dict):
147
150
  message = json.dumps(message)
148
151
  _log.debug("Sending topic: {} body: {}".format(topic, message))
149
- self._conn.send(body=message, destination=topic,
150
- headers={'GOSS_HAS_SUBJECT': True,
151
- 'GOSS_SUBJECT': self.__token})
152
-
152
+ self._conn.send(body=message,
153
+ destination=topic,
154
+ headers={
155
+ 'GOSS_HAS_SUBJECT': True,
156
+ 'GOSS_SUBJECT': self.__token
157
+ })
158
+
153
159
  def get_response(self, topic, message, timeout=5):
154
- id = datetime.now().strftime("%Y%m%d%h%M%S")
160
+ id = datetime.now().strftime("%Y%m%d%h%M%S%f")[:-3]
155
161
  reply_to = "/temp-queue/response.{}".format(id)
156
-
162
+
157
163
  if isinstance(message, str):
158
164
  message = json.loads(message)
159
-
165
+
160
166
  if 'resultFormat' in message:
161
- self.result_format = message['resultFormat']
167
+ self.result_format = message['resultFormat']
162
168
 
163
169
  # Change message to string if we have a dictionary.
164
170
  if isinstance(message, dict):
165
171
  message = json.dumps(message)
166
172
  elif isinstance(message, list):
167
173
  message = json.dumps(message)
168
-
174
+
169
175
  class ResponseListener(object):
176
+
170
177
  def __init__(self, topic, result_format):
171
178
  self.response = None
172
179
  self._topic = topic
@@ -181,7 +188,7 @@ class GOSS(object):
181
188
  self.response = message
182
189
  else:
183
190
  self.response = json.loads(message)
184
- else:
191
+ else:
185
192
  self.response = message
186
193
  except ValueError:
187
194
  self.response = dict(error="Invalid json returned",
@@ -198,9 +205,13 @@ class GOSS(object):
198
205
  listener = ResponseListener(reply_to, self.result_format)
199
206
  self.subscribe(reply_to, listener)
200
207
 
201
- self._conn.send(body=message, destination=topic,
202
- headers={'reply-to': reply_to, 'GOSS_HAS_SUBJECT': True,
203
- 'GOSS_SUBJECT': self.__token})
208
+ self._conn.send(body=message,
209
+ destination=topic,
210
+ headers={
211
+ 'reply-to': reply_to,
212
+ 'GOSS_HAS_SUBJECT': True,
213
+ 'GOSS_SUBJECT': self.__token
214
+ })
204
215
  count = 0
205
216
 
206
217
  while count < timeout:
@@ -282,7 +293,7 @@ class GOSS(object):
282
293
 
283
294
  # get token
284
295
  # get initial connection
285
- dt=datetime.now()
296
+ dt = datetime.now()
286
297
  replyDest = f"temp.token_resp.{self.__user__}-{dt}"
287
298
 
288
299
  # create token request string
@@ -297,8 +308,9 @@ class GOSS(object):
297
308
  if self._override_thread_fc is not None:
298
309
  tmpConn.transport.override_threading(self._override_thread_fc)
299
310
  tmpConn.connect(self.__user__, self.__pass__, wait=True)
300
-
311
+
301
312
  class TokenResponseListener():
313
+
302
314
  def __init__(self):
303
315
  self.__token = None
304
316
 
@@ -307,7 +319,7 @@ class GOSS(object):
307
319
 
308
320
  def on_message(self, header, message):
309
321
  _log.debug("Internal on message is: {} {}".format(header, message))
310
-
322
+
311
323
  self.__token = str(message)
312
324
 
313
325
  def on_error(self, headers, message):
@@ -322,9 +334,10 @@ class GOSS(object):
322
334
  listener = TokenResponseListener()
323
335
 
324
336
  # self.subscribe(replyDest, listener)
325
- tmpConn.subscribe('/queue/'+replyDest, 123)
337
+ tmpConn.subscribe('/queue/' + replyDest, 123)
326
338
  tmpConn.set_listener('token_resp', listener)
327
- tmpConn.send(body=base64Str, destination=tokenTopic,
339
+ tmpConn.send(body=base64Str,
340
+ destination=tokenTopic,
328
341
  headers={'reply-to': replyDest})
329
342
  # while token is null or for x iterations
330
343
  iter = 0
@@ -65,9 +65,9 @@ class InvalidSimulationIdError(Exception):
65
65
  class GridAPPSD(GOSS):
66
66
  """ The main :class:`GridAPPSD` interface for connecting to a GridAPPSD instance
67
67
  """
68
+
68
69
  # TODO Get the caller from the traceback/inspect module.
69
- def __init__(self, simulation_id=None,
70
- address=None, **kwargs):
70
+ def __init__(self, simulation_id=None, address=None, **kwargs):
71
71
 
72
72
  if address is None:
73
73
  address = utils.get_gridappsd_address()
@@ -78,10 +78,7 @@ class GridAPPSD(GOSS):
78
78
  'stomp_port' in kwargs and not 'stomp_address' in kwargs:
79
79
  raise ValueError("If stomp_address is specified the so should stomp_port")
80
80
 
81
- super(GridAPPSD, self).__init__(
82
- stomp_address=address[0],
83
- stomp_port=address[1],
84
- **kwargs)
81
+ super(GridAPPSD, self).__init__(stomp_address=address[0], stomp_port=address[1], **kwargs)
85
82
  self._houses = Houses(self)
86
83
  self._simulation_log_topic = None
87
84
  self._simulation_id = None
@@ -104,11 +101,11 @@ class GridAPPSD(GOSS):
104
101
 
105
102
  def get_houses(self) -> Houses:
106
103
  return self._houses
107
-
104
+
108
105
  def get_simulation_id(self):
109
- """
106
+ """
110
107
  Return the simulation_id given to the GridAPPSD instance
111
-
108
+
112
109
  :return:
113
110
  """
114
111
  if not self._simulation_id:
@@ -116,7 +113,7 @@ class GridAPPSD(GOSS):
116
113
  if self._simulation_id:
117
114
  self._simulation_log_topic = t.simulation_log_topic(self._simulation_id)
118
115
  return self._simulation_id
119
-
116
+
120
117
  def set_application_status(self, status):
121
118
  """
122
119
  Set the application status.
@@ -125,8 +122,8 @@ class GridAPPSD(GOSS):
125
122
  try:
126
123
  self._process_status = ProcessStatusEnum(status)
127
124
  except ValueError:
128
- self.get_logger().warning("Unsuccessful change of application status."
129
- + f"Valid statuses are {ProcessStatusEnum.__members__}.")
125
+ self.get_logger().warning("Unsuccessful change of application status." +
126
+ f"Valid statuses are {ProcessStatusEnum.__members__}.")
130
127
 
131
128
  def set_service_status(self, status):
132
129
  """
@@ -136,8 +133,8 @@ class GridAPPSD(GOSS):
136
133
  try:
137
134
  self._process_status = ProcessStatusEnum(status)
138
135
  except ValueError:
139
- self.get_logger().warning("Unsuccessful change of service status."
140
- + f"Valid statuses are {ProcessStatusEnum.__members__}.")
136
+ self.get_logger().warning("Unsuccessful change of service status." +
137
+ f"Valid statuses are {ProcessStatusEnum.__members__}.")
141
138
 
142
139
  def set_simulation_id(self, simulation_id):
143
140
  if simulation_id is None:
@@ -156,17 +153,17 @@ class GridAPPSD(GOSS):
156
153
 
157
154
  def get_application_id(self):
158
155
  return utils.get_gridappsd_application_id()
159
-
156
+
160
157
  def get_service_status(self):
161
158
  """
162
159
  Return the service status
163
160
  :return:
164
161
  """
165
162
  return self._process_status.value
166
-
163
+
167
164
  def query_object_types(self, model_id=None):
168
165
  """ Allows the caller to query the different object types.
169
-
166
+
170
167
  :param model_id:
171
168
  :return:
172
169
  """
@@ -186,7 +183,7 @@ class GridAPPSD(GOSS):
186
183
  def query_model_info(self):
187
184
  payload = self._build_query_payload("QUERY_MODEL_INFO")
188
185
  return self.get_response(t.REQUEST_POWERGRID_DATA, payload, timeout=30)
189
-
186
+
190
187
  def query_model(self, model_id=None, object_type=None, object_id=None, response_format='JSON'):
191
188
  args = {}
192
189
  if model_id is not None:
@@ -194,9 +191,9 @@ class GridAPPSD(GOSS):
194
191
  if object_type is not None:
195
192
  args["objectType"] = object_type
196
193
  if object_id is not None:
197
- args["objectId"] = object_id
194
+ args["objectId"] = object_id
198
195
  if response_format is not None:
199
- args["resultFormat"] = response_format
196
+ args["resultFormat"] = response_format
200
197
  payload = self._build_query_payload("QUERY_MODEL", **args)
201
198
  return self.get_response(t.REQUEST_POWERGRID_DATA, payload, timeout=30)
202
199
 
@@ -208,13 +205,13 @@ class GridAPPSD(GOSS):
208
205
  args["modelId"] = model_id
209
206
  payload = self._build_query_payload("QUERY_OBJECT", **args)
210
207
  return self.get_response(t.REQUEST_POWERGRID_DATA, payload, timeout=30)
211
-
208
+
212
209
  def query_object_dictionary(self, model_id, object_type=None, object_id=None):
213
210
  if not model_id:
214
211
  raise ValueError("model_id is not specified.")
215
212
  if not object_id and not object_type:
216
213
  raise ValueError("No obejct_id or object_type specified.")
217
- args={}
214
+ args = {}
218
215
  args["modelId"] = model_id
219
216
  if object_id is not None:
220
217
  args["objectId"] = object_id
@@ -235,9 +232,15 @@ class GridAPPSD(GOSS):
235
232
  request_topic = '.'.join((t.REQUEST_DATA, database_type))
236
233
  return self.get_response(request_topic, json.dumps(payload), timeout=timeout)
237
234
 
238
- def get_platform_status(self, applications=True, services=True, appInstances=True, serviceInstances=True):
235
+ def get_platform_status(self,
236
+ applications=True,
237
+ services=True,
238
+ appInstances=True,
239
+ serviceInstances=True):
239
240
  _log.debug("Retrieving platform status from GridAPPSD")
240
- msg = dict(appInstances=appInstances, applications=applications, services=services,
241
+ msg = dict(appInstances=appInstances,
242
+ applications=applications,
243
+ services=services,
241
244
  serviceInstances=serviceInstances)
242
245
  return self.get_response(t.REQUEST_PLATFORM_STATUS, json.dumps(msg), timeout=30)
243
246
 
@@ -256,8 +259,7 @@ class GridAPPSD(GOSS):
256
259
 
257
260
  def send_status(self, status, topic, log_level=INFO):
258
261
  self.set_application_status(status)
259
- status_message = self.build_message_json(self.get_application_status(),
260
- "", log_level)
262
+ status_message = self.build_message_json(self.get_application_status(), "", log_level)
261
263
  self.send(topic, status_message)
262
264
 
263
265
  def build_message_json(self, status, message, log_level):
@@ -265,7 +267,7 @@ class GridAPPSD(GOSS):
265
267
  status_message = {
266
268
  "source": utils.get_gridappsd_application_id(),
267
269
  "processId": f"{self._simulation_id}",
268
- "timestamp": int(time.mktime(t_now.timetuple()))*1000,
270
+ "timestamp": int(time.mktime(t_now.timetuple())) * 1000,
269
271
  "procesStatus": status,
270
272
  "logMessage": str(message),
271
273
  "logLevel": logging.getLevelName(log_level),
@@ -279,6 +281,3 @@ class GridAPPSD(GOSS):
279
281
  d = dict(requestType=request_type, resultFormat=response_format)
280
282
  d.update(**kwargs)
281
283
  return d
282
-
283
-
284
-
@@ -1,18 +1,8 @@
1
1
  from collections import namedtuple
2
2
 
3
3
  house_keys = [
4
- 'name',
5
- 'parent',
6
- 'coolingSetpoint',
7
- 'coolingSystem',
8
- 'floorArea',
9
- 'heatingSetpoint',
10
- 'heatingSystem',
11
- 'hvacPowerFactor',
12
- 'numberOfStories',
13
- 'thermalIntegrity',
14
- 'id',
15
- 'fdrid'
4
+ 'name', 'parent', 'coolingSetpoint', 'coolingSystem', 'floorArea', 'heatingSetpoint',
5
+ 'heatingSystem', 'hvacPowerFactor', 'numberOfStories', 'thermalIntegrity', 'id', 'fdrid'
16
6
  ]
17
7
  House = namedtuple('House', house_keys)
18
8
 
@@ -21,7 +11,9 @@ House = namedtuple('House', house_keys)
21
11
 
22
12
 
23
13
  class Houses:
14
+
24
15
  class __SingltonHouses:
16
+
25
17
  def __init__(self, gappsd: 'GridAPPSD'):
26
18
  self._gappsd = gappsd
27
19
  self._houses = {}
@@ -38,8 +30,8 @@ class Houses:
38
30
  query = """# list houses - DistHouse
39
31
  PREFIX r: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
40
32
  PREFIX c: <http://iec.ch/TC57/CIM100#>
41
- SELECT ?fdrname ?name ?parent ?coolingSetpoint ?coolingSystem ?floorArea ?heatingSetpoint ?heatingSystem ?hvacPowerFactor ?numberOfStories ?thermalIntegrity ?id ?fdrid
42
- WHERE {
33
+ SELECT ?fdrname ?name ?parent ?coolingSetpoint ?coolingSystem ?floorArea ?heatingSetpoint ?heatingSystem ?hvacPowerFactor ?numberOfStories ?thermalIntegrity ?id ?fdrid
34
+ WHERE {
43
35
  # VALUES ?fdrid {"_9CE150A8-8CC5-A0F9-B67E-BBD8C79D3095"} # R2 12.47 3
44
36
  # VALUES ?fdrid {"_4F76A5F9-271D-9EB8-5E31-AA362D86F2C3"} # 8500-node
45
37
  # VALUES ?fdrid {"_E407CBB6-8C8D-9BC9-589C-AB83FBF0826D"} # 123 PV/Triplex
@@ -54,7 +46,7 @@ WHERE {
54
46
  OPTIONAL{?h c:House.heatingSetpoint ?heatingSetpoint.}
55
47
  OPTIONAL{?h c:House.hvacPowerFactor ?hvacPowerFactor.}
56
48
  ?h c:House.coolingSystem ?coolingSystemRaw.
57
- bind(strafter(str(?coolingSystemRaw),"HouseCooling.") as ?coolingSystem)
49
+ bind(strafter(str(?coolingSystemRaw),"HouseCooling.") as ?coolingSystem)
58
50
  ?h c:House.heatingSystem ?heatingSystemRaw.
59
51
  bind(strafter(str(?heatingSystemRaw),"HouseHeating.") as ?heatingSystem)
60
52
  ?h c:House.thermalIntegrity ?thermalIntegrityRaw.
@@ -83,7 +75,6 @@ WHERE {
83
75
 
84
76
  self._houses[feeder] = houses
85
77
 
86
-
87
78
  instance = None
88
79
 
89
80
  def __init__(self, gappsd):
@@ -94,4 +85,3 @@ WHERE {
94
85
 
95
86
  def __getattr__(self, name):
96
87
  return getattr(self.instance, name)
97
-
@@ -0,0 +1,123 @@
1
+ import dataclasses
2
+ import json as _json
3
+ from typing import Any, TextIO
4
+
5
+
6
+ @dataclasses.dataclass
7
+ class JsonComplex:
8
+ real: float
9
+ imag: float
10
+
11
+
12
+ def jsonDecoderExtension(obj: Any):
13
+ rv = obj
14
+ if isinstance(obj, dict):
15
+ try:
16
+ complexInstance = JsonComplex(**obj)
17
+ rv = complex(complexInstance.real, complexInstance.imag)
18
+ except:
19
+ rv = obj
20
+ return rv
21
+
22
+
23
+ class JsonEncoderExtension(_json.JSONEncoder):
24
+
25
+ def default(self, obj: Any) -> Any:
26
+ rv = None
27
+ if isinstance(obj, complex):
28
+ jsonComplexInstance = JsonComplex(real=obj.real, imag=obj.imag)
29
+ rv = dataclasses.asdict(jsonComplexInstance)
30
+ elif dataclasses.is_dataclass(obj):
31
+ rv = dataclasses.asdict(obj)
32
+ else:
33
+ rv = super().default(obj)
34
+ return rv
35
+
36
+
37
+ def dump(data: Any,
38
+ fo: TextIO,
39
+ *,
40
+ skipkeys=False,
41
+ ensure_ascii=True,
42
+ check_circular=True,
43
+ allow_nan=True,
44
+ indent=None,
45
+ separators=None,
46
+ default=None,
47
+ sort_keys=False,
48
+ **kw):
49
+ rv = _json.dump(data,
50
+ fo,
51
+ skipkeys=skipkeys,
52
+ ensure_ascii=ensure_ascii,
53
+ check_circular=check_circular,
54
+ allow_nan=allow_nan,
55
+ cls=JsonEncoderExtension,
56
+ indent=indent,
57
+ separators=separators,
58
+ default=default,
59
+ sort_keys=sort_keys,
60
+ **kw)
61
+
62
+
63
+ def dumps(data: Any,
64
+ *,
65
+ skipkeys=False,
66
+ ensure_ascii=True,
67
+ check_circular=True,
68
+ allow_nan=True,
69
+ indent=None,
70
+ separators=None,
71
+ default=None,
72
+ sort_keys=False,
73
+ **kw) -> str:
74
+ rv = _json.dumps(data,
75
+ skipkeys=skipkeys,
76
+ ensure_ascii=ensure_ascii,
77
+ check_circular=check_circular,
78
+ allow_nan=allow_nan,
79
+ cls=JsonEncoderExtension,
80
+ indent=indent,
81
+ separators=separators,
82
+ default=default,
83
+ sort_keys=sort_keys,
84
+ **kw)
85
+ return rv
86
+
87
+
88
+ def load(fo: TextIO,
89
+ *,
90
+ cls=None,
91
+ parse_float=None,
92
+ parse_int=None,
93
+ parse_constant=None,
94
+ object_pairs_hook=None,
95
+ **kw) -> Any:
96
+ rv = _json.load(fo,
97
+ cls=cls,
98
+ object_hook=jsonDecoderExtension,
99
+ parse_float=parse_float,
100
+ parse_int=parse_int,
101
+ parse_constant=parse_constant,
102
+ object_pairs_hook=object_pairs_hook,
103
+ **kw)
104
+ return rv
105
+
106
+
107
+ def loads(data: str,
108
+ *,
109
+ cls=None,
110
+ parse_float=None,
111
+ parse_int=None,
112
+ parse_constant=None,
113
+ object_pairs_hook=None,
114
+ **kw) -> Any:
115
+ rv = _json.loads(data,
116
+ cls=cls,
117
+ object_hook=jsonDecoderExtension,
118
+ parse_float=parse_float,
119
+ parse_int=parse_int,
120
+ parse_constant=parse_constant,
121
+ object_pairs_hook=object_pairs_hook,
122
+ **kw)
123
+ return rv
@@ -35,6 +35,7 @@ class Logger:
35
35
  """
36
36
  The `Logger` class handles logging to the main gridappsd server.
37
37
  """
38
+
38
39
  def __init__(self, gaps, level=INFO):
39
40
  self._gaps = gaps
40
41
  self._level = level
@@ -71,8 +72,9 @@ class Logger:
71
72
  raise AttributeError(f"Log level must be one of {[x for x in _levelToName.values()]}")
72
73
 
73
74
  if not process_identifier:
74
- raise AttributeError(f"Must have GRIDAPPSD_APPLICATION_ID or GRIDAPPSD_SERVICE_ID or GRIDAPPSD_PROCESS_ID "
75
- "set in os environments.")
75
+ raise AttributeError(
76
+ f"Must have GRIDAPPSD_APPLICATION_ID or GRIDAPPSD_SERVICE_ID or GRIDAPPSD_PROCESS_ID "
77
+ "set in os environments.")
76
78
  status = self._gaps.get_application_status()
77
79
  sim_id = self._gaps.get_simulation_id()
78
80
 
@@ -89,13 +91,12 @@ class Logger:
89
91
  "logLevel": _levelToName[level],
90
92
  "storeToDb": True
91
93
  }
92
-
94
+
93
95
  gridappsd_log_level = os.getenv('GRIDAPPSD_LOG_LEVEL')
94
96
  if gridappsd_log_level == None:
95
97
  gridappsd_log_level = level
96
98
  else:
97
99
  gridappsd_log_level = _nameToLevel[gridappsd_log_level]
98
-
100
+
99
101
  if level >= gridappsd_log_level:
100
102
  self._gaps.send(topic, status_message)
101
-
@@ -5,9 +5,11 @@ import time
5
5
  import stomp
6
6
  from gridappsd import ApplicationController, GridAPPSD, utils, json_extension as json
7
7
 
8
+
8
9
  def main():
9
10
  loglevel = logging.INFO
10
- logging.basicConfig(stream=sys.stdout, level=loglevel,
11
+ logging.basicConfig(stream=sys.stdout,
12
+ level=loglevel,
11
13
  format="%(asctime)s - %(name)s;%(levelname)s|%(message)s",
12
14
  datefmt="%Y-%m-%d %H:%M:%S")
13
15
  logging.getLogger('stomp.py').setLevel(logging.ERROR)
@@ -43,7 +45,7 @@ def main():
43
45
  if gap is None:
44
46
  gap = GridAPPSD()
45
47
 
46
- except ConnectionRefusedError: # Python 3 specific error code
48
+ except ConnectionRefusedError: # Python 3 specific error code
47
49
  _log.debug("Retry in 10 seconds")
48
50
  gap = appreg = None
49
51
  time.sleep(10)
@@ -57,6 +59,7 @@ def main():
57
59
  break
58
60
  else:
59
61
  if appreg is None:
62
+
60
63
  def end_app():
61
64
  sys.exit(0)
62
65
 
@@ -78,5 +81,3 @@ def main():
78
81
  gap = None
79
82
 
80
83
  time.sleep(2)
81
-
82
-
@@ -56,6 +56,7 @@ class SimulationArgs(ConfigBase):
56
56
  timestep_frequency: str = "1000"
57
57
  timestep_increment: str = "1000"
58
58
  run_realtime: bool = True
59
+ pause_after_measurements: bool = False
59
60
  simulation_name: str = "ieee13nodeckt"
60
61
  power_flow_solver_method: str = "NR"
61
62
  model_creation_config: ModelCreationConfig = __default_model_creation_config__
@@ -120,12 +121,14 @@ class Simulation:
120
121
  add_onmeasurement_callback, add_oncomplete_callback or add_onstart_callback method respectively.
121
122
  """
122
123
 
123
- def __init__(self, gapps: 'GridAPPSD',
124
- run_config: Union[Dict, SimulationConfig]):
125
- assert type(
126
- gapps).__name__ == 'GridAPPSD', "Must be an instance of GridAPPSD"
127
-
128
- self._run_config = run_config
124
+ def __init__(self, gapps: 'GridAPPSD', run_config: Union[Dict, SimulationConfig]):
125
+ assert type(gapps).__name__ == 'GridAPPSD', "Must be an instance of GridAPPSD"
126
+ if isinstance(run_config, SimulationConfig):
127
+ self._run_config = run_config.asdict()
128
+ elif isinstance(run_config, dict):
129
+ self._run_config = run_config
130
+ else:
131
+ raise TypeError("run_config must be a dictionary or a SimulationConfig")
129
132
  # if isinstance(run_config, SimulationConfig):
130
133
  # self._run_config = run_config
131
134
  # else:
@@ -157,8 +160,7 @@ class Simulation:
157
160
  self._log_count = 0
158
161
  self._platform_log_count = 0
159
162
 
160
- self._num_timesteps = round(
161
- float(self._run_config["simulation_config"]["duration"]))
163
+ self._num_timesteps = round(float(self._run_config["simulation_config"]["duration"]))
162
164
 
163
165
  # self._num_timesteps = round(
164
166
  # float(self._run_config.simulation_config.duration))
@@ -171,9 +173,7 @@ class Simulation:
171
173
  def start_simulation(self, timeout=30):
172
174
  """ Start the configured simulation by calling the REQUEST_SIMULATION endpoint.
173
175
  """
174
- resp = self._gapps.get_response(t.REQUEST_SIMULATION,
175
- self._run_config,
176
- timeout=timeout)
176
+ resp = self._gapps.get_response(t.REQUEST_SIMULATION, self._run_config, timeout=timeout)
177
177
 
178
178
  if 'simulationId' not in resp:
179
179
  message = "Simulation was not able to run\n" + str(resp)
@@ -184,10 +184,8 @@ class Simulation:
184
184
 
185
185
  # Subscribe to the different components necessary to run and receive
186
186
  # simulated measurements and messages.
187
- self._gapps.subscribe(t.simulation_log_topic(self.simulation_id),
188
- self.__on_simulation_log)
189
- self._gapps.subscribe(t.simulation_output_topic(self.simulation_id),
190
- self.__onmeasurement)
187
+ self._gapps.subscribe(t.simulation_log_topic(self.simulation_id), self.__on_simulation_log)
188
+ self._gapps.subscribe(t.simulation_output_topic(self.simulation_id), self.__onmeasurement)
191
189
  self._gapps.subscribe(t.platform_log_topic(), self.__on_platformlog)
192
190
 
193
191
  for p in self.__on_start:
@@ -197,24 +195,21 @@ class Simulation:
197
195
  """ Pause simulation"""
198
196
  _log.debug("Pausing simulation")
199
197
  command = dict(command="pause")
200
- self._gapps.send(t.simulation_input_topic(self.simulation_id),
201
- json.dumps(command))
198
+ self._gapps.send(t.simulation_input_topic(self.simulation_id), json.dumps(command))
202
199
  self._running_or_paused = True
203
200
 
204
201
  def stop(self):
205
202
  """ Stop the simulation"""
206
203
  _log.debug("Stopping simulation")
207
204
  command = dict(command="stop")
208
- self._gapps.send(t.simulation_input_topic(self.simulation_id),
209
- json.dumps(command))
205
+ self._gapps.send(t.simulation_input_topic(self.simulation_id), json.dumps(command))
210
206
  self._running_or_paused = True
211
207
 
212
208
  def resume(self):
213
209
  """ Resume the simulation"""
214
210
  _log.debug("Resuming simulation")
215
211
  command = dict(command="resume")
216
- self._gapps.send(t.simulation_input_topic(self.simulation_id),
217
- json.dumps(command))
212
+ self._gapps.send(t.simulation_input_topic(self.simulation_id), json.dumps(command))
218
213
  self._running_or_paused = True
219
214
 
220
215
  def run_loop(self):
@@ -242,14 +237,12 @@ class Simulation:
242
237
 
243
238
  def resume_pause_at(self, pause_in):
244
239
  """ Resume the simulation and have it automatically pause after specified amount of seconds later.
245
-
240
+
246
241
  :param pause_in: number of seconds to run before pausing the simulation
247
242
  """
248
- _log.debug("Resuming simulation. Will pause after {} seconds".format(
249
- pause_in))
243
+ _log.debug("Resuming simulation. Will pause after {} seconds".format(pause_in))
250
244
  command = dict(command="resumePauseAt", input=dict(pauseIn=pause_in))
251
- self._gapps.send(t.simulation_input_topic(self.simulation_id),
252
- json.dumps(command))
245
+ self._gapps.send(t.simulation_input_topic(self.simulation_id), json.dumps(command))
253
246
  self._running_or_paused = True
254
247
 
255
248
  def add_onmeasurement_callback(self, callback, device_filter=()):
@@ -271,8 +264,7 @@ class Simulation:
271
264
  :param device_filter: Future filter of measurements
272
265
  :return:
273
266
  """
274
- self.__filterable_measurement_callback_set.add(
275
- (callback, device_filter))
267
+ self.__filterable_measurement_callback_set.add((callback, device_filter))
276
268
 
277
269
  def add_onstart_callback(self, callback):
278
270
  """ registers a start callback that is called when the simulation is started
@@ -361,4 +353,4 @@ if __name__ == '__main__':
361
353
 
362
354
  print(psc.asjson())
363
355
  print(sim.asjson())
364
- pprint(json.loads(sim.asjson()), indent=2)
356
+ pprint(json.loads(sim.asjson()), indent=2)
@@ -3,11 +3,10 @@ from gridappsd import topics, json_extension as json
3
3
  from typing import Optional
4
4
 
5
5
 
6
-
7
6
  class Query:
8
7
  """ Class to create and execute request to query timeseries data
9
8
  """
10
-
9
+
11
10
  def __init__(self, measurement):
12
11
  self.queryMeasurement = measurement
13
12
  self.queryFilter = []
@@ -16,11 +15,11 @@ class Query:
16
15
 
17
16
  def select(self, *keys):
18
17
  """ Defines what fields should be returned from the query.
19
-
20
- If this function is not called or is called without argument then
18
+
19
+ If this function is not called or is called without argument then
21
20
  all the fields are returned.
22
-
23
- :param keys: list of fields to be returned
21
+
22
+ :param keys: list of fields to be returned
24
23
  """
25
24
  for key in keys:
26
25
  self.selectCriteria.append(key)
@@ -28,78 +27,79 @@ class Query:
28
27
 
29
28
  def first(self, n=Optional[int]):
30
29
  """ Method to add request to return first or oldest data to the query.
31
-
30
+
32
31
  When n is specified, query will return first or oldest 'n' rows.
33
-
34
- :param n:
32
+
33
+ :param n:
35
34
  """
36
35
  if n is not None:
37
- self.first = n;
36
+ self.first = n
38
37
  return self
39
38
 
40
39
  def last(self, n=Optional[int]):
41
40
  """ Method to add request to return last or latest data to the query.
42
-
41
+
43
42
  When n is specified, query will return last or latest 'n' rows.
44
-
45
- :param n:
43
+
44
+ :param n:
46
45
  """
47
46
  if n is not None:
48
- self.last = n;
47
+ self.last = n
49
48
  return self
50
-
49
+
51
50
  def ge(self, value):
52
51
  """ Method to add 'value greater than or equal to' filter to a key.
53
-
54
- :param value:
52
+
53
+ :param value:
55
54
  """
56
55
  if self.key is None:
57
56
  raise ValueError("Key is not specified. Call where_key first.")
58
- obj = {"key":self.key,"ge":value}
57
+ obj = {"key": self.key, "ge": value}
59
58
  self.queryFilter.append(obj)
60
59
  return self
61
-
60
+
62
61
  def le(self, value):
63
62
  """ Method to add 'value less than or equal to' filter to a key.
64
-
65
- :param value:
63
+
64
+ :param value:
66
65
  """
67
66
  if self.key is None:
68
67
  raise ValueError("Key is not specified. Call where_key first.")
69
- obj = {"key":self.key,"le":value}
68
+ obj = {"key": self.key, "le": value}
70
69
  self.queryFilter.append(obj)
71
70
  return self
72
-
71
+
73
72
  def eq(self, value):
74
73
  """ Method to add 'value equal to' filter to a key.
75
-
76
- :param value:
74
+
75
+ :param value:
77
76
  """
78
77
  if self.key is None:
79
78
  raise ValueError("Key is not specified. Call where_key first.")
80
- obj = {"key":self.key,"eq":value}
79
+ obj = {"key": self.key, "eq": value}
81
80
  self.queryFilter.append(obj)
82
81
  return self
83
-
82
+
84
83
  def between(self, value1, value2):
85
84
  """ Method to add 'value between' value1 and value2 filter to a key.
86
-
85
+
87
86
  :param value1: defines 'greater than equal to' filter for key's value
88
87
  :param value2: defines 'less than equal to' filter for key's value
89
88
  """
90
89
  if self.key is None:
91
90
  raise ValueError("Key is not specified. Call where_key first.")
92
- obj = {"key":self.key,"ge":value1}
91
+ obj = {"key": self.key, "ge": value1}
93
92
  self.queryFilter.append(obj)
94
- obj = {"key":self.key,"le":value2}
93
+ obj = {"key": self.key, "le": value2}
95
94
  self.queryFilter.append(obj)
96
95
  return self
97
-
96
+
98
97
  def where_key(self, key):
99
98
  self.key = key
100
99
  return self
101
100
 
102
101
  def execute(self, gridappsd_obj):
103
- del self.key
104
- response = gridappsd_obj.get_response(topics.REQUEST_TIMESERIES_DATA, json.dumps(self.__dict__))
105
- return response
102
+ del self.key
103
+ response = gridappsd_obj.get_response(topics.REQUEST_TIMESERIES_DATA,
104
+ json.dumps(self.__dict__))
105
+ return response
@@ -73,6 +73,7 @@ REQUEST_REGISTER_APP = ".".join((PROCESS_PREFIX, "request.app.remote.register"))
73
73
  REQUEST_APP_START = ".".join((PROCESS_PREFIX, "request.app.start"))
74
74
  BASE_APPLICATION_HEARTBEAT = ".".join((BASE_TOPIC_PREFIX, "heartbeat"))
75
75
 
76
+
76
77
  def platform_log_topic():
77
78
  """ Utility method for getting the platform.log base topic
78
79
  """
@@ -175,7 +176,8 @@ def simulation_log_topic(simulation_id):
175
176
  """
176
177
  return "{}.{}".format(BASE_SIMULATION_LOG_TOPIC, simulation_id)
177
178
 
178
- def field_message_bus_topic(message_bus_id:str, app_id: str=None, agent_id: str=None):
179
+
180
+ def field_message_bus_topic(message_bus_id: str, app_id: str = None, agent_id: str = None):
179
181
  """ Utility method for getting the publish/subscribe topic for a specific message bus.
180
182
 
181
183
  :param message_bus_id:
@@ -185,7 +187,7 @@ def field_message_bus_topic(message_bus_id:str, app_id: str=None, agent_id: str=
185
187
  """
186
188
  assert message_bus_id, "message_bus_id cannot be empty"
187
189
 
188
- return f"{BASE_FIELD_TOPIC}.{message_bus_id}"
190
+ return f"{BASE_FIELD_TOPIC}.{message_bus_id}"
189
191
 
190
192
 
191
193
  def field_message_bus_app_topic(message_bus_id, app_id=None):
@@ -198,6 +200,7 @@ def field_message_bus_app_topic(message_bus_id, app_id=None):
198
200
  assert message_bus_id, "message_bus_id cannot be empty"
199
201
  return "{}.{}.{}".format(BASE_FIELD_TOPIC, message_bus_id, app_id)
200
202
 
203
+
201
204
  def field_message_bus_agent_topic(message_bus_id, agent_id=None):
202
205
  """ Utility method for getting the publish/subscribe topic for a specific message bus.
203
206
 
@@ -208,6 +211,7 @@ def field_message_bus_agent_topic(message_bus_id, agent_id=None):
208
211
  assert message_bus_id, "message_bus_id cannot be empty"
209
212
  return "{}.{}.{}".format(BASE_FIELD_TOPIC, message_bus_id, agent_id)
210
213
 
214
+
211
215
  def field_agent_request_queue(message_bus_id, agent_id):
212
216
  """ Utility method for getting the request topic for a specific distributed agent
213
217
 
@@ -218,6 +222,7 @@ def field_agent_request_queue(message_bus_id, agent_id):
218
222
  assert message_bus_id, "message_bus_id cannot be empty"
219
223
  return "{}.request.{}.{}".format(BASE_FIELD_QUEUE, message_bus_id, agent_id)
220
224
 
225
+
221
226
  def context_request_queue(message_bus_id):
222
227
  """ Utility method for getting the request topic for context manager
223
228
 
@@ -225,35 +230,40 @@ def context_request_queue(message_bus_id):
225
230
  :return:
226
231
  """
227
232
  assert message_bus_id, "message_bus_id cannot be empty"
228
-
229
- return "{}.request.{}.{}".format(BASE_FIELD_QUEUE, message_bus_id, message_bus_id+'.context_manager')
233
+
234
+ return "{}.request.{}.{}".format(BASE_FIELD_QUEUE, message_bus_id,
235
+ message_bus_id + '.context_manager')
236
+
230
237
 
231
238
  def field_output_topic(message_bus_id=None, simulation_id=None):
232
239
  """ Utility method for getting the field output topic.
233
240
  If message_bus_id is None, it returns topic used by centralized device interfaces to publish measurements.
234
- If message_bus_id is not None, it returns topic used by distributed devices interfaces to publish measurements which is then subscribed by distributed agents.
235
-
241
+ If message_bus_id is not None, it returns topic used by distributed devices interfaces to publish measurements which is then subscribed by distributed agents.
242
+
236
243
  :param message_bus_id:
237
244
  :param simulation_id
238
- :return: str: Topic to receive field measurements
245
+ :return: str: Topic to receive field measurements
239
246
  """
240
-
247
+
241
248
  if simulation_id is None:
242
249
  return "{}.{}".format(BASE_FIELD_TOPIC, "output")
243
250
  else:
244
- return "{}.{}.{}.{}".format(BASE_FIELD_TOPIC,"simulation.output",simulation_id,message_bus_id)
245
-
251
+ return "{}.{}.{}.{}".format(BASE_FIELD_TOPIC, "simulation.output", simulation_id,
252
+ message_bus_id)
253
+
254
+
246
255
  def field_input_topic(message_bus_id=None, simulation_id=None):
247
256
  """ Utility method for getting the field input topic.
248
257
  If message_bus_id is None, it returns topic used by centralized device interfaces to subscribe to control commands.
249
- If message_bus_id is not None, it returns topic used by distributed devices interfaces to subscribe to control commands.
250
-
258
+ If message_bus_id is not None, it returns topic used by distributed devices interfaces to subscribe to control commands.
259
+
251
260
  :param message_bus_id:
252
261
  :param simulation_id
253
- :return: str: Topic to receive input control commands
262
+ :return: str: Topic to receive input control commands
254
263
  """
255
-
264
+
256
265
  if simulation_id is None:
257
266
  return "{}.{}".format(BASE_FIELD_TOPIC, "input")
258
267
  else:
259
- return "{}.{}.{}.{}".format(BASE_FIELD_TOPIC,"simulation.input",simulation_id,message_bus_id)
268
+ return "{}.{}.{}.{}".format(BASE_FIELD_TOPIC, "simulation.input", simulation_id,
269
+ message_bus_id)
@@ -4,12 +4,11 @@ from typing import Optional
4
4
 
5
5
  from dateutil import parser
6
6
  import os
7
- try: # python2.7
7
+ try: # python2.7
8
8
  from urlparse import urlparse
9
9
  except ImportError:
10
10
  from urllib.parse import urlparse
11
11
 
12
-
13
12
  __GRIDAPPSD_URI__ = os.environ.get("GRIDAPPSD_URI", "localhost:61613")
14
13
 
15
14
  if not __GRIDAPPSD_URI__.startswith("tcp://"):
@@ -68,8 +67,7 @@ def get_gridappsd_address():
68
67
 
69
68
  return address, port
70
69
 
71
- return (__GRIDAPPSD_URI_PARSED__.hostname,
72
- __GRIDAPPSD_URI_PARSED__.port)
70
+ return (__GRIDAPPSD_URI_PARSED__.hostname, __GRIDAPPSD_URI_PARSED__.port)
73
71
 
74
72
 
75
73
  def get_gridappsd_application_id():
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "gridappsd-python"
3
- version = "2024.1.4a0"
3
+ version = "2024.4.0a0"
4
4
  description = "A GridAPPS-D Python Adapter"
5
5
  authors = [
6
6
  "C. Allwardt <3979063+craig8@users.noreply.github.com>",
@@ -56,7 +56,7 @@ ignore_patterns = [
56
56
  "dist/**",
57
57
  "docs/**"
58
58
  ]
59
-
59
+
60
60
  [tool.yapf]
61
61
  based_on_style = "pep8"
62
62
  spaces_before_comment = 4
@@ -1,55 +0,0 @@
1
- import dataclasses
2
- import json as _json
3
- from typing import Any, TextIO
4
-
5
-
6
- @dataclasses.dataclass
7
- class JsonComplex:
8
- real: float
9
- imag: float
10
-
11
-
12
- def jsonDecoderExtension(obj: Any):
13
- rv = obj
14
- if isinstance(obj, dict):
15
- try:
16
- complexInstance = JsonComplex(**obj)
17
- rv = complex(complexInstance.real, complexInstance.imag)
18
- except:
19
- rv = obj
20
- return rv
21
-
22
-
23
- class JsonEncoderExtension(_json.JSONEncoder):
24
- def default(self, obj: Any) -> Any:
25
- rv = None
26
- if isinstance(obj, complex):
27
- jsonComplexInstance = JsonComplex(
28
- real = obj.real,
29
- imag = obj.imag
30
- )
31
- rv = dataclasses.asdict(jsonComplexInstance)
32
- elif dataclasses.is_dataclass(obj):
33
- rv = dataclasses.asdict(obj)
34
- else:
35
- rv = super().default(obj)
36
- return rv
37
-
38
-
39
- def dump(data: Any, fo: TextIO):
40
- rv = _json.dump(data, fo, cls=JsonEncoderExtension)
41
-
42
-
43
- def dumps(data: Any) -> str:
44
- rv = _json.dumps(data, cls=JsonEncoderExtension)
45
- return rv
46
-
47
-
48
- def load(fo: TextIO) -> Any:
49
- rv = _json.load(fo, object_hook=jsonDecoderExtension)
50
- return rv
51
-
52
-
53
- def loads(data: str) -> Any:
54
- rv = _json.loads(data, object_hook=jsonDecoderExtension)
55
- return rv