边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发

边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发

  • 使用SDK开发真实设备接入服务
    • 着手编写一个温湿度设备接入
      • 准备相关文件及目录
      • 脚本可选,用于单文件编译测试
    • 编写温湿度设备接入设备服务
      • 功能框架
        • 设备名的解析
        • 配置文件xx.yaml和configuration.toml
        • 获取数据接口中的参数使用
        • 编译
        • 运行
      • 假定一个实际需求
      • 定义它设备配置文件
        • 设备配置文件定义的一些关系
        • 浮点数精度问题
      • 设备驱动

整个EdgeXFoundry框架整体功能来说是比较全面的,但是也只是框架而已,具体的边缘计算实现的功能:各类传感器数据的上报、各类算法的部署、各类数据的流转、各种场景的控制等等皆须要设备服务的实现

为了实现它,你需要考虑以下:

  • 设备驱动的扩展性
  • 数据的完整及安全性
  • 部署的简易性
  • 设备服务性能(容量、速度、兼容)

使用SDK开发真实设备接入服务

当然博客不会贴出全部代码,重在(个人理解下的)功能框架,然后按需开发!

着手编写一个温湿度设备接入

准备相关文件及目录

建立一个文件夹如:temperature-device-driver

mkdir temperature-device-driver && cd temperature-device-driver

建立device-temperature.c文件、建立res目录

脚本可选,用于单文件编译测试

temperature-device-driver目录下建立build.sh脚本文件,内容如下:

#!/bin/sh

#定义SDK源码头文件目录
SDK_INC_DIR=/home/aron566/Workspace/C_SDK/device-sdk-c/include

#定义SDK动态库文件目录
SDK_LIB_DIR=/home/aron566/Workspace/C_SDK/device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-1.3.0/lib

#定义编译生成的APP名称
TARGET_APP_NAME=device_driver

#定义源码文件名称
SOURCE_FILE_NAME=device_driver.c

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SDK_LIB_DIR

case "$1" in
	make)
		gcc -I$SDK_INC_DIR -L$SDK_LIB_DIR -o $TARGET_APP_NAME $SOURCE_FILE_NAME -lcsdk
		;;
	run)
		./$TARGET_APP_NAME -c res
		;;
	*)
		echo "Usage: $0 {make|run}"
		exit 1
esac

添加可执行权限

sudo chmod +x build.sh

编写温湿度设备接入设备服务

功能框架

参考阿里云方式,控制台以每个边缘网关为首向下拓展

  • 边缘网关,运行设备服务,并预先编写好设备配置文件与服务配置文件部署到网关并建立服务
  • 控制台中设备服务列表代表着不同功能的边缘网关,实际就是多协议支持的网关服务
  • 遵照驱动中支持的通讯协议设备,由控制台端编写不同的设备配置文件(modbus温度传感器、光照等各类型传感器或者其他自定义协议设备)
  • 动态部署使用,在控制台的上级网关的设备服务下建立设备,为其配置特定设备配置文件及其他参数,特别指定设备名(协议-位置信息(应包含上级网关信息+地理位置)-设备名(可重复)-地址号或者序号(同名设备不可重复),这里设备配置即物模型可以下发给网关也可以不下发由设备服务自动获取控制台端的设备配置文件
  • 控制台创建号设备信息后,通过已正常通讯的上级网关设备服务下发更新后的设备驱动库文件到远程网关,给设备服务调用,完成动态加载设备驱动。
  • 完成驱动的更新下发后,下发新增设备后的服务配置文件即configuration.toml文件
    这样后台浏览设备易于查看:物理通讯TOPO关系、通讯地址及协议、地理区域信息。

设备名的解析

#include  /**< need definition of uint8_t */                          
#include  /**< need definition of NULL    */                          
#include /**< need definition of BOOL    */                        
#include   /**< if need printf             */                          
#include                                                              
#include   

typedef struct 
{
    char protocol_str[64];      /**< 协议名*/
    char location_str[128];     /**< 设备位置信息*/
    char dev_type_name[64];     /**< 设备类型名称*/
    char dev_address[16];       /**< 设备地址号*/
}DEV_INFO_Typedef_t;

int main(int argc ,char *argv[])
{
    DEV_INFO_Typedef_t dev_info;
    int ret = sscanf(argv[1], "'%[^-]-%[^-]-%[^-]-%[^']'", dev_info.protocol_str, 
                    dev_info.location_str, dev_info.dev_type_name, dev_info.dev_address);
    if(ret != 4)
    {
        printf("parse dev_name error.\r\n");
        return -1;
    }
    else
    {
        printf("proto:%s\t\t location_str:%s\t\tdev_type_name:%s\t\tdev_address:%s\n",dev_info.protocol_str, 
                    dev_info.location_str, dev_info.dev_type_name, dev_info.dev_address);
        return 0;
    }
}

测试

#编译运行测试
gcc test.c
sudo chmod +x a.out
./a.out "'modbus_rtu-01_hangzhou-temperature_device-1'"

记住,完善接口即可
以下代码中,我已将设备服务设备驱动分离,即设备驱动为动态库方式存在
设备服务的需求:获取设备数据、控制设备、更新设备
设备驱动的功能:提供数据获取接口、提供控制设备接口、提供更新设备接口

/**                                                                            
 *  @file main_device_service.c                                                   
 *                                                                              
 *  @date 2020年11月08日 11:14:06 星期天
 *
 *  @author aron566
 *
 *  @copyright None
 *
 *  @brief EdgeXFoudry 设备服务驱动.
 *
 *  @details Pseudo-device service illustrating resource aggregation using C SDK .
 *
 *  @version V1.0
 */
#ifdef __cplusplus ///
extern "C" {
#endif
/** Includes -----------------------------------------------------------------*/
#include 
#include 
#include 
#include 
#include 
#include 
/* Private includes ----------------------------------------------------------*/
#include "devsdk/devsdk.h"
#include "main_device_service.h"
/** Private typedef ----------------------------------------------------------*/

typedef struct custom_device_driver
{
  iot_logger_t * lc;
} custom_device_driver;
/** Private macros -----------------------------------------------------------*/
#define ERR_CHECK(x) if(x.code){fprintf(stderr, "Error: %d: %s\n", x.code, x.reason); devsdk_service_free (service); free (impl); return x.code;}
#define ERR_BUFSZ 1024
#define ERR_CUSTOM_DEVICE_WRITE "PUT called for custom_device device. This is a read-only device."
#define ERR_CUSTOM_DEVICE_NO_PARAM "No parameter attribute in GET request."

/** Private constants --------------------------------------------------------*/
/** Public variables ---------------------------------------------------------*/
/** Private variables --------------------------------------------------------*/

/** Private function prototypes ----------------------------------------------*/
/*初始化设备服务*/
static bool custom_device_init(void * impl, struct iot_logger_t * lc, const iot_data_t * config);
/*响应GET请求*/
static bool custom_device_get_handler(void * impl, const char * devname,
                                      const devsdk_protocols * protocols,
                                      uint32_t nreadings,
                                      const devsdk_commandrequest * requests,devsdk_commandresult * readings,
                                      const devsdk_nvpairs * qparams,iot_data_t ** exception);
/*响应PUT请求*/
static bool custom_device_put_handler(void * impl, const char * devname, const devsdk_protocols * protocols,
                                      uint32_t nvalues,
                                      const devsdk_commandrequest * requests,
                                      const iot_data_t * values[],
                                      iot_data_t ** exception);
/*响应重置请求*/
static void custom_device_reconfigure(void *impl, const iot_data_t *config);
/*响应发现请求*/
static void custom_device_discover(void *impl);
/*响应停止请求*/
static void custom_device_stop(void * impl, bool force);
/** Private user code --------------------------------------------------------*/                                                                         
/** Private application code -------------------------------------------------*/
/*******************************************************************************
*                                                                               
*       Static code                                                             
*                                                                               
********************************************************************************
*/
/**
 * @brief Function called during service start operation.
 * @param impl The context data passed in when the service was created.
 * @param lc A logging client for the device service.
 * @param config A string map containing the configuration specified in the service's "Driver" section.
 * @return true if the operation was successful, false otherwise.
 */
static bool custom_device_init(void * impl, struct iot_logger_t * lc, const iot_data_t * config)
{
  custom_device_driver * driver = (custom_device_driver *)impl;
  driver->lc = lc;

  /*初始化设备驱动*/
  device_driver_opt_init(lc, config);

  return true;
}

/**
 * @brief Callback issued to handle GET requests for device readings.
 * @param impl The context data passed in when the service was created.
 * @param devname The name of the device to be queried.
 * @param protocols The location of the device to be queried.
 * @param nreadings The number of readings requested.
 * @param requests An array specifying the readings that have been requested.
 * @param readings An array in which to return the requested readings.
 * @param qparams Query Parameters which were set for this request.
 * @param exception Set this to an IOT_DATA_STRING to give more information if the operation fails.
 * @return true if the operation was successful, false otherwise.
 */
static bool custom_device_get_handler(
  void * impl,                            /**< 自定义数据*/
  const char * devname,                   /**< 设备名称*/
  const devsdk_protocols * protocols,     /**< 请求设备所归属的协议*/
  uint32_t nreadings,                     /**< 请求类型的数量*/
  const devsdk_commandrequest * requests, /**< 请求参数列表*/
  devsdk_commandresult * readings,        /**< 返回结果给Edge控制台*/
  const devsdk_nvpairs * qparams,         /**< 请求的附加参数*/
  iot_data_t ** exception)                /**< 返回请求结果说明信息*/
{
  int ret = 0;
  const char *param;

  custom_device_driver * driver = (custom_device_driver *) impl;

  for(uint32_t i = 0; i < nreadings; i++)
  {
    /*获取GET参数*/
    /*参数来自.yaml中deviceResources列表中attributes项parameter键值*/
    param = devsdk_nvpairs_value (requests[i].attributes, "parameter");
    if(param == NULL)
    {
      iot_log_error (driver->lc, ERR_CUSTOM_DEVICE_NO_PARAM);
      * exception = iot_data_alloc_string (ERR_CUSTOM_DEVICE_NO_PARAM, IOT_DATA_REF);
      return false;
    }

    ret = device_driver_opt_get(devname, param, &readings[i], driver->lc);
    if(ret != 0)
    {
      iot_log_error(driver->lc, "get dev: %s par: error.", devname, param);
    }
  }
  return true;
}

/**
 * @brief Callback issued to handle PUT requests for setting device values.
 * @param impl The context data passed in when the service was created.
 * @param devname The name of the device to be queried.
 * @param protocols The location of the device to be queried.
 * @param nvalues The number of set operations requested.
 * @param requests An array specifying the resources to which to write.
 * @param values An array specifying the values to be written.
 * @param exception Set this to an IOT_DATA_STRING to give more information if the operation fails.
 * @return true if the operation was successful, false otherwise.
 */
static bool custom_device_put_handler(
  void * impl,
  const char * devname,
  const devsdk_protocols * protocols,
  uint32_t nvalues,
  const devsdk_commandrequest * requests,
  const iot_data_t * values[],
  iot_data_t ** exception)
{
  int ret = 0;
  const char *param;

  custom_device_driver * driver = (custom_device_driver *) impl;
 
  for(uint32_t i = 0; i < nvalues; i++)
  {
    param = devsdk_nvpairs_value (requests[i].attributes, "parameter");
    if(param == NULL)
    {
      iot_log_error (driver->lc, ERR_CUSTOM_DEVICE_NO_PARAM);
      * exception = iot_data_alloc_string (ERR_CUSTOM_DEVICE_NO_PARAM, IOT_DATA_REF);
      continue;
    }

    /*设置请求*/
    ret = device_driver_opt_set(devname, param, values[i], driver->lc);
    if(ret != 0)
    {
      iot_log_error(driver->lc, "set dev: %s par: error.", devname, param);
    }
    
  }
  return true;
}

/**
 * @brief Function called when configuration is updated.
 * @param impl The context data passed in when the service was created.
 * @param config A string map containing the new configuration.
 */
static void custom_device_reconfigure(void *impl, const iot_data_t *config)
{
  custom_device_driver * driver = (custom_device_driver *) impl;

  device_driver_opt_reconfigure(driver->lc, config);
}

/**
 * @brief Optional callback for dynamic discovery of devices. The implementation should detect devices and register them using
 *        the devsdk_add_device API call.
 * @param impl The context data passed in when the service was created.
 */
static void custom_device_discover(void *impl) 
{
  custom_device_driver * driver = (custom_device_driver *) impl;

  device_driver_opt_discover(driver->lc);
}

/**
 * @brief Callback issued during device service shutdown. The implementation should stop processing and release any resources that were being used.
 * @param impl The context data passed in when the service was created.
 * @param force A 'force' stop has been requested. An unclean shutdown may be performed if necessary.
 */
static void custom_device_stop(void *impl, bool force)
{
  /* Stop performs any final actions before the device service is terminated */
  custom_device_driver * driver = (custom_device_driver *) impl;

  device_driver_opt_stop(driver->lc, force);
}

/** Public application code --------------------------------------------------*/
/*******************************************************************************
*                                                                               
*       Public code                                                             
*                                                                               
********************************************************************************
*/
/**
 * @brief 设备服务驱动入口
 */
int main (int argc, char * argv[])
{
  sigset_t set;
  int sigret;
  
  custom_device_driver * impl = malloc (sizeof (custom_device_driver));
  impl->lc = NULL;

  devsdk_error e;
  e.code = 0;

  devsdk_callbacks custom_deviceImpls =
  {
    custom_device_init,         /* Initialize */
    custom_device_reconfigure,  /* Reconfigure */
    custom_device_discover,     /* Discovery */
    custom_device_get_handler,  /* Get */
    custom_device_put_handler,  /* Put */
    custom_device_stop          /* Stop */
  };

  devsdk_service_t * service = devsdk_service_new("device-custom_device", "1.0", impl, custom_deviceImpls, &argc, argv, &e);
  ERR_CHECK (e);

  int n = 1;
  while (n < argc)
  {
    if (strcmp (argv[n], "-h") == 0 || strcmp (argv[n], "--help") == 0)
    {
      printf ("Options:\n");
      printf ("  -h, --help\t\t\tShow this text\n");
      return 0;
    }
    else
    {
      printf ("%s: Unrecognized option %s\n", argv[0], argv[n]);
      return 0;
    }
  }

  devsdk_service_start (service, NULL, &e);
  ERR_CHECK (e);

  sigemptyset (&set);
  sigaddset (&set, SIGINT);
  sigprocmask (SIG_BLOCK, &set, NULL);
  sigwait (&set, &sigret);
  sigprocmask (SIG_UNBLOCK, &set, NULL);

  devsdk_service_stop (service, true, &e);
  ERR_CHECK (e);

  devsdk_service_free (service);
  free (impl);
  return 0;
}

#ifdef __cplusplus ///
}                                                                               
#endif                                                                          
/******************************** End of file *********************************/                                                                                                                                                 

配置文件xx.yaml和configuration.toml

xx.yaml的修改需遵照设备服务提供的设备功能
官方对配置的介绍
其他配置的介绍

设备服务配置文件
配置文件中,主要关注AutoEvents项,配置它可以实现主动上报功能,这个可以自行运行demo测试

[Service]
  Port = 50001
  Timeout = 5000
  ConnectRetries = 10
  Labels = [ 'MQTT_Protocol' ,'MODBUS_Protocol' ]
  StartupMsg = 'mqtt modbus device service started'
  CheckInterval = '10s'

[Clients]
  [Clients.Data]
    Host = 'localhost'
    Port = 48080

  [Clients.Metadata]
    Host = 'localhost'
    Port = 48081

[Device]
  DataTransform = false
  Discovery = false
  MaxCmdOps = 128
  MaxCmdResultLen = 256

[Logging]
  LogLevel = 'DEBUG'

[[DeviceList]]
  Name = 'mqtt-gateway_01_hangzhou-gateway_device-1'
  Profile = 'mqtt_gateway_device_profile'
  Description = 'An gatway device'
  [DeviceList.Protocols]
    [DeviceList.Protocols.mqtt]
      Schema = 'tcp'
      Host = 'localhost'
      Port = 1883
      User = ''
      Password = ''
      ClientId = 1
      Topic = ''
      SubTopic = 'subcribe_test'
      PubTopic = 'publish_test'
  [[DeviceList.AutoEvents]]
    Resource = 'temperature'
    OnChange = false
    Frequency = '10s'
  [[DeviceList.AutoEvents]]
    Resource = 'run_state'
    OnChange = true
    Frequency = '15000ms'

[[DeviceList]]
  Name = 'modbus_rtu-gateway_01_hangzhou-temperature_device-1'
  Profile = 'modbus_temperature_device_profile'
  Description = 'An temperature device'
  [DeviceList.Protocols]
    [DeviceList.Protocols.modbus-rtu]
      Address = '/tmp/slave'
      BaudRate = 9600
      DataBits = 8
      StopBits = 1
      Parity = 'N'
      UnitID = 1
  [[DeviceList.AutoEvents]]
    Resource = 'temperature'
    OnChange = false
    Frequency = '10s'
  [[DeviceList.AutoEvents]]
    Resource = 'humidity'
    OnChange = true
    Frequency = '15000ms'

[[DeviceList]]
  Name = 'modbus_rtu-gateway_01_hangzhou-temperature_device-2'
  Profile = 'modbus_temperature_device_profile'
  Description = 'An temperature device'
  [DeviceList.Protocols]
    [DeviceList.Protocols.modbus-rtu]
      Address = '/tmp/slave'
      BaudRate = 9600
      DataBits = 8
      StopBits = 1
      Parity = 'N'
      UnitID = 2
  [[DeviceList.AutoEvents]]
    Resource = 'temperature'
    OnChange = false
    Frequency = '10s'
  [[DeviceList.AutoEvents]]
    Resource = 'humidity'
    OnChange = true
    Frequency = '15000ms'

获取数据接口中的参数使用

如下是一个温湿度的设备配置文件,当一个get请求过来
边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发_第1张图片

/*获取 parameter: "temperature" 中temperature字符串*/
const char *param = devsdk_nvpairs_value (requests[i].attributes, "parameter");

/*获取 name: temperature 中temperature字符串*/
const char *name = requests[i].resname;

/*获取数据类型*/
const iot_typecode_t type = *(requests[i].type);

上传设备配置文件即物模型,如遇以下情况,请检查:

  • 网络
  • 文件内容是否标准,因为是yaml格式特别注意空格对齐问题
    边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发_第2张图片

编译

./build make

运行

运行可以使用脚本,当然需要在脚本中修改定义好变量

./build run

输出内容如下

aron566@MinT-machine:~/Workspace/custom_device_driver/build/devices_service$ ./custom-device -c res
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="iot_threadpool_alloc (threads: 8 max_jobs: 0 default_priority: -1 affinity: -1)"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-0 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-1 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-2 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-3 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-5 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-6 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-4 starting"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-0-7 starting"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="iot_scheduler_alloc (priority: -1 affinity: -1)"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="iot_threadpool_start()"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=TRACE ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread waiting for new job"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Setting LogLevel to DEBUG"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Starting device-custom_device device service, version 1.0"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="EdgeX device SDK for C, version 1.3.0"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service configuration follows:"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/Host="MinT-machine""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/Port=50000"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/Timeout=5000"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/ConnectRetries=10"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/StartupMsg="mqtt modbus device service started""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/CheckInterval="10s""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/Labels="""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Service/ServerBindAddr="""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/DataTransform=false"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/Discovery/Enabled=true"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/Discovery/Interval=0"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/MaxCmdOps=128"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/MaxCmdResultLen=256"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/ProfilesDir="res""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/UpdateLastConnected=false"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device/EventQLength=0"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Logging/LogLevel="DEBUG""
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Logging/EnableRemote=false"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Logging/File="""
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="iot_threadpool_alloc (threads: 1 max_jobs: 0 default_priority: -1 affinity: -1)"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Thread iot-1-0 starting"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Found core-data service at localhost:48080"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Found core-metadata service at localhost:48081"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Processing Device Profiles from res"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Checking existence of DeviceProfile mqtt_gateway_device_profile"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="DeviceProfile mqtt_gateway_device_profile already exists: skipped"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Checking existence of DeviceProfile modbus_temperature_device_profile"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="DeviceProfile modbus_temperature_device_profile already exists: skipped"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Starting HTTP server on interface MinT-machine, port 50000"
level=DEBUG ts=2020-11-28T16:49:17Z app=device-custom_device msg="Resolved interface is 2408:823c:815:3a8:5351:4fa2:768d:cd1c"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Processing DeviceList from configuration"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device mqtt-gateway_01_hangzhou-gateway_device-1 already exists: skipped"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device modbus_rtu-gateway_01_hangzhou-temperature_device-1 already exists: skipped"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="Device modbus_rtu-gateway_01_hangzhou-temperature_device-2 already exists: skipped"
level=INFO ts=2020-11-28T16:49:17Z app=device-custom_device msg="start device driver now."
Name = mqtt-gateway_01_hangzhou-gateway_device-1
gatway device register.
Name = modbus_rtu-gateway_01_hangzhou-temperature_device-1
temperature device register.
Name = modbus_rtu-gateway_01_hangzhou-temperature_device-2
temperature device register.

控制台状态
设备服务,与下级温湿度设备存在
边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发_第3张图片
以上为建立一个新设备的过程

假定一个实际需求

  • 协议:modbus-rtu,很常见
  • 设备:温湿度设备,很常见
  • 设备自身可提供的数值信息:温度、湿度
  • 设备地址:modbus地址1
  • 读取数据发送:0x01,0x03,0x00,0x00,0x00,0x02,0xc4,0x0b
  • 需要提供的功能:1、读取设备温湿度数据2、可设定温湿度数值的上下限3、可提供报警功能(掉线报警、高低温、高低湿度报警)

定义它设备配置文件

设备资源

  • 温度参数
  • 湿度参数
  • 温度报警值设定(分高低)
  • 湿度报警值设定(分高低)
  • 设备在线状态
  • 设备事件(高低温事件和高低湿度事件)

设备配置文件定义的一些关系

coreCommands 里面的name就是UI平台上的标签,并无多大意义
定义coreCommands 的path: “/api/v1/device/{deviceId}/modbus_temperature_device” modbus_temperature_device必须是个资源名称
可以是deviceResources里面的name的值
也可以是deviceCommands里面name的值
deviceCommands里面get/set 只能是deviceResources里面的name

浮点数精度问题

如果 rawType 属性存在,设备服务将根据定义的 rawType 解析二进制数据,然后根据设备资源属性中定义的值类型转换值。

  • 比如设备读取的湿度寄存器值为52,实际为52%
  • 依据下面配置则,52 以二进制数读取,存储类型为INT16,之后转为浮点数乘上0.01给设备服务
    边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发_第4张图片
    边缘计算开源框架EdgeXFoundry的部署应用开发(三)设备服务开发_第5张图片
    name: humidity
    description: "humidity realtime value"
    attributes:
      { parameter: "humidity", rawType: "INT16" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00", scale: "0.01"}
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}

我的温湿度设备配置文件如下,其实还是可以写寄存器地址给设备驱动读取

name: "modbus_temperature_device_profile"
manufacturer: "IoTechSystems"
model: "IoT6"
description: "Temperature & Humidity Device Profile"
labels:
  - "temperature_sensor"
  - "modbus_protocol"

deviceResources:
  -
    name: temperature
    description: "temperature realtime value"
    attributes:
      { parameter: "temperature" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "degrees Celsius"}
  -
    name: humidity
    description: "humidity realtime value"
    attributes:
      { parameter: "humidity" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}
  -
    name: humiditymin
    description: "humidity min value"
    attributes:
      { parameter: "humiditymin" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "RW", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}
  -
    name: temperaturemin
    description: "temperature min value"
    attributes:
      { parameter: "temperaturemin" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "RW", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "degrees Celsius"}
  -
    name: humiditymax
    description: "humidity max value"
    attributes:
      { parameter: "humiditymax" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "RW", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}
  -
    name: temperaturemax
    description: "temperature max value"
    attributes:
      { parameter: "temperaturemax" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "RW", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "degrees Celsius"}
  -
    name: temperatureHI
    description: "temperature max value is arrived"
    attributes:
      { parameter: "temperatureHI" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "degrees Celsius"}
  -
    name: humidityHI
    description: "humidity max value is arrived"
    attributes:
      { parameter: "humidityHI" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}
  -
    name: temperatureLOW
    description: "temperature low value is arrived"
    attributes:
      { parameter: "temperatureLOW" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "degrees Celsius"}
  -
    name: humidityLOW
    description: "humidity low value is arrived"
    attributes:
      { parameter: "humidityLOW" }
    properties:
      value:
       { type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "-1000.00", maximum: "1000.00" }
      units:
       { type: "String", readWrite: "R", defaultValue: "%RH"}
  -
    name: online_state
    description: "device online state"
    attributes:
      { parameter: "online_state" }
    properties:
      value:
       { type: "int32", size: "4", readWrite: "R", defaultValue: "0", minimum: "0", maximum: "1" }
      units:
       { type: "String", readWrite: "R", defaultValue: "leave time"}

deviceCommands:
  -
    name: modbus_temperature_device
    get:
      - { deviceResource: "temperature" }
      - { deviceResource: "humidity" }
      - { deviceResource: "temperaturemin" }
      - { deviceResource: "humiditymin" }
      - { deviceResource: "temperaturemax" }
      - { deviceResource: "humiditymax" }
      - { deviceResource: "online_state" }
    set:
      - { deviceResource: "temperaturemin" }
      - { deviceResource: "humiditymin" }
      - { deviceResource: "temperaturemax" }
      - { deviceResource: "humiditymax" }
coreCommands:
  -
    name: get_temperature_device_all
    get:
      path: "/api/v1/device/{deviceId}/modbus_temperature_device"
      responses:
      - code: "200"
        description: "Successfully read the modbus_temperature_device sensors."
        expectedValues: [ "modbus_temperature_device" ]
        #expectedValues: [ "temperature", "humidity", "temperaturemin", "humiditymin", "temperaturemax", "humiditymax", "online_state" ]
      - code: "503"
        description: "service unavailable"
        expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/modbus_temperature_device"
      parameterNames: [ "temperaturemin", "humiditymin", "temperaturemax", "humiditymax" ]
      responses:
      - code: "200"
        description: "Successfully set the temperaturemin and humiditymin."
        expectedValues: []
      - code: "503"
        description: "service unavailable"
        expectedValues: []
  -
    name: temperature
    get:
        path: "/api/v1/device/{deviceId}/temperature"
        responses:
          -
            code: "200"
            description: "Get the temperature reading."
            expectedValues: ["temperature"]
          -
            code: "503"
            description: "service unavailable"
            expectedValues: []

  -
    name: humidity
    get:
      path: "/api/v1/device/{deviceId}/humidity"
      responses:
        -
          code: "200"
          description: "Get the humidity reading."
          expectedValues: ["humidity"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []

  -
    name: temperaturemin
    get:
      path: "/api/v1/device/{deviceId}/temperaturemin"
      responses:
      - code: "200"
        description: "Get the temperaturemin value."
        expectedValues: ["temperaturemin"]
      - code: "503"
        description: "service unavailable"
        expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/temperaturemin"
      parameterNames: ["temperaturemin"]
      responses:
      - code: "200"
        description: "Successfully set the temperaturemin value."
        expectedValues: []
      - code: "503"
        description: "service unavailable"
        expectedValues: []
  -
    name: humiditymin
    get:
      path: "/api/v1/device/{deviceId}/humiditymin"
      responses:
      - code: "200"
        description: "Get the humiditymin value."
        expectedValues: ["humiditymin"]
      - code: "503"
        description: "service unavailable"
        expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/humiditymin"
      parameterNames: ["humiditymin"]
      responses:
      - code: "200"
        description: "Successfully set the humiditymin value."
        expectedValues: []
      - code: "503"
        description: "service unavailable"
        expectedValues: []
  -
    name: temperaturemax
    get:
      path: "/api/v1/device/{deviceId}/temperaturemax"
      responses:
      - code: "200"
        description: "Get the temperaturemax value."
        expectedValues: ["temperaturemax"]
      - code: "503"
        description: "service unavailable"
        expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/temperaturemax"
      parameterNames: ["temperaturemax"]
      responses:
      - code: "200"
        description: "Successfully set the temperaturemax value."
        expectedValues: []
      - code: "503"
        description: "service unavailable"
        expectedValues: []
  -
    name: humiditymax
    get:
      path: "/api/v1/device/{deviceId}/humiditymax"
      responses:
      - code: "200"
        description: "Get the humiditymax value."
        expectedValues: ["humiditymax"]
      - code: "503"
        description: "service unavailable"
        expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/humiditymax"
      parameterNames: ["humiditymax"]
      responses:
      - code: "200"
        description: "Successfully set the humiditymax value."
        expectedValues: []
      - code: "503"
        description: "service unavailable"
        expectedValues: []
  -
    name: temperatureHI
    get:
      path: "/api/v1/device/{deviceId}/temperatureHI"
      responses:
        -
          code: "200"
          description: "Get the temperatureHI reading."
          expectedValues: ["temperatureHI"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []
  -
    name: humidityHI
    get:
      path: "/api/v1/device/{deviceId}/humidityHI"
      responses:
        -
          code: "200"
          description: "Get the humidityHI reading."
          expectedValues: ["humidityHI"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []
  -
    name: temperatureLOW
    get:
      path: "/api/v1/device/{deviceId}/temperatureLOW"
      responses:
        -
          code: "200"
          description: "Get the temperatureLOW reading."
          expectedValues: ["temperatureLOW"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []
  -
    name: humidityLOW
    get:
      path: "/api/v1/device/{deviceId}/humidityLOW"
      responses:
        -
          code: "200"
          description: "Get the humidityLOW reading."
          expectedValues: ["humidityLOW"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []
  -
    name: online_state
    get:
      path: "/api/v1/device/{deviceId}/online_state"
      responses:
        -
          code: "200"
          description: "Get the online state reading."
          expectedValues: ["online_state"]
        -
          code: "503"
          description: "service unavailable"
          expectedValues: []

出现如下错误请先检查参数配置文件中是否有问题
在这里插入图片描述

设备驱动

以上贴出的main_device_service.c实则实现设备服务,其内部get、put接口将调用设备驱动的接口
设备驱动你需要做的事:

  • 设备硬件初始化
  • 实现解析网关下挂载的设备类型信息
  • 实现取对应不同类型的硬件设备数据
  • 实现对不同硬件设备的控制
  • 实现事件功能(功能的延申)
  • 实现驱动更新功能(动态扩展)

其实边缘网关实现减轻云端压力,释放边缘端的计算能力,最终各种算法的部署(更多种情况数据分析那种利用云端处理例如阿里,你上报的数据在云端数据库存储,它提供云计算服务你缴费.!),计算对于我来说就是一种设备,这个设备可以是虚拟的、也可是真实的

你可能感兴趣的:(边缘计算)