Skip to content

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:

  1. Application Class - The main entry point for your application logic
  2. Device Agent Interface - Communication with the Doover cloud and other devices
  3. Platform Interface - Hardware I/O access (digital/analog inputs and outputs)
  4. Modbus Interface - Industrial protocol communication with Modbus devices
  5. 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

BridgePortPurpose
Device Agent (DDA)50051Cloud connectivity and channel management
App Controller50052Application lifecycle management
Platform Interface50053Hardware I/O (DI/DO/AI/AO)
Modbus Interface50054Modbus RTU/TCP communication
Camera Interface50050RTSP 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:

AttributeTypeDescription
configSchemaConfiguration schema for the application
device_agentDeviceAgentInterfaceInterface to the Doover cloud
platform_ifacePlatformInterfaceInterface to device hardware I/O
modbus_ifaceModbusInterfaceInterface for Modbus communication
ui_managerUIManagerManages UI elements and commands
app_keystrUnique 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():

  1. Command-line arguments and environment variables are parsed
  2. gRPC interfaces are initialized with appropriate URIs
  3. Logging is configured
  4. The async event loop is started
  5. The application lifecycle begins (see Application Lifecycle)

Cloud Communication

Data flows between your application and the cloud through channels:

  1. Your application publishes data to a channel via device_agent.publish_to_channel()
  2. The Device Agent batches and syncs data to the cloud
  3. Cloud updates are pushed back through channel subscriptions
  4. Your callbacks receive the updated data

Hardware Communication

Hardware access follows a request-response pattern:

  1. Your application calls a Platform Interface method
  2. The request is sent via gRPC to the platform interface service
  3. The service interacts with hardware and returns the response
  4. 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_loop trigger 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.