Search in sources :

Example 1 with InjectEnvironment

use of me.retrodaredevil.action.node.environment.InjectEnvironment in project solarthing by wildmountainfarms.

the class CommandHandler method createInjectEnvironment.

public InjectEnvironment createInjectEnvironment(String commandName) {
    var builder = new InjectEnvironment.Builder().add(new NanoTimeProviderEnvironment(NanoTimeProvider.SYSTEM_NANO_TIME)).add(new SolarThingDatabaseEnvironment(solarThingDatabase)).add(new TimeZoneEnvironment(zoneId));
    RestCommandConfig.Command command = restCommandConfig.getCommandToActionFileMap().get(commandName);
    if (command == null) {
        throw new IllegalStateException("You should not be calling createInjectEnvironment if the command doesn't exist for commandName: " + commandName);
    }
    String sourceId = command.getSourceId();
    if (sourceId != null) {
        builder.add(new SourceIdEnvironment(sourceId));
    }
    return builder.build();
}
Also used : SolarThingDatabaseEnvironment(me.retrodaredevil.solarthing.actions.environment.SolarThingDatabaseEnvironment) SourceIdEnvironment(me.retrodaredevil.solarthing.actions.environment.SourceIdEnvironment) TimeZoneEnvironment(me.retrodaredevil.solarthing.actions.environment.TimeZoneEnvironment) NanoTimeProviderEnvironment(me.retrodaredevil.action.node.environment.NanoTimeProviderEnvironment) InjectEnvironment(me.retrodaredevil.action.node.environment.InjectEnvironment)

Example 2 with InjectEnvironment

use of me.retrodaredevil.action.node.environment.InjectEnvironment in project solarthing by wildmountainfarms.

the class CommandController method runCommand.

/**
 * Runs the given action. A response is not returned until the action is done running
 *
 * @param apiKey The api key
 * @param commandName The name of the command, which corresponds to an action
 * @return
 */
@GetMapping(path = "/run", produces = "application/json")
public CommandRequestResponse runCommand(String apiKey, String commandName) {
    // Also consider using this way instead of exceptions: https://stackoverflow.com/a/60079942
    if (apiKey == null) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "apiKey is required!");
    }
    if (commandName == null) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "commandName is required!");
    }
    if (!commandHandler.isAuthorized(apiKey)) {
        throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "You are not authorized with the given api key!");
    }
    ActionNode actionNode = commandHandler.getActionNode(commandName);
    if (actionNode == null) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No corresponding command with commandName: " + commandName);
    }
    InjectEnvironment injectEnvironment = commandHandler.createInjectEnvironment(commandName);
    // We don't know or care what thread this is running on, so we won't have a shared global variable environment.
    // We could make a shared global environment a feature of this down the line, but for now let's keep this simple
    ActionEnvironment actionEnvironment = new ActionEnvironment(new VariableEnvironment(), new VariableEnvironment(), injectEnvironment);
    Action action = actionNode.createAction(actionEnvironment);
    while (!action.isDone()) {
        action.update();
        try {
            // This is here to make sure our CPU doesn't go to 100% for no reason
            Thread.sleep(5);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            LOGGER.error("Interrupted while action was being performed.", e);
            action.end();
            return new CommandRequestResponse(false);
        }
    }
    action.end();
    return new CommandRequestResponse(true);
}
Also used : ActionEnvironment(me.retrodaredevil.action.node.environment.ActionEnvironment) Action(me.retrodaredevil.action.Action) ActionNode(me.retrodaredevil.action.node.ActionNode) VariableEnvironment(me.retrodaredevil.action.node.environment.VariableEnvironment) ResponseStatusException(org.springframework.web.server.ResponseStatusException) InjectEnvironment(me.retrodaredevil.action.node.environment.InjectEnvironment) GetMapping(org.springframework.web.bind.annotation.GetMapping)

Example 3 with InjectEnvironment

use of me.retrodaredevil.action.node.environment.InjectEnvironment in project solarthing by wildmountainfarms.

the class AutomationMain method startAutomation.

public static int startAutomation(List<ActionNode> actionNodes, DatabaseTimeZoneOptionBase options, long periodMillis) {
    LOGGER.info(SolarThingConstants.SUMMARY_MARKER, "Starting automation program.");
    final CouchDbDatabaseSettings couchSettings;
    try {
        couchSettings = ConfigUtil.expectCouchDbDatabaseSettings(options);
    } catch (IllegalArgumentException ex) {
        LOGGER.error("(Fatal)", ex);
        return SolarThingConstants.EXIT_CODE_INVALID_CONFIG;
    }
    SolarThingDatabase database = CouchDbSolarThingDatabase.create(CouchDbUtil.createInstance(couchSettings.getCouchProperties(), couchSettings.getOkHttpProperties()));
    VariableEnvironment variableEnvironment = new VariableEnvironment();
    // Use atomic reference so that access is thread safe
    AtomicReference<FragmentedPacketGroup> latestPacketGroupReference = new AtomicReference<>(null);
    // Use atomic reference so that access is thread safe
    AtomicReference<List<VersionedPacket<StoredAlterPacket>>> alterPacketsReference = new AtomicReference<>(null);
    // note this may return null, and that's OK // This is thread safe if needed
    FragmentedPacketGroupProvider fragmentedPacketGroupProvider = latestPacketGroupReference::get;
    Clock clock = Clock.systemUTC();
    SimpleDatabaseCache statusDatabaseCache = SimpleDatabaseCache.createDefault(clock);
    // not thread safe
    ResourceManager<SimpleDatabaseCache> statusDatabaseCacheManager = new BasicResourceManager<>(statusDatabaseCache);
    SimpleDatabaseCache eventDatabaseCache = SimpleDatabaseCache.createDefault(clock);
    ResourceManager<SimpleDatabaseCache> eventDatabaseCacheManager = new ReadWriteResourceManager<>(eventDatabaseCache);
    SimpleDatabaseCache openDatabaseCache = new SimpleDatabaseCache(Duration.ofMinutes(60), Duration.ofMinutes(40), Duration.ofMinutes(20), Duration.ofMinutes(15), clock);
    // not thread safe
    ResourceManager<SimpleDatabaseCache> openDatabaseCacheManager = new BasicResourceManager<>(openDatabaseCache);
    SimplePacketCache<AuthorizationPacket> authorizationPacketCache = new SimplePacketCache<>(Duration.ofSeconds(20), DatabaseDocumentKeyMap.createPacketSourceFromDatabase(database), false);
    String sourceId = options.getSourceId();
    InjectEnvironment injectEnvironment = new InjectEnvironment.Builder().add(new NanoTimeProviderEnvironment(NanoTimeProvider.SYSTEM_NANO_TIME)).add(new SourceIdEnvironment(sourceId)).add(// most of the time, it's better to use SolarThingDatabaseEnvironment instead, but this option is here in case it's needed
    new CouchDbEnvironment(couchSettings)).add(new SolarThingDatabaseEnvironment(CouchDbSolarThingDatabase.create(CouchDbUtil.createInstance(couchSettings.getCouchProperties(), couchSettings.getOkHttpProperties())))).add(new TimeZoneEnvironment(options.getZoneId())).add(// access is thread safe if needed
    new LatestPacketGroupEnvironment(fragmentedPacketGroupProvider)).add(// access is thread safe if needed
    new LatestFragmentedPacketGroupEnvironment(fragmentedPacketGroupProvider)).add(new EventDatabaseCacheEnvironment(eventDatabaseCacheManager)).add(new OpenDatabaseCacheEnvironment(openDatabaseCache)).add(// access is thread safe if needed
    new AlterPacketsEnvironment(alterPacketsReference::get)).add(new AuthorizationEnvironment(new DatabaseDocumentKeyMap(authorizationPacketCache))).build();
    ActionMultiplexer multiplexer = new Actions.ActionMultiplexerBuilder().build();
    while (!Thread.currentThread().isInterrupted()) {
        queryAndFeed(database.getStatusDatabase(), statusDatabaseCacheManager, true);
        queryAndFeed(database.getEventDatabase(), eventDatabaseCacheManager, true);
        queryAndFeed(database.getOpenDatabase(), openDatabaseCacheManager, false);
        {
            // Never cache alter packets, because it's always important that we have up-to-date data, or no data at all.
            List<VersionedPacket<StoredAlterPacket>> alterPackets = null;
            try {
                alterPackets = database.getAlterDatabase().queryAll(sourceId);
                LOGGER.debug("Got " + alterPackets.size() + " alter packets");
            } catch (SolarThingDatabaseException e) {
                LOGGER.error("Could not get alter packets", e);
            }
            alterPacketsReference.set(alterPackets);
        }
        // we have auto update turned off, so we have to call this
        authorizationPacketCache.updateIfNeeded();
        List<FragmentedPacketGroup> statusPacketGroups = PacketUtil.getPacketGroups(options.getSourceId(), options.getDefaultInstanceOptions(), statusDatabaseCache.getAllCachedPackets());
        if (statusPacketGroups != null) {
            FragmentedPacketGroup statusPacketGroup = statusPacketGroups.get(statusPacketGroups.size() - 1);
            latestPacketGroupReference.set(statusPacketGroup);
        }
        for (ActionNode actionNode : actionNodes) {
            multiplexer.add(actionNode.createAction(new ActionEnvironment(variableEnvironment, new VariableEnvironment(), injectEnvironment)));
        }
        multiplexer.update();
        LOGGER.debug("There are " + multiplexer.getActiveActions().size() + " active actions");
        try {
            Thread.sleep(periodMillis);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(ex);
        }
    }
    return 0;
}
Also used : ActionEnvironment(me.retrodaredevil.action.node.environment.ActionEnvironment) NanoTimeProviderEnvironment(me.retrodaredevil.action.node.environment.NanoTimeProviderEnvironment) ActionNode(me.retrodaredevil.action.node.ActionNode) BasicResourceManager(me.retrodaredevil.solarthing.util.sync.BasicResourceManager) Clock(java.time.Clock) FragmentedPacketGroupProvider(me.retrodaredevil.solarthing.FragmentedPacketGroupProvider) InjectEnvironment(me.retrodaredevil.action.node.environment.InjectEnvironment) AuthorizationPacket(me.retrodaredevil.solarthing.type.closed.authorization.AuthorizationPacket) SimpleDatabaseCache(me.retrodaredevil.solarthing.database.cache.SimpleDatabaseCache) SimplePacketCache(me.retrodaredevil.solarthing.database.cache.SimplePacketCache) ArrayList(java.util.ArrayList) List(java.util.List) ActionMultiplexer(me.retrodaredevil.action.ActionMultiplexer) CouchDbSolarThingDatabase(me.retrodaredevil.solarthing.database.couchdb.CouchDbSolarThingDatabase) ReadWriteResourceManager(me.retrodaredevil.solarthing.util.sync.ReadWriteResourceManager) FragmentedPacketGroup(me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup) StoredAlterPacket(me.retrodaredevil.solarthing.type.alter.StoredAlterPacket) Actions(me.retrodaredevil.action.Actions) AtomicReference(java.util.concurrent.atomic.AtomicReference) SolarThingDatabaseException(me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException) CouchDbDatabaseSettings(me.retrodaredevil.solarthing.config.databases.implementations.CouchDbDatabaseSettings) VariableEnvironment(me.retrodaredevil.action.node.environment.VariableEnvironment)

Example 4 with InjectEnvironment

use of me.retrodaredevil.action.node.environment.InjectEnvironment in project solarthing by wildmountainfarms.

the class DeserializeTest method testRunAlertGeneratorOffWhileAuxOn.

@Test
void testRunAlertGeneratorOffWhileAuxOn() throws IOException, ParsePacketAsciiDecimalDigitException, CheckSumException {
    File file = new File(ACTION_CONFIG_DIRECTORY, "alert_generator_off_while_aux_on.json");
    ActionNode actionNode = MAPPER.readValue(file, ActionNode.class);
    // We need to simulate an automation program environment to run this action
    Duration[] timeReference = new Duration[] { Duration.ZERO };
    FragmentedPacketGroup[] packetGroupReference = new FragmentedPacketGroup[] { null };
    FragmentedPacketGroupProvider fragmentedPacketGroupProvider = () -> requireNonNull(packetGroupReference[0]);
    InjectEnvironment injectEnvironment = new InjectEnvironment.Builder().add(new NanoTimeProviderEnvironment(() -> timeReference[0].toNanos())).add(new LatestPacketGroupEnvironment(fragmentedPacketGroupProvider)).add(new LatestFragmentedPacketGroupEnvironment(fragmentedPacketGroupProvider)).build();
    FXStatusPacket auxOnNoAC = FXStatusPackets.createFromChars("\n1,00,00,02,123,123,00,10,000,00,252,136,000,999\r".toCharArray(), IgnoreCheckSum.IGNORE);
    FXStatusPacket auxOffNoAC = FXStatusPackets.createFromChars("\n1,00,00,02,123,123,00,10,000,00,252,008,000,999\r".toCharArray(), IgnoreCheckSum.IGNORE);
    FXStatusPacket auxOnACUse = FXStatusPackets.createFromChars("\n1,00,00,02,123,123,00,10,000,02,252,136,000,999\r".toCharArray(), IgnoreCheckSum.IGNORE);
    FXStatusPacket auxOffACUse = FXStatusPackets.createFromChars("\n1,00,00,02,123,123,00,10,000,02,252,008,000,999\r".toCharArray(), IgnoreCheckSum.IGNORE);
    for (FXStatusPacket packet : new FXStatusPacket[] { auxOffNoAC, auxOnACUse, auxOffACUse }) {
        // for these three cases, the action should end immediately
        packetGroupReference[0] = PacketGroups.createInstancePacketGroup(Collections.singleton(packet), 0L, "my_source_id", 999);
        Action action = actionNode.createAction(new ActionEnvironment(new VariableEnvironment(), new VariableEnvironment(), injectEnvironment));
        action.update();
        assertTrue(action.isDone());
    }
    {
        // Test that no alert is sent unless the aux is on, and it's in No AC for 30 seconds
        packetGroupReference[0] = PacketGroups.createInstancePacketGroup(Collections.singleton(auxOnNoAC), 0L, "my_source_id", 999);
        Action action = actionNode.createAction(new ActionEnvironment(new VariableEnvironment(), new VariableEnvironment(), injectEnvironment));
        action.update();
        assertFalse(action.isDone());
        timeReference[0] = timeReference[0].plus(Duration.ofSeconds(29));
        action.update();
        assertFalse(action.isDone());
        packetGroupReference[0] = PacketGroups.createInstancePacketGroup(Collections.singleton(auxOnACUse), 0L, "my_source_id", 999);
        action.update();
        // No alert has been sent, since it started to AC Use before the 30 second period completed.
        assertTrue(action.isDone());
    }
    {
        // Test that the alert gets sent and the action doesn't end until the 300-second timeout completes
        packetGroupReference[0] = PacketGroups.createInstancePacketGroup(Collections.singleton(auxOnNoAC), 0L, "my_source_id", 999);
        Action action = actionNode.createAction(new ActionEnvironment(new VariableEnvironment(), new VariableEnvironment(), injectEnvironment));
        action.update();
        assertFalse(action.isDone());
        timeReference[0] = timeReference[0].plus(Duration.ofSeconds(30));
        action.update();
        assertFalse(action.isDone());
        packetGroupReference[0] = PacketGroups.createInstancePacketGroup(Collections.singleton(auxOnACUse), 0L, "my_source_id", 999);
        action.update();
        // Alert has been sent, so the action isn't going to end
        assertFalse(action.isDone());
        timeReference[0] = timeReference[0].plus(Duration.ofSeconds(299));
        action.update();
        assertFalse(action.isDone());
        timeReference[0] = timeReference[0].plus(Duration.ofSeconds(1));
        action.update();
        // the 300-second timeout has completed, so the action will end
        assertTrue(action.isDone());
    }
}
Also used : FragmentedPacketGroup(me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup) ActionEnvironment(me.retrodaredevil.action.node.environment.ActionEnvironment) Action(me.retrodaredevil.action.Action) NanoTimeProviderEnvironment(me.retrodaredevil.action.node.environment.NanoTimeProviderEnvironment) LatestPacketGroupEnvironment(me.retrodaredevil.solarthing.actions.environment.LatestPacketGroupEnvironment) SerialConfigBuilder(me.retrodaredevil.io.serial.SerialConfigBuilder) ActionNode(me.retrodaredevil.action.node.ActionNode) Duration(java.time.Duration) FragmentedPacketGroupProvider(me.retrodaredevil.solarthing.FragmentedPacketGroupProvider) InjectEnvironment(me.retrodaredevil.action.node.environment.InjectEnvironment) LatestFragmentedPacketGroupEnvironment(me.retrodaredevil.solarthing.actions.environment.LatestFragmentedPacketGroupEnvironment) FXStatusPacket(me.retrodaredevil.solarthing.solar.outback.fx.FXStatusPacket) VariableEnvironment(me.retrodaredevil.action.node.environment.VariableEnvironment) File(java.io.File) Test(org.junit.jupiter.api.Test)

Aggregations

InjectEnvironment (me.retrodaredevil.action.node.environment.InjectEnvironment)4 ActionNode (me.retrodaredevil.action.node.ActionNode)3 ActionEnvironment (me.retrodaredevil.action.node.environment.ActionEnvironment)3 NanoTimeProviderEnvironment (me.retrodaredevil.action.node.environment.NanoTimeProviderEnvironment)3 VariableEnvironment (me.retrodaredevil.action.node.environment.VariableEnvironment)3 Action (me.retrodaredevil.action.Action)2 FragmentedPacketGroupProvider (me.retrodaredevil.solarthing.FragmentedPacketGroupProvider)2 FragmentedPacketGroup (me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup)2 File (java.io.File)1 Clock (java.time.Clock)1 Duration (java.time.Duration)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 ActionMultiplexer (me.retrodaredevil.action.ActionMultiplexer)1 Actions (me.retrodaredevil.action.Actions)1 SerialConfigBuilder (me.retrodaredevil.io.serial.SerialConfigBuilder)1 LatestFragmentedPacketGroupEnvironment (me.retrodaredevil.solarthing.actions.environment.LatestFragmentedPacketGroupEnvironment)1 LatestPacketGroupEnvironment (me.retrodaredevil.solarthing.actions.environment.LatestPacketGroupEnvironment)1 SolarThingDatabaseEnvironment (me.retrodaredevil.solarthing.actions.environment.SolarThingDatabaseEnvironment)1