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();
}
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);
}
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;
}
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());
}
}
Aggregations