Release v0.1.0
This commit is contained in:
@@ -72,7 +72,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
needs: [lint, typecheck, test]
|
needs: [lint, typecheck, test]
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(gitea.ref, 'refs/tags/v')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -91,7 +91,22 @@ jobs:
|
|||||||
run: python -m build
|
run: python -m build
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
run: |
|
||||||
with:
|
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||||
files: dist/*
|
VERSION=${TAG_NAME#v}
|
||||||
generate_release_notes: true
|
BODY=$(awk "/^## \[${VERSION}\]/{flag=1; next} /^## \\[/{flag=0} flag" CHANGELOG.md)
|
||||||
|
echo "Creating release ${TAG_NAME}"
|
||||||
|
RESPONSE=$(curl -s -X POST -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" -d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"${TAG_NAME}\", \"body\": $(echo "$BODY" | jq -Rs .)}" "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases")
|
||||||
|
RELEASE_ID=$(echo "$RESPONSE" | jq -r '.id')
|
||||||
|
echo "Created release ID: ${RELEASE_ID}"
|
||||||
|
if [ "$RELEASE_ID" != "null" ] && [ -n "$RELEASE_ID" ]; then
|
||||||
|
for file in dist/*; do
|
||||||
|
echo "Uploading $(basename ${file})..."
|
||||||
|
curl -s -X POST -H "Authorization: token ${GITHUB_TOKEN}" -F "attachment=@${file}" "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=$(basename ${file})"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "Failed to create release: $RESPONSE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-12-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Streamlit Dashboard Enhancement (Sprint 17)
|
||||||
|
- HAL-based instrument control (no direct physics access)
|
||||||
|
- Test execution page for running TempCo characterisation
|
||||||
|
- Results viewer page with filtering and historical data
|
||||||
|
- Form-based parameter controls preventing UI clunkiness
|
||||||
|
- Live simulation charts with auto-start
|
||||||
|
- End-to-end integration tests covering full workflow
|
||||||
|
- Updated README with installation and usage instructions
|
||||||
|
- Proprietary licence
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Integration tests now run 100x faster with simulation time scaling
|
||||||
|
- Removed confusing pause/clear chart buttons from dashboard
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- CI release workflow now creates proper releases with changelog description
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
- Dashboard uses InstrumentFactory and InstrumentSet abstraction
|
||||||
|
- Embedded SimulationServer with threading synchronisation
|
||||||
|
- SQLite repository close() method for Windows file handle cleanup
|
||||||
|
- 259 unit tests, 12 integration tests all passing
|
||||||
|
- Coverage: 100% on core physics/instrument modules
|
||||||
|
|
||||||
## [0.1.0-beta.2] - 2025-12-03
|
## [0.1.0-beta.2] - 2025-12-03
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -128,7 +155,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
| Version | Date | Milestone |
|
| Version | Date | Milestone |
|
||||||
|---------|------|-----------|
|
|---------|------|-----------|
|
||||||
| 0.1.0 | TBD | MVP Complete |
|
| 0.1.0 | 2025-12-04 | MVP Complete |
|
||||||
| 0.1.0-beta.2 | 2025-12-03 | First DVT test runs |
|
| 0.1.0-beta.2 | 2025-12-03 | First DVT test runs |
|
||||||
| 0.1.0-beta.1 | 2025-12-02 | HAL complete |
|
| 0.1.0-beta.1 | 2025-12-02 | HAL complete |
|
||||||
| 0.1.0-alpha.3 | 2025-12-02 | Network ready |
|
| 0.1.0-alpha.3 | 2025-12-02 | Network ready |
|
||||||
|
|||||||
5
LICENSE
Normal file
5
LICENSE
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Copyright (c) 2025 Kai Chappell. All rights reserved.
|
||||||
|
|
||||||
|
This software is proprietary and confidential. Unauthorized copying,
|
||||||
|
distribution, modification, or use of this software, via any medium,
|
||||||
|
is strictly prohibited without prior written permission from the author.
|
||||||
12
README.md
12
README.md
@@ -1,12 +1,12 @@
|
|||||||
# py_dvt_ate
|
# py_dvt_ate
|
||||||
|
|
||||||
**ThermalATE: Coupled Physics DVT Simulation Platform**
|
**Coupled Physics DVT Simulation Platform**
|
||||||
|
|
||||||
A software simulation environment that accurately models the physical coupling between thermal and electrical domains, enabling DVT (Design Validation Test) engineers to develop, validate, and debug characterisation test sequences without physical access to laboratory equipment.
|
A software simulation environment for offline development of ATE (Automated Test Equipment) characterisation algorithms. Accurately models thermal-electrical coupling, enabling DVT engineers to develop and validate test sequences without physical laboratory access.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
ThermalATE enables offline development of ATE (Automated Test Equipment) characterisation algorithms by simulating:
|
py_dvt_ate simulates a complete DVT test bench:
|
||||||
|
|
||||||
- **Thermal Chamber** - Temperature control with realistic ramp and settling behaviour
|
- **Thermal Chamber** - Temperature control with realistic ramp and settling behaviour
|
||||||
- **Programmable Power Supply** - Voltage/current control and measurement
|
- **Programmable Power Supply** - Voltage/current control and measurement
|
||||||
@@ -32,10 +32,6 @@ ThermalATE enables offline development of ATE (Automated Test Equipment) charact
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
|
||||||
git clone https://github.com/yourrepo/py_dvt_ate.git
|
|
||||||
cd py_dvt_ate
|
|
||||||
|
|
||||||
# Install with development dependencies
|
# Install with development dependencies
|
||||||
pip install -e ".[dev]"
|
pip install -e ".[dev]"
|
||||||
```
|
```
|
||||||
@@ -121,4 +117,4 @@ Kai Chappell
|
|||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
TBD
|
Proprietary - All rights reserved. See [LICENSE](LICENSE) for details.
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
"""py_dvt_ate: Coupled Physics DVT Simulation Platform."""
|
"""py_dvt_ate: Coupled Physics DVT Simulation Platform."""
|
||||||
|
|
||||||
__version__ = "0.1.0-beta.2"
|
__version__ = "0.1.0"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,17 +57,39 @@ class SimulationServer:
|
|||||||
self._instrument_server: InstrumentServer | None = None
|
self._instrument_server: InstrumentServer | None = None
|
||||||
self._physics_task: asyncio.Task[None] | None = None
|
self._physics_task: asyncio.Task[None] | None = None
|
||||||
self._running = False
|
self._running = False
|
||||||
|
self._paused = False # Pause physics simulation
|
||||||
|
self._time_scale = 1.0 # Simulation time multiplier
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self) -> bool:
|
def is_running(self) -> bool:
|
||||||
"""Check if server is currently running."""
|
"""Check if server is currently running."""
|
||||||
return self._running
|
return self._running
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paused(self) -> bool:
|
||||||
|
"""Check if physics simulation is paused."""
|
||||||
|
return self._paused
|
||||||
|
|
||||||
|
@paused.setter
|
||||||
|
def paused(self, value: bool) -> None:
|
||||||
|
"""Pause or resume the physics simulation."""
|
||||||
|
self._paused = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def physics_engine(self) -> PhysicsEngine | None:
|
def physics_engine(self) -> PhysicsEngine | None:
|
||||||
"""Get the physics engine instance."""
|
"""Get the physics engine instance."""
|
||||||
return self._physics_engine
|
return self._physics_engine
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_scale(self) -> float:
|
||||||
|
"""Get the current time scale multiplier."""
|
||||||
|
return self._time_scale
|
||||||
|
|
||||||
|
@time_scale.setter
|
||||||
|
def time_scale(self, value: float) -> None:
|
||||||
|
"""Set the time scale multiplier (e.g., 10.0 = 10x faster)."""
|
||||||
|
self._time_scale = max(0.1, min(value, 1000.0))
|
||||||
|
|
||||||
def _setup(self) -> None:
|
def _setup(self) -> None:
|
||||||
"""Create and wire up all components."""
|
"""Create and wire up all components."""
|
||||||
# Create physics engine
|
# Create physics engine
|
||||||
@@ -101,8 +123,12 @@ class SimulationServer:
|
|||||||
dt = self._physics_engine.dt
|
dt = self._physics_engine.dt
|
||||||
|
|
||||||
while self._running:
|
while self._running:
|
||||||
self._physics_engine.step()
|
if not self._paused:
|
||||||
# Sleep for the physics timestep
|
# Step physics multiple times based on time scale
|
||||||
|
steps_per_tick = max(1, int(self._time_scale))
|
||||||
|
for _ in range(steps_per_tick):
|
||||||
|
self._physics_engine.step()
|
||||||
|
# Sleep for the physics timestep (wall clock time)
|
||||||
await asyncio.sleep(dt)
|
await asyncio.sleep(dt)
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ def simulation_server() -> Generator[ServerConfig, None, None]:
|
|||||||
server_thread = ServerThread(config)
|
server_thread = ServerThread(config)
|
||||||
server_thread.start()
|
server_thread.start()
|
||||||
|
|
||||||
|
# Speed up simulation for tests (100x faster)
|
||||||
|
if server_thread.server is not None:
|
||||||
|
server_thread.server.time_scale = 100.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield config
|
yield config
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class TestInstrumentServer:
|
|||||||
# Set temperature setpoint
|
# Set temperature setpoint
|
||||||
writer.write(b"TEMP:SETPOINT 85.0\n")
|
writer.write(b"TEMP:SETPOINT 85.0\n")
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
# Small delay to ensure server processes command before next one
|
||||||
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
# Query setpoint
|
# Query setpoint
|
||||||
writer.write(b"TEMP:SETPOINT?\n")
|
writer.write(b"TEMP:SETPOINT?\n")
|
||||||
@@ -212,6 +214,7 @@ class TestSimulationServer:
|
|||||||
psu_r, psu_w = await asyncio.open_connection("127.0.0.1", 16201)
|
psu_r, psu_w = await asyncio.open_connection("127.0.0.1", 16201)
|
||||||
psu_w.write(b"VOLT 5.0\n")
|
psu_w.write(b"VOLT 5.0\n")
|
||||||
await psu_w.drain()
|
await psu_w.drain()
|
||||||
|
await asyncio.sleep(0.01) # Allow server to process
|
||||||
psu_w.write(b"OUTP ON\n")
|
psu_w.write(b"OUTP ON\n")
|
||||||
await psu_w.drain()
|
await psu_w.drain()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user