Search in sources :

Example 1 with UnexpectedResponseException

use of me.retrodaredevil.solarthing.rest.exceptions.UnexpectedResponseException in project solarthing by wildmountainfarms.

the class SolarThingGraphQLFXService method queryFXCharging.

@GraphQLQuery(description = "Gives the timer values for the master FX of a single fragment over a time range")
public List<DataNode<FXChargingPacket>> queryFXCharging(@GraphQLArgument(name = "from") long from, @GraphQLArgument(name = "to") long to, @GraphQLArgument(name = "fragmentId") int fragmentId) {
    MetaDatabase metaDatabase = simpleQueryHandler.queryMeta();
    FXChargingSettingsPacket fxChargingSettingsPacket = null;
    FXChargingTemperatureAdjustPacket fxChargingTemperatureAdjustPacket = null;
    for (TargetedMetaPacket targetedMetaPacket : metaDatabase.getMeta(to, fragmentId)) {
        if (targetedMetaPacket.getPacketType() == TargetedMetaPacketType.FX_CHARGING_SETTINGS) {
            fxChargingSettingsPacket = (FXChargingSettingsPacket) targetedMetaPacket;
        } else if (targetedMetaPacket.getPacketType() == TargetedMetaPacketType.FX_CHARGING_TEMPERATURE_ADJUST) {
            fxChargingTemperatureAdjustPacket = (FXChargingTemperatureAdjustPacket) targetedMetaPacket;
        }
    }
    if (fxChargingSettingsPacket == null) {
        throw new UnexpectedResponseException("Could not find FX Charging settings in meta!");
    }
    // 3 hours back
    long startTime = from - 3 * 60 * 60 * 1000;
    List<? extends InstancePacketGroup> packets = simpleQueryHandler.queryStatus(startTime, to, null);
    // We make masterIdIgnoreDistance null because we will only be using fragmentId as the master fragment ID
    Map<String, List<FragmentedPacketGroup>> map = // separate based on source ID
    PacketGroups.sortPackets(packets, simpleQueryHandler.getDefaultInstanceOptions(), SolarThingConstants.STANDARD_MAX_TIME_DISTANCE.toMillis(), null, // make fragmentId be the master ID
    FragmentUtil.createPriorityComparator(fragmentId));
    List<FragmentedPacketGroup> sortedPackets = null;
    for (List<FragmentedPacketGroup> fragmentedPacketGroups : map.values()) {
        if (fragmentedPacketGroups.get(0).hasFragmentId(fragmentId)) {
            sortedPackets = fragmentedPacketGroups;
            break;
        }
    }
    if (sortedPackets == null) {
        throw new UnexpectedResponseException("Could not find fragment ID: " + fragmentId);
    }
    FXChargingSettings settings = fxChargingSettingsPacket.getFXChargingSettings();
    FXChargingStateHandler stateHandler = new FXChargingStateHandler(settings);
    Long lastUpdate = null;
    List<DataNode<FXChargingPacket>> r = new ArrayList<>();
    for (FragmentedPacketGroup packetGroup : sortedPackets) {
        List<FXStatusPacket> fxPackets = new ArrayList<>();
        Integer temperature = null;
        for (Packet packet : packetGroup.getPackets()) {
            if (packet instanceof FXStatusPacket && packetGroup.getFragmentId(packet) == fragmentId) {
                fxPackets.add((FXStatusPacket) packet);
            } else if (packet instanceof BatteryTemperature) {
                temperature = Math.round(((BatteryTemperature) packet).getBatteryTemperatureCelsius().floatValue());
            }
        }
        if (fxPackets.isEmpty()) {
            continue;
        }
        if (temperature == null) {
            // we need temperature data for accurate results // TODO add option for systems that don't use temperature compensation
            continue;
        }
        temperature += fxChargingTemperatureAdjustPacket == null ? 0 : fxChargingTemperatureAdjustPacket.getTemperatureAdjustCelsius();
        FXStatusPacket fx = OutbackUtil.getMasterFX(fxPackets);
        if (fx == null) {
            continue;
        }
        final long delta;
        if (lastUpdate == null) {
            delta = 1000;
        } else {
            delta = packetGroup.getDateMillis() - lastUpdate;
        }
        lastUpdate = packetGroup.getDateMillis();
        stateHandler.update(delta, fx, temperature);
        FXChargingPacket fxChargingPacket = new ImmutableFXChargingPacket(fx.getIdentifier(), stateHandler.getMode(), stateHandler.getRemainingAbsorbTimeMillis(), stateHandler.getRemainingFloatTimeMillis(), stateHandler.getRemainingEqualizeTimeMillis(), settings.getAbsorbTimeMillis(), settings.getFloatTimeMillis(), settings.getEqualizeTimeMillis());
        r.add(new DataNode<>(fxChargingPacket, fx, packetGroup.getDateMillis(), packetGroup.getSourceId(fx), fragmentId));
    }
    if (r.isEmpty() && !sortedPackets.isEmpty()) {
        throw new UnexpectedResponseException("There must have been no FX packets or no rover packets!");
    }
    return r;
}
Also used : ArrayList(java.util.ArrayList) ImmutableFXChargingPacket(me.retrodaredevil.solarthing.solar.outback.fx.charge.ImmutableFXChargingPacket) FXChargingPacket(me.retrodaredevil.solarthing.solar.outback.fx.charge.FXChargingPacket) FXStatusPacket(me.retrodaredevil.solarthing.solar.outback.fx.FXStatusPacket) FXChargingSettings(me.retrodaredevil.solarthing.solar.outback.fx.charge.FXChargingSettings) DataNode(me.retrodaredevil.solarthing.rest.graphql.packets.nodes.DataNode) ArrayList(java.util.ArrayList) List(java.util.List) FragmentedPacketGroup(me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup) Packet(me.retrodaredevil.solarthing.packets.Packet) FXStatusPacket(me.retrodaredevil.solarthing.solar.outback.fx.FXStatusPacket) FXChargingTemperatureAdjustPacket(me.retrodaredevil.solarthing.solar.outback.fx.meta.FXChargingTemperatureAdjustPacket) ImmutableFXChargingPacket(me.retrodaredevil.solarthing.solar.outback.fx.charge.ImmutableFXChargingPacket) TargetedMetaPacket(me.retrodaredevil.solarthing.type.closed.meta.TargetedMetaPacket) FXChargingPacket(me.retrodaredevil.solarthing.solar.outback.fx.charge.FXChargingPacket) FXChargingSettingsPacket(me.retrodaredevil.solarthing.solar.outback.fx.meta.FXChargingSettingsPacket) MetaDatabase(me.retrodaredevil.solarthing.type.closed.meta.MetaDatabase) BatteryTemperature(me.retrodaredevil.solarthing.solar.common.BatteryTemperature) ImmutableFXChargingPacket(me.retrodaredevil.solarthing.solar.outback.fx.charge.ImmutableFXChargingPacket) TargetedMetaPacket(me.retrodaredevil.solarthing.type.closed.meta.TargetedMetaPacket) FXChargingTemperatureAdjustPacket(me.retrodaredevil.solarthing.solar.outback.fx.meta.FXChargingTemperatureAdjustPacket) UnexpectedResponseException(me.retrodaredevil.solarthing.rest.exceptions.UnexpectedResponseException) FXChargingSettingsPacket(me.retrodaredevil.solarthing.solar.outback.fx.meta.FXChargingSettingsPacket) FXChargingStateHandler(me.retrodaredevil.solarthing.solar.outback.fx.charge.FXChargingStateHandler) GraphQLQuery(io.leangen.graphql.annotations.GraphQLQuery)

Example 2 with UnexpectedResponseException

use of me.retrodaredevil.solarthing.rest.exceptions.UnexpectedResponseException in project solarthing by wildmountainfarms.

the class CacheHandler method queryOrCalculateCaches.

private <T extends CacheDataPacket> List<T> queryOrCalculateCaches(TypeReference<T> typeReference, String cacheName, String sourceId, long startPeriodNumber, long endPeriodNumber) {
    // the document IDs needed to return data
    List<String> documentIds = new ArrayList<>();
    // a map from a document ID to a period number
    Map<String, Long> documentIdPeriodNumberMap = new HashMap<>();
    for (long periodNumber = startPeriodNumber; periodNumber <= endPeriodNumber; periodNumber++) {
        Instant periodStart = getPeriodStartFromNumber(periodNumber);
        String documentId = CacheUtil.getDocumentId(periodStart, duration, sourceId, cacheName);
        documentIds.add(documentId);
        documentIdPeriodNumberMap.put(documentId, periodNumber);
    }
    BulkGetRequest request = BulkGetRequest.from(documentIds);
    final BulkGetResponse response;
    try {
        response = cacheDatabase.getDocumentsBulk(request);
    } catch (CouchDbException e) {
        throw new DatabaseException("CouchDB exception | message: " + e.getMessage(), e);
    }
    // map for documents that need to be updated. The value represents the revision that needs to be used to update it
    Map<String, String> documentIdRevisionMapForUpdate = new HashMap<>();
    // Map for period number -> cached data. This helps us make sure we only return a single piece of data for each period
    Map<Long, T> periodNumberPacketMap = new TreeMap<>();
    // Set for document IDs that we already have and do not need to be updated
    Set<String> doNotUpdateDocumentIdsSet = new HashSet<>();
    Long queryStartPeriodNumber = null;
    Long queryEndPeriodNumber = null;
    for (BulkGetResponse.Result result : response.getResults()) {
        if (result.hasConflicts()) {
            // replication going on with their databases.
            throw new UnexpectedResponseException("cache document with conflict! doc id: " + result.getDocumentId());
        }
        Long periodNumber = documentIdPeriodNumberMap.get(result.getDocumentId());
        if (periodNumber == null) {
            throw new IllegalStateException("Could not get period number for doc id: " + result.getDocumentId() + ". This should never happen.");
        }
        T value = null;
        if (!result.isError()) {
            JsonData jsonData = result.getJsonDataAssertNotConflicted();
            try {
                value = CouchDbJacksonUtil.readValue(mapper, jsonData, typeReference);
                if (value.getSourceId().equals(sourceId) && value.getCacheName().equals(cacheName)) {
                    periodNumberPacketMap.put(periodNumber, value);
                }
                doNotUpdateDocumentIdsSet.add(value.getDbId());
            } catch (JsonProcessingException ex) {
                // If we are in this catch block, one of two things has happened:
                // The JSON is invalid (unlikely), we updated how a given cache is serialized/deserialized, or we're dumb and never tested to see if the deserialization works.
                // If the JSON is actually invalid, getRevisionFromJsonData will throw an exception. Otherwise, we need to calculate the given cache again
                String revision = getRevisionFromJsonData(jsonData);
                documentIdRevisionMapForUpdate.put(result.getDocumentId(), revision);
            }
        }
        if (value == null) {
            if (queryStartPeriodNumber == null) {
                queryStartPeriodNumber = periodNumber;
                queryEndPeriodNumber = periodNumber;
            } else {
                queryStartPeriodNumber = Math.min(queryStartPeriodNumber, periodNumber);
                queryEndPeriodNumber = Math.max(queryEndPeriodNumber, periodNumber);
            }
        }
    }
    if (queryStartPeriodNumber != null) {
        List<CacheDataPacket> calculatedPackets = calculatePeriod(queryStartPeriodNumber, queryEndPeriodNumber);
        List<JsonData> calculatedPacketsJsonDataList = new ArrayList<>();
        int updateAttemptCount = 0;
        for (CacheDataPacket packet : calculatedPackets) {
            if (doNotUpdateDocumentIdsSet.contains(packet.getDbId())) {
                continue;
            }
            if (!sourceId.equals(packet.getSourceId()) || !cacheName.equals(packet.getCacheName())) {
                // This is the same with different cache names, we don't know which ones we need to update and which ones we cannot
                continue;
            }
            JsonData json;
            try {
                String revision = documentIdRevisionMapForUpdate.get(packet.getDbId());
                if (revision == null) {
                    json = new StringJsonData(mapper.writeValueAsString(packet));
                } else {
                    json = new StringJsonData(mapper.writeValueAsString(new DocumentRevisionWrapper(revision, packet)));
                    updateAttemptCount++;
                }
            } catch (JsonProcessingException e) {
                throw new RuntimeException("Should be able to serialize!", e);
            }
            calculatedPacketsJsonDataList.add(json);
        }
        final List<BulkDocumentResponse> postResponse;
        try {
            postResponse = cacheDatabase.postDocumentsBulk(new BulkPostRequest(calculatedPacketsJsonDataList));
        } catch (CouchDbException e) {
            throw new DatabaseException("Could not update cache", e);
        }
        int successCount = 0;
        int failCount = 0;
        for (BulkDocumentResponse documentResponse : postResponse) {
            if (documentResponse.isOk()) {
                successCount++;
            } else {
                failCount++;
                LOGGER.info("Error: " + documentResponse.getError() + " reason: " + documentResponse.getReason() + " on id: " + documentResponse.getId());
            }
        }
        LOGGER.debug("(Cache updating) Success: " + successCount + " fail: " + failCount + ". Tried to update: " + updateAttemptCount);
        int numberOfWantedType = 0;
        for (CacheDataPacket cacheDataPacket : calculatedPackets) {
            if (cacheDataPacket.getSourceId().equals(sourceId) && cacheDataPacket.getCacheName().equals(cacheName)) {
                @SuppressWarnings("unchecked") T packet = (T) cacheDataPacket;
                Long periodNumber = documentIdPeriodNumberMap.get(cacheDataPacket.getDbId());
                if (periodNumber == null) {
                    throw new NullPointerException("No period number for id: " + cacheDataPacket.getDbId());
                }
                periodNumberPacketMap.put(periodNumber, packet);
                numberOfWantedType++;
            }
        }
        LOGGER.debug("Calculated " + calculatedPackets.size() + " and " + numberOfWantedType + " were of type " + cacheName);
    } else {
        LOGGER.trace("Didn't have to get any data");
    }
    return new ArrayList<>(periodNumberPacketMap.values());
}
Also used : JsonData(me.retrodaredevil.couchdbjava.json.JsonData) StringJsonData(me.retrodaredevil.couchdbjava.json.StringJsonData) CacheDataPacket(me.retrodaredevil.solarthing.type.cache.packets.CacheDataPacket) BulkGetResponse(me.retrodaredevil.couchdbjava.response.BulkGetResponse) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) StringJsonData(me.retrodaredevil.couchdbjava.json.StringJsonData) CouchDbException(me.retrodaredevil.couchdbjava.exception.CouchDbException) BulkDocumentResponse(me.retrodaredevil.couchdbjava.response.BulkDocumentResponse) Instant(java.time.Instant) BulkGetRequest(me.retrodaredevil.couchdbjava.request.BulkGetRequest) BulkPostRequest(me.retrodaredevil.couchdbjava.request.BulkPostRequest) UnexpectedResponseException(me.retrodaredevil.solarthing.rest.exceptions.UnexpectedResponseException) DatabaseException(me.retrodaredevil.solarthing.rest.exceptions.DatabaseException) SolarThingDatabaseException(me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException)

Aggregations

UnexpectedResponseException (me.retrodaredevil.solarthing.rest.exceptions.UnexpectedResponseException)2 JsonProcessingException (com.fasterxml.jackson.core.JsonProcessingException)1 GraphQLQuery (io.leangen.graphql.annotations.GraphQLQuery)1 Instant (java.time.Instant)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1 CouchDbException (me.retrodaredevil.couchdbjava.exception.CouchDbException)1 JsonData (me.retrodaredevil.couchdbjava.json.JsonData)1 StringJsonData (me.retrodaredevil.couchdbjava.json.StringJsonData)1 BulkGetRequest (me.retrodaredevil.couchdbjava.request.BulkGetRequest)1 BulkPostRequest (me.retrodaredevil.couchdbjava.request.BulkPostRequest)1 BulkDocumentResponse (me.retrodaredevil.couchdbjava.response.BulkDocumentResponse)1 BulkGetResponse (me.retrodaredevil.couchdbjava.response.BulkGetResponse)1 SolarThingDatabaseException (me.retrodaredevil.solarthing.database.exception.SolarThingDatabaseException)1 Packet (me.retrodaredevil.solarthing.packets.Packet)1 FragmentedPacketGroup (me.retrodaredevil.solarthing.packets.collection.FragmentedPacketGroup)1 DatabaseException (me.retrodaredevil.solarthing.rest.exceptions.DatabaseException)1 DataNode (me.retrodaredevil.solarthing.rest.graphql.packets.nodes.DataNode)1 BatteryTemperature (me.retrodaredevil.solarthing.solar.common.BatteryTemperature)1 FXStatusPacket (me.retrodaredevil.solarthing.solar.outback.fx.FXStatusPacket)1