package com.dataart.android.devicehive.device; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import com.dataart.android.devicehive.ApiInfo; import com.dataart.android.devicehive.Command; import com.dataart.android.devicehive.DeviceData; import com.dataart.android.devicehive.DeviceHive; import com.dataart.android.devicehive.Notification; import com.dataart.android.devicehive.commands.GetApiInfoCommand; import com.dataart.android.devicehive.device.commands.GetDeviceCommand; import com.dataart.android.devicehive.device.commands.PollDeviceCommandsCommand; import com.dataart.android.devicehive.device.commands.RegisterDeviceCommand; import com.dataart.android.devicehive.device.commands.SendNotificationCommand; import com.dataart.android.devicehive.device.commands.UpdateCommandStatusCommand; import com.dataart.android.devicehive.network.DeviceHiveResultReceiver; import com.dataart.android.devicehive.network.NetworkCommand; import com.dataart.android.devicehive.network.ServiceConnection; /* package */class DeviceServiceConnection extends ServiceConnection { private final static String EQUIPMENT_PARAMETER = "equipment"; private Device device; private final Queue<Command> commandQueue = new LinkedList<Command>(); private boolean isProcessingCommands = false; private boolean isPollRequestInProgress = false; private String lastCommandPollTimestamp; private Integer commandPollWaitTimeout; public DeviceServiceConnection(Context context) { super(context); } public void setLastCommandPollTimestamp(String timestamp) { this.lastCommandPollTimestamp = timestamp; } /* package */void setCommandPollWaitTimeout(Integer timeout) { this.commandPollWaitTimeout = timeout; } @Override public void setApiEndpointUrl(String url) { if (apiEndpointUrl != null && !apiEndpointUrl.equals(url)) { isPollRequestInProgress = false; } super.setApiEndpointUrl(url); } /* package */void sendNotification(Notification notification) { logD("Sending notification: " + notification.getName()); device.onStartSendingNotification(notification); final DeviceData deviceData = device.getDeviceData(); startNetworkCommand(new SendNotificationCommand(deviceData.getId(), deviceData.getKey(), notification)); } /* package */void startProcessingCommands() { if (isProcessingCommands) { stopProcessingCommands(); } isProcessingCommands = true; executeNextCommand(); } /* package */void stopProcessingCommands() { detachResultReceiver(); isProcessingCommands = false; isPollRequestInProgress = false; } /* package */void setDevice(Device device) { this.device = device; } /* package */boolean isProcessingCommands() { return isProcessingCommands; } /* package */void registerDevice() { startNetworkCommand(new RegisterDeviceCommand(device.getDeviceData())); } /* package */void unregisterDevice() { logD("Unregistering device"); unregisterEquipment(); } /* package */void reloadDeviceData() { final DeviceData deviceData = device.getDeviceData(); startNetworkCommand(new GetDeviceCommand(deviceData.getId(), deviceData.getKey())); } private void runCommandOnRunner(final CommandRunner commandRunner, final Command command) { if (commandRunner.shouldRunCommandAsynchronously(command)) { asyncHandler.post(new Runnable() { @Override public void run() { final CommandResult result = commandRunner .runCommand(command); mainThreadHandler.post(new Runnable() { @Override public void run() { updateCommandStatus(command, result); } }); } }); } else { final CommandResult result = commandRunner.runCommand(command); updateCommandStatus(command, result); } } @SuppressWarnings("rawtypes") private void executeNextCommand() { final Command command = commandQueue.poll(); if (command != null) { device.onBeforeRunCommand(command); Object parameters = command.getParameters(); if (parameters == null || !(parameters instanceof Map) || !((Map) parameters).containsKey(EQUIPMENT_PARAMETER)) { runCommandOnRunner(device, command); } else { Equipment equipment = device .getEquipmentWithCode((String) ((Map) parameters) .get(EQUIPMENT_PARAMETER)); if (equipment != null) { equipment.onBeforeRunCommand(command); runCommandOnRunner(equipment, command); } else { updateCommandStatus(command, new CommandResult( CommandResult.STATUS_FAILED, "Equipment not found")); } } } else { if (!isPollRequestInProgress) { isPollRequestInProgress = true; if (lastCommandPollTimestamp == null) { // timestamp wasn't specified. Request and use server // timestamp instead. logD("Starting Get API info command"); startNetworkCommand(new GetApiInfoCommand()); } else { startPollCommandsRequest(); } } } } private void startPollCommandsRequest() { logD("Starting polling request with lastCommandPollTimestamp = " + lastCommandPollTimestamp); final DeviceData deviceData = device.getDeviceData(); startNetworkCommand(new PollDeviceCommandsCommand(deviceData.getId(), deviceData.getKey(), lastCommandPollTimestamp, commandPollWaitTimeout)); } private void updateCommandStatus(Command deviceCommand, CommandResult result) { logD(String.format("Update command(%s) status(%s) and result(%s)", deviceCommand.getCommand(), result.getStatus(), result.getResult())); final DeviceData deviceData = device.getDeviceData(); startNetworkCommand(new UpdateCommandStatusCommand(deviceData.getId(), deviceData.getKey(), deviceCommand.getId(), result)); } private int enqueueCommands(List<Command> commands) { if (commands == null || commands.isEmpty()) { return 0; } int enqueuedCount = 0; for (Command command : commands) { if (TextUtils.isEmpty(command.getStatus())) { boolean added = commandQueue.offer(command); if (!added) { Log.e(DeviceHive.TAG, "Failed to add command to the command queue"); } else { enqueuedCount++; } } } return enqueuedCount; } @Override protected void onReceiveResult(final int resultCode, final int tagId, final Bundle resultData) { switch (resultCode) { case DeviceHiveResultReceiver.MSG_HANDLED_RESPONSE: logD("Handled response"); if (tagId == TAG_REGISTER) { DeviceData deviceData = RegisterDeviceCommand .getDeviceData(resultData); logD("Device registration finished with data: " + deviceData); sendNotification(DeviceStatusNotification.STATUS_ONLINE); } else if (tagId == TAG_SEND_NOTIFICATION) { Notification notification = SendNotificationCommand .getNotification(resultData); logD("Notification sent with response: " + notification); device.onFinishSendingNotification(notification); if (!device.isRegistered()) { registerEquipment(); } } else if (tagId == TAG_POLL_COMMANDS) { logD("Poll request finished"); isPollRequestInProgress = false; List<Command> commands = PollDeviceCommandsCommand .getCommands(resultData); logD("-------Received commands: " + commands); logD("Commands count: " + commands.size()); int enqueuedCount = enqueueCommands(commands); logD("Enqueued commands count: " + enqueuedCount); if (!commands.isEmpty()) { lastCommandPollTimestamp = commands .get(commands.size() - 1).getTimestamp(); } if (isProcessingCommands) { executeNextCommand(); } } else if (tagId == TAG_UPDATE_COMMAND_STATUS) { Command command = UpdateCommandStatusCommand .getUpdatedCommand(resultData); logD("Updated command: " + command); if (isProcessingCommands) { executeNextCommand(); } } else if (tagId == TAG_GET_DEVICE) { logD("Get device request finished"); final DeviceData deviceData = GetDeviceCommand .getDevice(resultData); device.onReloadDeviceDataFinishedInternal(deviceData); } else if (tagId == TAG_GET_API_INFO) { final ApiInfo apiInfo = GetApiInfoCommand .getApiInfo(resultData); logD("Get API info request finished: " + apiInfo); lastCommandPollTimestamp = apiInfo.getServerTimestamp(); startPollCommandsRequest(); } break; case DeviceHiveResultReceiver.MSG_EXCEPTION: final Throwable exception = NetworkCommand.getThrowable(resultData); Log.e(DeviceHive.TAG, "DeviceHiveResultReceiver.MSG_EXCEPTION", exception); case DeviceHiveResultReceiver.MSG_STATUS_FAILURE: if (tagId == TAG_REGISTER) { device.onFailRegistration(); } if (tagId == TAG_POLL_COMMANDS || tagId == TAG_GET_API_INFO) { isPollRequestInProgress = false; if (isProcessingCommands) { executeNextCommand(); } } else if (tagId == TAG_UPDATE_COMMAND_STATUS) { logD("Failed to update command status"); // for now skip this command and try to execute next if (isProcessingCommands) { executeNextCommand(); } } else if (tagId == TAG_SEND_NOTIFICATION) { SendNotificationCommand command = (SendNotificationCommand) NetworkCommand .getCommand(resultData); Notification notification = command.getNotification(); device.onFailSendingNotification(notification); if (!device.isRegistered()) { device.onFailRegistration(); } } else if (tagId == TAG_GET_DEVICE) { device.onReloadDeviceDataFailedInternal(); } break; } } private final Runnable registerEquipmentRunnable = new Runnable() { @Override public void run() { boolean equipmentRegistrationResult = true; for (Equipment eq : device.getEquipment()) { equipmentRegistrationResult = equipmentRegistrationResult && eq.onRegisterEquipment(); if (!equipmentRegistrationResult) { break; } } final boolean result = equipmentRegistrationResult; mainThreadHandler.post(new Runnable() { @Override public void run() { device.equipmentRegistrationFinished(result); } }); } }; private final Runnable unregisterEquipmentRunnable = new Runnable() { @Override public void run() { boolean equipmentUnregistrationResult = true; for (Equipment eq : device.getEquipment()) { equipmentUnregistrationResult = equipmentUnregistrationResult && eq.onUnregisterEquipment(); if (!equipmentUnregistrationResult) { break; } } final boolean result = equipmentUnregistrationResult; mainThreadHandler.post(new Runnable() { @Override public void run() { device.equipmentUnregistrationFinished(result); } }); } }; private void registerEquipment() { if (device.performsEquipmentRegistrationCallbacksAsynchronously()) { asyncHandler.post(registerEquipmentRunnable); } else { registerEquipmentRunnable.run(); } } private void unregisterEquipment() { if (device.performsEquipmentRegistrationCallbacksAsynchronously()) { asyncHandler.post(unregisterEquipmentRunnable); } else { unregisterEquipmentRunnable.run(); } } private final static int TAG_REGISTER = getTagId(RegisterDeviceCommand.class); private final static int TAG_SEND_NOTIFICATION = getTagId(SendNotificationCommand.class); private final static int TAG_POLL_COMMANDS = getTagId(PollDeviceCommandsCommand.class); private final static int TAG_UPDATE_COMMAND_STATUS = getTagId(UpdateCommandStatusCommand.class); private final static int TAG_GET_DEVICE = getTagId(GetDeviceCommand.class); private static final int TAG_GET_API_INFO = getTagId(GetApiInfoCommand.class); }