use of me.retrodaredevil.solarthing.FragmentedPacketGroupProvider 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.solarthing.FragmentedPacketGroupProvider in project solarthing by wildmountainfarms.
the class SlackChatBotActionNode method createAction.
@Override
public Action createAction(ActionEnvironment actionEnvironment) {
LatestFragmentedPacketGroupEnvironment latestPacketGroupEnvironment = actionEnvironment.getInjectEnvironment().get(LatestFragmentedPacketGroupEnvironment.class);
AlterPacketsEnvironment alterPacketsEnvironment = actionEnvironment.getInjectEnvironment().get(AlterPacketsEnvironment.class);
SolarThingDatabaseEnvironment solarThingDatabaseEnvironment = actionEnvironment.getInjectEnvironment().get(SolarThingDatabaseEnvironment.class);
SourceIdEnvironment sourceIdEnvironment = actionEnvironment.getInjectEnvironment().get(SourceIdEnvironment.class);
TimeZoneEnvironment timeZoneEnvironment = actionEnvironment.getInjectEnvironment().get(TimeZoneEnvironment.class);
EventDatabaseCacheEnvironment eventDatabaseCacheEnvironment = actionEnvironment.getInjectEnvironment().get(EventDatabaseCacheEnvironment.class);
// Note that all objects listed here must be thread safe, as data will be accessed from them on a separate thread
FragmentedPacketGroupProvider packetGroupProvider = latestPacketGroupEnvironment.getFragmentedPacketGroupProvider();
AlterPacketsProvider alterPacketsProvider = alterPacketsEnvironment.getAlterPacketsProvider();
SolarThingDatabase database = solarThingDatabaseEnvironment.getSolarThingDatabase();
String sourceId = sourceIdEnvironment.getSourceId();
ZoneId zoneId = timeZoneEnvironment.getZoneId();
ResourceManager<? extends DatabaseCache> eventDatabaseCacheManager = eventDatabaseCacheEnvironment.getEventDatabaseCacheManager();
Slack slack = Slack.getInstance(new SlackConfig(), new SlackHttpClient(new OkHttpClient.Builder().callTimeout(Duration.ofSeconds(10)).connectTimeout(Duration.ofSeconds(4)).build()));
ChatBotCommandHelper commandHelper = new ChatBotCommandHelper(permissionMap, packetGroupProvider, new CommandManager(keyDirectory, sender));
return new SlackChatBotAction(appToken, new SlackMessageSender(authToken, channelId, slack), slack, new HelpChatBotHandler(new ChatBotHandlerMultiplexer(Arrays.asList(// note: this isn't applied to "help" commands
new StaleMessageHandler(), new ScheduleCommandChatBotHandler(commandHelper, database, sourceId, zoneId), new CancelCommandChatBotHandler(commandHelper, database, sourceId, zoneId, alterPacketsProvider), new FlagCommandChatBotHandler(commandHelper, database, sourceId, zoneId, alterPacketsProvider), new CommandChatBotHandler(commandHelper, database, sourceId, zoneId), new StatusChatBotHandler(packetGroupProvider, alterPacketsProvider), new HeartbeatCommandChatBotHandler(eventDatabaseCacheManager), (message, messageSender) -> {
messageSender.sendMessage("Unknown command!");
return true;
}))));
}
use of me.retrodaredevil.solarthing.FragmentedPacketGroupProvider 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