/******************************************************************************* * Copyright (c) 2011, 2016 Eurotech and/or its affiliates * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.demo.heater; import java.util.Date; import java.util.Map; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.kura.cloud.CloudClient; import org.eclipse.kura.cloud.CloudClientListener; import org.eclipse.kura.cloud.CloudService; import org.eclipse.kura.configuration.ConfigurableComponent; import org.eclipse.kura.message.KuraPayload; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.ComponentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Heater implements ConfigurableComponent, CloudClientListener { private static final Logger s_logger = LoggerFactory.getLogger(Heater.class); // Cloud Application identifier private static final String APP_ID = "heater"; // Publishing Property Names private static final String MODE_PROP_NAME = "mode"; private static final String MODE_PROP_PROGRAM = "Program"; private static final String MODE_PROP_MANUAL = "Manual"; private static final String MODE_PROP_VACATION = "Vacation"; private static final String PROGRAM_SETPOINT_NAME = "program.setPoint"; private static final String MANUAL_SETPOINT_NAME = "manual.setPoint"; private static final String TEMP_INITIAL_PROP_NAME = "temperature.initial"; private static final String TEMP_INCREMENT_PROP_NAME = "temperature.increment"; private static final String PUBLISH_RATE_PROP_NAME = "publish.rate"; private static final String PUBLISH_TOPIC_PROP_NAME = "publish.semanticTopic"; private static final String PUBLISH_QOS_PROP_NAME = "publish.qos"; private static final String PUBLISH_RETAIN_PROP_NAME = "publish.retain"; private CloudService m_cloudService; private CloudClient m_cloudClient; private final ScheduledExecutorService m_worker; private ScheduledFuture<?> m_handle; private float m_temperature; private Map<String, Object> m_properties; private final Random m_random; // ---------------------------------------------------------------- // // Dependencies // // ---------------------------------------------------------------- public Heater() { super(); this.m_random = new Random(); this.m_worker = Executors.newSingleThreadScheduledExecutor(); } public void setCloudService(CloudService cloudService) { this.m_cloudService = cloudService; } public void unsetCloudService(CloudService cloudService) { this.m_cloudService = null; } // ---------------------------------------------------------------- // // Activation APIs // // ---------------------------------------------------------------- protected void activate(ComponentContext componentContext, Map<String, Object> properties) { s_logger.info("Activating Heater..."); this.m_properties = properties; for (String s : properties.keySet()) { s_logger.info("Activate - " + s + ": " + properties.get(s)); } // get the mqtt client for this application try { // Acquire a Cloud Application Client for this Application s_logger.info("Getting CloudClient for {}...", APP_ID); this.m_cloudClient = this.m_cloudService.newCloudClient(APP_ID); this.m_cloudClient.addCloudClientListener(this); // Don't subscribe because these are handled by the default // subscriptions and we don't want to get messages twice doUpdate(false); } catch (Exception e) { s_logger.error("Error during component activation", e); throw new ComponentException(e); } s_logger.info("Activating Heater... Done."); } protected void deactivate(ComponentContext componentContext) { s_logger.debug("Deactivating Heater..."); // shutting down the worker and cleaning up the properties this.m_worker.shutdown(); // Releasing the CloudApplicationClient s_logger.info("Releasing CloudApplicationClient for {}...", APP_ID); this.m_cloudClient.release(); s_logger.debug("Deactivating Heater... Done."); } public void updated(Map<String, Object> properties) { s_logger.info("Updated Heater..."); // store the properties received this.m_properties = properties; for (String s : properties.keySet()) { s_logger.info("Update - " + s + ": " + properties.get(s)); } // try to kick off a new job doUpdate(true); s_logger.info("Updated Heater... Done."); } // ---------------------------------------------------------------- // // Cloud Application Callback Methods // // ---------------------------------------------------------------- @Override public void onControlMessageArrived(String deviceId, String appTopic, KuraPayload msg, int qos, boolean retain) { // TODO Auto-generated method stub } @Override public void onMessageArrived(String deviceId, String appTopic, KuraPayload msg, int qos, boolean retain) { // TODO Auto-generated method stub } @Override public void onConnectionLost() { // TODO Auto-generated method stub } @Override public void onConnectionEstablished() { // TODO Auto-generated method stub } @Override public void onMessageConfirmed(int messageId, String appTopic) { // TODO Auto-generated method stub } @Override public void onMessagePublished(int messageId, String appTopic) { // TODO Auto-generated method stub } // ---------------------------------------------------------------- // // Private Methods // // ---------------------------------------------------------------- /** * Called after a new set of properties has been configured on the service */ private void doUpdate(boolean onUpdate) { // cancel a current worker handle if one if active if (this.m_handle != null) { this.m_handle.cancel(true); } if (!this.m_properties.containsKey(TEMP_INITIAL_PROP_NAME) || !this.m_properties.containsKey(PUBLISH_RATE_PROP_NAME)) { s_logger.info( "Update Heater - Ignore as properties do not contain TEMP_INITIAL_PROP_NAME and PUBLISH_RATE_PROP_NAME."); return; } // reset the temperature to the initial value if (!onUpdate) { this.m_temperature = (Float) this.m_properties.get(TEMP_INITIAL_PROP_NAME); } // schedule a new worker based on the properties of the service int pubrate = (Integer) this.m_properties.get(PUBLISH_RATE_PROP_NAME); this.m_handle = this.m_worker.scheduleAtFixedRate(new Runnable() { @Override public void run() { Thread.currentThread().setName(getClass().getSimpleName()); doPublish(); } }, 0, pubrate, TimeUnit.SECONDS); } /** * Called at the configured rate to publish the next temperature measurement. */ private void doPublish() { // fetch the publishing configuration from the publishing properties String topic = (String) this.m_properties.get(PUBLISH_TOPIC_PROP_NAME); Integer qos = (Integer) this.m_properties.get(PUBLISH_QOS_PROP_NAME); Boolean retain = (Boolean) this.m_properties.get(PUBLISH_RETAIN_PROP_NAME); String mode = (String) this.m_properties.get(MODE_PROP_NAME); // Increment the simulated temperature value float setPoint = 0; float tempIncr = (Float) this.m_properties.get(TEMP_INCREMENT_PROP_NAME); if (MODE_PROP_PROGRAM.equals(mode)) { setPoint = (Float) this.m_properties.get(PROGRAM_SETPOINT_NAME); } else if (MODE_PROP_MANUAL.equals(mode)) { setPoint = (Float) this.m_properties.get(MANUAL_SETPOINT_NAME); } else if (MODE_PROP_VACATION.equals(mode)) { setPoint = 6.0F; } if (this.m_temperature + tempIncr < setPoint) { this.m_temperature += tempIncr; } else { this.m_temperature -= 4 * tempIncr; } // Allocate a new payload KuraPayload payload = new KuraPayload(); // Timestamp the message payload.setTimestamp(new Date()); // Add the temperature as a metric to the payload payload.addMetric("temperatureInternal", this.m_temperature); payload.addMetric("temperatureExternal", 5.0F); payload.addMetric("temperatureExhaust", 30.0F); int code = this.m_random.nextInt(); if (this.m_random.nextInt() % 5 == 0) { payload.addMetric("errorCode", code); } else { payload.addMetric("errorCode", 0); } // Publish the message try { this.m_cloudClient.publish(topic, payload, qos, retain); s_logger.info("Published to {} message: {}", topic, payload); } catch (Exception e) { s_logger.error("Cannot publish topic: " + topic, e); } } }