db-process 0.1.0__tar.gz → 0.3.1__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 (30) hide show
  1. {db_process-0.1.0 → db_process-0.3.1}/LICENSE +2 -2
  2. db_process-0.3.1/PKG-INFO +215 -0
  3. db_process-0.3.1/README.md +199 -0
  4. {db_process-0.1.0 → db_process-0.3.1}/db_process/__init__.py +89 -83
  5. db_process-0.3.1/db_process/__main__.py +6 -0
  6. db_process-0.3.1/db_process/cli.py +92 -0
  7. {db_process-0.1.0 → db_process-0.3.1}/db_process/commands.py +449 -449
  8. {db_process-0.1.0 → db_process-0.3.1}/db_process/executable.py +76 -76
  9. {db_process-0.1.0 → db_process-0.3.1}/db_process/runner.py +330 -298
  10. db_process-0.3.1/db_process.egg-info/PKG-INFO +215 -0
  11. {db_process-0.1.0 → db_process-0.3.1}/db_process.egg-info/SOURCES.txt +11 -1
  12. db_process-0.3.1/db_process.egg-info/entry_points.txt +2 -0
  13. db_process-0.3.1/pyproject.toml +41 -0
  14. db_process-0.3.1/tests/test_cli.py +108 -0
  15. db_process-0.3.1/tests/test_cli_output.py +174 -0
  16. {db_process-0.1.0 → db_process-0.3.1}/tests/test_commands.py +316 -316
  17. db_process-0.3.1/tests/test_commands_extra.py +169 -0
  18. {db_process-0.1.0 → db_process-0.3.1}/tests/test_executable.py +47 -47
  19. db_process-0.3.1/tests/test_executable_defaults.py +92 -0
  20. db_process-0.3.1/tests/test_runner.py +441 -0
  21. db_process-0.3.1/tests/test_runner_subprocess.py +84 -0
  22. db_process-0.3.1/tests/test_status.py +59 -0
  23. db_process-0.1.0/PKG-INFO +0 -18
  24. db_process-0.1.0/README.md +0 -2
  25. db_process-0.1.0/db_process.egg-info/PKG-INFO +0 -18
  26. db_process-0.1.0/pyproject.toml +0 -29
  27. {db_process-0.1.0 → db_process-0.3.1}/db_process.egg-info/dependency_links.txt +0 -0
  28. {db_process-0.1.0 → db_process-0.3.1}/db_process.egg-info/requires.txt +0 -0
  29. {db_process-0.1.0 → db_process-0.3.1}/db_process.egg-info/top_level.txt +0 -0
  30. {db_process-0.1.0 → db_process-0.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Tokarzewski
3
+ Copyright (c) 2026 DesignBuilder Software
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: db-process
3
+ Version: 0.3.1
4
+ Summary: Python interface to DesignBuilder command line processing.
5
+ Author: DesignBuilder Software
6
+ Project-URL: Homepage, https://github.com/DesignBuilderSoftware/db-process
7
+ Project-URL: Bug Tracker, https://github.com/DesignBuilderSoftware/db-process/issues
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: psutil>=5.9.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest; extra == "dev"
14
+ Requires-Dist: ruff; extra == "dev"
15
+ Dynamic: license-file
16
+
17
+ # db-process
18
+
19
+ Python interface to DesignBuilder's command-line driver — typed `/process=` commands, subprocess execution, and process discovery.
20
+
21
+ DesignBuilder is a Windows GUI app, but it exposes a `/process=` argument that runs a sequence of commands non-interactively and exits. `db-process` wraps that surface in real Python: dataclass commands instead of stringly-typed flags, a `ProcessChain` builder, blocking and non-blocking runners, and helpers to find/kill the process.
22
+
23
+ > For diagnostic-log parsing see [db-diag](https://github.com/DesignBuilderSoftware/db-diag).
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install db-process
29
+ # or, from the cloned repo:
30
+ pip install -e .[dev]
31
+ ```
32
+
33
+ Requires Python 3.10+ and `psutil`. DesignBuilder itself only runs on Windows;
34
+ on other platforms, the package imports fine but `run()` and the process
35
+ helpers will raise `FileNotFoundError`.
36
+
37
+ ## Quick start
38
+
39
+ ### Python — run a model with a chain of commands
40
+
41
+ ```python
42
+ from db_process import ProcessChain, Screen, run
43
+
44
+ chain = (
45
+ ProcessChain()
46
+ .use_sim_manager()
47
+ .switch_screen(Screen.SIMULATION)
48
+ .sim_start_date(1, 1)
49
+ .sim_end_date(31, 12)
50
+ .change_attribute("OccupancyValue", 0.5)
51
+ .run()
52
+ )
53
+
54
+ result = run("models/office.dsb", chain, timeout=600)
55
+ print(result.success, result.duration_seconds)
56
+ ```
57
+
58
+ ### Python — convenience factories for common runs
59
+
60
+ ```python
61
+ from db_process import eplus_simulation, run
62
+
63
+ # A pre-built ProcessChain for "open model, run EnergyPlus, close".
64
+ result = run("models/office.dsb", eplus_simulation())
65
+ ```
66
+
67
+ Other factories: `sbem_calculation`, `export_xml`, `heating_and_cooling_design`,
68
+ `daylighting`, `cfd_simulation`.
69
+
70
+ ### Python — non-blocking with idle-detection kill
71
+
72
+ ```python
73
+ from db_process import run_async
74
+
75
+ handle = run_async("models/office.dsb")
76
+ # Block until DesignBuilder finishes its work and CPU drops, then kill it.
77
+ handle.kill_when_idle(idle_threshold=10, cpu_threshold=0.1)
78
+ ```
79
+
80
+ `kill_when_idle` is the workaround for runs where DesignBuilder finishes the
81
+ calculation but doesn't exit — common with the GUI Sim Manager path.
82
+
83
+ ### CLI
84
+
85
+ ```bash
86
+ db-process status # is DesignBuilder running? show pid + exe path
87
+ db-process open # launch DesignBuilder
88
+ db-process open path/to.dsb # launch with a model
89
+ db-process close # kill any running DesignBuilder
90
+ db-process restart [model] # close + open (with a 1.5 s settle)
91
+ ```
92
+
93
+ `db-process` is the entry point installed by the package; you can also run it as `python -m db_process`.
94
+
95
+ ## Public API
96
+
97
+ ```python
98
+ from db_process import (
99
+ # Executable discovery
100
+ find_designbuilder, # () -> Path
101
+
102
+ # Process status
103
+ is_running, find_process, status, # status() -> ProcessStatus
104
+ kill_process, kill_when_idle,
105
+ ProcessStatus, # is_running, pid, exe_path
106
+
107
+ # Run
108
+ run, run_async, RunHandle, RunResult,
109
+
110
+ # Command model
111
+ ProcessChain, Screen,
112
+ SwitchScreen, RunCalculation, TabChange,
113
+ SimStartDate, SimEndDate, ChangeAttributeValue,
114
+ ExternalCommand, ImportModelData, ImportLibraryData,
115
+ ExportAsXML, UseSimManager, NoClose,
116
+
117
+ # Convenience factories
118
+ eplus_simulation, sbem_calculation, export_xml,
119
+ heating_and_cooling_design, daylighting, cfd_simulation,
120
+ )
121
+ ```
122
+
123
+ ### Executable discovery
124
+
125
+ `find_designbuilder(exe_path=None)` resolves DesignBuilder.exe in this order:
126
+
127
+ 1. The explicit `exe_path` argument, if given.
128
+ 2. `DESIGNBUILDER_EXE` environment variable.
129
+ 3. `C:\Program Files (x86)\DesignBuilder\DesignBuilder.exe`,
130
+ then `C:\Program Files\DesignBuilder\DesignBuilder.exe`.
131
+ 4. The system `PATH` (`shutil.which("DesignBuilder")`).
132
+
133
+ Raises `FileNotFoundError` with a hint to set `DESIGNBUILDER_EXE` if none match.
134
+
135
+ ### Process status
136
+
137
+ `status()` is a one-shot snapshot:
138
+
139
+ ```python
140
+ from db_process import status
141
+
142
+ s = status()
143
+ # ProcessStatus(is_running=True, pid=12345, exe_path='C:\\...\\DesignBuilder.exe')
144
+ if not s.is_running and s.exe_path:
145
+ # Discovered but not running — safe to launch.
146
+ ...
147
+ ```
148
+
149
+ `exe_path` is populated regardless of whether the process is running, so
150
+ callers can decide whether to launch without a second `find_designbuilder`
151
+ call.
152
+
153
+ ### `ProcessChain` — building a `/process=` argument
154
+
155
+ `ProcessChain` is a fluent builder for the `/process=` command sequence
156
+ documented in DesignBuilder Help. Example output of `chain.to_string()`:
157
+
158
+ ```
159
+ /process=UseSimManager, miGSS, SimStartDate 1 1, SimEndDate 31 12,
160
+ ChangeAttributeValue OccupancyValue 0.5, miTUpdate
161
+ ```
162
+
163
+ Every command is also exposed as a plain dataclass, so you can construct
164
+ chains without the builder when that's clearer.
165
+
166
+ ## Compatibility
167
+
168
+ - **DesignBuilder**: any version that supports the `/process=` argument.
169
+ Tested against the v25.1 / v26.1 lines.
170
+ - **Python**: 3.10+.
171
+ - **OS**: Windows for live runs. The command model is pure Python and imports on any platform — useful for unit tests on Linux CI.
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ pip install -e .[dev]
177
+ pytest
178
+ ruff check .
179
+ ```
180
+
181
+ Tests are split by area:
182
+
183
+ | File | Covers |
184
+ | --- | --- |
185
+ | `test_commands.py` | `ProcessChain` building + every command's `to_string()` |
186
+ | `test_executable.py` | `find_designbuilder` resolution order |
187
+ | `test_status.py` | `status` / `ProcessStatus` |
188
+ | `test_cli.py` | CLI argparse wiring (mocked subprocess) |
189
+
190
+ ## Releasing
191
+
192
+ Every version is a tag. `pyproject.toml`'s `version` and the git tag must never
193
+ drift apart — consumers pin to `@vX.Y.Z`, and an untagged bump leaves them with
194
+ nothing to pin to but a commit SHA.
195
+
196
+ To cut a release, bump `version` in `pyproject.toml` on `main`:
197
+
198
+ ```toml
199
+ [project]
200
+ version = "0.3.0"
201
+ ```
202
+
203
+ The `Release` workflow then tags that commit `v0.3.0` and opens a matching
204
+ GitHub Release with generated notes. It only fires on changes to
205
+ `pyproject.toml`, and skips silently if the tag already exists, so re-runs are
206
+ safe.
207
+
208
+ To tag manually instead — or to backfill a version that predates the workflow:
209
+
210
+ ```bash
211
+ git tag -a v0.3.0 -m "v0.3.0" && git push origin v0.3.0
212
+ ```
213
+
214
+ Bump the version in the same pull request as the change it describes, not as a
215
+ separate follow-up — that's how the two fell out of step before.
@@ -0,0 +1,199 @@
1
+ # db-process
2
+
3
+ Python interface to DesignBuilder's command-line driver — typed `/process=` commands, subprocess execution, and process discovery.
4
+
5
+ DesignBuilder is a Windows GUI app, but it exposes a `/process=` argument that runs a sequence of commands non-interactively and exits. `db-process` wraps that surface in real Python: dataclass commands instead of stringly-typed flags, a `ProcessChain` builder, blocking and non-blocking runners, and helpers to find/kill the process.
6
+
7
+ > For diagnostic-log parsing see [db-diag](https://github.com/DesignBuilderSoftware/db-diag).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install db-process
13
+ # or, from the cloned repo:
14
+ pip install -e .[dev]
15
+ ```
16
+
17
+ Requires Python 3.10+ and `psutil`. DesignBuilder itself only runs on Windows;
18
+ on other platforms, the package imports fine but `run()` and the process
19
+ helpers will raise `FileNotFoundError`.
20
+
21
+ ## Quick start
22
+
23
+ ### Python — run a model with a chain of commands
24
+
25
+ ```python
26
+ from db_process import ProcessChain, Screen, run
27
+
28
+ chain = (
29
+ ProcessChain()
30
+ .use_sim_manager()
31
+ .switch_screen(Screen.SIMULATION)
32
+ .sim_start_date(1, 1)
33
+ .sim_end_date(31, 12)
34
+ .change_attribute("OccupancyValue", 0.5)
35
+ .run()
36
+ )
37
+
38
+ result = run("models/office.dsb", chain, timeout=600)
39
+ print(result.success, result.duration_seconds)
40
+ ```
41
+
42
+ ### Python — convenience factories for common runs
43
+
44
+ ```python
45
+ from db_process import eplus_simulation, run
46
+
47
+ # A pre-built ProcessChain for "open model, run EnergyPlus, close".
48
+ result = run("models/office.dsb", eplus_simulation())
49
+ ```
50
+
51
+ Other factories: `sbem_calculation`, `export_xml`, `heating_and_cooling_design`,
52
+ `daylighting`, `cfd_simulation`.
53
+
54
+ ### Python — non-blocking with idle-detection kill
55
+
56
+ ```python
57
+ from db_process import run_async
58
+
59
+ handle = run_async("models/office.dsb")
60
+ # Block until DesignBuilder finishes its work and CPU drops, then kill it.
61
+ handle.kill_when_idle(idle_threshold=10, cpu_threshold=0.1)
62
+ ```
63
+
64
+ `kill_when_idle` is the workaround for runs where DesignBuilder finishes the
65
+ calculation but doesn't exit — common with the GUI Sim Manager path.
66
+
67
+ ### CLI
68
+
69
+ ```bash
70
+ db-process status # is DesignBuilder running? show pid + exe path
71
+ db-process open # launch DesignBuilder
72
+ db-process open path/to.dsb # launch with a model
73
+ db-process close # kill any running DesignBuilder
74
+ db-process restart [model] # close + open (with a 1.5 s settle)
75
+ ```
76
+
77
+ `db-process` is the entry point installed by the package; you can also run it as `python -m db_process`.
78
+
79
+ ## Public API
80
+
81
+ ```python
82
+ from db_process import (
83
+ # Executable discovery
84
+ find_designbuilder, # () -> Path
85
+
86
+ # Process status
87
+ is_running, find_process, status, # status() -> ProcessStatus
88
+ kill_process, kill_when_idle,
89
+ ProcessStatus, # is_running, pid, exe_path
90
+
91
+ # Run
92
+ run, run_async, RunHandle, RunResult,
93
+
94
+ # Command model
95
+ ProcessChain, Screen,
96
+ SwitchScreen, RunCalculation, TabChange,
97
+ SimStartDate, SimEndDate, ChangeAttributeValue,
98
+ ExternalCommand, ImportModelData, ImportLibraryData,
99
+ ExportAsXML, UseSimManager, NoClose,
100
+
101
+ # Convenience factories
102
+ eplus_simulation, sbem_calculation, export_xml,
103
+ heating_and_cooling_design, daylighting, cfd_simulation,
104
+ )
105
+ ```
106
+
107
+ ### Executable discovery
108
+
109
+ `find_designbuilder(exe_path=None)` resolves DesignBuilder.exe in this order:
110
+
111
+ 1. The explicit `exe_path` argument, if given.
112
+ 2. `DESIGNBUILDER_EXE` environment variable.
113
+ 3. `C:\Program Files (x86)\DesignBuilder\DesignBuilder.exe`,
114
+ then `C:\Program Files\DesignBuilder\DesignBuilder.exe`.
115
+ 4. The system `PATH` (`shutil.which("DesignBuilder")`).
116
+
117
+ Raises `FileNotFoundError` with a hint to set `DESIGNBUILDER_EXE` if none match.
118
+
119
+ ### Process status
120
+
121
+ `status()` is a one-shot snapshot:
122
+
123
+ ```python
124
+ from db_process import status
125
+
126
+ s = status()
127
+ # ProcessStatus(is_running=True, pid=12345, exe_path='C:\\...\\DesignBuilder.exe')
128
+ if not s.is_running and s.exe_path:
129
+ # Discovered but not running — safe to launch.
130
+ ...
131
+ ```
132
+
133
+ `exe_path` is populated regardless of whether the process is running, so
134
+ callers can decide whether to launch without a second `find_designbuilder`
135
+ call.
136
+
137
+ ### `ProcessChain` — building a `/process=` argument
138
+
139
+ `ProcessChain` is a fluent builder for the `/process=` command sequence
140
+ documented in DesignBuilder Help. Example output of `chain.to_string()`:
141
+
142
+ ```
143
+ /process=UseSimManager, miGSS, SimStartDate 1 1, SimEndDate 31 12,
144
+ ChangeAttributeValue OccupancyValue 0.5, miTUpdate
145
+ ```
146
+
147
+ Every command is also exposed as a plain dataclass, so you can construct
148
+ chains without the builder when that's clearer.
149
+
150
+ ## Compatibility
151
+
152
+ - **DesignBuilder**: any version that supports the `/process=` argument.
153
+ Tested against the v25.1 / v26.1 lines.
154
+ - **Python**: 3.10+.
155
+ - **OS**: Windows for live runs. The command model is pure Python and imports on any platform — useful for unit tests on Linux CI.
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ pip install -e .[dev]
161
+ pytest
162
+ ruff check .
163
+ ```
164
+
165
+ Tests are split by area:
166
+
167
+ | File | Covers |
168
+ | --- | --- |
169
+ | `test_commands.py` | `ProcessChain` building + every command's `to_string()` |
170
+ | `test_executable.py` | `find_designbuilder` resolution order |
171
+ | `test_status.py` | `status` / `ProcessStatus` |
172
+ | `test_cli.py` | CLI argparse wiring (mocked subprocess) |
173
+
174
+ ## Releasing
175
+
176
+ Every version is a tag. `pyproject.toml`'s `version` and the git tag must never
177
+ drift apart — consumers pin to `@vX.Y.Z`, and an untagged bump leaves them with
178
+ nothing to pin to but a commit SHA.
179
+
180
+ To cut a release, bump `version` in `pyproject.toml` on `main`:
181
+
182
+ ```toml
183
+ [project]
184
+ version = "0.3.0"
185
+ ```
186
+
187
+ The `Release` workflow then tags that commit `v0.3.0` and opens a matching
188
+ GitHub Release with generated notes. It only fires on changes to
189
+ `pyproject.toml`, and skips silently if the tag already exists, so re-runs are
190
+ safe.
191
+
192
+ To tag manually instead — or to backfill a version that predates the workflow:
193
+
194
+ ```bash
195
+ git tag -a v0.3.0 -m "v0.3.0" && git push origin v0.3.0
196
+ ```
197
+
198
+ Bump the version in the same pull request as the change it describes, not as a
199
+ separate follow-up — that's how the two fell out of step before.
@@ -1,83 +1,89 @@
1
- """
2
- db-process
3
- ====================================
4
- Python interface to DesignBuilder command line processing.
5
-
6
- This package provides:
7
-
8
- - **Executable discovery** — find DesignBuilder.exe across env vars,
9
- default install paths, and the system PATH.
10
- - **Typed command model** — dataclasses for every ``/process=`` command
11
- documented in DesignBuilder Help, with a fluent builder API.
12
- - **Subprocess execution** — blocking and non-blocking modes, timeout
13
- handling, and idle-detection termination.
14
- """
15
-
16
- from db_process.commands import (
17
- ChangeAttributeValue,
18
- ExportAsXML,
19
- ExternalCommand,
20
- ImportLibraryData,
21
- ImportModelData,
22
- NoClose,
23
- ProcessChain,
24
- RunCalculation,
25
- Screen,
26
- SimEndDate,
27
- SimStartDate,
28
- SwitchScreen,
29
- TabChange,
30
- UseSimManager,
31
- # Convenience factories
32
- cfd_simulation,
33
- daylighting,
34
- eplus_simulation,
35
- export_xml,
36
- heating_and_cooling_design,
37
- sbem_calculation,
38
- )
39
- from db_process.executable import find_designbuilder
40
- from db_process.runner import (
41
- RunHandle,
42
- RunResult,
43
- find_process,
44
- kill_process,
45
- kill_when_idle,
46
- run,
47
- run_async,
48
- )
49
-
50
- __all__ = [
51
- # Executable
52
- "find_designbuilder",
53
- # Commands
54
- "ChangeAttributeValue",
55
- "ExportAsXML",
56
- "ExternalCommand",
57
- "ImportLibraryData",
58
- "ImportModelData",
59
- "NoClose",
60
- "ProcessChain",
61
- "RunCalculation",
62
- "Screen",
63
- "SimEndDate",
64
- "SimStartDate",
65
- "SwitchScreen",
66
- "TabChange",
67
- "UseSimManager",
68
- # Convenience factories
69
- "cfd_simulation",
70
- "daylighting",
71
- "eplus_simulation",
72
- "export_xml",
73
- "heating_and_cooling_design",
74
- "sbem_calculation",
75
- # Runner
76
- "RunHandle",
77
- "RunResult",
78
- "find_process",
79
- "kill_process",
80
- "kill_when_idle",
81
- "run",
82
- "run_async",
83
- ]
1
+ """
2
+ db-process
3
+ ====================================
4
+ Python interface to DesignBuilder command line processing.
5
+
6
+ This package provides:
7
+
8
+ - **Executable discovery** — find DesignBuilder.exe across env vars,
9
+ default install paths, and the system PATH.
10
+ - **Typed command model** — dataclasses for every ``/process=`` command
11
+ documented in DesignBuilder Help, with a fluent builder API.
12
+ - **Subprocess execution** — blocking and non-blocking modes, timeout
13
+ handling, and idle-detection termination.
14
+ """
15
+
16
+ from db_process.commands import (
17
+ ChangeAttributeValue,
18
+ ExportAsXML,
19
+ ExternalCommand,
20
+ ImportLibraryData,
21
+ ImportModelData,
22
+ NoClose,
23
+ ProcessChain,
24
+ RunCalculation,
25
+ Screen,
26
+ SimEndDate,
27
+ SimStartDate,
28
+ SwitchScreen,
29
+ TabChange,
30
+ UseSimManager,
31
+ # Convenience factories
32
+ cfd_simulation,
33
+ daylighting,
34
+ eplus_simulation,
35
+ export_xml,
36
+ heating_and_cooling_design,
37
+ sbem_calculation,
38
+ )
39
+ from db_process.executable import find_designbuilder
40
+ from db_process.runner import (
41
+ ProcessStatus,
42
+ RunHandle,
43
+ RunResult,
44
+ find_process,
45
+ is_running,
46
+ kill_process,
47
+ kill_when_idle,
48
+ run,
49
+ run_async,
50
+ status,
51
+ )
52
+
53
+ __all__ = [
54
+ # Executable
55
+ "find_designbuilder",
56
+ # Commands
57
+ "ChangeAttributeValue",
58
+ "ExportAsXML",
59
+ "ExternalCommand",
60
+ "ImportLibraryData",
61
+ "ImportModelData",
62
+ "NoClose",
63
+ "ProcessChain",
64
+ "RunCalculation",
65
+ "Screen",
66
+ "SimEndDate",
67
+ "SimStartDate",
68
+ "SwitchScreen",
69
+ "TabChange",
70
+ "UseSimManager",
71
+ # Convenience factories
72
+ "cfd_simulation",
73
+ "daylighting",
74
+ "eplus_simulation",
75
+ "export_xml",
76
+ "heating_and_cooling_design",
77
+ "sbem_calculation",
78
+ # Runner / status
79
+ "RunHandle",
80
+ "RunResult",
81
+ "ProcessStatus",
82
+ "find_process",
83
+ "is_running",
84
+ "kill_process",
85
+ "kill_when_idle",
86
+ "run",
87
+ "run_async",
88
+ "status",
89
+ ]
@@ -0,0 +1,6 @@
1
+ """Allow `python -m db_process [subcommand]`."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -0,0 +1,92 @@
1
+ """db-process CLI.
2
+
3
+ Small command-line wrapper over the existing process-control primitives:
4
+
5
+ db-process status # is DB running? where's the exe?
6
+ db-process close # kill any running DesignBuilder
7
+ db-process open [MODEL.dsb] # launch DB; opens MODEL if given
8
+ db-process restart [MODEL.dsb] # close + open
9
+
10
+ Equivalent to:
11
+
12
+ python -m db_process <subcommand> [args]
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import subprocess
19
+ import sys
20
+ import time
21
+ from pathlib import Path
22
+
23
+ from .executable import find_designbuilder
24
+ from .runner import find_process, is_running, kill_process, run_async, status as proc_status
25
+
26
+
27
+ def cmd_status(args: argparse.Namespace) -> int:
28
+ s = proc_status()
29
+ print(f"DesignBuilder exe : {s.exe_path or '<not found>'}")
30
+ if not s.is_running:
31
+ print("DesignBuilder process : not running")
32
+ return 1
33
+ print(f"DesignBuilder process : pid={s.pid}")
34
+ return 0
35
+
36
+
37
+ def cmd_close(args: argparse.Namespace) -> int:
38
+ if kill_process():
39
+ print("Closed running DesignBuilder.")
40
+ return 0
41
+ print("No DesignBuilder process to close.")
42
+ return 1
43
+
44
+
45
+ def cmd_open(args: argparse.Namespace) -> int:
46
+ exe = find_designbuilder()
47
+ if args.model is not None:
48
+ print(f"Opening {args.model} with {exe} ...")
49
+ run_async(str(args.model))
50
+ else:
51
+ print(f"Launching {exe} (no model)...")
52
+ subprocess.Popen([str(exe)])
53
+ return 0
54
+
55
+
56
+ def cmd_restart(args: argparse.Namespace) -> int:
57
+ if kill_process():
58
+ print(f"Closed existing process; settling for {args.settle}s ...")
59
+ time.sleep(args.settle)
60
+ return cmd_open(args)
61
+
62
+
63
+ def build_parser() -> argparse.ArgumentParser:
64
+ p = argparse.ArgumentParser(prog="db-process",
65
+ description="DesignBuilder process control")
66
+ sub = p.add_subparsers(dest="cmd", required=True)
67
+
68
+ sub.add_parser("status", help="Show DesignBuilder exe path and PID").set_defaults(func=cmd_status)
69
+ sub.add_parser("close", help="Kill any running DesignBuilder").set_defaults(func=cmd_close)
70
+
71
+ p_open = sub.add_parser("open", help="Launch DesignBuilder")
72
+ p_open.add_argument("model", nargs="?", type=Path, default=None,
73
+ help="Optional .dsb model to open")
74
+ p_open.set_defaults(func=cmd_open)
75
+
76
+ p_restart = sub.add_parser("restart", help="Close then open DesignBuilder")
77
+ p_restart.add_argument("model", nargs="?", type=Path, default=None,
78
+ help="Optional .dsb model to open after restart")
79
+ p_restart.add_argument("--settle", type=float, default=1.5,
80
+ help="Seconds to wait between close and open (default 1.5)")
81
+ p_restart.set_defaults(func=cmd_restart)
82
+
83
+ return p
84
+
85
+
86
+ def main(argv: list[str] | None = None) -> int:
87
+ args = build_parser().parse_args(argv)
88
+ return args.func(args)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ sys.exit(main())