Package it.freedomotic.objects

Source Code of it.freedomotic.objects.EnvObjectLogic

/**
*
* Copyright (c) 2009-2013 Freedomotic team http://freedomotic.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
package it.freedomotic.objects;

import com.google.inject.Inject;

import it.freedomotic.app.Freedomotic;
import it.freedomotic.bus.BusService;
import it.freedomotic.core.Resolver;
import it.freedomotic.environment.EnvironmentLogic;
import it.freedomotic.environment.EnvironmentPersistence;
import it.freedomotic.environment.ZoneLogic;
import it.freedomotic.events.ObjectHasChangedBehavior;
import it.freedomotic.exceptions.VariableResolutionException;
import it.freedomotic.model.ds.Config;
import it.freedomotic.model.geometry.FreedomPolygon;
import it.freedomotic.model.geometry.FreedomShape;
import it.freedomotic.model.object.EnvObject;
import it.freedomotic.model.object.Representation;
import it.freedomotic.reactions.Command;
import it.freedomotic.reactions.CommandPersistence;
import it.freedomotic.reactions.Reaction;
import it.freedomotic.reactions.ReactionPersistence;
import it.freedomotic.reactions.Statement;
import it.freedomotic.reactions.Trigger;
import it.freedomotic.reactions.TriggerPersistence;
import it.freedomotic.security.Auth;
import it.freedomotic.util.TopologyUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.shiro.authz.annotation.RequiresPermissions;

/**
*
* @author Enrico
*/
public class EnvObjectLogic {

  @Inject
    private BusService busService;
   
    private EnvObject pojo;
    private boolean changed;
    // private String message;
    private HashMap<String, Command> commandsMapping; //mapping between action name -> hardware command instance
    private HashMap<String,BehaviorLogic> behaviors = new HashMap<String, BehaviorLogic>();
    private EnvironmentLogic environment;

   
   
    public EnvObjectLogic() {
    super();
    this.busService = Freedomotic.INJECTOR.getInstance(BusService.class);
  }

  /**
     * gets the hardware command mapped to the action in input for example:
     * Action -> Hardware Command Turn on -> Turn on light with X10 Actuator
     * Turn off -> Turn off light with X10 Actuator
     *
     * @param action
     * @return a Command or null if action doesn't exist or the mapping is not
     * valid
     */
    @RequiresPermissions("objects:read")
    public final Command getHardwareCommand(String action) {
        if ((action != null) && (!action.trim().isEmpty())) {
            Command commandToSearch = commandsMapping.get(action.trim().toLowerCase());

            if (commandToSearch != null) {
                return commandToSearch;
            } else {
                LOG.log(Level.SEVERE,"Doesn''t exists a valid hardware command associated to action ''{0}'' of object ''{1}"
                        + "''. \n"
                        + "This are the available mappings between action -> command for object ''{2}'': {3}",
                        new Object[]{action, pojo.getName(), pojo.getName(), commandsMapping.toString()});

                return null;
            }
        } else {
            LOG.log(Level.SEVERE, "The action ''{0}'' is not valid in object ''{1}''",
                    new Object[]{action, pojo.getName()});

            return null;
        }
    }

    /**
     * Create an HashMap with all object properties useful in an event
     *
     * @return a set of key/values of object properties
     */
    @RequiresPermissions("objects:read")
    public Map<String, String> getExposedProperties() {
        HashMap<String, String> result = pojo.getExposedProperties();
        return result;
    }

    @RequiresPermissions("objects:read")
    public Map<String, String> getExposedBehaviors() {
        Map<String, String> result = new HashMap<String, String>();
        for (BehaviorLogic behavior : getBehaviors()) {
            result.put("object.behavior." + behavior.getName(),
                    behavior.getValueAsString());
        }

        return result;
    }

    @RequiresPermissions("objects:update")
    public final void rename(String newName) {
        String oldName = this.getPojo().getName();
        newName = newName.trim();
        LOG.log(Level.WARNING, "Renaming object ''{0}'' in ''{1}''", new Object[]{oldName, newName});
        //change the object name
        this.getPojo().setName(newName);

        //change trigger references to this object
        for (Trigger t : TriggerPersistence.getTriggers()) {
            renameValuesInTrigger(t, oldName, newName);
        }

        //change commands references to this object
        for (Command c : CommandPersistence.getUserCommands()) {
            renameValuesInCommand(c, oldName, newName);
        }

        //rebuild reactions description
        for (Reaction r : ReactionPersistence.getReactions()) {
            r.setChanged();
        }
    }

    @RequiresPermissions("objects:update")
    public void setAction(String action, Command command) {
        if ((action != null) && !action.isEmpty() && (command != null)) {
            commandsMapping.put(action.trim(),
                    command);
            pojo.getActions().setProperty(action.trim(),
                    command.getName());
        }
    }

    @RequiresPermissions("objects:update")
    public void addTriggerMapping(Trigger trigger, String behaviorName) {
        //checking input parameters
        if ((behaviorName == null) || behaviorName.isEmpty() || (trigger == null)) {
            throw new IllegalArgumentException("behavior name and trigger cannot be null");
        }

        //parameters in input are ok, continue...
        Iterator<Entry<String, String>> it = pojo.getTriggers().entrySet().iterator();
        //remove old references if any
        while (it.hasNext()) {
            Entry<String, String> e = it.next();
            if (e.getValue().equals(behaviorName)) {
                it.remove(); //remove the old value that had to be updated
            }
        }

        pojo.getTriggers().setProperty(trigger.getName(),
                behaviorName);
        LOG.log(Level.CONFIG, "Trigger mapping in object {0}: behavior ''{1}'' is now associated to trigger named ''{2}''",
                new Object[]{this.getPojo().getName(), behaviorName, trigger.getName()});
    }

    @RequiresPermissions("objects:read")
    public String getAction(String t) {
        return getPojo().getTriggers().getProperty(t);
    }

    @RequiresPermissions("objects:update")
    public synchronized void setChanged(boolean value) {
        if (value == true) {
            this.changed = true;

            ObjectHasChangedBehavior objectEvent = new ObjectHasChangedBehavior(this, this);
            //send multicast because an event must be received by all triggers registred on the destination channel
            LOG.log(Level.CONFIG, "Object {0} changes something in its status (eg: a behavior value)",
                    this.getPojo().getName());
            busService.send(objectEvent);
        } else {
            changed = false;
        }
    }

    /**
     * When defining an object logic the registration of its behaviors is needed
     * otherwise they are not used.
     *
     * @param b
     */
    @RequiresPermissions("objects:update")
    public final void registerBehavior(BehaviorLogic b) {
        if (getBehavior(b.getName()) != null) {
            throw new IllegalArgumentException("Impossible to register behavior " + b.getName() + " in object "
                    + this.getPojo().getName() + " because it is already registed");
        }

        behaviors.put(b.getName(),b);
    }

    /**
     * Finds a behavior using its name (case sensitive)
     *
     * @param name
     * @return the reference to the behavior or null if it doesn't exists
     */
    @RequiresPermissions("objects:read")
    public final BehaviorLogic getBehavior(String name) {
        return behaviors.get(name);
    }

    /**
     * Caches developers level commands and creates user level commands as
     * specified in the createCommands() method of its subclasses
     */
    @RequiresPermissions("objects:read")
    public void init() {
        //validation
        if (pojo == null) {
            throw new IllegalStateException("An object must have a valid pojo before initialization");
        }
        pojo.initTags();
        createCommands();
        createTriggers();
        commandsMapping = new HashMap<String, Command>();
        cacheDeveloperLevelCommand();
        //assign to an environment
        pojo.getEnvironmentID();
        this.setEnvironment(environment);
        checkTopology();

    }

    @Deprecated
    @RequiresPermissions("objects:read")
    private boolean isChanged() {
        return changed;
    }

    @RequiresPermissions("objects:read")
    public EnvironmentLogic getEnvironment() {
        return this.environment;
    }

    @RequiresPermissions("objects:read")
    public EnvObject getPojo() {
       // if (pojo.getUUID() == null  || auth.isPermitted("objects:read:" + pojo.getUUID().substring(0, 5))
       //         ) {
            return pojo;
      //  }
      //  return null;
    }

    @RequiresPermissions("objects:delete")
    public final void destroy() {
        pojo = null;
        commandsMapping.clear();
        commandsMapping = null;
        behaviors.clear();
        behaviors = null;
    }

    @Override
    @RequiresPermissions("objects:read")
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        final EnvObjectLogic other = (EnvObjectLogic) obj;

        if ((this.pojo != other.pojo) && ((this.pojo == null) || !this.pojo.equals(other.pojo))) {
            return false;
        }

        return true;
    }

    @Override
    @RequiresPermissions("objects:read")
    public int hashCode() {
        int hash = 7;
        hash = (53 * hash) + ((this.pojo != null) ? this.pojo.hashCode() : 0);

        return hash;
    }

    @RequiresPermissions("objects:read")
    public Iterable<BehaviorLogic> getBehaviors() {
        return behaviors.values();
    }

    @RequiresPermissions("objects:create")
    public final void setRandomLocation() {
        int randomX =
                0
                + (int) (Math.random() * EnvironmentPersistence.getEnvironments().get(0).getPojo().getWidth());
        int randomY =
                0
                + (int) (Math.random() * EnvironmentPersistence.getEnvironments().get(0).getPojo().getHeight());
        setLocation(randomX, randomY);
    }

    @RequiresPermissions("objects:update")
    public void setLocation(int x, int y) {
        for (Representation rep : getPojo().getRepresentations()) {
            rep.setOffset(x, y);
        }

        checkTopology();
    }

    @RequiresPermissions({"objects:read", "zones.update"})
    private void checkTopology() {
        FreedomShape shape = getPojo().getRepresentations().get(0).getShape();
        int xoffset = getPojo().getCurrentRepresentation().getOffset().getX();
        int yoffset = getPojo().getCurrentRepresentation().getOffset().getY();

        //now apply offset to the shape
        FreedomPolygon translatedObject =
                (FreedomPolygon) TopologyUtils.translate((FreedomPolygon) shape, xoffset, yoffset);

        for (EnvironmentLogic locEnv : EnvironmentPersistence.getEnvironments()) {
            for (ZoneLogic zone : locEnv.getZones()) {
                //remove from every zone
                zone.getPojo().getObjects().remove(this.getPojo());
                if (this.getEnvironment() == locEnv && TopologyUtils.intersects(translatedObject, zone.getPojo().getShape())) {
                    //DEBUG: System.out.println("object " + getPojo().getName() + " intersects zone " + zone.getPojo().getName());
                    //add to the zones this object belongs
                    zone.getPojo().getObjects().add(this.getPojo());
                    LOG.log(Level.CONFIG, "Object {0} is in zone {1}",
                            new Object[]{getPojo().getName(), zone.getPojo().getName()});
                } else {
                    //DEBUG: System.out.println("object " + getPojo().getName() + " NOT intersects zone " + zone.getPojo().getName());
                }
            }
        }
    }

    public final boolean executeTrigger(Trigger t) {
        String behavior = getAction(t.getName());

        if (behavior == null ) {
            //LOG.severe("Hardware trigger '" + t.getName() + "' is not bound to any action of object " + this.getPojo().getName());
            //check if the behavior name is written in the trigger
           
            behavior = t.getPayload().getStatements("behavior.name").isEmpty() ""  : t.getPayload().getStatements("behavior.name").get(0).getValue();

            if (behavior.isEmpty()) {
                return false;
            }
        }

        Statement valueStatement = t.getPayload().getStatements("behaviorValue").get(0);

        if (valueStatement == null) {
            LOG.log(Level.WARNING,
                    "No value in hardware trigger ''{0}'' to apply to object action ''{1}'' of object {2}",
                    new Object[]{t.getName(), behavior, getPojo().getName()});

            return false;
        }

        LOG.log(Level.CONFIG,
                "Sensors notification ''{0}'' has changed ''{1}'' behavior ''{2}'' to {3}",
                new Object[]{t.getName(), getPojo().getName(), behavior, valueStatement.getValue()});

        Config params = new Config();
        params.setProperty("value",
                valueStatement.getValue());
        getBehavior(behavior).filterParams(params, false); //false means not fire commands, only change behavior value

        return true;
    }

    /**
     * Executes the hardware command related to the action passed as paramenter
     * using an user command.
     *
     * @param action the name of the action to executeCommand as defined in the
     * object XML
     * @param params parameters of the event that have started the reaction
     * execution
     * @return true if the command is succesfully executed by the actuator and
     * false otherways
     */
    @RequiresPermissions("objects:read")
    protected final boolean executeCommand(final String action, final Config params) {
        LOG.log(Level.FINE, "Executing action ''{0}'' of object ''{1}''",
                new Object[]{action, getPojo().getName()});

        if (getPojo().getActAs().equalsIgnoreCase("virtual")) {
            //it's a virtual object like a button, not needed real execution of a command
            LOG.log(Level.CONFIG,
                    "The object ''{0}'' act as virtual device, so its hardware commands are not executed.",
                    getPojo().getName());

            return true;
        }

        final Command command = getHardwareCommand(action.trim());

        if (command == null) {
            LOG.log(Level.WARNING,
                    "The hardware level command for action ''{0}'' in object ''{1}'' doesn''t exists or is not setted",
                    new Object[]{action, pojo.getName()});

            return false; //command not executed
        }

        //resolves developer level command parameters like myObjectName = "@event.object.name" -> myObjectName = "Light 1"
        //in this case the parameter in the userLevelCommand are used as basis for the resolution process (the context)
        //along with the parameters getted from the relative behavior (if exists)
        LOG.log(Level.FINE,
                "Environment object ''{0}'' tries to ''{1}'' itself using hardware command ''{2}''",
                new Object[]{pojo.getName(), action, command.getName()});

        Resolver resolver = new Resolver();
        //adding a resolution context for object that owns this hardware level command. 'owner.' is the prefix of this context
        resolver.addContext("owner.",
                getExposedProperties());
        resolver.addContext("owner.",
                getExposedBehaviors());

        try {
            final Command resolvedCommand = resolver.resolve(command); //eg: turn on an X10 device
            //            XStream s = FreedomXStream.getXstream();
            //            System.out.println(s.toXML(resolvedCommand));

            Command result = busService.send(resolvedCommand); //blocking wait until timeout

            if ((result != null) && result.isExecuted()) {
                return true; //succesfully executed
            }
        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(EnvObjectLogic.class.getName()).log(Level.SEVERE, null, ex);
        } catch (VariableResolutionException ex) {
            Logger.getLogger(EnvObjectLogic.class.getName()).log(Level.SEVERE, null, ex);
        }

        return false; //command not executed
    }

    protected void createCommands() {
        //default empty implementation
    }

    protected void createTriggers() {
        //default empty implementation
    }

    @RequiresPermissions("objects:update")
    protected void setPojo(EnvObject pojo) {
        if (((pojo.getEnvironmentID() == null) || pojo.getEnvironmentID().isEmpty())
                && (EnvironmentPersistence.getEnvironments().size() > 0)) {
            pojo.setEnvironmentID(EnvironmentPersistence.getEnvironments().get(0).getPojo().getUUID());
        }

        this.pojo = pojo;
        this.environment = EnvironmentPersistence.getEnvByUUID(pojo.getEnvironmentID());
    }

    @RequiresPermissions({"objects:update", "triggers:update"})
    private void renameValuesInTrigger(Trigger t, String oldName, String newName) {
        if (!t.isHardwareLevel()) {
            if (t.getName().contains(oldName)) {
                t.setName(t.getName().replace(oldName, newName));
                LOG.log(Level.WARNING, "trigger name renamed to {0}", t.getName());
            }
            Iterator<Statement> it = t.getPayload().iterator();
            while (it.hasNext()) {
                Statement statement = it.next();
                if (statement.getValue().contains(oldName)) {
                    statement.setValue(statement.getValue().replace(oldName, newName));
                    LOG.log(Level.WARNING, "Trigger value in payload renamed to {0}", statement.getValue());
                }
            }
        }
    }

    @RequiresPermissions({"objects:read", "commands:update"})
    private void renameValuesInCommand(Command c, String oldName, String newName) {
        if (c.getName().contains(oldName)) {
            c.setName(c.getName().replace(oldName, newName));
            LOG.log(Level.WARNING, "Command name renamed to {0}", c.getName());
        }

        if (c.getProperty("object") != null) {
            if (c.getProperty("object").contains(oldName)) {
                c.setProperty("object",
                        c.getProperty("object").replace(oldName, newName));
                LOG.log(Level.WARNING, "Property ''object'' in command renamed to {0}", c.getProperty("object"));
            }
        }
    }

    private void cacheDeveloperLevelCommand() {
        if (commandsMapping == null) {
            commandsMapping = new HashMap<String, Command>();
        }

        for (String action : pojo.getActions().stringPropertyNames()) {
            String commandName = pojo.getActions().getProperty(action);
            Command command = CommandPersistence.getHardwareCommand(commandName);

            if (command != null) {
                LOG.log(Level.CONFIG,
                        "Caching the command ''{0}'' as related to action ''{1}'' ",
                        new Object[]{command.getName(), action});
                setAction(action, command);
            } else {
                LOG.log(Level.CONFIG,
                        "Don''t exist a command called ''{0}'' is not possible to bound this command to action ''{1}'' of {2}",
                        new Object[]{commandName, action, this.getPojo().getName()});
            }
        }
    }

    @RequiresPermissions("objects:update")
    public void setEnvironment(EnvironmentLogic selEnv) {
        if (selEnv == null) {
            throw new IllegalArgumentException("Selected environment cannot be "
                    + "null for object " + getPojo().getName());
        }
        this.environment = selEnv;
        getPojo().setEnvironmentID(selEnv.getPojo().getUUID());
    }

    @RequiresPermissions("objects:update")
    public void addTags(String tagList) {
        String[] tags = tagList.toLowerCase().split(",");
        getPojo().getTagsList().addAll(Arrays.asList(tags));
    }
    private static final Logger LOG = Logger.getLogger(EnvObjectLogic.class.getName());
}
TOP

Related Classes of it.freedomotic.objects.EnvObjectLogic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.