use of me.retrodaredevil.solarthing.type.alter.StoredAlterPacket in project solarthing by wildmountainfarms.
the class AlterManagerAction method onUpdate.
@Override
protected void onUpdate() {
super.onUpdate();
// Supply queried solarthing_open packets to the security packet receiver, which may execute code to put commands requested to be scheduled in solarthing_alter
List<StoredPacketGroup> packets = openDatabaseCache.getAllCachedPackets();
securityPacketReceiver.receivePacketGroups(packets);
// Check packets in solarthing_alter and see if we need to send a command because of a scheduled command packet
Instant now = Instant.now();
List<VersionedPacket<StoredAlterPacket>> alterPackets = alterPacketsProvider.getPackets();
if (alterPackets == null) {
LOGGER.info("alterPackets is null. Maybe query failed? Maybe additional info in previous logs?");
} else {
for (VersionedPacket<StoredAlterPacket> versionedPacket : alterPackets) {
AlterPacket packet = versionedPacket.getPacket().getPacket();
if (packet instanceof ScheduledCommandPacket) {
ScheduledCommandPacket scheduledCommandPacket = (ScheduledCommandPacket) packet;
ScheduledCommandData data = scheduledCommandPacket.getData();
if (data.getScheduledTimeMillis() <= now.toEpochMilli()) {
if (now.toEpochMilli() - data.getScheduledTimeMillis() > Duration.ofMinutes(5).toMillis()) {
LOGGER.warn("Not going to send a command scheduled for more than 5 minutes ago! data: " + data);
} else {
doSendCommand(versionedPacket, scheduledCommandPacket);
}
}
} else if (packet instanceof FlagPacket) {
FlagPacket flagPacket = (FlagPacket) packet;
FlagData data = flagPacket.getFlagData();
ActivePeriod activePeriod = data.getActivePeriod();
if (activePeriod instanceof TimeRangeActivePeriod) {
// We only try to "manage" flags that use this type of ActivePeriod
TimeRangeActivePeriod period = (TimeRangeActivePeriod) activePeriod;
TimeRange timeRange = period.getTimeRange();
Instant endTime = timeRange.getEndTime();
if (endTime != null && endTime.compareTo(now) < 0) {
// If there is an end time, and it is in the past, then we should remove the flag
executorService.execute(() -> {
try {
database.getAlterDatabase().delete(versionedPacket);
} catch (SolarThingDatabaseException e) {
LOGGER.error("Could not delete a FlagPacket with an expired time", e);
// If we cannot delete it, no need to try again, it'll still be here next time around
}
});
}
}
}
}
}
}
use of me.retrodaredevil.solarthing.type.alter.StoredAlterPacket 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.type.alter.StoredAlterPacket in project solarthing by wildmountainfarms.
the class FlagUtil method isFlagActive.
public static boolean isFlagActive(Instant now, String flagName, Stream<? extends StoredAlterPacket> packetStream) {
return packetStream.anyMatch(storedAlterPacket -> {
AlterPacket alterPacket = storedAlterPacket.getPacket();
if (alterPacket instanceof FlagPacket) {
FlagPacket flagPacket = (FlagPacket) alterPacket;
FlagData data = flagPacket.getFlagData();
return data.getFlagName().equals(flagName) && data.getActivePeriod().isActive(now);
}
return false;
});
}
use of me.retrodaredevil.solarthing.type.alter.StoredAlterPacket in project solarthing by wildmountainfarms.
the class AlterManagerAction method receivePacketWithIntegrity.
private void receivePacketWithIntegrity(String sender, TargetPacketGroup packetGroup) {
LOGGER.debug("Sender: " + sender + " is authorized and sent a packet targeting no fragments, so we will see if we want to handle anything from it.");
long now = System.currentTimeMillis();
List<StoredAlterPacket> storedAlterPacketsToUpload = new ArrayList<>();
List<DeleteAlterPacket> deleteAlterPackets = new ArrayList<>();
for (Packet packet : packetGroup.getPackets()) {
if (packet instanceof ScheduleCommandPacket) {
ScheduleCommandPacket scheduleCommandPacket = (ScheduleCommandPacket) packet;
ScheduledCommandData data = scheduleCommandPacket.getData();
ExecutionReason executionReason = new OpenSourceExecutionReason(new OpenSource(sender, packetGroup.getDateMillis(), scheduleCommandPacket, // this is legacy data and shouldn't be used anywhere, so it doesn't matter what we put here
scheduleCommandPacket.getUniqueString()));
ScheduledCommandPacket scheduledCommandPacket = new ScheduledCommandPacket(data, executionReason);
// This databaseId is basically an arbitrary way to generate a unique ID. It contains some stuff such as the command name to debug more easily
String databaseId = "alter-scheduled-command-" + data.getCommandName() + "-" + Long.toHexString(data.getScheduledTimeMillis()) + "-" + sender + "-" + Math.random();
StoredAlterPacket storedAlterPacket = new ImmutableStoredAlterPacket(databaseId, now, scheduledCommandPacket, this.sourceId);
storedAlterPacketsToUpload.add(storedAlterPacket);
} else if (packet instanceof DeleteAlterPacket) {
DeleteAlterPacket deleteAlterPacket = (DeleteAlterPacket) packet;
try {
database.validateUpdateToken(deleteAlterPacket.getUpdateToken());
deleteAlterPackets.add(deleteAlterPacket);
} catch (IncompatibleUpdateTokenException ex) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "For some reason we have an incompatible update token!", ex);
}
} else if (packet instanceof RequestFlagPacket) {
RequestFlagPacket requestFlagPacket = (RequestFlagPacket) packet;
// Originally I was going to not upload a FlagPacket if an existing FlagPacket's
// time range fully encapsulated the newly requested one, but that adds some complexity
// that is not needed at the moment
FlagData flagData = requestFlagPacket.getFlagData();
ExecutionReason executionReason = new OpenSourceExecutionReason(new OpenSource(sender, packetGroup.getDateMillis(), requestFlagPacket, // this is legacy data and shouldn't be used anywhere, so it doesn't matter what we put here
requestFlagPacket.getUniqueString()));
FlagPacket flagPacket = new FlagPacket(flagData, executionReason);
String databaseId = "alter-flag-" + flagData.getFlagName() + "-" + sender + "-" + Math.random();
StoredAlterPacket storedAlterPacket = new ImmutableStoredAlterPacket(databaseId, now, flagPacket, sourceId);
storedAlterPacketsToUpload.add(storedAlterPacket);
}
}
if (storedAlterPacketsToUpload.isEmpty() && deleteAlterPackets.isEmpty()) {
// Nothing for us to do, so no need to schedule a runnable
return;
}
executorService.execute(() -> {
int uploadCount = 0;
try {
for (StoredAlterPacket storedAlterPacket : storedAlterPacketsToUpload) {
this.database.getAlterDatabase().upload(storedAlterPacket);
uploadCount++;
}
} catch (SolarThingDatabaseException e) {
// TODO in future we should try multiple times to upload
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "Could not upload a stored alter packet! uploaded: " + uploadCount + " / " + storedAlterPacketsToUpload.size(), e);
}
int deleteCount = 0;
try {
for (DeleteAlterPacket deleteAlterPacket : deleteAlterPackets) {
this.database.getAlterDatabase().delete(deleteAlterPacket.getDocumentIdToDelete(), deleteAlterPacket.getUpdateToken());
deleteCount++;
}
} catch (SolarThingDatabaseException e) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "Could not delete alter packets! deleted: " + deleteCount + " / " + deleteAlterPackets.size(), e);
}
});
}
use of me.retrodaredevil.solarthing.type.alter.StoredAlterPacket in project solarthing by wildmountainfarms.
the class CouchDbAlterDatabase method queryAll.
@Override
@NotNull
public List<VersionedPacket<StoredAlterPacket>> queryAll(String sourceId) throws SolarThingDatabaseException {
final ViewResponse allDocs;
try {
allDocs = database.allDocs(new ViewQueryParamsBuilder().includeDocs(true).build());
} catch (CouchDbException e) {
throw ExceptionUtil.createFromCouchDbException(e);
}
List<ViewResponse.DocumentEntry> rows = allDocs.getRows();
List<VersionedPacket<StoredAlterPacket>> r = new ArrayList<>(rows.size());
for (ViewResponse.DocumentEntry row : rows) {
if (row.getId().startsWith("_")) {
// ignore design documents
continue;
}
// Since we're using _all_docs with include_docs=true, we have to use the doc, since the value is just the ID for _all_docs
JsonData jsonData = row.getDoc();
final JsonNode jsonNode;
try {
jsonNode = CouchDbJacksonUtil.getNodeFrom(jsonData);
} catch (JsonProcessingException e) {
throw new SolarThingDatabaseException("We couldn't parse some of the data into JSON. This should never happen", e);
}
if (!jsonNode.isObject()) {
throw new SolarThingDatabaseException("Something must be wrong with _all_docs!");
}
ObjectNode objectNode = (ObjectNode) jsonNode;
final StoredAlterPacket storedAlterPacket;
try {
storedAlterPacket = mapper.treeToValue(objectNode, StoredAlterPacket.class);
;
} catch (JsonProcessingException e) {
throw new SolarThingDatabaseException("Could not parse. JsonData: " + jsonData.getJson(), e);
}
// String documentId = objectNode.get("_id").asText();
String documentRevision = objectNode.get("_rev").asText();
VersionedPacket<StoredAlterPacket> versionedPacket = new VersionedPacket<>(storedAlterPacket, new RevisionUpdateToken(documentRevision));
r.add(versionedPacket);
}
return r;
}
Aggregations