gridappsd-python 2025.3.2a13__tar.gz → 2025.3.2a17__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 (46) hide show
  1. gridappsd_python-2025.3.2a17/.gitignore +123 -0
  2. gridappsd_python-2025.3.2a17/CHANGELOG.md +0 -0
  3. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/PKG-INFO +14 -22
  4. gridappsd_python-2025.3.2a17/conf/entrypoint.sh +18 -0
  5. gridappsd_python-2025.3.2a17/conf/run-gridappsd.sh +16 -0
  6. gridappsd_python-2025.3.2a17/example.env +4 -0
  7. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/__init__.py +8 -7
  8. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/__main__.py +18 -18
  9. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/app_registration.py +34 -41
  10. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/difference_builder.py +18 -15
  11. gridappsd_python-2025.3.2a17/gridappsd/field_interface/__init__.py +52 -0
  12. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/goss.py +50 -64
  13. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/gridappsd.py +27 -28
  14. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/houses.py +34 -16
  15. gridappsd_python-2025.3.2a17/gridappsd/json_extension.py +124 -0
  16. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/loghandler.py +21 -20
  17. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/register_app.py +11 -10
  18. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/simulation.py +73 -61
  19. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/timeseries.py +10 -12
  20. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/topics.py +28 -33
  21. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/utils.py +6 -4
  22. gridappsd_python-2025.3.2a17/gridappsd_python_testing.ipynb +102 -0
  23. gridappsd_python-2025.3.2a17/info/CHANGELOG.md +0 -0
  24. gridappsd_python-2025.3.2a17/info/VERSION +1 -0
  25. gridappsd_python-2025.3.2a17/poetry.toml +2 -0
  26. gridappsd_python-2025.3.2a17/pyproject.toml +37 -0
  27. gridappsd_python-2025.3.2a17/tests/conftest.py +138 -0
  28. gridappsd_python-2025.3.2a17/tests/run_gridappsd.py +32 -0
  29. gridappsd_python-2025.3.2a17/tests/simulation_fixtures/13_node_2_min_base.json +33 -0
  30. gridappsd_python-2025.3.2a17/tests/test_containers.py +101 -0
  31. gridappsd_python-2025.3.2a17/tests/test_difference_builder.py +82 -0
  32. gridappsd_python-2025.3.2a17/tests/test_docker_handler.py +188 -0
  33. gridappsd_python-2025.3.2a17/tests/test_goss.py +204 -0
  34. gridappsd_python-2025.3.2a17/tests/test_gridappsd.py +139 -0
  35. gridappsd_python-2025.3.2a17/tests/test_houses.py +12 -0
  36. gridappsd_python-2025.3.2a17/tests/test_json.py +42 -0
  37. gridappsd_python-2025.3.2a17/tests/test_logging.py +114 -0
  38. gridappsd_python-2025.3.2a17/tests/test_logging_integration.py +94 -0
  39. gridappsd_python-2025.3.2a17/tests/test_simulation.py +64 -0
  40. gridappsd_python-2025.3.2a17/tests/test_topics.py +63 -0
  41. gridappsd_python-2025.3.2a17/tests/unittest_test_template.py +32 -0
  42. gridappsd_python-2025.3.2a13/gridappsd/field_interface/__init__.py +0 -32
  43. gridappsd_python-2025.3.2a13/gridappsd/json_extension.py +0 -123
  44. gridappsd_python-2025.3.2a13/pyproject.toml +0 -71
  45. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/README.md +0 -0
  46. {gridappsd_python-2025.3.2a13 → gridappsd_python-2025.3.2a17}/gridappsd/docker_handler.py +0 -0
@@ -0,0 +1,123 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ poetry.lock
12
+ env/
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ .hypothesis/
49
+
50
+ # Translations
51
+ *.mo
52
+ *.pot
53
+
54
+ # Django stuff:
55
+ *.log
56
+ local_settings.py
57
+
58
+ # Flask stuff:
59
+ instance/
60
+ .webassets-cache
61
+
62
+ # Scrapy stuff:
63
+ .scrapy
64
+
65
+ # Sphinx documentation
66
+ docs/_build/
67
+
68
+ # PyBuilder
69
+ target/
70
+
71
+ # Jupyter Notebook
72
+ .ipynb_checkpoints
73
+
74
+ # pyenv
75
+ .python-version
76
+
77
+ # celery beat schedule file
78
+ celerybeat-schedule
79
+
80
+ # SageMath parsed files
81
+ *.sage.py
82
+
83
+ # dotenv
84
+ .env
85
+
86
+ # virtualenv
87
+ .venv
88
+ venv/
89
+ ENV/
90
+
91
+ # Spyder project settings
92
+ .spyderproject
93
+ .spyproject
94
+
95
+ # Rope project settings
96
+ .ropeproject
97
+
98
+ # mkdocs documentation
99
+ /site
100
+
101
+ # mypy
102
+ .mypy_cache/
103
+
104
+ # idea project
105
+ .idea/
106
+
107
+ # visual studio code
108
+ .vscode/
109
+
110
+ # pytest cache
111
+ .pytest_cache
112
+ /gridappsd-python.code-workspace
113
+ .qodo
114
+
115
+ # pixi
116
+ .pixi/
117
+ pixi.lock
118
+
119
+ # uv
120
+ uv.lock
121
+
122
+ # ruff
123
+ .ruff_cache/
File without changes
@@ -1,27 +1,20 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: gridappsd-python
3
- Version: 2025.3.2a13
3
+ Version: 2025.3.2a17
4
4
  Summary: A GridAPPS-D Python Adapter
5
- License: BSD-3-Clause
6
- Keywords: gridappsd,grid,activemq,powergrid,simulation,library
7
- Author: C. Allwardt
8
- Author-email: 3979063+craig8@users.noreply.github.com
9
- Requires-Python: >=3.10,<4.0
10
- Classifier: License :: OSI Approved :: BSD License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: PyYAML (>=6.0,<7.0)
17
- Requires-Dist: dateutils (>=0.6.7,<0.7.0)
18
- Requires-Dist: dotenv (>=0.9.9,<0.10.0)
19
- Requires-Dist: loguru (>=0.7.3,<0.8.0)
20
- Requires-Dist: pytz (>=2022.7,<2023.0)
21
- Requires-Dist: requests (==2.28.2)
22
- Requires-Dist: stomp-py (==6.0.0)
23
- Project-URL: Homepage, https://gridappsd.readthedocs.io
24
5
  Project-URL: Repository, https://github.com/GRIDAPPSD/gridappsd-python
6
+ Project-URL: Homepage, https://gridappsd.readthedocs.io
7
+ Author-email: "C. Allwardt" <3979063+craig8@users.noreply.github.com>, "P. Sharma" <poorva.sharma@pnnl.gov>, "A. Fisher" <andrew.fisher@pnnl.gov>
8
+ License-Expression: BSD-3-Clause
9
+ Keywords: activemq,grid,gridappsd,library,powergrid,simulation
10
+ Requires-Python: <4.0,>=3.10
11
+ Requires-Dist: dateutils>=0.6.7
12
+ Requires-Dist: loguru>=0.7
13
+ Requires-Dist: python-dotenv>=0.9
14
+ Requires-Dist: pytz>=2022.7
15
+ Requires-Dist: pyyaml>=6.0
16
+ Requires-Dist: requests>=2.28
17
+ Requires-Dist: stomp-py>=6.0.0
25
18
  Description-Content-Type: text/markdown
26
19
 
27
20
  [![Run All Pytests](https://github.com/GRIDAPPSD/gridappsd-python/actions/workflows/run-pytest.yml/badge.svg)](https://github.com/GRIDAPPSD/gridappsd-python/actions/workflows/run-pytest.yml)
@@ -351,4 +344,3 @@ def test_gridappsd_status(gridappsd_client):
351
344
  assert gappsd.get_service_status() == ProcessStatusEnum.COMPLETE.value
352
345
  assert gappsd.get_application_status() == ProcessStatusEnum.COMPLETE.value
353
346
  ```
354
-
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+
3
+ set -eux
4
+
5
+ # Install application python requirements
6
+ for reqfile in `ls /gridappsd/services/*/requirements.txt 2>/dev/null`; do
7
+ echo "[Entrypoint] Installing requirements $reqfile"
8
+ sudo pip install -q --disable-pip-version-check -r $reqfile
9
+ done
10
+ for reqfile in `ls /gridappsd/applications/*/requirements.txt 2>/dev/null`; do
11
+ echo "[Entrypoint] Installing requirements $reqfile"
12
+ sudo pip install -q --disable-pip-version-check -r $reqfile
13
+ done
14
+
15
+ echo "[Entrypoint] Starting the platform"
16
+ /startup/conf/run-gridappsd.sh
17
+
18
+ tail -f /dev/null
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+
3
+ set -eux
4
+
5
+ # Setup the path for running the gridappsd framework
6
+ export PATH=/gridappsd/services/fncsgossbridge/service:$PATH
7
+ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib64
8
+
9
+ cd /gridappsd
10
+
11
+ # clean up log files
12
+ if [ -d /gridappsd/log ]; then
13
+ /bin/rm -rf /gridappsd/log/* 2 > /dev/null
14
+ fi
15
+ cd /gridappsd
16
+ java -Dgosh.args=--nointeractive -agentlib:jdwp=transport=dt_socket,server=y,suspend=n -jar lib/run.bnd.jar
@@ -0,0 +1,4 @@
1
+ GRIDAPPSD_USER=
2
+ GRIDAPPSD_PASSWORD=
3
+ GRIDAPPSD_ADDRESS = tcp://127.0.0.1:61613
4
+ DATABASE = powergridmodel
@@ -37,20 +37,21 @@
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
- """ GridAPPSD Python Connection Library
40
+ """GridAPPSD Python Connection Library
41
41
 
42
42
  The :mod:`gridappsd` contains a connection class :class:`gridappsd.GridAPPSD` for connecting with the
43
43
  main GridAPPSD executable.
44
44
 
45
45
  """
46
+
46
47
  import pathlib
47
48
  import typing
48
49
 
49
50
  StrPath = typing.Union[str, pathlib.Path]
50
51
 
51
- from gridappsd.goss import GOSS
52
- from gridappsd.utils import ProcessStatusEnum
53
- from gridappsd.gridappsd import GridAPPSD
54
- from gridappsd.difference_builder import DifferenceBuilder
55
- from gridappsd.app_registration import ApplicationController
56
- import gridappsd.json_extension as json
52
+ from gridappsd.goss import GOSS as GOSS
53
+ from gridappsd.utils import ProcessStatusEnum as ProcessStatusEnum
54
+ from gridappsd.gridappsd import GridAPPSD as GridAPPSD
55
+ from gridappsd.difference_builder import DifferenceBuilder as DifferenceBuilder
56
+ from gridappsd.app_registration import ApplicationController as ApplicationController
57
+ from gridappsd import json_extension as json
@@ -48,28 +48,30 @@ from dotenv import load_dotenv
48
48
  from pathlib import Path
49
49
 
50
50
  from gridappsd import GridAPPSD
51
+ from gridappsd.simulation import Simulation
51
52
 
52
53
  assert sys.version_info >= (3, 10), "Minimum version is python 3.10"
53
54
 
54
- logging.basicConfig(stream=sys.stdout,
55
- level=logging.INFO,
56
- format="'%(asctime)s: %(name)-20s - %(levelname)-6s - %(message)s")
55
+ logging.basicConfig(
56
+ stream=sys.stdout, level=logging.INFO, format="'%(asctime)s: %(name)-20s - %(levelname)-6s - %(message)s"
57
+ )
57
58
 
58
- logging.getLogger('stomp.py').setLevel(logging.WARNING)
59
+ logging.getLogger("stomp.py").setLevel(logging.WARNING)
59
60
  _log = logging.getLogger("gridappsd.__main__")
60
61
 
61
- if __name__ == '__main__':
62
-
62
+ if __name__ == "__main__":
63
63
  parser = ArgumentParser()
64
64
 
65
65
  group = parser.add_mutually_exclusive_group(required=True)
66
- group.add_argument("-s",
67
- "--run-simulation",
68
- type=argparse.FileType('r'),
69
- help="Start running a simulation from a passed simulation file.")
70
- group.add_argument("--env", required=False, type=str,
71
- default=".env",
72
- help="Load environment variables from a .env file.")
66
+ group.add_argument(
67
+ "-s",
68
+ "--run-simulation",
69
+ type=argparse.FileType("r"),
70
+ help="Start running a simulation from a passed simulation file.",
71
+ )
72
+ group.add_argument(
73
+ "--env", required=False, type=str, default=".env", help="Load environment variables from a .env file."
74
+ )
73
75
  opts = parser.parse_args()
74
76
 
75
77
  if opts.run_simulation:
@@ -91,8 +93,6 @@ if __name__ == '__main__':
91
93
  gappsd = GridAPPSD()
92
94
  run_args = yaml.safe_load(opts.run_simulation)
93
95
 
94
- # if wanting to use the above next_timestep function use this
95
- # instead of the one below.
96
- # simulation = gappsd.run_simulation(run_args, next_timestep)
97
- simulation = gappsd.run_simulation(run_args)
98
- simulation.simulation_main_loop()
96
+ # Create and start the simulation
97
+ simulation = Simulation(gappsd, run_args)
98
+ simulation.start_simulation()
@@ -1,17 +1,12 @@
1
- #import json
1
+ # import json
2
2
  import logging
3
3
  import os
4
- try:
5
- from queue import Queue
6
- except ImportError:
7
- from Queue import Queue
4
+ from queue import Queue
8
5
  import time
9
- import select
10
6
  import subprocess
11
7
  import threading
12
8
  import shlex
13
9
  import sys
14
- import os
15
10
 
16
11
  from .gridappsd import GridAPPSD
17
12
  from .topics import REQUEST_REGISTER_APP
@@ -21,12 +16,11 @@ _log = logging.getLogger(__name__)
21
16
 
22
17
  # determine OS type
23
18
  posix = False
24
- if os.name == 'posix':
19
+ if os.name == "posix":
25
20
  posix = True
26
21
 
27
22
 
28
23
  class Job(threading.Thread):
29
-
30
24
  def __init__(self, args, out=sys.stdout, err=sys.stderr):
31
25
  threading.Thread.__init__(self)
32
26
  _log.debug("Creating job")
@@ -41,31 +35,30 @@ class Job(threading.Thread):
41
35
  def run(self):
42
36
  try:
43
37
  self.running = True
44
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'RUNNING'
38
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "RUNNING"
45
39
 
46
40
  p = subprocess.Popen(args=self._args, shell=False, stdout=self._out, stderr=self._err)
47
41
 
48
42
  # Loop while process is executing
49
43
  while p.poll() is None and self.running:
50
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'RUNNING'
44
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "RUNNING"
51
45
  time.sleep(1)
52
46
 
53
47
  except Exception as e:
54
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'ERROR'
48
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "ERROR"
55
49
  _log.error(repr(e))
56
50
  else:
57
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPED'
51
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPED"
58
52
 
59
53
 
60
54
  class ApplicationController(object):
61
-
62
55
  def __init__(self, config, gridappsd=None, heatbeat_period=10):
63
56
  if not isinstance(config, dict):
64
57
  raise ValueError("Config should be dictionary")
65
58
  if not isinstance(gridappsd, GridAPPSD):
66
59
  raise ValueError("Invalid gridappsd instance passed.")
67
60
 
68
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPED'
61
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPED"
69
62
  self._configDict = config.copy()
70
63
  self._validate_config()
71
64
  self._gapd = gridappsd
@@ -87,15 +80,16 @@ class ApplicationController(object):
87
80
  self._end_callback = None
88
81
  self._print_queue = Queue()
89
82
  self._heartbeat_thread = None
90
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPED'
83
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPED"
91
84
 
92
- if "type" not in self._configDict or self._configDict['type'] != 'REMOTE':
93
- _log.warning('Setting type to REMOTE you can remove this error by putting '
94
- '"type": "REMOTE" in the app config file.')
95
- self._configDict['type'] = 'REMOTE'
85
+ if "type" not in self._configDict or self._configDict["type"] != "REMOTE":
86
+ _log.warning(
87
+ 'Setting type to REMOTE you can remove this error by putting "type": "REMOTE" in the app config file.'
88
+ )
89
+ self._configDict["type"] = "REMOTE"
96
90
 
97
91
  def _validate_config(self):
98
- required = ['id', 'execution_path']
92
+ required = ["id", "execution_path"]
99
93
  missing = [x for x in required if x not in self._configDict]
100
94
 
101
95
  if missing:
@@ -114,30 +108,30 @@ class ApplicationController(object):
114
108
  self._gapd.get_logger().debug("Started App Registration")
115
109
 
116
110
  response = self._gapd.get_response(REQUEST_REGISTER_APP, self._configDict, 60)
117
- if 'message' in response:
111
+ if "message" in response:
118
112
  _log.error("An error regisering the application occured")
119
- _log.error(response.get('message'))
120
- raise ValueError(response.get('message'))
121
- self._application_id = response.get('applicationId')
122
- self._heartbeat_topic = response.get('heartbeatTopic')
123
- self._heartbeat_period = response.get('heartbeatPeriod', 10)
124
- self._start_control_topic = response.get('startControlTopic')
125
- self._stop_control_topic = response.get('stopControlTopic')
113
+ _log.error(response.get("message"))
114
+ raise ValueError(response.get("message"))
115
+ self._application_id = response.get("applicationId")
116
+ self._heartbeat_topic = response.get("heartbeatTopic")
117
+ self._heartbeat_period = response.get("heartbeatPeriod", 10)
118
+ self._start_control_topic = response.get("startControlTopic")
119
+ self._stop_control_topic = response.get("stopControlTopic")
126
120
 
127
121
  os.environ["GRIDAPPSD_APPLICATION_ID"] = self._application_id
128
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPED'
122
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPED"
129
123
 
130
124
  self._gapd.subscribe(self._stop_control_topic, self.__handle_stop)
131
125
  self._gapd.subscribe(self._start_control_topic, self.__handle_start)
132
126
  self._end_callback = end_callback
133
127
 
134
128
  # TODO assuming good response start the heartbeat
135
- self._heartbeat_thread = threading.Thread(target=self.__start_heartbeat,
136
- args=[self.__heartbeat_error])
129
+ self._heartbeat_thread = threading.Thread(target=self.__start_heartbeat, args=[self.__heartbeat_error])
137
130
  self._heartbeat_thread.daemon = True
138
131
  self._heartbeat_thread.start()
139
- self._gapd.get_logger().debug("Heartbeat registereed for application {}".format(
140
- utils.get_gridappsd_application_id()))
132
+ self._gapd.get_logger().debug(
133
+ "Heartbeat registereed for application {}".format(utils.get_gridappsd_application_id())
134
+ )
141
135
 
142
136
  def __heartbeat_error(self):
143
137
  self._heartbeat_thread = None
@@ -151,8 +145,7 @@ class ApplicationController(object):
151
145
  # print("Seanding heartbeat {} {}".format(self._heartbeat_topic, self._application_id))
152
146
  # print("Heartbeat period: {}".format(self._heartbeat_period))
153
147
  self._gapd.send(self._heartbeat_topic, self._application_id)
154
- time.sleep(self._heartbeat_period -
155
- ((time.time() - starttime) % self._heartbeat_period))
148
+ time.sleep(self._heartbeat_period - ((time.time() - starttime) % self._heartbeat_period))
156
149
  except:
157
150
  error_callback()
158
151
 
@@ -167,15 +160,15 @@ class ApplicationController(object):
167
160
  obj = json.loads(message)
168
161
  else:
169
162
  obj = message
170
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STARTING'
163
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STARTING"
171
164
  self._gapd.get_logger().debug("Handling Start: {}\ndict:\n{}".format(headers, obj))
172
165
 
173
- if 'command' not in obj:
166
+ if "command" not in obj:
174
167
  # Send log to gridappsd
175
168
  _log.error("Invalid message sent on start app.")
176
169
  else:
177
170
  _log.debug("CWD IS: {}".format(os.getcwd()))
178
- args = shlex.split(obj['command'])
171
+ args = shlex.split(obj["command"])
179
172
  job = Job(args)
180
173
  job.daemon = True
181
174
  job.start()
@@ -183,12 +176,12 @@ class ApplicationController(object):
183
176
 
184
177
  def __handle_stop(self, headers, message):
185
178
  print("Handling Stop: {} {}".format(headers, message))
186
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPING'
179
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPING"
187
180
  if self._thread:
188
181
  self._thread.join()
189
182
  if self._end_callback is not None:
190
183
  self._end_callback()
191
- os.environ['GRIDAPPSD_APPLICATION_STATUS'] = 'STOPPED'
184
+ os.environ["GRIDAPPSD_APPLICATION_STATUS"] = "STOPPED"
192
185
 
193
186
  def shutdown(self):
194
187
  self._shutting_down = True
@@ -44,43 +44,46 @@ import time
44
44
 
45
45
 
46
46
  class DifferenceBuilder(object):
47
- """ Automates the building of forward and reverse cim differences
48
-
49
- """
47
+ """Automates the building of forward and reverse cim differences"""
50
48
 
51
49
  def __init__(self, simulation_id: str | int | None = None):
52
-
53
50
  self._simulation_id = simulation_id
54
51
  self._forward: list[dict] = []
55
52
  self._reverse: list[dict] = []
56
53
 
57
54
  def add_difference(self, object_id, attribute, forward_value, reverse_value):
58
- """ Add forward and reverse unit for an object attribute.
55
+ """Add forward and reverse unit for an object attribute.
59
56
 
60
- All of the parameters must be serializable for sending the GOSS message bus.
61
- """
57
+ All of the parameters must be serializable for sending the GOSS message bus.
58
+ """
62
59
  forward = dict(object=object_id, attribute=attribute, value=forward_value)
63
60
  reverse = dict(object=object_id, attribute=attribute, value=reverse_value)
64
61
  self._forward.append(forward)
65
62
  self._reverse.append(reverse)
66
63
 
67
64
  def clear(self):
68
- """ Clear the forward and reverse differences """
65
+ """Clear the forward and reverse differences"""
69
66
  self._forward = []
70
67
  self._reverse = []
71
68
 
72
69
  def get_message(self, epoch=None):
73
- """ Get the message to send to the GOSS message bus
70
+ """Get the message to send to the GOSS message bus
74
71
 
75
72
  :param epoch: The epoch time to use for the message timestamp. If None, the current time (GMT) is used.
76
73
  """
77
74
  if epoch is None:
78
75
  epoch = calendar.timegm(time.gmtime())
79
- msg = dict(command="update",
80
- input=dict(message=dict(timestamp=epoch,
81
- difference_mrid=str(uuid4()),
82
- reverse_differences=self._reverse,
83
- forward_differences=self._forward)))
76
+ msg = dict(
77
+ command="update",
78
+ input=dict(
79
+ message=dict(
80
+ timestamp=epoch,
81
+ difference_mrid=str(uuid4()),
82
+ reverse_differences=self._reverse,
83
+ forward_differences=self._forward,
84
+ )
85
+ ),
86
+ )
84
87
  if self._simulation_id is not None:
85
- msg['input']['simulation_id'] = self._simulation_id
88
+ msg["input"]["simulation_id"] = self._simulation_id
86
89
  return msg.copy()
@@ -0,0 +1,52 @@
1
+ import logging
2
+ import sys
3
+ import warnings
4
+
5
+ _log = logging.getLogger(__name__)
6
+
7
+ # These will be populated if gridappsd-field-bus is installed
8
+ context = None
9
+ context_managers = None
10
+ agents = None
11
+ field_proxy_forwarder = None
12
+ gridappsd_field_bus = None
13
+ interfaces = None
14
+
15
+ try:
16
+ import gridappsd_field_bus.field_interface.context as _context
17
+ import gridappsd_field_bus.field_interface.context_managers as _context_managers
18
+ import gridappsd_field_bus.field_interface as _field_interface
19
+ import gridappsd_field_bus.field_interface.agents as _agents
20
+ import gridappsd_field_bus.field_interface.gridappsd_field_bus as _gridappsd_field_bus
21
+ import gridappsd_field_bus.field_interface.interfaces as _interfaces
22
+
23
+ # Expose as module attributes for `from gridappsd.field_interface import X` syntax
24
+ context = _context
25
+ context_managers = _context_managers
26
+ agents = _agents
27
+ gridappsd_field_bus = _gridappsd_field_bus
28
+ interfaces = _interfaces
29
+
30
+ # Also register in sys.modules for backwards compatibility
31
+ sys.modules["gridappsd.field_interface.interfaces"] = _interfaces
32
+ sys.modules["gridappsd.field_interface.context"] = _context
33
+ sys.modules["gridappsd.field_interface.context_managers"] = _context_managers
34
+ sys.modules["gridappsd.field_interface.agents"] = _agents
35
+ sys.modules["gridappsd.field_interface.gridappsd_field_bus"] = _gridappsd_field_bus
36
+
37
+ # field_proxy_forwarder has optional dependencies that may not be available
38
+ try:
39
+ import gridappsd_field_bus.field_interface.field_proxy_forwarder as _field_proxy_forwarder
40
+
41
+ field_proxy_forwarder = _field_proxy_forwarder
42
+ sys.modules["gridappsd.field_interface.field_proxy_forwarder"] = _field_proxy_forwarder
43
+ except ImportError:
44
+ _log.debug("field_proxy_forwarder not available (missing optional dependencies)")
45
+
46
+ warnings.warn(
47
+ message="gridappsd.field_interface is deprecated and will be removed in a future release. "
48
+ "Use gridappsd_field_bus.field_interface instead.",
49
+ category=DeprecationWarning,
50
+ )
51
+ except ImportError as e:
52
+ _log.error(f"Could not import field_interface: {e}. Install gridappsd-field-bus to get those functions.")