All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.huaweicloud.sdk.iot.device.service.AbstractService Maven / Gradle / Ivy

/*
 * Copyright (c) 2020-2023 Huawei Cloud Computing Technology Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.huaweicloud.sdk.iot.device.service;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.huaweicloud.sdk.iot.device.client.IotResult;
import com.huaweicloud.sdk.iot.device.client.requests.Command;
import com.huaweicloud.sdk.iot.device.client.requests.CommandRsp;
import com.huaweicloud.sdk.iot.device.client.requests.DeviceEvent;
import com.huaweicloud.sdk.iot.device.utils.ExceptionUtil;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 抽象服务类,提供了属性自动读写和命令调用能力,用户可以继承此类,根据物模型定义自己的服务
 */
@JsonFilter("AbstractService")
public abstract class AbstractService implements IService {
    private static final Logger log = LogManager.getLogger(AbstractService.class);

    private AbstractDevice iotDevice;

    private final Map commands = new HashMap<>();

    private final Map writeableFields = new HashMap<>();

    private final Map readableFields = new HashMap<>();

    private Timer timer;

    private String serviceId;

    private static class FieldPair {
        String propertyName;

        Field field;

        FieldPair(String propertyName, Field field) {
            this.propertyName = propertyName;
            this.field = field;
        }
    }

    public AbstractService() {
        for (Field field : this.getClass().getDeclaredFields()) {

            Property property = field.getAnnotation(Property.class);
            if (property == null) {
                continue;
            }

            String name = property.name();
            if (name.isEmpty()) {
                name = field.getName();
            }
            if (property.writeable()) {
                writeableFields.put(name, field);
            }

            // 这里key是字段名,pair里保存属性名
            readableFields.put(field.getName(), new FieldPair(name, field));
        }

        for (Method method : this.getClass().getDeclaredMethods()) {
            DeviceCommand deviceCommand = method.getAnnotation(DeviceCommand.class);
            if (deviceCommand == null) {
                continue;
            }
            String name = deviceCommand.name();
            if (name.isEmpty()) {
                name = method.getName();
            }
            commands.put(name, method);
        }
    }

    private Object getFiledValue(String fieldName) {
        Field field = readableFields.get(fieldName).field;
        if (field == null) {
            log.error("field is null: " + fieldName);
            return null;
        }
        String getter = "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
        Method method;

        try {
            method = this.getClass().getDeclaredMethod(getter);
        } catch (NoSuchMethodException e) {
            log.error(ExceptionUtil.getBriefStackTrace(e));
            return null;
        }

        if (method == null) {
            log.error("method is null: " + getter);
            return null;
        }

        try {
            return method.invoke(this);
        } catch (IllegalAccessException | InvocationTargetException e) {
            log.error(ExceptionUtil.getBriefStackTrace(e));
        }

        return null;
    }

    /**
     * 读属性回调
     *
     * @param fields 指定读取的字段名,不指定则读取全部可读字段
     * @return 属性值
     */
    @Override
    public Map onRead(String... fields) {
        Map ret = new HashMap<>();

        // 读取指定的字段
        if (fields.length > 0) {
            for (String fieldName : fields) {
                if (readableFields.get(fieldName) == null) {
                    log.error("field is not readable:" + fieldName);
                    continue;
                }

                Object value = getFiledValue(fieldName);
                if (value != null) {
                    ret.put(readableFields.get(fieldName).propertyName, value);
                }
            }

            return ret;
        }

        // 读取全部字段
        for (Map.Entry entry : readableFields.entrySet()) {
            Object value = getFiledValue(entry.getKey());
            if (value != null) {
                ret.put(entry.getValue().propertyName, value);
            }
        }
        return ret;

    }

    /**
     * 写属性。收到平台下发的写属性操作时此接口被自动调用。
     * 如果用户希望在写属性时增加额外处理,可以重写此接口
     *
     * @param properties 平台期望属性的值
     * @return 操作结果
     */
    @Override
    public IotResult onWrite(Map properties) {
        List changedProps = new ArrayList<>();
        for (Map.Entry entry : properties.entrySet()) {

            Field field = writeableFields.get(entry.getKey());
            if (field == null) {
                log.error("field not found or not writeable " + entry.getKey());
                return new IotResult(-1, "field not found or not writeable " + entry.getKey());

            }

            Object value = entry.getValue();
            String setter = "set" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
            Method method = null;

            try {
                method = this.getClass().getDeclaredMethod(setter, field.getType());
            } catch (NoSuchMethodException e) {
                log.error(ExceptionUtil.getBriefStackTrace(e));

            }

            if (method == null) {
                log.error("method not found, the method is {}", setter);
                return new IotResult(-1, "method not found: " + setter);
            }

            try {
                method.invoke(this, value);
                log.info("write property ok, {}", entry.getKey());
                changedProps.add(field.getName());
            } catch (Exception e) {
                log.error(ExceptionUtil.getBriefStackTrace(e));
                return new IotResult(-1, e.getMessage());
            }
        }

        // 上报变化的属性
        if (changedProps.size() > 0) {
            firePropertiesChanged(changedProps.toArray(new String[changedProps.size()]));
        }

        return IotResult.SUCCESS;
    }

    /**
     * 事件处理。收到平台下发的事件时此接口被自动调用。默认为空实现
     *
     * @param deviceEvent 服务事件
     */
    @Override
    public void onEvent(DeviceEvent deviceEvent) {
        log.info("onEvent no op");
    }

    /**
     * 事件处理。收到平台下发的事件时此接口被自动调用。用户网桥场景下具体service来实现
     *
     * @param deviceEvent 服务事件
     */
    @Override
    public void onBridgeEvent(String deviceId, DeviceEvent deviceEvent) {
        log.info("onEvent no op");
    }


    /**
     * 通知服务属性变化
     *
     * @param properties 变化的属性,不指定默认读取全部可读属性
     */
    public void firePropertiesChanged(String... properties) {
        iotDevice.firePropertiesChanged(getServiceId(), properties);
    }

    /**
     * 执行设备命令。收到平台下发的命令时此接口被自动调用
     *
     * @param command 命令请求
     * @return 命令响应
     */
    @Override
    public CommandRsp onCommand(Command command) {

        Method method = commands.get(command.getCommandName());
        if (method == null) {
            log.error("command not found " + command.getCommandName());
            return new CommandRsp(CommandRsp.FAIL, "command not found");
        }

        try {
            return (CommandRsp) method.invoke(this, command.getParas());
        } catch (Exception e) {
            log.error(ExceptionUtil.getBriefStackTrace(e));
            return new CommandRsp(CommandRsp.FAIL, e.getCause());
        }
    }

    /**
     * 获取设备实例
     *
     * @return 设备实例
     */
    protected AbstractDevice getIotDevice() {
        return iotDevice;
    }

    /**
     * 设置设备实例
     *
     * @param iotDevice 设备实例
     */
    void setIotDevice(AbstractDevice iotDevice) {
        this.iotDevice = iotDevice;
    }

    public String getServiceId() {
        return serviceId;
    }

    public void setServiceId(String serviceId) {
        this.serviceId = serviceId;
    }

    /**
     * 开启自动周期上报属性
     *
     * @param reportInterval 上报周期,单位ms
     */
    public void enableAutoReport(int reportInterval) {
        if (timer != null) {
            log.error("timer is already enabled");
            return;
        }

        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                firePropertiesChanged();
            }
        }, reportInterval, reportInterval);
    }

    /**
     * 关闭自动周期上报,您可以通过firePropertiesChanged触发上报
     */
    public void disableAutoReport() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy