extend device simulator of create DDI and DMF devices and a Vaadin UI

for manage them.

Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>
This commit is contained in:
Michael Hirsch
2016-01-21 17:56:26 +01:00
parent 4ce623fa40
commit 380370c026
26 changed files with 1767 additions and 17 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,180 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
/**
* The bean of a simulated device which can be stored in the
* {@link DeviceSimulatorRepository} or shown in the UI.
*
* @author Michael Hirsch
*
*/
public abstract class AbstractSimulatedDevice {
private String id;
private String tenant;
private Status status;
private double progress;
private String swversion = "unknown";
private ResponseStatus responseStatus = ResponseStatus.SUCCESSFUL;
private Protocol protocol = Protocol.DMF_AMQP;
private int nextPollCounterSec;
/**
* Enum definition of the protocol to be used for the simulated device.
*
* @author Michael Hirsch
*
*/
public enum Protocol {
/**
* Device Management Federation API via AMQP, push mechanism.
*/
DMF_AMQP,
/**
* Direct Device Interface via HTTP, poll mechanism.
*/
DDI_HTTP;
}
/**
* The current status of the simulated device.
*
* @author Michael Hirsch
*
*/
public enum Status {
/**
* device is in status unknown.
*/
UNKNWON,
/**
* device is in status pending which represents is updating software.
*/
PEDNING,
/**
* device has been updated successfully.
*/
FINISH,
/**
* device has been updated with an error.
*/
ERROR;
}
/**
* The status to response to the hawkbit update server if an simulated
* update process should be respond with successful or failure update.
*
* @author Michael Hirsch
*
*/
public enum ResponseStatus {
/**
* updated has been successful and response the successful update.
*/
SUCCESSFUL,
/**
* updated has been not successful and response the error update.
*/
ERROR;
}
/**
* empty constructor.
*/
AbstractSimulatedDevice() {
}
/**
* Creates a new simulated device.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
*/
AbstractSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
this.id = id;
this.tenant = tenant;
this.status = Status.UNKNWON;
this.progress = 0.0;
this.protocol = protocol;
}
/**
* Method to clean-up resource e.g. when the simulated device has been
* removed from the repository.
*/
public void clean() {
}
public String getId() {
return id;
}
public Status getStatus() {
return status;
}
public double getProgress() {
return progress;
}
public String getTenant() {
return tenant;
}
public void setId(final String id) {
this.id = id;
}
public void setTenant(final String tenant) {
this.tenant = tenant;
}
public void setStatus(final Status status) {
this.status = status;
}
public void setProgress(final double progress) {
this.progress = progress;
}
public String getSwversion() {
return swversion;
}
public void setSwversion(final String swversion) {
this.swversion = swversion;
}
public ResponseStatus getResponseStatus() {
return responseStatus;
}
public void setResponseStatus(final ResponseStatus responseStatus) {
this.responseStatus = responseStatus;
}
public Protocol getProtocol() {
return protocol;
}
public int getNextPollCounterSec() {
return nextPollCounterSec;
}
public void setNextPollCounterSec(final int nextPollDelayInSec) {
this.nextPollCounterSec = nextPollDelayInSec;
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
/**
* @author Michael Hirsch
*
*/
public class DDISimulatedDevice extends AbstractSimulatedDevice {
private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
private final int pollDelaySec;
private final ScheduledExecutorService pollthreadpool;
private final ControllerResource controllerResource;
private volatile boolean removed;
private volatile Long currentActionId;
private final DeviceSimulatorUpdater deviceUpdater;
/**
* @param id
* the ID of the device
* @param tenant
* the tenant of the simulated device
* @param pollDelaySec
* the delay of the poll interval in sec
* @param controllerResource
* the http controller resource
* @param pollthreadpool
* the threadpool for polling endpoint
* @param deviceUpdater
* the service to update devices
*/
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
final ControllerResource controllerResource, final ScheduledExecutorService pollthreadpool,
final DeviceSimulatorUpdater deviceUpdater) {
super(id, tenant, Protocol.DDI_HTTP);
this.pollDelaySec = pollDelaySec;
this.controllerResource = controllerResource;
this.pollthreadpool = pollthreadpool;
this.deviceUpdater = deviceUpdater;
setNextPollCounterSec(pollDelaySec);
}
@Override
public void clean() {
super.clean();
removed = true;
}
public int getPollDelaySec() {
return pollDelaySec;
}
/**
* Polls the base URL for the DDI API interface.
*/
public void poll() {
if (!removed) {
final String basePollJson = controllerResource.get(getTenant(), getId());
try {
final String href = JsonPath.parse(basePollJson).read("_links.deploymentBase.href");
final long actionId = Long.parseLong(href.substring(href.lastIndexOf("/") + 1, href.indexOf("?")));
if (currentActionId == null) {
final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId);
final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version");
currentActionId = actionId;
deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, new UpdaterCallback() {
@Override
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
switch (device.getResponseStatus()) {
case SUCCESSFUL:
controllerResource.postSuccessFeedback(getTenant(), getId(), actionId);
break;
case ERROR:
controllerResource.postErrorFeedback(getTenant(), getId(), actionId);
break;
default:
throw new IllegalStateException("simulated device has an unknown response status + "
+ device.getResponseStatus());
}
currentActionId = null;
}
});
}
} catch (final PathNotFoundException e) {
// href might not be in the json response, so ignore
// exception here.
LOGGER.trace("Response does not contain a deploymentbase href link, ignoring.");
}
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
/**
* An simulated device using the DMF API of the hawkbit update server.
*
* @author Michael Hirsch
*
*/
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
/**
* @param id
* the ID of the device
* @param tenant
* the tenant of the simulated device
*/
public DMFSimulatedDevice(final String id, final String tenant) {
super(id, tenant, Protocol.DMF_AMQP);
}
}

View File

@@ -8,8 +8,15 @@
*/
package org.eclipse.hawkbit.simulator;
import java.util.concurrent.Executors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.vaadin.spring.annotation.EnableVaadin;
/**
* The main-method to start the Spring-Boot application.
@@ -18,12 +25,21 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
*
*/
@SpringBootApplication
@EnableVaadin
public class DeviceSimulator {
private DeviceSimulator() {
public DeviceSimulator() {
// utility class
}
/**
* @return an asynchronous event bus to publish and retrieve events.
*/
@Bean
public EventBus eventBus() {
return new AsyncEventBus(Executors.newFixedThreadPool(4));
}
/**
* Start the Spring Boot Application.
*

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* An in-memory simulated device repository to hold the simulated device in
* memory and be able to retrieve them again.
*
* @author Michael Hirsch
*
*/
@Service
public class DeviceSimulatorRepository {
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new LinkedHashMap<>();
@Autowired
private SimulatedDeviceFactory deviceFactory;
/**
* Adds a simulated device to the repository.
*
* @param simulatedDevice
* the device to add
* @return the device which has been added to the repository
*/
public AbstractSimulatedDevice add(final AbstractSimulatedDevice simulatedDevice) {
devices.put(new DeviceKey(simulatedDevice.getTenant().toLowerCase(), simulatedDevice.getId()), simulatedDevice);
return simulatedDevice;
}
/**
* @return all simulated devices
*/
public Collection<AbstractSimulatedDevice> getAll() {
return devices.values();
}
/**
* Retrieves a single simulated devices or {@code null} if device does not
* exists.
*
* @param tenant
* the tenant of the simulated device
* @param id
* the ID of the device
* @return a simulated device from the repository or {@code null} if device
* does not exixts.
*/
public AbstractSimulatedDevice get(final String tenant, final String id) {
return devices.get(new DeviceKey(tenant.toLowerCase(), id));
}
/**
* Clears all stored devices.
*/
public void clear() {
devices.values().forEach(device -> device.clean());
devices.clear();
}
private static final class DeviceKey {
private final String tenant;
private final String id;
private DeviceKey(final String tenant, final String id) {
this.tenant = tenant;
this.id = id;
}
@Override
public int hashCode() {// NOSONAR - as this is generated
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {// NOSONAR - as this is
// generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DeviceKey other = (DeviceKey) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (tenant == null) {
if (other.tenant != null) {
return false;
}
} else if (!tenant.equals(other.tenant)) {
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* @author Michael Hirsch
*
*/
@Service
public class DeviceSimulatorUpdater {
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
@Autowired
private SpSenderService spSenderService;
@Autowired
private EventBus eventbus;
@Autowired
private DeviceSimulatorRepository repository;
/**
* Starting an simulated update process of an simulated device.
*
* @param tenant
* the tenant of the device
* @param id
* the ID of the simulated device
* @param actionId
* the actionId from the hawkbit update server to start the
* update.
* @param swVersion
* the software module version from the hawkbit update server
* @param callback
* the callback which gets called when the simulated update
* process has been finished
*/
public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion,
final UpdaterCallback callback) {
final AbstractSimulatedDevice device = repository.get(tenant, id);
device.setProgress(0.0);
device.setSwversion(swVersion);
eventbus.post(new InitUpdate(device));
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
2000, TimeUnit.MILLISECONDS);
}
private static final class DeviceSimulatorUpdateThread implements Runnable {
private static final Random rndSleep = new Random();
private final AbstractSimulatedDevice device;
private final SpSenderService spSenderService;
private final long actionId;
private final EventBus eventbus;
private final UpdaterCallback callback;
private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device,
final SpSenderService spSenderService, final long actionId, final EventBus eventbus,
final UpdaterCallback callback) {
this.device = device;
this.spSenderService = spSenderService;
this.actionId = actionId;
this.eventbus = eventbus;
this.callback = callback;
}
@Override
public void run() {
final double newProgress = device.getProgress() + 0.2;
device.setProgress(newProgress);
if (newProgress < 1.0) {
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus,
callback), rndSleep.nextInt(3000), TimeUnit.MILLISECONDS);
} else {
callback.updateFinished(device, actionId);
}
eventbus.post(new ProgressUpdate(device));
}
}
/**
* Callback interface which is called when the simulated update process has
* been finished and the caller of starting the simulated update process can
* send the result to the hawkbit update server back.
*
* @author Michael Hirsch
*
*/
@FunctionalInterface
public interface UpdaterCallback {
/**
* Callback method to indicate that the simulated update process has
* been finished.
*
* @param device
* the device which has been updated
* @param actionId
* the ID of the action from the hawkbit update server
*/
void updateFinished(AbstractSimulatedDevice device, final Long actionId);
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.common.eventbus.EventBus;
/**
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
* second.
*
* @author Michael Hirsch
*
*/
@Component
public class NextPollTimeController {
private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private static final ExecutorService pollService = Executors.newFixedThreadPool(1);
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private EventBus eventBus;
/**
* Constructor which schedules the poll trigger runnable every second.
*/
public NextPollTimeController() {
executorService.scheduleWithFixedDelay(new NextPollUpdaterRunnable(), 1, 1, TimeUnit.SECONDS);
}
private class NextPollUpdaterRunnable implements Runnable {
@Override
public void run() {
final List<AbstractSimulatedDevice> devices = repository.getAll().stream()
.filter(device -> device instanceof DDISimulatedDevice).collect(Collectors.toList());
devices.forEach(device -> {
int nextCounter = device.getNextPollCounterSec() - 1;
if (nextCounter < 0) {
if (device instanceof DDISimulatedDevice) {
try {
pollService.submit(new Runnable() {
@Override
public void run() {
((DDISimulatedDevice) device).poll();
}
});
} catch (final Exception e) {
}
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
}
}
device.setNextPollCounterSec(nextCounter);
});
eventBus.post(new NextPollCounterUpdate(devices));
}
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import feign.Feign;
import feign.Logger;
/**
* The simulated device factory to create either {@link DMFSimulatedDevice} or
* {@link DDISimulatedDevice#}.
*
* @author Michael Hirsch
*
*/
@Service
public class SimulatedDeviceFactory {
private static final ScheduledExecutorService pollThreadPool = Executors.newScheduledThreadPool(4);
@Autowired
private DeviceSimulatorUpdater deviceUpdater;
/**
* Creating a simulated devices.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
* @param protocol
* the protocol of the device
* @return the created simulated device
*/
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
return createSimulatedDevice(id, tenant, protocol, 30, null, null);
}
/**
* Creating a simulated device.
*
* @param id
* the ID of the simulated device
* @param tenant
* the tenant of the simulated device
* @param protocol
* the protocol which should be used be the simulated device
* @param pollDelaySec
* the poll delay time in seconds which should be used for
* {@link DDISimulatedDevice}s
* @param baseEndpoint
* the http base endpoint which should be used for
* {@link DDISimulatedDevice}s
* @param gatewayToken
* the gatewayToken to be used to authenticate
* {@link DDISimulatedDevice}s at the endpoint
* @return the created simulated device
*/
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol,
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
switch (protocol) {
case DMF_AMQP:
return new DMFSimulatedDevice(id, tenant);
case DDI_HTTP:
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
.target(ControllerResource.class, baseEndpoint.toString());
return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, pollThreadPool, deviceUpdater);
default:
throw new IllegalArgumentException("Protocol " + protocol + " unknown");
}
}
}

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.simulator;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,6 +27,12 @@ public class SimulationController {
@Autowired
private SpSenderService spSenderService;
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private SimulatedDeviceFactory deviceFactory;
/**
* The start resource to start a device creation.
*
@@ -43,7 +50,9 @@ public class SimulationController {
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant) {
for (int i = 0; i < amount; i++) {
spSenderService.createOrUpdateThing(tenant, name + i);
final String deviceId = name + i;
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, Protocol.DMF_AMQP));
spSenderService.createOrUpdateThing(tenant, deviceId);
}
return "Updated " + amount + " DMF connected targets!";

View File

@@ -132,6 +132,9 @@ public class AmqpConfiguration {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory);
containerFactory.setConcurrentConsumers(20);
containerFactory.setMaxConcurrentConsumers(20);
containerFactory.setPrefetchCount(20);
return containerFactory;
}

View File

@@ -15,6 +15,9 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater.UpdaterCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
@@ -40,6 +43,8 @@ public class SpReceiverService extends ReceiverService {
private final SpSenderService spSenderService;
private final DeviceSimulatorUpdater deviceUpdater;
/**
* Constructor.
*
@@ -51,12 +56,15 @@ public class SpReceiverService extends ReceiverService {
* the lwm2mSenderService
* @param spSenderService
* the spSenderService
* @param deviceUpdater
* the updater service to simulate update process
*/
@Autowired
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
final SpSenderService spSenderService) {
final SpSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater) {
super(rabbitTemplate, amqpProperties);
this.spSenderService = spSenderService;
this.deviceUpdater = deviceUpdater;
}
@@ -139,16 +147,23 @@ public class SpReceiverService extends ReceiverService {
spSenderService.sendActionStatusMessage(tenant, ActionStatus.RUNNING,
"device Simulator retrieved update request. proceeding with simulation.", actionId);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
try {
Thread.sleep(1_000);
} catch (final InterruptedException e) {
LOGGER.error("Sleep interrupted", e);
}
spSenderService.finishUpdateProcess(update, "Simulation complete!");
deviceUpdater.startUpdate(tenant, thingId, actionId, downloadAndUpdateRequest.getSoftwareModules().get(0)
.getModuleVersion(), new UpdaterCallback() {
@Override
public void updateFinished(final AbstractSimulatedDevice device, final Long actionId) {
switch (device.getResponseStatus()) {
case SUCCESSFUL:
spSenderService.finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(),
actionId), "Simulation complete!");
break;
case ERROR:
spSenderService.finishUpdateProcessWithError(new SimulatedUpdate(device.getTenant(),
device.getId(), actionId), "Simulation complete with error!");
break;
default:
break;
}
}
});
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.event;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/**
* Event description which indicates the initialization of an update.
*
* @author Michael Hirsch
*
*/
public class InitUpdate {
private final AbstractSimulatedDevice device;
/**
* Creates new progress update event.
*
* @param device
* the device which progress has been updated
*/
public InitUpdate(final AbstractSimulatedDevice device) {
this.device = device;
}
/**
* @return the device of the event
*/
public AbstractSimulatedDevice getDevice() {
return device;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.event;
import java.util.List;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/**
* Event description which indicates an poll time update.
*
* @author Michael Hirsch
*
*/
public class NextPollCounterUpdate {
private final List<AbstractSimulatedDevice> devices;
/**
* Creates poll timer update event.
*
* @param devices
* the devices which progress has been updated
*/
public NextPollCounterUpdate(final List<AbstractSimulatedDevice> devices) {
this.devices = devices;
}
/**
* @return the devices of the event
*/
public List<AbstractSimulatedDevice> getDevices() {
return devices;
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.event;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/**
* Event definition object which is published if the simulated device updated
* its update progress.
*
* @author Michael Hirsch
*
*/
public class ProgressUpdate {
private final AbstractSimulatedDevice device;
/**
* Creates new progress update event.
*
* @param device
* the device which progress has been updated
*/
public ProgressUpdate(final AbstractSimulatedDevice device) {
this.device = device;
}
/**
* @return the device of the event
*/
public AbstractSimulatedDevice getDevice() {
return device;
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.http;
import org.eclipse.hawkbit.simulator.DDISimulatedDevice;
import feign.Body;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
/**
* A feign based controller resource interface declaration for
* {@link DDISimulatedDevice}s using over HTTP.
*
* @author Michael Hirsch
*
*/
public interface ControllerResource {
/**
* The base poll URL for the devices to retrieve if there is an update
* available.
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @return the plain json response of the http request
*/
@RequestLine("GET /{tenant}/controller/v1/{controllerId}")
@Headers({ "Content-Type: application/json" })
String get(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId);
/**
* Retrieving the deployment job response from the hawkbit update server.
*
* @param tenant
* the tenant for the simulated device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to retrieve
* @return the json response of the http request
*/
@RequestLine("GET /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}")
@Headers({ "Content-Type: application/json" })
String getDeployment(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
/**
* Post a success update feedback to the hawkbit update server
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to post feedback back
*/
@RequestLine("POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback")
@Headers("Content-Type: application/json")
@Body("%7B\"id\":{actionId},\"time\":\"20140511T121314\",\"status\":%7B\"execution\":\"closed\",\"result\":%7B\"finished\":\"success\",\"progress\":%7B%7D%7D%7D%7D")
void postSuccessFeedback(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
/**
* Post a failure update feedback to the hawkbit update server
*
* @param tenant
* the tenant of the device
* @param controllerId
* the ID of the device
* @param actionId
* the ID of the action to post feedback back
*/
@RequestLine("POST /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback")
@Headers("Content-Type: application/json")
@Body("%7B\"id\":{actionId},\"time\":\"20140511T121314\",\"status\":%7B\"execution\":\"closed\",\"result\":%7B\"finished\":\"failure\",\"progress\":%7B%7D%7D%7D%7D")
void postErrorFeedback(@Param("tenant") final String tenant, @Param("controllerId") final String controllerId,
@Param("actionId") final long actionId);
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.http;
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* A feign interceptor to apply the gateway-token header to each http-request.
*
* @author Michael Hirsch
*
*/
public class GatewayTokenInterceptor implements RequestInterceptor {
private final String gatewayToken;
/**
* @param gatewayToken
* the gatwway token to be used in the http-header
*/
public GatewayTokenInterceptor(final String gatewayToken) {
this.gatewayToken = gatewayToken;
}
@Override
public void apply(final RequestTemplate template) {
template.header("Authorization", "GatewayToken " + gatewayToken);
}
}

View File

@@ -0,0 +1,233 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.ui;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.data.validator.RangeValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;
/**
* Popup dialog window for setting the values of generating the simulated
* devices, e.g. the amount.
*
* @author Michael Hirsch
*
*/
public class GenerateDialog extends Window {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(GenerateDialog.class);
private final FormLayout formLayout = new FormLayout();
/**
* Creates a new pop window for setting the configuration of simulating
* devices.
*
* @param callback
* the callback which is called when the dialog has been
* successfully confirmed.
*/
public GenerateDialog(final GenerateDialogCallback callback) {
formLayout.setSpacing(true);
formLayout.setMargin(true);
final TextField tf1 = new TextField("name prefix", "dmfSimulated");
tf1.setIcon(FontAwesome.INFO);
tf1.setRequired(true);
tf1.addValidator(new NullValidator("Must be given", false));
final TextField tf2 = new TextField("amount", new ObjectProperty<Integer>(10));
tf2.setIcon(FontAwesome.GEAR);
tf2.setRequired(true);
tf2.addValidator(new RangeValidator<Integer>("Must be between 1 and 1000", Integer.class, 1, 1000));
final TextField tf3 = new TextField("tenant", "default");
tf3.setIcon(FontAwesome.USER);
tf3.setRequired(true);
tf3.addValidator(new NullValidator("Must be given", false));
final TextField tf4 = new TextField("poll delay (sec)", new ObjectProperty<Integer>(10));
tf4.setIcon(FontAwesome.CLOCK_O);
tf4.setRequired(true);
tf4.setVisible(false);
tf4.addValidator(new RangeValidator<Integer>("Must be between 1 and 60", Integer.class, 1, 60));
final TextField tf5 = new TextField("base poll URL endpoint", "http://localhost:8080");
tf5.setColumns(50);
tf5.setIcon(FontAwesome.FLAG_O);
tf5.setRequired(true);
tf5.setVisible(false);
tf5.addValidator(new RegexpValidator(
"^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "is not an URL"));
final TextField tf6 = new TextField("gateway token", "");
tf6.setColumns(50);
tf6.setIcon(FontAwesome.FLAG_O);
tf6.setRequired(true);
tf6.setVisible(false);
final OptionGroup protocolGroup = new OptionGroup("Simulated Device Protocol");
protocolGroup.addItem(Protocol.DMF_AMQP);
protocolGroup.addItem(Protocol.DDI_HTTP);
protocolGroup.setItemCaption(Protocol.DMF_AMQP, "Device Management Federation API (AMQP push)");
protocolGroup.setItemCaption(Protocol.DDI_HTTP, "Direct Device Interface (HTTP poll)");
protocolGroup.setNullSelectionAllowed(false);
protocolGroup.select(Protocol.DMF_AMQP);
protocolGroup.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = 1L;
@Override
public void valueChange(final ValueChangeEvent event) {
if (event.getProperty().getValue().equals(Protocol.DDI_HTTP)) {
tf4.setVisible(true);
tf5.setVisible(true);
tf6.setVisible(true);
} else {
tf4.setVisible(false);
tf5.setVisible(false);
tf6.setVisible(false);
}
}
});
final Button buttonOk = new Button("generate");
buttonOk.setImmediate(true);
buttonOk.setIcon(FontAwesome.GEARS);
buttonOk.addClickListener(new ClickListener() {
private static final long serialVersionUID = 1L;
@Override
public void buttonClick(final ClickEvent event) {
try {
callback.okButton(tf1.getValue(), tf3.getValue(), Integer.valueOf(tf2.getValue().replace(".", "")),
Integer.valueOf(tf4.getValue().replace(".", "")), new URL(tf5.getValue()), tf6.getValue(),
(Protocol) protocolGroup.getValue());
} catch (final NumberFormatException e) {
LOGGER.info(e.getMessage(), e);
} catch (final MalformedURLException e) {
LOGGER.info(e.getMessage(), e);
}
GenerateDialog.this.close();
}
});
tf1.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
tf2.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
tf3.addValueChangeListener(event -> checkValid(tf1, tf2, tf3, tf4, buttonOk));
formLayout.addComponent(tf1);
formLayout.addComponent(tf2);
formLayout.addComponent(tf3);
formLayout.addComponent(protocolGroup);
formLayout.addComponent(tf4);
formLayout.addComponent(tf5);
formLayout.addComponent(tf6);
formLayout.addComponent(buttonOk);
setCaption("Simulate Devices");
setContent(formLayout);
setResizable(false);
center();
}
private void checkValid(final TextField tf1, final TextField tf2, final TextField tf3, final TextField tf4,
final Button buttonOk) {
if (tf1.isValid() && tf2.isValid() && tf3.isValid() && tf4.isValid()) {
buttonOk.setEnabled(true);
} else {
buttonOk.setEnabled(false);
}
}
@Override
public int hashCode() {// NOSONAR - as this is generated
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((formLayout == null) ? 0 : formLayout.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {// NOSONAR - as this is generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final GenerateDialog other = (GenerateDialog) obj;
if (formLayout == null) {
if (other.formLayout != null) {
return false;
}
} else if (!formLayout.equals(other.formLayout)) {
return false;
}
return true;
}
/**
* Callback interface to retrieve the result from the dialog window.
*
* @author Michael Hirsch
*
*/
@FunctionalInterface
interface GenerateDialogCallback {
/**
* Callback method which is called when dialog closes with the OK
* button.
*
* @param namePrefix
* the parameter for name prefix for the simulated devices
* @param tenant
* the tenant for the simulated devices
* @param amount
* the number of simulated devices to be created
* @param pollDelay
* the delay poll time in seconds for DDI devices
* @param basePollURL
* the base http URL endpoint for DDI devices
* @param gatewayToken
* the gateway token header for authentication for DDI
* devices
* @param protocol
* the protocol to be used for the simulated devices to be
* generated
*/
void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay,
final URL basePollURL, final String gatewayToken, final Protocol protocol);
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.ui;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.navigator.Navigator;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.navigator.SpringViewProvider;
import com.vaadin.ui.Panel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* The vaadin simulator UI which allows to generate simulated devices and show
* their current status and update progress.
*
* @author Michael Hirsch
*
*/
@SpringUI(path = "")
@Title("hawkBit Device Simulator")
@Theme(value = "simulator")
@Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET)
public class SimulatorUI extends UI {
private static final long serialVersionUID = 1L;
private final VerticalLayout rootLayout = new VerticalLayout();
@Autowired
private SpringViewProvider viewProvider;
@Override
protected void init(final VaadinRequest request) {
rootLayout.setSizeFull();
final Panel viewContainer = new Panel();
viewContainer.setSizeFull();
rootLayout.addComponent(viewContainer);
rootLayout.setExpandRatio(viewContainer, 1.0F);
final Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
setContent(rootLayout);
}
}

View File

@@ -0,0 +1,341 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
package org.eclipse.hawkbit.simulator.ui;
import java.net.URL;
import java.util.List;
import java.util.Locale;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.ResponseStatus;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Status;
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
import org.eclipse.hawkbit.simulator.ui.GenerateDialog.GenerateDialogCallback;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanContainer;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.FontAwesome;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.CellReference;
import com.vaadin.ui.Grid.CellStyleGenerator;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.renderers.HtmlRenderer;
import com.vaadin.ui.renderers.ProgressBarRenderer;
/**
* Vaadin view which allows to generate devices through the DMF API and show the
* current simulated devices in a grid with their current status and update
* progress.
*
* @author Michael Hirsch
*
*/
@SpringView(name = "")
public class SimulatorView extends VerticalLayout implements View {
private static final long serialVersionUID = 1L;
@Autowired
private transient SpSenderService spSenderService;
@Autowired
private transient DeviceSimulatorRepository repository;
@Autowired
private transient SimulatedDeviceFactory deviceFactory;
@Autowired
private transient EventBus eventbus;
private final Label caption = new Label("DMF/DDI Simulated Devices");
private final HorizontalLayout toolbar = new HorizontalLayout();
private final Grid grid = new Grid();
private final ComboBox responseComboBox = new ComboBox("", Lists.newArrayList(ResponseStatus.SUCCESSFUL,
ResponseStatus.ERROR));
private BeanContainer<String, AbstractSimulatedDevice> beanContainer;
@Override
public void enter(final ViewChangeEvent event) {
eventbus.register(this);
setSizeFull();
// caption
caption.addStyleName("h2");
// toolbar
createToolbar();
beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class);
beanContainer.setBeanIdProperty("id");
grid.setSizeFull();
grid.setCellStyleGenerator(new CellStyleGenerator() {
@Override
public String getStyle(final CellReference cellReference) {
return cellReference.getPropertyId().equals("status") ? "centeralign" : null;
}
});
grid.setSelectionMode(SelectionMode.NONE);
grid.setContainerDataSource(beanContainer);
grid.appendHeaderRow().getCell("responseStatus").setComponent(responseComboBox);
grid.setColumnOrder("id", "status", "swversion", "progress", "tenant", "protocol", "responseStatus",
"nextPollCounterSec");
// header widths
grid.getColumn("status").setMaximumWidth(80);
grid.getColumn("protocol").setMaximumWidth(180);
grid.getColumn("responseStatus").setMaximumWidth(240);
grid.getColumn("nextPollCounterSec").setMaximumWidth(210);
grid.getColumn("nextPollCounterSec").setHeaderCaption("Next Poll in (sec)");
grid.getColumn("swversion").setHeaderCaption("SW Version");
grid.getColumn("responseStatus").setHeaderCaption("Response Update Status");
grid.getColumn("progress").setRenderer(new ProgressBarRenderer());
grid.getColumn("protocol").setConverter(new Converter<String, Protocol>() {
@Override
public Protocol convertToModel(final String value, final Class<? extends Protocol> targetType,
final Locale locale) {
return null;
}
@Override
public String convertToPresentation(final Protocol value, final Class<? extends String> targetType,
final Locale locale) {
switch (value) {
case DDI_HTTP:
return "DDI API (http)";
case DMF_AMQP:
return "DMF API (amqp)";
default:
return "unknown";
}
}
@Override
public Class<Protocol> getModelType() {
return Protocol.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
});
grid.getColumn("status").setRenderer(new HtmlRenderer(), new Converter<String, Status>() {
private static final long serialVersionUID = 1L;
@Override
public Status convertToModel(final String value, final Class<? extends Status> targetType,
final Locale locale) {
return null;
}
@Override
public String convertToPresentation(final Status value, final Class<? extends String> targetType,
final Locale locale) {
String style = null;
switch (value) {
case UNKNWON:
style = "<span class=\"v-icon grayicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"gray\";\">&#x"
+ Integer.toHexString(FontAwesome.QUESTION_CIRCLE.getCodepoint()) + ";</span>";
break;
case PEDNING:
style = "<span class=\"v-icon yellowicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"yellow\";\">&#x" + Integer.toHexString(FontAwesome.REFRESH.getCodepoint())
+ ";</span>";
break;
case FINISH:
style = "<span class=\"v-icon greenicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"green\";\">&#x"
+ Integer.toHexString(FontAwesome.CHECK_CIRCLE.getCodepoint()) + ";</span>";
break;
case ERROR:
style = "<span class=\"v-icon redicon\" style=\"font-family: " + FontAwesome.FONT_FAMILY
+ ";\"color\":\"red\";\">&#x"
+ Integer.toHexString(FontAwesome.EXCLAMATION_CIRCLE.getCodepoint()) + ";</span>";
break;
default:
throw new IllegalStateException("unknown value");
}
return style;
}
@Override
public Class<Status> getModelType() {
return Status.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
});
grid.removeColumn("tenant");
// grid combobox
responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE);
responseComboBox.setItemIcon(ResponseStatus.ERROR, FontAwesome.EXCLAMATION_CIRCLE);
responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
responseComboBox.addValueChangeListener(new ValueChangeListener() {
@Override
public void valueChange(final ValueChangeEvent event) {
beanContainer.getItemIds().forEach(
itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
.setValue(event.getProperty().getValue()));
}
});
// add all components
addComponent(caption);
addComponent(toolbar);
addComponent(grid);
setExpandRatio(grid, 1.0F);
// load beans
repository.getAll().forEach(device -> beanContainer.addBean(device));
}
@Override
public void detach() {
super.detach();
eventbus.unregister(this);
}
@Subscribe
public void pollCounterUpdate(final NextPollCounterUpdate update) {
final List<AbstractSimulatedDevice> devices = update.getDevices();
this.getUI().access(new Runnable() {
@Override
public void run() {
devices.forEach(device -> {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) {
item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec());
}
});
}
});
}
/**
* Method to retrieve {@link InitUpdate} events from the event bus.
*
* @param update
* the update event posted on the event bus
*/
@Subscribe
public void initUpdate(final InitUpdate update) {
final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() {
@Override
public void run() {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) {
item.getItemProperty("progress").setValue(device.getProgress());
item.getItemProperty("status").setValue(Status.PEDNING);
item.getItemProperty("swversion").setValue(device.getSwversion());
}
}
});
}
/**
* Method to retrieve {@link ProgressUpdate} events from the event bus.
*
* @param update
* the update event posted on the event bus
*/
@Subscribe
public void progessUpdate(final ProgressUpdate update) {
final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() {
@Override
public void run() {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) {
item.getItemProperty("progress").setValue(device.getProgress());
if (device.getProgress() >= 1) {
switch (device.getResponseStatus()) {
case SUCCESSFUL:
item.getItemProperty("status").setValue(Status.FINISH);
break;
case ERROR:
item.getItemProperty("status").setValue(Status.ERROR);
break;
default:
item.getItemProperty("status").setValue(Status.UNKNWON);
}
} else {
item.getItemProperty("status").setValue(Status.PEDNING);
}
}
}
});
}
private void createToolbar() {
final Button createDevicesButton = new Button("generate...");
createDevicesButton.setIcon(FontAwesome.GEARS);
createDevicesButton.addClickListener(event -> openGenerateDialog());
final Button clearDevicesButton = new Button("clear");
clearDevicesButton.setIcon(FontAwesome.ERASER);
clearDevicesButton.addClickListener(event -> clearSimulatedDevices());
toolbar.addComponent(createDevicesButton);
toolbar.addComponent(clearDevicesButton);
toolbar.setSpacing(true);
}
private void clearSimulatedDevices() {
repository.clear();
beanContainer.removeAllItems();
}
private void openGenerateDialog() {
UI.getCurrent().addWindow(new GenerateDialog(new GenerateDialogCallback() {
@Override
public void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay,
final URL basePollUrl, final String gatewayToken, final Protocol protocol) {
for (int index = 0; index < amount; index++) {
final String deviceId = namePrefix + index;
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
spSenderService.createOrUpdateThing(tenant, deviceId);
}
}
}));
}
}

View File

@@ -25,7 +25,7 @@ spring.rabbitmq.virtualHost=/
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.dynamic=true
spring.rabbitmq.listener.prefetch=100
# SECURITY (SecurityProperties)
security.user.name=${BASIC_USERNAME:admin}
@@ -44,6 +44,6 @@ security.headers.frame=false
security.headers.content-type=false
security.headers.hsts=all
security.sessions=stateless
security.ignored=
security.ignored=/VAADIN/**
server.port=8083

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* 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
*/
// Import valo after setting the parameters
@import "../valo/valo";
.simulator{
@include valo;
.yellowicon {
color: orange;
}
.greenicon {
color: green;
}
.grayicon {
color: gray;
}
.redicon {
color: red;
}
.v-grid-cell.centeralign {
text-align: center;
}
}