use of me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException 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.database.exception.SolarThingDatabaseException in project solarthing by wildmountainfarms.
the class FlagCommandChatBotHandler method setFlag.
private void setFlag(MessageSender messageSender, String flagName) {
TimeRange timeRange = TimeRange.createAfter(Instant.now());
FlagData data = new FlagData(flagName, new TimeRangeActivePeriod(timeRange));
RequestFlagPacket requestFlagPacket = new ImmutableRequestFlagPacket(data);
PacketCollectionCreator creator = commandHelper.getCommandManager().makeCreator(sourceId, zoneId, null, requestFlagPacket, PacketCollectionIdGenerator.Defaults.UNIQUE_GENERATOR);
// TODO We should check if the flag being requested is already active.
executorService.execute(() -> {
PacketCollection packetCollection = creator.create(Instant.now());
boolean success = false;
try {
database.getOpenDatabase().uploadPacketCollection(packetCollection, null);
success = true;
} catch (SolarThingDatabaseException e) {
LOGGER.error("Could not upload request flag packet", e);
}
if (success) {
messageSender.sendMessage("Successfully requested flag: '" + flagName + "' to be set.");
} else {
messageSender.sendMessage("Was unable to request flag set. See logs for details or try again.");
}
});
}
use of me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException in project solarthing by wildmountainfarms.
the class ScheduleCommandChatBotHandler method schedule.
private void schedule(MessageSender messageSender, Instant now, Instant targetTime, AvailableCommand availableCommand) {
if (now.plus(Duration.ofMinutes(1)).isAfter(targetTime)) {
messageSender.sendMessage("Cannot schedule a command less than one minute from now.");
return;
}
if (now.plus(Duration.ofHours(72)).isBefore(targetTime)) {
messageSender.sendMessage("Cannot schedule a command more than 72 hours from now.");
return;
}
UUID uniqueId = UUID.randomUUID();
PacketCollectionCreator creator = commandHelper.getCommandManager().makeCreator(sourceId, zoneId, // We don't have an InstanceTargetPacket because scheduling commands is not handled by a program with a fragment ID // also look at PacketGroups.parseToTargetPacketGroup() for interpretation without a TargetInstancePacket
null, new ImmutableScheduleCommandPacket(new ScheduledCommandData(targetTime.toEpochMilli(), availableCommand.getCommandInfo().getName(), Collections.singleton(availableCommand.getFragmentId())), uniqueId), PacketCollectionIdGenerator.Defaults.UNIQUE_GENERATOR);
PacketCollection packetCollection = creator.create(now);
messageSender.sendMessage("Scheduling " + availableCommand.getCommandInfo().getDisplayName() + " at " + TimeUtil.instantToSlackDateSeconds(targetTime));
executorService.execute(() -> {
boolean success = false;
try {
database.getOpenDatabase().uploadPacketCollection(packetCollection, null);
success = true;
} catch (SolarThingDatabaseException e) {
LOGGER.error("Could not upload schedule command packet collection", e);
}
if (success) {
messageSender.sendMessage("Successfully requested schedule. ID: " + uniqueId);
} else {
messageSender.sendMessage("Could not upload schedule command request.");
}
});
}
use of me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException in project solarthing by wildmountainfarms.
the class SimpleQueryHandler method queryMeta.
public MetaDatabase queryMeta() {
final VersionedPacket<RootMetaPacket> metadata;
synchronized (this) {
final VersionedPacket<RootMetaPacket> currentCache = metadataCache;
final Long lastMetadataCacheNanos = this.lastMetadataCacheNanos;
if (lastMetadataCacheNanos != null && System.nanoTime() - lastMetadataCacheNanos < METADATA_CACHE_VALID.toNanos()) {
requireNonNull(currentCache);
metadata = currentCache;
} else {
UpdateToken updateToken = currentCache == null ? null : currentCache.getUpdateToken();
final VersionedPacket<RootMetaPacket> newMetadata;
try {
newMetadata = database.queryMetadata(updateToken);
} catch (NotFoundSolarThingDatabaseException e) {
// If we have not defined metadata, then we return an "empty" instance
return EmptyMetaDatabase.getInstance();
} catch (SolarThingDatabaseException e) {
throw new DatabaseException("Could not query meta", e);
}
this.lastMetadataCacheNanos = System.nanoTime();
if (newMetadata == null) {
requireNonNull(currentCache);
metadata = currentCache;
} else {
metadataCache = newMetadata;
metadata = newMetadata;
}
}
}
return new DefaultMetaDatabase(metadata.getPacket());
}
use of me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException in project solarthing by wildmountainfarms.
the class SimpleQueryHandler method queryPackets.
/**
* @param from The date millis from
* @param to The date millis to
* @param sourceId The source ID or null. If null, the returned List may contain packet groups from multiple sources
* @return The resulting packets
*/
private List<? extends InstancePacketGroup> queryPackets(MillisDatabase database, long from, long to, String sourceId) {
MillisQuery millisQuery = new MillisQueryBuilder().startKey(from).endKey(to).build();
UniqueQuery uniqueQuery = new UniqueQuery(database, millisQuery);
final Future<? extends List<? extends PacketGroup>> future;
{
// Many times a Grafana dashboard will make many graphql requests with the same from and to parameters.
// Without this code, each graphql request would result in a separate request to the database.
// Most of the time, these requests are being executed at the same time.
// This piece of code takes advantage of the fact that we are requesting the same data at the same time.
// If we find a Future that is already executing for a given query, we wait for that to complete instead of performing a separate request.
// This is sort of a caching mechanism, but it's a VERY temporary caching mechanism since data is not kept after is it queried.
executingQueryMutex.lock();
var currentFuture = executingQueryMap.get(uniqueQuery);
if (currentFuture != null) {
future = currentFuture;
executingQueryMutex.unlock();
} else {
RunnableFuture<? extends List<? extends PacketGroup>> runnableFuture = new FutureTask<>(() -> {
try {
return database.query(millisQuery);
} catch (SolarThingDatabaseException e) {
throw new DatabaseException("Exception querying from " + from + " to " + to, e);
}
});
executingQueryMap.put(uniqueQuery, runnableFuture);
executingQueryMutex.unlock();
runnableFuture.run();
future = runnableFuture;
executingQueryMutex.lock();
executingQueryMap.remove(uniqueQuery);
executingQueryMutex.unlock();
}
}
final List<? extends PacketGroup> rawPacketGroups;
try {
rawPacketGroups = future.get();
} catch (InterruptedException e) {
throw new DatabaseException("Interrupted!", e);
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw new DatabaseException("Unknown execution exception", e);
}
if (rawPacketGroups.isEmpty()) {
if (to - from > 60 * 1000) {
// Only debug this message if the requester is actually asking for a decent chunk of data
LOGGER.debug("No packets were queried between " + from + " and " + to);
}
return Collections.emptyList();
}
if (sourceId == null) {
return PacketGroups.parseToInstancePacketGroups(rawPacketGroups, defaultInstanceOptions);
}
Map<String, List<InstancePacketGroup>> map = PacketGroups.parsePackets(rawPacketGroups, defaultInstanceOptions);
if (map.containsKey(sourceId)) {
List<InstancePacketGroup> instancePacketGroupList = map.get(sourceId);
return PacketGroups.orderByFragment(instancePacketGroupList);
}
throw new NoSuchElementException("No element with sourceId: '" + sourceId + "' available keys are: " + map.keySet());
}
Aggregations