This topic describes how to develop a driver with the Connector architecture, which is clear and flexible. For your convenience, we recommend that you use the Connector architecture to develop drivers.

Currently, the Connector architecture is only applicable to device SDKs developed in Node.js and Python.

Overview

The following figure shows the Connector architecture.Connector architecture
A driver with the Connector architecture consists of the following classes:
  • ThingAccessClient

    The ThingAccessClient class is encapsulated in a device SDK and provides multiple methods for sub-devices to send data to and receive data from Link IoT Edge. The ThingAccessClient class can call callback functions of the ThingAccessClientCallbacks class. When receiving a request with the pointer of a callback function specified, the ThingAccessClient class obtains required data from Link IoT Edge and then calls the callback function. In the Connector architecture, callback functions of the ThingAccessClientCallbacks class are implemented in the Connector class.

  • Connector

    The Connector class is the core of the Connector architecture. It provides the connect method for connecting sub-devices to Link IoT Edge and the disconnect method for disconnecting sub-devices from Link IoT Edge. In addition, the Connector class supports interfaces encapsulated by the Thing class for sub-devices to connect to Link IoT Edge. The Connector class implements callback functions of the ThingAccessClientCallbacks class. When constructing a ThingAccessClient object, the Connector class specifies the pointer of a callback function and transmits the pointer to the ThingAccessClient class. When receiving required data from Link IoT Edge, the ThingAccessClient class calls the callback function.

  • Thing

    The Thing class directly interacts with sub-devices. It encapsulates interfaces of physical sub-devices for the Connector class to call, and provides object-oriented API operations for sub-devices to call. When the driver connects to a specific sub-device, the Thing class refers to the abstract class of the sub-device, for example, the Light class of a light.

  • Entry

    The Entry class is the main entry point of a driver. It obtains the driver configuration, initializes the Thing and Connector classes, and then calls the connect method to connect a sub-device to Link IoT Edge. The Entry class can also call the disconnect method to disconnect a sub-device from Link IoT Edge.

The Connector class connects the abstract class of a sub-device and that of Link IoT Edge by combining the classes, hence the name. In this example, the abstract class of a sub-device and that of Link IoT Edge are Thing and ThingAccessClient, respectively.

The following figure shows a Unified Modeling Language (UML) class diagram.

UML

Procedure

This section describes how to use a Node.js SDK to develop a driver with the Connector architecture. For more information about how to use a Python SDK to develop a driver, see Link IoT Edge Thing Access SDK for Python.

Light

To develop a driver for a simulated light, follow these steps:
  1. Define an abstract class for the simulated light that can be turned on or off by changing the value of the isOn property to true or false.

    The sample code is as follows:

    /**
     * Define an abstract class for the simulated light that can be turned on or off by changing the value of the
     * <code>isOn</code> property.
     */
    class Light {
      constructor() {
        this._isOn = true;
      }
    
      get isOn() {
        return this._isOn;
      }
    
      set isOn(value) {
        return this._isOn = value;
      }
  2. Define the Connector class. The Connector class provides the following features:
    • Receives the configuration and an abstract object of the simulated light and constructs a ThingAccessClient object for interacting with Link IoT Edge.
    • Implements three callback functions of the ThingAccessClientCallbacks class and uses the callback functions to call interfaces encapsulated by the Light class.
    • Provides the connect and disconnect methods. The connect method can connect the simulated light to Link IoT Edge and the disconnect method can disconnect the simulated light from Link IoT Edge.

    The sample code is as follows:

    /**
     * Construct a class to combine ThingAccessClient and the abstract class of the simulated light that connects
     * to Link IoT Edge.
     */
    class Connector {
      constructor(config, light) {
        this.config = config;
        this.light = light;
        this._client = new ThingAccessClient(config, {
          setProperties: this._setProperties.bind(this),
          getProperties: this._getProperties.bind(this),
          callService: this._callService.bind(this),
        });
      }
    
      /**
       * Connect to Link IoT Edge and publish properties to it.
       */
      connect() {
        registerAndOnlineWithBackOffRetry(this._client, 1)
          .then(() => {
            return new Promise(() => {
              // Publish properties to Link IoT Edge.
              const properties = { 'LightSwitch': this.light.isOn ? 1 : 0 };
              this._client.reportProperties(properties);
            });
          })
          .catch(err => {
            console.log(err);
            return this._client.cleanup();
          })
          .catch(err => {
            console.log(err);
          });
      }
    
      /**
       * Disconnect from Link IoT Edge and stop publishing properties to it.
       */
      disconnect() {
        this._client.cleanup()
          .catch(err => {
            console.log(err);
          });
      }
    
      _setProperties(properties) {
        console.log('Set properties %s to thing %s-%s', JSON.stringify(properties),
          this.config.productKey, this.config.deviceName);
        if ('LightSwitch' in properties) {
          var value = properties['LightSwitch'];
          var isOn = value === 1;
          if (this.light.isOn !== isOn) {
            // Report property changes to Link IoT Edge.
            this.light.isOn = isOn;
            if (this._client) {
              properties = {'LightSwitch': value};
              console.log(`Report properties: ${JSON.stringify(properties)}`);
              this._client.reportProperties(properties);
            }
          }
          return {
            code: RESULT_SUCCESS,
            message: 'success',
          };
        }
        return {
          code: RESULT_FAILURE,
          message: 'The requested properties does not exist.',
        };
      }
    
      _getProperties(keys) {
        console.log('Get properties %s from thing %s-%s', JSON.stringify(keys),
          this.config.productKey, this.config.deviceName);
        if (keys.includes('LightSwitch')) {
          return {
            code: RESULT_SUCCESS,
            message: 'success',
            params: {
              'LightSwitch': this.light.isOn ? 1 : 0,
            }
          };
        }
        return {
          code: RESULT_FAILURE,
          message: 'The requested properties does not exist.',
        }
      }
    
      _callService(name, args) {
        console.log('Call service %s with %s on thing %s-%s', JSON.stringify(name),
          JSON.stringify(args), this.config.productKey, this.config.deviceName);
        return {
          code: RESULT_FAILURE,
          message: 'The requested service does not exist.',
        };
      }
    }
  3. Obtain the driver configuration and initialize the Connector class.
    • Call the getConfig operation to obtain the driver configuration.
    • Call the getThingInfos operation to obtain the information about and configuration of the simulated light.
    • Initialize the Connector class.
    • Call the connect method to connect the simulated light to Link IoT Edge.

    The sample code is as follows:

    // Obtain the configuration that is automatically generated when the simulated light is bound to this driver. getConfig()
      .then((config) => {
        // Obtain the simulated light information, for example, the product key and device
        // name of the simulated light, from config.
        const thingInfos = config.getThingInfos();
        thingInfos.forEach((thingInfo) => {
          const light = new Light();
          // The value format of the ThingInfo parameter is supported by config of Connector. Pass the ThingInfo parameter directly.
          const connector = new Connector(thingInfo, light);
          connector.connect();
        });
      });

LightSensor

To develop a driver for a simulated light sensor, follow these steps:
  1. Define an abstract class for the simulated light sensor that automatically runs when a listener listens to it and stops running when the listener is cleaned.

    The sample code is as follows:

    /**
     * Define an abstract class for the simulated light sensor that starts to publish illuminance between 100
     * and 600 with 100 delta changes when a listener listens to it.
     */
    class LightSensor {
      constructor() {
        this._illuminance = 200;
        this._delta = 100;
      }
    
      get illuminance() {
        return this._illuminance;
      }
    
      // Start to work.
      start() {
        if (this._clearInterval) {
          this._clearInterval();
        }
        console.log('Starting light sensor...');
        const timeout = setInterval(() => {
          // Update illuminance and delta.
          let delta = this._delta;
          let illuminance = this._illuminance;
          if (illuminance >= 600 || illuminance <= 100) {
            delta = -delta;
          }
          illuminance += delta;
          this._delta = delta;
          this._illuminance = illuminance;
    
          if (this._listener) {
            this._listener({
              properties: {
                illuminance,
              }
            });
          }
        }, 2000);
        this._clearInterval = () => {
          clearInterval(timeout);
          this._clearInterval = undefined;
        };
        return this._clearInterval;
      }
    
      stop() {
        console.log('Stopping light sensor ...');
        if (this._clearInterval) {
          this._clearInterval();
        }
      }
    
      listen(callback) {
        if (callback) {
          this._listener = callback;
          // Start to work when a listener listens to it.
          this.start();
        } else {
          this._listener = undefined;
          this.stop();
        }
      }
    }
  2. Define the Connector class. The Connector class provides the following features:
    • Receives the configuration and an abstract object of the simulated light sensor and constructs a ThingAccessClient object for interacting with Link IoT Edge.
    • Implements three callback functions of the ThingAccessClientCallbacks class and uses the callback functions to call interfaces encapsulated by the LightSensor class.
    • Provides the connect and disconnect methods. The connect method can connect the simulated light sensor to Link IoT Edge and the disconnect method can disconnect the simulated light sensor from Link IoT Edge.

    The sample code is as follows:

    /**
     * Construct a class to combine ThingAccessClient and the abstract class of the simulated light sensor that connects to Link IoT Edge. */
    class Connector {
      constructor(config, lightSensor) {
        this.config = config;
        this.lightSensor = lightSensor;
        this._client = new ThingAccessClient(config, {
          setProperties: this._setProperties.bind(this),
          getProperties: this._getProperties.bind(this),
          callService: this._callService.bind(this),
        });
      }
    
      /**
       * Connect to Link IoT Edge and publish properties to it.   */
      connect() {
        registerAndOnlineWithBackOffRetry(this._client, 1)
          .then(() => {
            return new Promise(() => {
              // Run, listen to the simulated light sensor, and report property data changes of the sensor to Link IoT Edge.
              this.lightSensor.listen((data) => {
                const properties = {'MeasuredIlluminance': data.properties.illuminance};
                console.log(`Report properties: ${JSON.stringify(properties)}`);
                this._client.reportProperties(properties);
              });
            });
          })
          .catch(err => {
            console.log(err);
            return this._client.cleanup();
          })
          .catch(err => {
            console.log(err);
          });
      }
    
      /**
       * Disconnect from Link IoT Edge.
       */
      disconnect() {
        // Clean the listener.
        this.lightSensor.listen(undefined);
        this._client.cleanup()
          .catch(err => {
            console.log(err);
          });
      }
    
      _setProperties(properties) {
        console.log('Set properties %s to thing %s-%s', JSON.stringify(properties),
          this.config.productKey, this.config.deviceName);
        return {
          code: RESULT_FAILURE,
          message: 'The property is read-only.',
        };
      }
    
      _getProperties(keys) {
        console.log('Get properties %s from thing %s-%s', JSON.stringify(keys),
          this.config.productKey, this.config.deviceName);
        if (keys.includes('MeasuredIlluminance')) {
          return {
            code: RESULT_SUCCESS,
            message: 'success',
            params: {
              'MeasuredIlluminance': this.lightSensor.illuminance,
            }
          };
        }
        return {
          code: RESULT_FAILURE,
          message: 'The requested properties does not exist.',
        }
      }
    
      _callService(name, args) {
        console.log('Call service %s with %s on thing %s-%s', JSON.stringify(name),
          JSON.stringify(args), this.config.productKey, this.config.deviceName);
        return {
          code: RESULT_FAILURE,
          message: 'The requested service does not exist.',
        };
      }
    }
  3. Obtain the driver configuration and initialize the Connector class.
    • Call the getConfig operation to obtain the driver configuration.
    • Call the getThingInfos operation to obtain the information about and configuration of the simulated light sensor.
    • Initialize the Connector class.
    • Call the connect method to connect the simulated light sensor to Link IoT Edge.

    The sample code is as follows:

    // Obtain the configuration that is automatically generated when the simulated light sensor is bound to this driver. getConfig()
      .then((config) => {
        // Obtain the information about the simulated light sensor, for example, the product key and device    // name of the simulated light sensor, from config.    const thingInfos = config.getThingInfos();
        thingInfos.forEach((thingInfo) => {
          const lightSensor = new LightSensor();
          // The value format of the ThingInfo parameter is supported by config of Connector. Pass the ThingInfo parameter directly.      const connector = new Connector(thingInfo, lightSensor);
          connector.connect();
        });
      });