/*******************************************************************************
* 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.example.publisher;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.kura.KuraException;
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 ExamplePublisher implements ConfigurableComponent, CloudClientListener {
private static final Logger s_logger = LoggerFactory.getLogger(ExamplePublisher.class);
// Cloud Application identifier
private static final String APP_ID = "EXAMPLE_PUBLISHER";
// Publishing Property Names
private static final String PUBLISH_RATE_PROP_NAME = "publish.rate";
private static final String PUBLISH_TOPIC_PROP_NAME = "publish.appTopic";
private static final String PUBLISH_QOS_PROP_NAME = "publish.qos";
private static final String PUBLISH_RETAIN_PROP_NAME = "publish.retain";
private static final String TEMP_INITIAL_PROP_NAME = "metric.temperature.initial";
private static final String TEMP_INCREMENT_PROP_NAME = "metric.temperature.increment";
private static final String[] METRIC_PROP_NAMES = { "metric.string", "metric.string.oneof", "metric.long",
"metric.integer", "metric.integer.fixed", "metric.short", "metric.double", "metric.float", "metric.char",
"metric.byte", "metric.boolean", "metric.password" };
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;
// ----------------------------------------------------------------
//
// Dependencies
//
// ----------------------------------------------------------------
public ExamplePublisher() {
super();
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 ExamplePublisher...");
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 CloudApplicationClient 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();
} catch (Exception e) {
s_logger.error("Error during component activation", e);
throw new ComponentException(e);
}
s_logger.info("Activating ExamplePublisher... Done.");
}
protected void deactivate(ComponentContext componentContext) {
s_logger.debug("Deactivating ExamplePublisher...");
// 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 ExamplePublisher... Done.");
}
public void updated(Map<String, Object> properties) {
s_logger.info("Updated ExamplePublisher...");
// 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();
s_logger.info("Updated ExamplePublisher... Done.");
}
// ----------------------------------------------------------------
//
// Cloud Application Callback Methods
//
// ----------------------------------------------------------------
@Override
public void onConnectionEstablished() {
s_logger.info("Connection established");
try {
// Getting the lists of unpublished messages
s_logger.info("Number of unpublished messages: {}", this.m_cloudClient.getUnpublishedMessageIds().size());
} catch (KuraException e) {
s_logger.error("Cannot get the list of unpublished messages");
}
try {
// Getting the lists of in-flight messages
s_logger.info("Number of in-flight messages: {}", this.m_cloudClient.getInFlightMessageIds().size());
} catch (KuraException e) {
s_logger.error("Cannot get the list of in-flight messages");
}
try {
// Getting the lists of dropped in-flight messages
s_logger.info("Number of dropped in-flight messages: {}",
this.m_cloudClient.getDroppedInFlightMessageIds().size());
} catch (KuraException e) {
s_logger.error("Cannot get the list of dropped in-flight messages");
}
}
@Override
public void onConnectionLost() {
s_logger.warn("Connection lost!");
}
@Override
public void onControlMessageArrived(String deviceId, String appTopic, KuraPayload msg, int qos, boolean retain) {
s_logger.info("Control message arrived on assetId: {} and semantic topic: {}", deviceId, appTopic);
}
@Override
public void onMessageArrived(String deviceId, String appTopic, KuraPayload msg, int qos, boolean retain) {
s_logger.info("Message arrived on assetId: {} and semantic topic: {}", deviceId, appTopic);
}
@Override
public void onMessagePublished(int messageId, String appTopic) {
s_logger.info("Published message with ID: {} on application topic: {}", messageId, appTopic);
}
@Override
public void onMessageConfirmed(int messageId, String appTopic) {
s_logger.info("Confirmed message with ID: {} on application topic: {}", messageId, appTopic);
}
// ----------------------------------------------------------------
//
// Private Methods
//
// ----------------------------------------------------------------
/**
* Called after a new set of properties has been configured on the service
*/
private void doUpdate() {
// 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 ExamplePublisher - Ignore as properties do not contain TEMP_INITIAL_PROP_NAME and PUBLISH_RATE_PROP_NAME.");
return;
}
// reset the temperature to the initial value
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() {
doPublish();
}
}, 0, pubrate, TimeUnit.MILLISECONDS);
}
/**
* 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);
// Increment the simulated temperature value
float tempIncr = (Float) this.m_properties.get(TEMP_INCREMENT_PROP_NAME);
this.m_temperature += 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("temperature", this.m_temperature);
// add all the other metrics
for (String metric : METRIC_PROP_NAMES) {
if ("metric.char".equals(metric)) {
// publish character as a string as the
// "char" type is not support in the EDC Payload
payload.addMetric(metric, String.valueOf(this.m_properties.get(metric)));
} else if ("metric.short".equals(metric)) {
// publish short as an integer as the
// "short " type is not support in the EDC Payload
payload.addMetric(metric, ((Short) this.m_properties.get(metric)).intValue());
} else if ("metric.byte".equals(metric)) {
// publish byte as an integer as the
// "byte" type is not support in the EDC Payload
payload.addMetric(metric, ((Byte) this.m_properties.get(metric)).intValue());
} else {
payload.addMetric(metric, this.m_properties.get(metric));
}
}
// Publish the message
try {
int messageId = this.m_cloudClient.publish(topic, payload, qos, retain);
s_logger.info("Published to {} message: {} with ID: {}", new Object[] { topic, payload, messageId });
} catch (Exception e) {
s_logger.error("Cannot publish topic: " + topic, e);
}
}
}