ex

Fork of mbed-os-example-mbed5-blinky by mbed-os-examples

dcs-sdk-java-master/app/src/main/java/com/baidu/duer/dcs/devicemodule/alerts/AlertsDeviceModule.java

Committer:
TMBOY
Date:
2017-07-18
Revision:
45:2aa9f933c8d2

File content as of revision 45:2aa9f933c8d2:

/*
 * Copyright (c) 2017 Baidu, Inc. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baidu.duer.dcs.devicemodule.alerts;

import com.baidu.duer.dcs.devicemodule.alerts.message.Alert;
import com.baidu.duer.dcs.devicemodule.alerts.message.AlertPayload;
import com.baidu.duer.dcs.devicemodule.alerts.message.AlertsStatePayload;
import com.baidu.duer.dcs.devicemodule.alerts.message.DeleteAlertPayload;
import com.baidu.duer.dcs.devicemodule.alerts.message.SetAlertPayload;
import com.baidu.duer.dcs.devicemodule.system.HandleDirectiveException;
import com.baidu.duer.dcs.framework.BaseDeviceModule;
import com.baidu.duer.dcs.framework.IMessageSender;
import com.baidu.duer.dcs.framework.message.ClientContext;
import com.baidu.duer.dcs.framework.message.Directive;
import com.baidu.duer.dcs.framework.message.Event;
import com.baidu.duer.dcs.framework.message.Header;
import com.baidu.duer.dcs.framework.message.MessageIdHeader;
import com.baidu.duer.dcs.framework.message.Payload;
import com.baidu.duer.dcs.systeminterface.IAlertsDataStore;
import com.baidu.duer.dcs.systeminterface.IHandler;
import com.baidu.duer.dcs.systeminterface.IMediaPlayer;
import com.baidu.duer.dcs.util.CommonUtil;
import com.baidu.duer.dcs.util.DateFormatterUtil;
import com.baidu.duer.dcs.util.LogUtil;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Alerts模块的核心处理逻辑,如执行SetAlert、DeleteAlert指令,上传SetAlertSucceeded、DeleteAlertSucceeded等事件,
 * 以及维护自身的端状态
 * <p>
 * Created by guxiuzhong@baidu.com on 2017/5/31.
 */
public class AlertsDeviceModule extends BaseDeviceModule implements AlertHandler {
    private static final String TAG = AlertsDeviceModule.class.getSimpleName();
    // 闹铃开始是播放的音频文件
    private static final String ALARM_NAME = "assets://alarm.mp3";
    // 已经离alert时间点超过30分钟了
    private static final int MINUTES_AFTER_PAST_ALERT_EXPIRES = 30;
    // 当前的闹钟/提醒
    private final Map<String, AlertScheduler> schedulers;
    // 当前处于播放的
    private final Set<String> activeAlerts;
    private final IAlertsDataStore dataStore;
    // 播放闹铃/提醒的mediaPlayer
    private IMediaPlayer mediaPlayer;
    private IHandler handler;
    private List<IAlertListener> alertListeners;

    // 提醒的状态
    private enum AlertState {
        PLAYING,
        INTERRUPTED,
        FINISHED
    }

    private AlertState alertState = AlertState.FINISHED;

    public AlertsDeviceModule(IMediaPlayer mediaPlayer, IAlertsDataStore dataStore,
                              IMessageSender messageSender, IHandler handler) {
        super(ApiConstants.NAMESPACE, messageSender);
        this.schedulers = new ConcurrentHashMap<>();
        this.activeAlerts = new HashSet<>();
        this.mediaPlayer = mediaPlayer;
        this.dataStore = dataStore;
        this.handler = handler;
        this.mediaPlayer.addMediaPlayerListener(mediaPlayerListener);
        this.alertListeners = Collections.synchronizedList(new ArrayList<IAlertListener>());
        // 先读取历史保存到文件中的提醒
        this.loadFromDisk();
    }

    @Override
    public ClientContext clientContext() {
        String namespace = ApiConstants.NAMESPACE;
        String name = ApiConstants.Events.AlertsState.NAME;
        Header header = new Header(namespace, name);
        Payload payload = getState();
        return new ClientContext(header, payload);
    }

    @Override
    public void handleDirective(Directive directive) throws HandleDirectiveException {
        String directiveName = directive.getName();
        if (directiveName.equals(ApiConstants.Directives.SetAlert.NAME)) {
            // 设置一个闹铃/提醒的指令处理逻辑
            LogUtil.d(TAG, "alert-SetAlertPayload");
            SetAlertPayload payload = (SetAlertPayload) directive.payload;

            String alertToken = payload.getToken();
            String scheduledTime = payload.getScheduledTime();
            LogUtil.d(TAG, "alert-scheduledTime:" + scheduledTime);

            // scheduledTime 时间为ISO8601格式转换为Date
            try {
                Date date = DateFormatterUtil.toDate(scheduledTime);
                LogUtil.d(TAG, "alert-ms:" + date.getTime());
                LogUtil.d(TAG, "alert-format:" + CommonUtil.formatToDataTime(date.getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            SetAlertPayload.AlertType type = payload.getType();
            // 如果存在一样的闹铃/提醒 就cancel掉
            if (hasAlert(alertToken)) {
                AlertScheduler scheduler = getScheduler(alertToken);
                if (scheduler.getAlert().getScheduledTime().equals(scheduledTime)) {
                    return;
                } else {
                    scheduler.cancel();
                }
            }
            // 设置一个闹铃/提醒
            Alert alert = new Alert(alertToken, type, scheduledTime);
            add(alert, false);
        } else if (ApiConstants.Directives.DeleteAlert.NAME.equals(directiveName)) {
            // 删除一个闹铃/提醒的指令处理
            LogUtil.d(TAG, "alert-DeleteAlertPayload");
            DeleteAlertPayload payload = (DeleteAlertPayload) directive.getPayload();
            delete(payload.getToken());
        } else {
            String message = "Alert cannot handle the directive";
            throw (new HandleDirectiveException(
                    HandleDirectiveException.ExceptionType.UNSUPPORTED_OPERATION, message));
        }
    }

    @Override
    public void startAlert(final String alertToken) {
        LogUtil.d(TAG, "alert-startAlert");
        handler.post(new Runnable() {
            @Override
            public void run() {
                activeAlerts.add(alertToken);
                sendAlertsRequest(ApiConstants.Events.AlertStarted.NAME, alertToken);
                fireOnAlertStarted(alertToken);
                if (!isAlarming()) {
                    alertState = AlertState.PLAYING;
                    if (mediaPlayer != null) {
                        mediaPlayer.play(new IMediaPlayer.MediaResource(ALARM_NAME));
                    }
                }
            }
        });
    }

    @Override
    public void stopAlert(final String alertToken) {
        LogUtil.d(TAG, "alert-stopAlert");
        handler.post(new Runnable() {
            @Override
            public void run() {
                activeAlerts.remove(alertToken);
                schedulers.remove(alertToken);
                alertStopped(alertToken);
                if (!hasActiveAlerts()) {
                    alertState = AlertState.FINISHED;
                    if (mediaPlayer != null) {
                        mediaPlayer.pause();
                    }
                }
            }
        });
    }

    private void sendAlertEnteredBackgroundEvent(String alertToken) {
        sendAlertsRequest(ApiConstants.Events.AlertEnteredBackground.NAME, alertToken);
    }

    private void sendAlertEnteredBackgroundEvent() {
        if (hasActiveAlerts()) {
            for (String alertToken : getActiveAlerts()) {
                sendAlertsRequest(ApiConstants.Events.AlertEnteredBackground.NAME, alertToken);
            }
        }
    }

    private void sendAlertEnteredForegroundEvent(String alertToken) {
        sendAlertsRequest(ApiConstants.Events.AlertEnteredForeground.NAME, alertToken);
    }

    public void sendAlertEnteredForegroundEvent() {
        if (hasActiveAlerts()) {
            for (String alertToken : getActiveAlerts()) {
                sendAlertsRequest(ApiConstants.Events.AlertEnteredForeground.NAME, alertToken);
            }
        }
    }

    public void sendAlertStartedEvent(boolean isSpeaking, String alertToken) {
        if (isSpeaking) {
            sendAlertEnteredBackgroundEvent(alertToken);
        } else {
            sendAlertEnteredForegroundEvent(alertToken);
        }
    }

    private void sendAlertsRequest(String eventName, String alertToken) {
        Header header = new MessageIdHeader(ApiConstants.NAMESPACE, eventName);
        Payload payload = new AlertPayload(alertToken);
        Event event = new Event(header, payload);
        messageSender.sendEvent(event);
    }

    /**
     * 设置提醒/闹钟成功或者失败的上报
     *
     * @param alertToken alertToken
     * @param success    success
     */
    private void setAlert(String alertToken, boolean success) {
        String eventName = success ? ApiConstants.Events.SetAlertSucceeded.NAME :
                ApiConstants.Events.SetAlertFailed.NAME;
        sendAlertsRequest(eventName, alertToken);
    }

    /**
     * 删除提醒/闹钟成功或者失败的上报
     *
     * @param alertToken alertToken
     * @param success    success
     */
    private void deleteAlert(String alertToken, boolean success) {
        String eventName = success ? ApiConstants.Events.DeleteAlertSucceeded.NAME :
                ApiConstants.Events.DeleteAlertFailed.NAME;
        sendAlertsRequest(eventName, alertToken);
    }

    /**
     * 到了定点时间,触发了提醒/闹钟 时上报
     *
     * @param alertToken alertToken
     */
    private void fireOnAlertStarted(String alertToken) {
        for (IAlertListener listener : alertListeners) {
            listener.onAlertStarted(alertToken);
        }
    }

    /**
     * 上报AlertStopped事件
     *
     * @param alertToken alertToken 闹钟/提醒 唯一的标识
     */
    private void alertStopped(String alertToken) {
        sendAlertsRequest(ApiConstants.Events.AlertStopped.NAME, alertToken);
    }

    /**
     * 从文件里读取之前设置的闹铃/提醒
     */
    private void loadFromDisk() {
        dataStore.readFromDisk(new IAlertsDataStore.ReadResultListener() {

            @Override
            public void onSucceed(List<Alert> alerts) {
                if (alerts == null || alerts.size() <= 0) {
                    return;
                }
                List<Alert> droppedAlerts = new LinkedList<>();
                for (final Alert alert : alerts) {
                    String scheduledTime = alert.getScheduledTime();
                    if (scheduledTime != null && scheduledTime.length() > 0) {
                        try {
                            Date date = DateFormatterUtil.toDate(alert.getScheduledTime());
                            long scheduledTimeLong = date.getTime();
                            // 已经离alert时间点超过30分钟了
                            long cur = System.currentTimeMillis();
                            if (scheduledTimeLong + MINUTES_AFTER_PAST_ALERT_EXPIRES * 60 * 1000 < cur) {
                                droppedAlerts.add(alert);
                            } else {
                                add(alert, true);
                            }
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    }
                }
                for (Alert alert : droppedAlerts) {
                    drop(alert);
                }
            }

            @Override
            public void onFailed(String errMsg) {

            }
        });
    }

    public boolean isAlarming() {
        return alertState == AlertState.PLAYING;
    }

    private synchronized boolean hasAlert(String alertToken) {
        return schedulers.containsKey(alertToken);
    }

    public synchronized boolean hasActiveAlerts() {
        return activeAlerts != null && activeAlerts.size() > 0;
    }

    private synchronized Set<String> getActiveAlerts() {
        return activeAlerts;
    }

    private synchronized AlertScheduler getScheduler(String alertToken) {
        return schedulers.get(alertToken);
    }

    /**
     * 设置一个闹钟/提醒
     *
     * @param alert         alert
     * @param suppressEvent suppressEvent 是否需要上报事件
     */
    private synchronized void add(final Alert alert, final boolean suppressEvent) {
        LogUtil.d(TAG, "add alertToken: " + alert.getToken());
        final AlertScheduler scheduler = new AlertScheduler(alert, this);
        schedulers.put(alert.getToken(), scheduler);
        dataStore.writeToDisk(getAllAlerts(), new IAlertsDataStore.WriteResultListener() {

            @Override
            public void onSucceed() {
                if (!suppressEvent) {
                    setAlert(alert.getToken(), true);
                }
            }

            @Override
            public void onFailed(String errMsg) {
                if (!suppressEvent) {
                    setAlert(alert.getToken(), false);
                }
                schedulers.remove(alert.getToken());
                scheduler.cancel();
            }
        });
    }

    /**
     * 删除一个闹钟
     *
     * @param alertToken alertToken
     */
    private synchronized void delete(final String alertToken) {
        LogUtil.d(TAG, "delete alertToken: " + alertToken);
        final AlertScheduler scheduler = schedulers.remove(alertToken);
        if (scheduler != null) {
            final Alert alert = scheduler.getAlert();
            dataStore.writeToDisk(getAllAlerts(), new IAlertsDataStore.WriteResultListener() {
                @Override
                public void onSucceed() {
                    LogUtil.d(TAG, "delete  onSucceed");
                    scheduler.cancel();
                    deleteAlert(alert.getToken(), true);
                }

                @Override
                public void onFailed(String errMsg) {
                    LogUtil.d(TAG, "delete  onFailed");
                    deleteAlert(alert.getToken(), false);
                }
            });
        } else {
            //  本地没有查询到就上报删除失败的事件
            LogUtil.d(TAG, "delete  scheduler is  null");
            deleteAlert(alertToken, false);
        }
    }

    private synchronized List<Alert> getAllAlerts() {
        List<Alert> list = new ArrayList<>(schedulers.size());
        for (AlertScheduler scheduler : schedulers.values()) {
            list.add(scheduler.getAlert());
        }
        return list;
    }

    /**
     * 如果有正在播放的闹铃/提醒,就停止播放并删除该闹铃/提醒
     */
    public synchronized void stopActiveAlert() {
        for (String alertToken : activeAlerts) {
            stopAlert(alertToken);
            return;
        }
    }

    private void drop(final Alert alert) {
        alertStopped(alert.getToken());
    }

    private synchronized AlertsStatePayload getState() {
        List<Alert> all = new ArrayList<>(schedulers.size());
        List<Alert> active = new ArrayList<>(activeAlerts.size());
        for (AlertScheduler scheduler : schedulers.values()) {
            Alert alert = scheduler.getAlert();
            all.add(alert);

            if (activeAlerts.contains(alert.getToken())) {
                active.add(alert);
            }
        }
        return new AlertsStatePayload(all, active);
    }

    @Override
    public void release() {
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer.removeMediaPlayerListener(mediaPlayerListener);
            mediaPlayer = null;
        }
        for (AlertScheduler scheduler : schedulers.values()) {
            scheduler.cancel();
        }
        alertListeners.clear();
    }

    private IMediaPlayer.IMediaPlayerListener mediaPlayerListener = new IMediaPlayer.SimpleMediaPlayerListener() {
        @Override
        public void onPrepared() {
            super.onPrepared();
            mediaPlayer.setActive(true);
        }

        @Override
        public void onCompletion() {
            alertState = AlertState.FINISHED;
            mediaPlayer.setActive(false);
        }

        @Override
        public void onError(String error, IMediaPlayer.ErrorType errorType) {
            super.onError(error, errorType);
            alertState = AlertState.FINISHED;
            mediaPlayer.setActive(false);
        }
    };


    /**
     * 闹铃/提醒回调
     */
    public interface IAlertListener {
        /**
         * 到了定点时间,触发了提醒/闹钟 时上报
         *
         * @param alertToken alertToken
         */
        void onAlertStarted(String alertToken);
    }

    public void addAlertListener(IAlertListener listener) {
        this.alertListeners.add(listener);
    }
}