/** * C-Nery - A home automation web application for C-Bus. * Copyright (C) 2008,2009,2012 Dave Oxley <dave@daveoxley.co.uk>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package com.daveoxley.cnery.scenes; import com.daveoxley.cbus.CGateException; import com.daveoxley.cnery.actions.SceneActionHome; import com.daveoxley.cnery.actions.SceneHome; import com.daveoxley.cnery.dao.SceneActionDAO; import com.daveoxley.cnery.entities.Scene; import com.daveoxley.cnery.entities.SceneAction; import com.daveoxley.cnery.entities.SceneCondition; import com.workplacesystems.queuj.Process; import com.workplacesystems.queuj.Queue; import com.workplacesystems.queuj.occurrence.RunOnce; import com.workplacesystems.queuj.occurrence.RunVariably; import com.workplacesystems.queuj.process.java.JavaProcessRunner; import com.workplacesystems.queuj.process.seam.SeamProcessBuilder; import com.workplacesystems.queuj.resilience.RunOnlyOnce; import com.workplacesystems.queuj.schedule.RelativeScheduleBuilder; import com.workplacesystems.queuj.schedule.VariableScheduleBuilder; import java.util.TreeSet; import javax.persistence.OptimisticLockException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; /** * * @author Dave Oxley <dave@daveoxley.co.uk> */ @Name("sceneRunner") public class SceneRunner extends AbstractRunner<SceneCondition> { private final static Log log = LogFactory.getLog(SceneRunner.class); @In private JavaProcessRunner PROCESS_RUNNER; @In private Scene scene; @In(create=true) private SceneHome sceneHome; @In(create=true) private SceneActionHome sceneActionHome; @In private SceneActionDAO sceneActionDAO; @In private Queue<SeamProcessBuilder> DEFAULT_QUEUE; public void activate() throws CGateException { sceneHome.clearInstance(); sceneHome.setId(scene.getId()); scene = sceneHome.getInstance(); if (scene.isActive()) { if (!scene.getDeactivateProcess().isRunning() && !scene.getDeactivateProcess().isWaitingToRun()) return; else { scene.getDeactivateProcess().attach(); if (scene.getDeactivateProcess().isFailed()) throw new CGateException("Process failed."); } } log.info("Checking conditions for scene " + scene.getName()); if (!conditionsMet(scene, false)) return; log.info("Activating scene " + scene.getName()); scene.setActive(true); sceneHome.update(); TreeSet<SceneAction> actions = new TreeSet<SceneAction>(); actions.addAll(scene.getSceneActions()); for (SceneAction action : actions) { boolean success = false; while (!success) { try { sceneActionHome.clearInstance(); sceneActionHome.setId(action.getId()); action = sceneActionHome.getInstance(); action.setOriginalLevel(action.getCBusGroup().getLevel()); action.setFirstRun(true); sceneActionHome.update(); RunVariably run = new RunVariably(); VariableScheduleBuilder vsb = run.newSchedulerBuilder(); vsb.setVariableSchedule(SceneActionSchedule.class); vsb.createSchedule(); ((SceneActionSchedule)run.getSchedule(0)).setSceneAction(action); action.getProcess().updateOccurrence(run); success = true; } catch (OptimisticLockException ole) {} } } if (scene.getStatePersistence() == Scene.StatePersistence.TIMED_ACTIVE) { RunOnce runOnce = new RunOnce(); RelativeScheduleBuilder rsb = runOnce.newRelativeScheduleBuilder(); rsb.setRunDelayMinutes(scene.getMinutes()); rsb.createSchedule(); scene.getDeactivateProcess().updateOccurrence(runOnce); } runConditionalSceneActions(); } public void deactiveForTrigger() throws CGateException { sceneHome.clearInstance(); sceneHome.setId(scene.getId()); scene = sceneHome.getInstance(); if (scene.getStatePersistence() != Scene.StatePersistence.TRIGGER) return; for (SceneAction action : scene.getSceneActions()) { Process process = action.getProcess(); if (process == null) throw new CGateException("Process is null."); do { process.attach(); } while (process.getNextRunTime() != null); if (process.isFailed()) throw new CGateException("Process failed."); } deactivate(false); } public void deactivate() throws CGateException { deactivate(true); } private void deactivate(boolean attach) throws CGateException { sceneHome.clearInstance(); sceneHome.setId(scene.getId()); scene = sceneHome.getInstance(); if (attach && !scene.isActive()) { scene.getActivateProcess().attach(); if (scene.getActivateProcess().isFailed()) throw new CGateException("Process failed."); } log.info("Deactivating scene " + scene.getName()); sceneHome.clearInstance(); sceneHome.setId(scene.getId()); scene = sceneHome.getInstance(); scene.setActive(false); sceneHome.update(); for (SceneAction action : scene.getSceneActions()) { sceneActionHome.clearInstance(); sceneActionHome.setId(action.getId()); action = sceneActionHome.getInstance(); RunOnce runOnce = new RunOnce(); RelativeScheduleBuilder rsb = runOnce.newRelativeScheduleBuilder(); rsb.setRunImmediately(); rsb.createSchedule(); boolean success = false; while (!success) { try { action.getProcess().updateOccurrence(runOnce); success = true; } catch (OptimisticLockException ole) {} } } runConditionalSceneActions(); } @Override boolean ignoreCondition(boolean scheduledCheck, SceneCondition condition) { return false; } @Override boolean doAction(SceneCondition condition) { return condition.getAction() == SceneCondition.Action.ALLOW; } private void runConditionalSceneActions() throws CGateException { RunOnce runOnce = new RunOnce(); RelativeScheduleBuilder rsb = runOnce.newRelativeScheduleBuilder(); rsb.setRunImmediately(); rsb.createSchedule(); SeamProcessBuilder spb = DEFAULT_QUEUE.newProcessBuilder(PROCESS_RUNNER.getLocale()); spb.setProcessName("RunConditionalSceneAction"); spb.setProcessDescription("Run Conditional Scene Action Runner"); spb.setProcessOccurrence(runOnce); spb.setProcessPersistence(true); spb.setProcessKeepCompleted(false); spb.setProcessResilience(new RunOnlyOnce()); spb.setProcessDetails(ConditionalSceneActionRunner.class, "run", new Class[] {}, new Object[] {}); for (SceneAction sa : sceneActionDAO.findSceneActionsByDependScene(scene)) { spb.setParameter("sceneAction", sa); spb.newProcess(); } } }