System Architecture
pydoover provides a comprehensive framework for building IoT applications that run on Doover devices. This page provides an overview of the system architecture and how the various components work together.
High-Level Overview
A pydoover application consists of several key components:
- Application Class - The main entry point for your application logic
- Device Agent Interface - Communication with the Doover cloud and other devices
- Platform Interface - Hardware I/O access (digital/analog inputs and outputs)
- Modbus Interface - Industrial protocol communication with Modbus devices
- UI Manager - User interface management for cloud-based dashboards
Device-Side Infrastructure
pydoover applications run on Doover devices alongside a set of containerized microservices called device bridges. These bridges provide hardware abstraction, allowing your application code to remain consistent across different hardware platforms.
Device Bridges
| Bridge | Port | Purpose |
|---|---|---|
| Device Agent (DDA) | 50051 | Cloud connectivity and channel management |
| App Controller | 50052 | Application lifecycle management |
| Platform Interface | 50053 | Hardware I/O (DI/DO/AI/AO) |
| Modbus Interface | 50054 | Modbus RTU/TCP communication |
| Camera Interface | 50050 | RTSP camera streaming and snapshots |
When your application calls self.platform_iface.get_di(0), pydoover sends a gRPC request to the Platform Interface bridge, which interacts with the actual hardware and returns the result.
See the Device Infrastructure section for detailed information about the bridge architecture.
Component Architecture
The Application Class
The Application class is the foundation of every pydoover application. It orchestrates all other components and manages the application lifecycle.
from pydoover.docker import Application, run_app
from pydoover.config import Schema
class MyApp(Application):
def setup(self):
# Initialize your application
pass
def main_loop(self):
# Your recurring application logic
pass
if __name__ == "__main__":
run_app(MyApp(config=Schema()))
Key attributes available on the Application instance:
| Attribute | Type | Description |
|---|---|---|
config | Schema | Configuration schema for the application |
device_agent | DeviceAgentInterface | Interface to the Doover cloud |
platform_iface | PlatformInterface | Interface to device hardware I/O |
modbus_iface | ModbusInterface | Interface for Modbus communication |
ui_manager | UIManager | Manages UI elements and commands |
app_key | str | Unique application identifier |
gRPC Communication Layer
pydoover uses gRPC for inter-process communication between your application and the system services. All interfaces extend from a common GRPCInterface base class that provides:
- Synchronous and asynchronous request handling
- Automatic connection management
- Response validation and error handling
class GRPCInterface:
def make_request(self, stub_call, request, *args, **kwargs):
# Synchronous gRPC call
...
async def make_request_async(self, stub_call, request, *args, **kwargs):
# Asynchronous gRPC call
...
Device Agent Interface
The DeviceAgentInterface handles all communication with the Doover Device Agent (DDA), which manages cloud connectivity. Key responsibilities include:
- Channel Subscriptions - Subscribe to real-time data channels
- Publishing Messages - Send data to channels for cloud synchronization
- Connection Status - Monitor DDA availability and online status
- Temporary Tokens - Obtain API tokens for authenticated requests
# Subscribe to a channel
self.device_agent.add_subscription("my_channel", my_callback)
# Publish to a channel
self.device_agent.publish_to_channel("my_channel", {"key": "value"})
# Check connection status
is_online = self.device_agent.get_is_dda_online()
Platform Interface
The PlatformInterface provides access to the device's hardware I/O capabilities:
- Digital Inputs (DI) - Read digital input pin states
- Digital Outputs (DO) - Control digital output pins
- Analog Inputs (AI) - Read analog sensor values
- Analog Outputs (AO) - Set analog output values
- System Information - Temperature, voltage, power readings
- Pulse Counting - High-frequency pulse detection
# Read digital input value = self.platform_iface.get_di(0) # Set digital output self.platform_iface.set_do(1, True) # Get system voltage voltage = self.platform_iface.get_system_voltage()
Modbus Interface
The ModbusInterface enables communication with Modbus devices over serial (RTU) or TCP connections:
- Bus Management - Open and close Modbus buses
- Register Operations - Read and write Modbus registers
- Subscriptions - Periodic polling with callbacks
# Read registers
values = self.modbus_iface.read_registers(
bus_id="default",
modbus_id=1,
start_address=0,
num_registers=10
)
# Subscribe to register changes
self.modbus_iface.add_read_register_subscription(
bus_id="default",
modbus_id=1,
start_address=0,
num_registers=10,
poll_secs=5,
callback=my_callback
)
Communication Flow
Application Startup
When an application starts via run_app():
- Command-line arguments and environment variables are parsed
- gRPC interfaces are initialized with appropriate URIs
- Logging is configured
- The async event loop is started
- The application lifecycle begins (see Application Lifecycle)
Cloud Communication
Data flows between your application and the cloud through channels:
- Your application publishes data to a channel via
device_agent.publish_to_channel() - The Device Agent batches and syncs data to the cloud
- Cloud updates are pushed back through channel subscriptions
- Your callbacks receive the updated data
Hardware Communication
Hardware access follows a request-response pattern:
- Your application calls a Platform Interface method
- The request is sent via gRPC to the platform interface service
- The service interacts with hardware and returns the response
- Your application receives the result
Error Handling
pydoover includes robust error handling at multiple levels:
- Connection Retries - Automatic reconnection to gRPC services
- Response Validation - Checks for successful responses before processing
- Loop Protection - Errors in
main_looptrigger a configurable wait period before restart - Graceful Shutdown - Proper cleanup when the application exits
Configuration
Applications are configured through:
- Schema Classes - Define configuration parameters with type validation
- Deployment Config - Runtime configuration pushed from the cloud
- Environment Variables - System-level configuration (APP_KEY, DDA_URI, etc.)
- Command-line Arguments - Override settings at startup
See the Configuration section for detailed information on configuring your application.