/* * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * 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 org.traccar.database; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseProtocol; import org.traccar.Context; import org.traccar.model.Command; import org.traccar.model.Typed; import org.traccar.model.Position; public class CommandsManager extends ExtendedObjectManager<Command> { private static final Logger LOGGER = LoggerFactory.getLogger(CommandsManager.class); private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>(); private boolean queueing; public CommandsManager(DataManager dataManager, boolean queueing) { super(dataManager, Command.class); this.queueing = queueing; } public boolean checkDeviceCommand(long deviceId, long commandId) { return !getAllDeviceItems(deviceId).contains(commandId); } public boolean sendCommand(Command command) throws Exception { long deviceId = command.getDeviceId(); if (command.getId() != 0) { command = getById(command.getId()).clone(); command.setDeviceId(deviceId); } if (command.getTextChannel()) { Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); String phone = Context.getIdentityManager().getById(deviceId).getPhone(); if (lastPosition != null) { BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol()); protocol.sendTextCommand(phone, command); } else if (command.getType().equals(Command.TYPE_CUSTOM)) { if (Context.getSmsManager() != null) { Context.getSmsManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true); } else { throw new RuntimeException("SMS is not enabled"); } } else { throw new RuntimeException("Command " + command.getType() + " is not supported"); } } else { ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId); if (activeDevice != null) { if (activeDevice.supportsLiveCommands()) { activeDevice.sendCommand(command); } else { getDeviceQueue(deviceId).add(command); return false; } } else if (!queueing) { throw new RuntimeException("Device is not online"); } else { getDeviceQueue(deviceId).add(command); return false; } } return true; } public Collection<Long> getSupportedCommands(long deviceId) { List<Long> result = new ArrayList<>(); Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); for (long commandId : getAllDeviceItems(deviceId)) { Command command = getById(commandId); if (lastPosition != null) { BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol()); if (command.getTextChannel() && protocol.getSupportedTextCommands().contains(command.getType()) || !command.getTextChannel() && protocol.getSupportedDataCommands().contains(command.getType())) { result.add(commandId); } } else if (command.getType().equals(Command.TYPE_CUSTOM)) { result.add(commandId); } } return result; } public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) { Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId); if (lastPosition != null) { return getCommandTypes(lastPosition.getProtocol(), textChannel); } else { return Collections.singletonList(new Typed(Command.TYPE_CUSTOM)); } } public Collection<Typed> getCommandTypes(String protocolName, boolean textChannel) { List<Typed> result = new ArrayList<>(); BaseProtocol protocol = Context.getServerManager().getProtocol(protocolName); Collection<String> commands; commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands(); for (String commandKey : commands) { result.add(new Typed(commandKey)); } return result; } public Collection<Typed> getAllCommandTypes() { List<Typed> result = new ArrayList<>(); Field[] fields = Command.class.getDeclaredFields(); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) { try { result.add(new Typed(field.get(null).toString())); } catch (IllegalArgumentException | IllegalAccessException error) { LOGGER.warn("Get command types error", error); } } } return result; } private Queue<Command> getDeviceQueue(long deviceId) { Queue<Command> deviceQueue; try { readLock(); deviceQueue = deviceQueues.get(deviceId); } finally { readUnlock(); } if (deviceQueue != null) { return deviceQueue; } else { try { writeLock(); return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>()); } finally { writeUnlock(); } } } public Collection<Command> readQueuedCommands(long deviceId) { return readQueuedCommands(deviceId, Integer.MAX_VALUE); } public Collection<Command> readQueuedCommands(long deviceId, int count) { Queue<Command> deviceQueue; try { readLock(); deviceQueue = deviceQueues.get(deviceId); } finally { readUnlock(); } Collection<Command> result = new ArrayList<>(); if (deviceQueue != null) { Command command = deviceQueue.poll(); while (command != null && result.size() < count) { result.add(command); command = deviceQueue.poll(); } } return result; } }