Instrument Driver

Basic Overview

The Instrument Driver is responsible to handle the communication to the devices that are attached to the OneIoT Gateway. There are two possible communication modes.

  1. Command Mode

  2. Stream Mode

Command Mode

In command mode the OneIoT Gateway serves as a master device. It sends commands to the slave devices attached to the gateway, handles their responses and communicates with the OneIoT Cloud. The basic skeleton for a command based Instrument Driver looks like the following (Modbus example).

import struct
import uasyncio as asyncio
import logging
from serial_communication import Serial
from queue import MESSAGE_BUS, Event, EVENT_TO_CLOUD_FOR_MQTT, EVENT_ERROR
import message_util
from libs.modbus_facade import ModbusFacade

class CustomHandler:

    def __init__(self, serial:Serial):
        # Every 30sec
        self.fire_every = 30
        self.modbus = ModbusFacade(serial)

    async def loop(self):
        # Read every x seconds
        while True:
            await asyncio.sleep_ms(self.fire_every*1000)
                # SEND COMMANDS OR READ VALUES HERE
                # PROCESS RESPONSES FROM DEVICE
                # EXAMPLE:
                answer_double = 
                    await self.modbus.read_holding_registers(0x10, 0x01, 0x02, double_valued=True)[0]
                await self.send_double(1, answer_double)
            
    async def handle_incoming(self, message):
        # Messages from the cloud are send to the driver and can be handled here
        if message_util.get_state_content_for_payload(message) is not None:
            item_number, state_str = message_util.get_state_content_for_payload(message)
            item_number = int(item_number)
            # DO SOMETHING WITH THE ITEM AND STATE, FOR EXAMPLE, SEND COMMAND TO DEVICES
            # EXAMPLE:
            state = int(state_str)
            if not await self.modbus.write_single_coil(0x10, 0x01, state):
                logging.log("Writing register failed.")        
        else:
            logging.log(str("Cannot handle message %s." % str(message)))        

    async def send_double(self, item, value):
        value = str(value)
        await self.send_message(item, value, "DOUBLE")

    async def send_message(self, item, value_string, type="DOUBLE"):
        message = None
        if value_string:
            message =  "S:" + str(item) + ":20:" + type + ":" + value_string
        else:
            message =  "S:" + str(item) + ":50"
            logging.log("No answer received for command...")

        await self.finalize_serial_to_cloud(message)
        # YOU CAN ALSO SEND SOME LOGS TO THE CLOUD HERE

    '''
    This finalizes a serial message given the OneIoT protocol. Timestamp is added and message is checked.
    Then it is sent to the cloud.
    '''
    async def finalize_serial_to_cloud(self, message):
        message = message_util.add_timestamp(message)
        correct = message_util.check_serial_message(message)
        if not correct:
            await MESSAGE_BUS.send(Event(EVENT_ERROR, "Incorrect handled serial: " + message))
        else:
            # just forward to devicecloud via MQTT
            await MESSAGE_BUS.send(Event(EVENT_TO_CLOUD_FOR_MQTT, [message]))

Stream Mode

In Stream Mode, the device proactively sends messages to the OneIoT Gateway. For this, the OneIoT Communication Protocol can be used and no further driver is used. If your device sends serial messages in other formats, a driver needs to be used.

class CustomHandler:

    def __init__(self):

    async def handle_message(self, message):
        # PARSE MESSAGE; EXAMPLE:
        item, value = parse_message(message)
        # SEND TO CLOUD; EXAMPLE:
        await send_double(item, value)
        
        
    def parse_message(message):
        # Semikolon  separated data (example only)
        data = message.split(';')
        item = int(data[0])
        value = data[1]
        return item, value
        
    async def send_double(self, item, value):
        await self.send_message(item, value, "DOUBLE")

    async def send_message(self, item, value_string, type="DOUBLE"):
        message = None
        if value_string:
            message =  "S:" + str(item) + ":20:" + type + ":" + value_string
        else:
            message =  "S:" + str(item) + ":50"
            logging.log("No answer received for command...")

        await self.finalize_serial_to_cloud(message)
        # YOU CAN ALSO SEND SOME LOGS TO THE CLOUD HERE

    '''
    This finalizes a serial message given the OneIoT protocol. Timestamp is added and message is checked.
    Then it is sent to the cloud.
    '''
    async def finalize_serial_to_cloud(self, message):
        message = message_util.add_timestamp(message)
        correct = message_util.check_serial_message(message)
        if not correct:
            await MESSAGE_BUS.send(Event(EVENT_ERROR, "Incorrect handled serial: " + message))
        else:
            # just forward to devicecloud via MQTT
            await MESSAGE_BUS.send(Event(EVENT_TO_CLOUD_FOR_MQTT, [message]))
        
        
        

API Specification

Modbus Facade

TBD

Message Util

Last updated