energy.usef.core.service.business.PrognosisConsolidationBusinessService.java Source code

Java tutorial

Introduction

Here is the source code for energy.usef.core.service.business.PrognosisConsolidationBusinessService.java

Source

/*
 * Copyright 2015-2016 USEF Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package energy.usef.core.service.business;

import energy.usef.core.config.Config;
import energy.usef.core.config.ConfigParam;
import energy.usef.core.model.ConnectionGroup;
import energy.usef.core.model.PlanboardMessage;
import energy.usef.core.model.PtuContainer;
import energy.usef.core.model.PtuPrognosis;
import energy.usef.core.repository.ConnectionGroupRepository;
import energy.usef.core.repository.PlanboardMessageRepository;
import energy.usef.core.repository.PtuContainerRepository;
import energy.usef.core.repository.PtuPrognosisRepository;
import energy.usef.core.util.PlanboardMessageUtil;
import energy.usef.core.util.PtuUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.ejb.Stateless;
import javax.inject.Inject;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Service class in charge of operations related to the Prognosis Consolidation.
 */
@Stateless
public class PrognosisConsolidationBusinessService {

    private static final Logger LOGGER = LoggerFactory.getLogger(PrognosisConsolidationBusinessService.class);

    @Inject
    private PlanboardMessageRepository planboardMessageRepository;

    @Inject
    private PtuPrognosisRepository ptuPrognosisRepository;

    @Inject
    private Config config;

    @Inject
    private PtuContainerRepository ptuContainerRepository;

    @Inject
    private ConnectionGroupRepository connectionGroupRepository;

    /**
     * Consolidate prognosis that were received for a certain date in order to have the correct data for settlement.
     *
     * @param period
     * @param connectionGroupIdentifier
     * @param participantDomain
     * @return
     */
    public List<PtuPrognosis> consolidatePrognosisForDate(LocalDate period, String connectionGroupIdentifier,
            String participantDomain) {

        // 1. Fetch all relevant prognosis from the Planboard for the period
        List<PlanboardMessage> planboardMessageList = fetchRelevantPrognosisOrderedByCreationTime(period,
                connectionGroupIdentifier, participantDomain);

        // 2. Initialize PtuPrognosis list and loop over PlanboardMessages to fill list, starting with most recent
        List<PtuPrognosis> consolidatedPtuPrognosisList = initializePrognosisList(period, connectionGroupIdentifier,
                participantDomain);

        if (consolidatedPtuPrognosisList.isEmpty()) {
            return consolidatedPtuPrognosisList;
        }

        for (PlanboardMessage planboardMessage : planboardMessageList) {
            if (doAllPtusHavePower(consolidatedPtuPrognosisList, planboardMessage)) {
                break;
            }
        }
        return consolidatedPtuPrognosisList;
    }

    private boolean doAllPtusHavePower(List<PtuPrognosis> consolidatedPtuPrognosisList,
            PlanboardMessage planboardMessage) {
        LOGGER.debug("Processing relevant prognosis [{}] for [{}] with PMID=[{}]", planboardMessage.getSequence(),
                planboardMessage.getParticipantDomain(), planboardMessage.getId());
        // 2a. Find PtuPrognosis based on sequence number of planboardMessage
        List<PtuPrognosis> ptuPrognosisList = ptuPrognosisRepository.findBySequence(planboardMessage.getSequence());
        LOGGER.debug("Fetched [{}] PtuPrognoses for the sequence [{}]", ptuPrognosisList.size(),
                planboardMessage.getSequence());
        // 2b. Filter out valid PtuPrognosis with regard to intra-day gate closure
        List<PtuPrognosis> validPtuPrognosisList = filterPtuPrognosisForIntradayGateClosureValidity(
                ptuPrognosisList, planboardMessage.getCreationDateTime());
        LOGGER.debug("[{}] PtuPrognoses after filterPtuPrognosisForIntradayGateClosureValidity",
                validPtuPrognosisList.size());
        if (!validPtuPrognosisList.isEmpty()) {
            // sort validPtuPrognosisList to accelerate the matching process in next step
            validPtuPrognosisList.stream().sorted(
                    (e1, e2) -> e2.getPtuContainer().getPtuIndex().compareTo(e1.getPtuContainer().getPtuIndex()))
                    .collect(Collectors.toList());
            // 2c. For each empty PTU: Find Power and Disposition and add to new list
            consolidatedPtuPrognosisList.stream().filter(consolidatedPtu -> consolidatedPtu.getPower() == null)
                    .map(consolidatedPtu -> {
                        // find corresponding ptu in validPtuPrognosisList
                        return validPtuPrognosisList.stream().map(validPtuPrognosis -> {
                            if (validPtuPrognosis.getPtuContainer().getPtuIndex()
                                    .equals(consolidatedPtu.getPtuContainer().getPtuIndex())) {
                                return matchValidAndConsolidatePtuPrognosis(validPtuPrognosis, consolidatedPtu);
                            }
                            return validPtuPrognosis;
                        }).collect(Collectors.toList());
                    }).collect(Collectors.toList());
            // 2d. Validate completeness of consolidated list, i.e. all PTUs have power, if not, continue with next
            // PlanboardMessage
            if (consolidatedPtuPrognosisList.stream()
                    .noneMatch(consolidatedPtuPrognosis -> consolidatedPtuPrognosis.getPower() == null)) {
                LOGGER.debug("All the PTUs in the ConsolidatedPtuPrognoses have a power");
                consolidatedPtuPrognosisList.stream()
                        .forEach(ptuPrognosis -> LOGGER.trace(
                                "Date of the consolidated prognosis: PTU index=[{}] for Power=[{}]",
                                ptuPrognosis.getPtuContainer().getPtuIndex(), ptuPrognosis.getPower()));
                return true;
            }
        }
        return false;
    }

    private List<PtuPrognosis> initializePrognosisList(LocalDate period, String connectionGroupIdentifier,
            String participantDomain) {
        ConnectionGroup connectionGroup = connectionGroupRepository.find(connectionGroupIdentifier);
        int ptusPerDay = PtuUtil.getNumberOfPtusPerDay(period, config.getIntegerProperty(ConfigParam.PTU_DURATION));
        final Map<Integer, PtuContainer> ptuContainers = ptuContainerRepository.findPtuContainersMap(period);

        return IntStream.rangeClosed(1, ptusPerDay).mapToObj(index -> {
            PtuPrognosis ptuPrognosis = new PtuPrognosis();
            PtuContainer ptuContainer = ptuContainers.get(index);
            ptuPrognosis.setPtuContainer(ptuContainer);
            ptuPrognosis.setParticipantDomain(participantDomain);
            ptuPrognosis.setConnectionGroup(connectionGroup);
            return ptuPrognosis;
        }).filter(ptuPrognosis -> ptuPrognosis.getPtuContainer() != null).collect(Collectors.toList());
    }

    private List<PlanboardMessage> fetchRelevantPrognosisOrderedByCreationTime(LocalDate period,
            String connectionGroupIdentifier, String participantDomain) {
        List<PlanboardMessage> planboardMessageList = planboardMessageRepository
                .findPrognosisRelevantForDateByUsefIdentifier(period, connectionGroupIdentifier, participantDomain);
        if (planboardMessageList.isEmpty()) {
            return new ArrayList<>();
        }
        LOGGER.debug("Fetched [{}] relevant prognoses for date [{}] in order to build a consolidated prognosis.",
                planboardMessageList.size(), period);

        // Reverse order by time of receipt
        return PlanboardMessageUtil.sortPlanboardMessageListDescByCreationTime(planboardMessageList);
    }

    private PtuPrognosis matchValidAndConsolidatePtuPrognosis(PtuPrognosis validPtuPrognosis,
            PtuPrognosis consolidatedPtu) {
        // REVIEW: any efficient way to deep copy values shorter/quicker?
        consolidatedPtu.setPower(validPtuPrognosis.getPower());
        consolidatedPtu.setPtuContainer(validPtuPrognosis.getPtuContainer());
        consolidatedPtu.setConnectionGroup(validPtuPrognosis.getConnectionGroup());
        consolidatedPtu.setSequence(validPtuPrognosis.getSequence());
        consolidatedPtu.setType(validPtuPrognosis.getType());
        consolidatedPtu.setParticipantDomain(validPtuPrognosis.getParticipantDomain());
        return consolidatedPtu;
    }

    private List<PtuPrognosis> filterPtuPrognosisForIntradayGateClosureValidity(List<PtuPrognosis> ptuPrognosisList,
            LocalDateTime planboardMessageCreationTime) {
        List<PtuPrognosis> filteredPrognoses = new ArrayList<>();
        LocalDate prognosisPeriod = ptuPrognosisList.get(0).getPtuContainer().getPtuDate();
        Integer gateClosurePtus = config.getIntegerProperty(ConfigParam.INTRADAY_GATE_CLOSURE_PTUS);
        Integer ptuDuration = config.getIntegerProperty(ConfigParam.PTU_DURATION);

        LocalDateTime creationTimePlusGateClosure = planboardMessageCreationTime
                .plusMinutes(gateClosurePtus * ptuDuration);
        Integer pivotIndex = PtuUtil.getPtuIndex(creationTimePlusGateClosure, ptuDuration);
        if (creationTimePlusGateClosure.toLocalDate().isEqual(prognosisPeriod)) {
            filteredPrognoses = ptuPrognosisList.stream()
                    .filter(ptuPrognosis -> ptuPrognosis.getPtuContainer().getPtuIndex() > pivotIndex)
                    .collect(Collectors.toList());
        } else if (creationTimePlusGateClosure.toLocalDate().isBefore(prognosisPeriod)) {
            filteredPrognoses = ptuPrognosisList.stream().collect(Collectors.toList());
        }
        return filteredPrognoses;
    }
}