9.3 KiB
Architecture Overview
This document describes the architecture and design of TLA+ Visualiser.
High-Level Architecture
┌─────────────────────────────────────────────────────────┐
│ QML UI Layer │
│ (ImportView, GraphView, TraceView, InvariantView) │
└────────────────────┬────────────────────────────────────┘
│ Qt Signals/Slots
┌────────────────────▼────────────────────────────────────┐
│ Qt Models (QAbstractListModel) │
│ StateGraphModel TraceViewerModel │
└──────────┬──────────────────────┬───────────────────────┘
│ │
┌──────────▼──────────┐ ┌────────▼─────────────────────┐
│ GitHubImporter │ │ TLCRunner │
│ - URL parsing │ │ - Process management │
│ - HTTP requests │ │ - Output parsing │
│ - Local caching │ │ - Result serialization │
└─────────────────────┘ └──────────────────────────────┘
Component Details
1. UI Layer (QML)
Purpose: Provides the user interface using Qt Quick/QML.
Components:
main.qml: Application window, menu bar, tab navigationImportView.qml: GitHub URL input and spec previewGraphView.qml: State/transition graph visualizationTraceView.qml: Step-by-step trace inspectionInvariantView.qml: Invariant status dashboard
Communication: Uses Qt's property binding and signal/slot mechanism to interact with C++ models.
2. Model Layer (Qt Models)
StateGraphModel
Responsibility: Provides data for state graph visualization.
Key Methods:
loadFromResults(): Populate from TLC resultsgetTransitions(): Return transition edgesgetStateDetails(): Get details for specific state
Data:
- States with positions (x, y coordinates)
- Transitions (edges between states)
- Layout calculated using circular algorithm
TraceViewerModel
Responsibility: Provides data for trace/counterexample viewing.
Key Methods:
loadTrace(): Load a counterexample traceexportToJson(): Export trace as JSONexportToMarkdown(): Export trace as Markdown
Data:
- Sequence of trace steps
- Current step index
- Variable values at each step
3. Business Logic Layer
GitHubImporter
Responsibility: Fetch TLA+ specs from GitHub.
Features:
-
URL Parsing: Regex-based parsing of GitHub URLs
- File URLs:
github.com/owner/repo/blob/branch/path - Raw URLs:
raw.githubusercontent.com/owner/repo/branch/path - Repo URLs:
github.com/owner/repo
- File URLs:
-
HTTP Communication: Uses libcurl for HTTP requests
- Follows redirects
- Sets User-Agent header
- Error handling
-
Local Caching: Saves fetched content to temp directory
- Cache key:
owner_repo_branch_filepath - Automatic cache lookup before fetching
- Cache key:
Design Pattern: PIMPL (Pointer to Implementation)
- Public interface in header
- Private implementation in .cpp
- Hides libcurl details from API users
TLCRunner
Responsibility: Execute TLC model checker and parse results.
Features:
-
Process Management: Runs TLC as external Java process
- Async execution in separate thread
- Cancellation support
- Status callbacks
-
Output Parsing: Parses TLC text output
- State count extraction
- Error message collection
- Timing information
-
Result Persistence: Save/load results
- Text-based format (upgradable to JSON)
- Deterministic runs
States:
- NotStarted
- Running
- Completed
- Failed
- Cancelled
Design Pattern: PIMPL + Observer (callbacks)
Data Flow
Import Flow
User enters URL
↓
ImportView captures input
↓
GitHubImporter.parseUrl()
↓
GitHubImporter.fetchFile()
├─→ Check cache
└─→ HTTP request (libcurl)
↓
Content displayed in ImportView
Model Check Flow
User clicks "Run Model Checker"
↓
TLCRunner.startModelCheck()
↓
Spawn Java process (TLC)
↓
Parse output in background thread
↓
Update status via callback
↓
TLCRunner.getResults()
↓
StateGraphModel.loadFromResults()
TraceViewerModel.loadTrace()
↓
UI updates via Qt property bindings
Visualization Flow
User switches to Graph View
↓
StateGraphModel provides data
├─→ Node positions (calculated)
├─→ Edge list
└─→ State details
↓
QML Canvas or GraphicsView renders
↓
User clicks node
↓
Model provides details for selected node
Design Patterns
PIMPL (Pointer to Implementation)
Used in: GitHubImporter, TLCRunner, StateGraphModel, TraceViewerModel
Benefits:
- Binary compatibility (ABI stability)
- Faster compilation (hidden dependencies)
- Implementation flexibility
Structure:
class MyClass {
public:
MyClass();
~MyClass();
void publicMethod();
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
Observer Pattern
Used in: TLCRunner callbacks
Benefits:
- Async notification
- Decoupling
Usage:
runner.setStatusCallback([](Status s) {
// Handle status change
});
Model-View (Qt)
Used throughout for UI
Benefits:
- Separation of concerns
- Automatic updates
- Testable models
Factory Pattern (Future)
Could be used for:
- Different graph layout algorithms
- Export format handlers
Threading Model
Main Thread
- Qt event loop
- UI updates
- User interaction
- Model data access
Background Threads
- TLC execution (via
std::thread) - HTTP requests (via libcurl)
Synchronization: Qt signals/slots (thread-safe when queued)
Memory Management
- Smart Pointers:
std::unique_ptrfor PIMPL idiom - Qt Parent-Child: QObjects use Qt's parent-child ownership
- RAII: Resources cleaned up in destructors
- No Manual new/delete: Prefer stack allocation or smart pointers
Error Handling
Current Strategy
- Return values (bool for success/failure)
- Empty returns for errors (empty string, empty vector)
- Error messages stored in result structures
Future Improvements
- Exception-based error handling
- Error codes enumeration
- Detailed error context
Dependencies
External Libraries
- Qt 6: UI framework, models, networking
- libcurl: HTTP requests
- C++ Standard Library: Containers, threading, filesystem
Dependency Management
- Conan: Package manager for libcurl
- CMake: Build system integration
- Find Modules: Qt6 via CMake's built-in finder
Extensibility Points
Adding New Visualizations
- Create new QML view (e.g.,
TimelineView.qml) - Create corresponding Qt model (e.g.,
TimelineModel) - Add tab in
main.qml - Load data from
TLCRunner::RunResults
Adding New Import Sources
- Create new importer class (e.g.,
GitLabImporter) - Implement common interface
- Add UI option to select source
- Integrate in
ImportView
Adding New Export Formats
- Add export method to model (e.g.,
exportToXML()) - Add UI button/menu item
- Implement serialization logic
Testing Strategy
Unit Tests
- GitHubImporter: URL parsing, cache operations
- TLCRunner: Status management, result saving
- Models: Data loading, transformations
Integration Tests (Future)
- End-to-end import flow
- TLC execution with sample specs
- Export functionality
UI Tests (Future)
- QML test framework
- Automated UI interaction
Performance Considerations
Current Optimizations
- Circular layout O(n) complexity
- Local caching to avoid redundant downloads
- Async TLC execution
Future Optimizations
- Lazy loading for large graphs
- Level-of-detail rendering
- Progressive result display
- Worker threads for layout calculation
Security Considerations
Current Measures
- No credential storage
- Read-only spec access
- Local cache in temp directory
Future Improvements
- GitHub token authentication (optional)
- HTTPS certificate validation
- Input sanitization for TLC invocation
- Sandboxed TLC execution
Build System
CMake Configuration
- Minimum version: 3.22
- C++20 standard requirement
- Qt6 modules: Core, Gui, Quick, Widgets
- Conan integration via toolchain file
Platform Support
- Linux: Native builds, ARM64 via QEMU
- macOS: Native builds (x86_64, arm64)
- Windows: MSVC and MinGW support
CI/CD
- GitHub Actions workflows
- Multi-platform builds
- Automated testing
- Artifact uploads
Future Architecture Improvements
- Plugin System: Allow third-party visualizations
- Async I/O: libuv or Boost.Asio for better performance
- Database: SQLite for result persistence
- Language Server: TLA+ LSP integration
- Web Backend: Optional web UI via REST API