package com.interplug.qcast.biz.pwrGnrSimulation; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.interplug.qcast.biz.estimate.dto.*; import com.interplug.qcast.biz.pwrGnrSimulation.dto.*; import com.interplug.qcast.config.Exception.ErrorCode; import com.interplug.qcast.config.Exception.QcastException; import com.interplug.qcast.config.message.Messages; import com.interplug.qcast.util.ExcelUtil; import com.interplug.qcast.util.InterfaceQsp; import com.interplug.qcast.util.PdfUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.*; import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; @Slf4j @Service @RequiredArgsConstructor public class PwrGnrSimService { private final InterfaceQsp interfaceQsp; @Autowired Messages message; @Value("${qsp.url}") private String qspUrl; @Value("${qsp.simulation-guide-info-url}") private String qspSimulationGuideInfUrl; private final PwrGnrSimMapper pwrGnrSimMapper; double[] snow = readValuesFromFile("template/pwrGnrSimulation/snow.txt"); // cosθz double[][] cosThetaZ = readValuesFromFile2("template/pwrGnrSimulation/cosThetaZ.txt"); // hinode_nichibotsu double[][] hinode_nichibotsu = readValuesFromFile2("template/pwrGnrSimulation/hinode_nichibotsu.txt"); double[] ion = readValuesFromFile("template/pwrGnrSimulation/ion.txt"); // 시간 각도 double[][] jikakudo = readValuesFromFile2("template/pwrGnrSimulation/jikakudo.txt"); double[] jimenhansharitsu = readValuesFromFile("template/pwrGnrSimulation/jimenhansharitsu.txt"); // 위도 경도[rad] double[][] latlng = readValuesFromFile2("template/pwrGnrSimulation/latlng.txt"); double[][] rd = readValuesFromFile2("template/pwrGnrSimulation/rd.txt"); double[][] rdx = readValuesFromFile2("template/pwrGnrSimulation/rdx.txt"); double[][] rtx = readValuesFromFile2("template/pwrGnrSimulation/rtx.txt"); // 산란 일사 double[] sanrannissha = readValuesFromFile("template/pwrGnrSimulation/sanrannissha.txt"); // 수평면 일사 double[] suiheimennissha = readValuesFromFile("template/pwrGnrSimulation/suiheimennissha.txt"); // 태양 적위 double[] taiyousekii = readValuesFromFile("template/pwrGnrSimulation/taiyousekii.txt"); double[][] peakcut_by_pcs_data = readValuesFromFile2("template/pwrGnrSimulation/peakcut_by_pcs_data.txt"); double[][] tashounen_data_deg0 = readValuesFromFile2("template/pwrGnrSimulation/tashounen_data_deg0.txt"); double[][] tashounen_data_deg90 = readValuesFromFile2("template/pwrGnrSimulation/tashounen_data_deg90.txt"); double[][] amp_peakcut_data_deg0 = readValuesFromFile2("template/pwrGnrSimulation/amp_peakcut_data_deg0.txt"); double[][] amp_peakcut_data_deg90 = readValuesFromFile2("template/pwrGnrSimulation/amp_peakcut_data_deg90.txt"); /** * 발전시뮬레이션 계산 결과와 견적서 정보 리턴 * * @param pwrGnrSimRequest * @return * @throws Exception */ public PwrGnrSimResponse selectPwrGnrSimulation(PwrGnrSimRequest pwrGnrSimRequest) { // 데이터가 없어서 오류가 발생한 경우, 빈값으로 리턴하기위한 값 PwrGnrSimResponse exceptionRes = new PwrGnrSimResponse(); String[] strExceptionData = formatAndPrintArray(new double[13]); exceptionRes.setHatsudenryouAll(strExceptionData); exceptionRes.setHatsudenryouAllSnow(strExceptionData); exceptionRes.setHatsudenryouPeakcutAll(strExceptionData); exceptionRes.setHatsudenryouPeakcutAllSnow(strExceptionData); // 1. 견적서 정보 조회 PwrGnrSimPlanResponse planInfo = pwrGnrSimMapper.selectPlanInfo(pwrGnrSimRequest); // 지역이 없으면 계산 불가능 if (planInfo == null) { log.error("물건정보 없음."); return exceptionRes; } else { exceptionRes.setObjectNo(planInfo.getObjectNo()); exceptionRes.setPlanNo(planInfo.getPlanNo()); exceptionRes.setDrawingEstimateCreateDate(planInfo.getDrawingEstimateCreateDate()); exceptionRes.setPrefName(planInfo.getPrefName()); exceptionRes.setAreaName(planInfo.getAreaName()); exceptionRes.setSnowfall(planInfo.getSnowfall()); exceptionRes.setStandardWindSpeedId(planInfo.getStandardWindSpeedId()); if (planInfo.getAreaId() == 0) { log.error("지역값이 없음."); return exceptionRes; } } // 2. 견적서 지붕재 목록 조회 List roofList = pwrGnrSimMapper.selectRoofList(pwrGnrSimRequest); int roofLength = roofList.size(); // Set의 크기 = 고유 roofSurfaceId 개수 // 지붕재 정보가 없음. if (roofList == null || roofList.isEmpty()) { log.error("지붕재 정보가 없음."); return exceptionRes; } // 3. 지붕재의 모듈 Item 조회 (item No 기준 Group) - 화면용 pwrGnrSimRequest.setItemGroup("MODULE_"); List roofGroupModuleList = pwrGnrSimMapper.selectRoofItemList(pwrGnrSimRequest); // 모듈 아이템이 없음. if (roofGroupModuleList.isEmpty()) { log.error("모듈 아이템이 없음."); return exceptionRes; } else { exceptionRes.setRoofModuleList(roofGroupModuleList); } // 4. 계산을 위해 order by 변경 (총용량 DESC) -계산용 List moduleGroupList = roofGroupModuleList.stream() .sorted( Comparator.comparingDouble(PwrGnrSimRoofResponse::getTotSpecification) .reversed()) // 내림차순 정렬 .collect(Collectors.toList()); // 5. PCS Item 조회 List pcsGroupList = pwrGnrSimMapper.selectRoofPcsGroupList(pwrGnrSimRequest); // pcs 아이템이 없음. if (pcsGroupList.isEmpty()) { log.error("PCS 아이템이 없음."); return exceptionRes; } else { exceptionRes.setPcsList(pcsGroupList); } // 견적서 정보 기준으로 발전시뮬레이션 계산을 진행한다. PwrGnrSimRequest pwrGnrSimReq = new PwrGnrSimRequest(); pwrGnrSimReq.setSimulationPointNumber(planInfo.getAreaId() - 1); pwrGnrSimReq.setSimulationPointCode(planInfo.getSimulationCode()); double[] dKoubai = new double[roofLength]; double[] dHoui = new double[roofLength]; int[] dModuleInput1 = new int[roofLength]; int[] dModuleInput2 = new int[roofLength]; int[] dModuleInput3 = new int[roofLength]; // pcs 정보 int k = 0; for (PwrGnrSimRoofResponse p : pcsGroupList) { if (k == 0) { pwrGnrSimReq.setPcs1(p.getItemId()); pwrGnrSimReq.setPcsInput1(Integer.parseInt(p.getAmount())); pwrGnrSimReq.setPcsCnvEff1(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff()); // 디폴트 값 1 pwrGnrSimReq.setPcsAmp1(p.getAmp()); pwrGnrSimReq.setPcsSpecification1(p.getSpecification() / 1000); } else if (k == 1) { pwrGnrSimReq.setPcs2(p.getItemId()); pwrGnrSimReq.setPcsInput2(Integer.parseInt(p.getAmount())); pwrGnrSimReq.setPcsCnvEff2(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff()); pwrGnrSimReq.setPcsAmp2(p.getAmp()); pwrGnrSimReq.setPcsSpecification2(p.getSpecification() / 1000); } else { pwrGnrSimReq.setPcs3(p.getItemId()); pwrGnrSimReq.setPcsInput3(Integer.parseInt(p.getAmount())); pwrGnrSimReq.setPcsCnvEff3(p.getCnvEff() == 0.0 ? 1 : p.getCnvEff()); pwrGnrSimReq.setPcsAmp3(p.getAmp()); pwrGnrSimReq.setPcsSpecification3(p.getSpecification() / 1000); } k++; } // module 정보 k = 0; for (PwrGnrSimRoofResponse m : moduleGroupList) { if (k == 0) { pwrGnrSimReq.setModule1(m.getItemId()); pwrGnrSimReq.setModuleAmp1(m.getAmp()); pwrGnrSimReq.setModuleSpecification1(m.getSpecification()); pwrGnrSimReq.setModuleTempCoeff1(m.getTempCoeff()); pwrGnrSimReq.setModuleTempLoss1(m.getTempLoss()); pwrGnrSimReq.setNorthModuleYn1(m.getNorthModuleYn()); //모듈 북면지원여부 } else if (k == 1) { pwrGnrSimReq.setModule2(m.getItemId()); pwrGnrSimReq.setModuleAmp2(m.getAmp()); pwrGnrSimReq.setModuleSpecification2(m.getSpecification()); pwrGnrSimReq.setModuleTempCoeff2(m.getTempCoeff()); pwrGnrSimReq.setModuleTempLoss2(m.getTempLoss()); pwrGnrSimReq.setNorthModuleYn2(m.getNorthModuleYn()); //모듈 북면지원여부 } else { pwrGnrSimReq.setModule3(m.getItemId()); pwrGnrSimReq.setModuleAmp3(m.getAmp()); pwrGnrSimReq.setModuleSpecification3(m.getSpecification()); pwrGnrSimReq.setModuleTempCoeff3(m.getTempCoeff()); pwrGnrSimReq.setModuleTempLoss3(m.getTempLoss()); pwrGnrSimReq.setNorthModuleYn3(m.getNorthModuleYn()); //모듈 북면지원여부 } k++; } // 전체 시스템 용량 double dSpecification = 0.0; int i = 0; for (PwrGnrSimRoofResponse data : roofList) { if (data.getClassType() == 0) { // 경사 dKoubai[i] = (2 * Math.PI) * ((Math.atan(Double.parseDouble(data.getSlopeAngle()) / 10) * 180 / Math.PI) / 360); } else { // 각도 dKoubai[i] = (2 * Math.PI) * (Double.parseDouble(data.getSlopeAngle()) / 360); } //초기 계산식 - 절대치인정 dHoui[i] = Math.abs((2 * Math.PI) * (Double.parseDouble(data.getAzimuth()) / 360)); //변경된 계산식 - 절대치 불인정 //dHoui[i] = (2 * Math.PI) * (Double.parseDouble(data.getAzimuth()) / 360); // 지붕별 모듈정보 셋팅 int j = 0; for (PwrGnrSimRoofResponse m : moduleGroupList) { if (data.getRoofSurfaceId().equals(m.getRoofSurfaceId())) { dSpecification += m.getTotSpecification(); if (j == 0) { if(pwrGnrSimReq.getModule1().equals(m.getItemId())){ dModuleInput1[i] = Integer.parseInt(m.getAmount()); }else if(pwrGnrSimReq.getModule2().equals(m.getItemId())){ dModuleInput2[i] = Integer.parseInt(m.getAmount()); }else if(pwrGnrSimReq.getModule3().equals(m.getItemId())){ dModuleInput3[i] = Integer.parseInt(m.getAmount()); } } else if (j == 1) { dModuleInput2[i] = Integer.parseInt(m.getAmount()); } else { dModuleInput3[i] = Integer.parseInt(m.getAmount()); } j++; } } i++; } pwrGnrSimReq.setKoubai(dKoubai); pwrGnrSimReq.setHoui(dHoui); pwrGnrSimReq.setModuleInput1(dModuleInput1); pwrGnrSimReq.setModuleInput2(dModuleInput2); pwrGnrSimReq.setModuleInput3(dModuleInput3); // 발전시뮬레이션 결과와 견적서 정보를 return 한다. PwrGnrSimResponse pwrGnrSimRes = new PwrGnrSimResponse(); try { //pwrGnrSimRes = this.calcResults(pwrGnrSimReq, roofLength); pwrGnrSimRes = this.calcResults2(pwrGnrSimReq, roofLength); } catch (Exception e) { log.error(e.toString()); //e.printStackTrace(); } // DecimalFormat 객체 생성 DecimalFormat decimalFormat = new DecimalFormat("#,##0.00"); // 포맷 적용 String formatSpecification = decimalFormat.format(dSpecification / 1000); pwrGnrSimRes.setObjectNo(planInfo.getObjectNo()); pwrGnrSimRes.setPlanNo(planInfo.getPlanNo()); pwrGnrSimRes.setDrawingEstimateCreateDate(planInfo.getDrawingEstimateCreateDate()); pwrGnrSimRes.setCapacity(formatSpecification); pwrGnrSimRes.setPrefName(planInfo.getPrefName()); pwrGnrSimRes.setAreaName(planInfo.getAreaName()); pwrGnrSimRes.setSnowfall(planInfo.getSnowfall()); pwrGnrSimRes.setStandardWindSpeedId(planInfo.getStandardWindSpeedId()); pwrGnrSimRes.setRoofModuleList(roofGroupModuleList); pwrGnrSimRes.setPcsList(pcsGroupList); pwrGnrSimRes.setObjectName(planInfo.getObjectName()); pwrGnrSimRes.setObjectNameOmit(planInfo.getObjectNameOmit()); return pwrGnrSimRes; } private PwrGnrSimResponse calcResults2(PwrGnrSimRequest pwrGnrSimReq, int roofLength) throws IOException { int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Load snow data List snow = loadJsonArray( "template/pwrGnrSimulation/snow_db/" + pwrGnrSimReq.getSimulationPointCode() + ".json", new TypeReference>() {} ); String module_name = null; double module_system_loss = pwrGnrSimReq.getModuleTempLoss1(); double ondo_Keisu = pwrGnrSimReq.getModuleTempCoeff1(); // 모듈 온도 계수 double module_amp = pwrGnrSimReq.getModuleAmp1(); if(Double.isNaN(module_system_loss)){ module_system_loss = pwrGnrSimReq.getModuleTempLoss2(); } if(Double.isNaN(ondo_Keisu)){ ondo_Keisu = pwrGnrSimReq.getModuleTempCoeff2(); } if(Double.isNaN(module_amp)){ module_amp = pwrGnrSimReq.getModuleAmp2(); } if(Double.isNaN(module_system_loss)){ module_system_loss = pwrGnrSimReq.getModuleTempLoss3(); } if(Double.isNaN(ondo_Keisu)){ ondo_Keisu = pwrGnrSimReq.getModuleTempCoeff3(); } if(Double.isNaN(module_amp)){ module_amp = pwrGnrSimReq.getModuleAmp3(); } if(Double.isNaN(module_system_loss) || Double.isNaN(ondo_Keisu) || Double.isNaN(module_amp)){ log.error("Module system loss, module amp, or module angle is NaN"); return null; } double module1_watt = pwrGnrSimReq.getModuleSpecification1(); double module2_watt = pwrGnrSimReq.getModuleSpecification2(); double module3_watt = pwrGnrSimReq.getModuleSpecification3(); double module1_watt_flash = module1_watt + 1; double module2_watt_flash = module2_watt + 1; double module3_watt_flash = module3_watt + 1; String northModuleYn1 = pwrGnrSimReq.getNorthModuleYn1(); boolean module1_north_reduction = (northModuleYn1 != null && northModuleYn1.equals("Y")); String northModuleYn2 = pwrGnrSimReq.getNorthModuleYn2(); boolean module2_north_reduction = (northModuleYn2 != null && northModuleYn2.equals("Y")); String northModuleYn3 = pwrGnrSimReq.getNorthModuleYn3(); boolean module3_north_reduction = (northModuleYn3 != null && northModuleYn3.equals("Y")); //モジュール枚数積算 int module1_number_total = 0; int module2_number_total = 0; int module3_number_total = 0; for (int i = 0; i < roofLength; i++) { if (!Double.isNaN(pwrGnrSimReq.getKoubai()[i]) && !Double.isNaN(pwrGnrSimReq.getHoui()[i])) { // 설치면 기울기와 방위가 입력되어 있으면 모듈의 매수를 취득 module1_number_total += pwrGnrSimReq.getModuleInput1()[i]; // amount module2_number_total += pwrGnrSimReq.getModuleInput2()[i]; module3_number_total += pwrGnrSimReq.getModuleInput3()[i]; } } // 모듈 1, 2, 3 총 용량 계산 double module1_youryou_total = module1_watt * module1_number_total; double module2_youryou_total = module2_watt * module2_number_total; double module3_youryou_total = module3_watt * module3_number_total; // 모듈 총 용량 계산 double module_youryou_total = module1_youryou_total + module2_youryou_total + module3_youryou_total; if(module1_youryou_total==0){ log.error("Noting valid roof,you shuld set 'module number','direction' and 'angle' correctly"); return null; } // 각 월별 온도 손실 계산 double ondo_keisu = pwrGnrSimReq.getModuleTempCoeff1(); // 모듈 온도 계수 double[] ondo_sonshitsu = new double[12]; ondo_sonshitsu[0] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[1] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[2] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[3] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[4] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[5] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[6] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[7] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[8] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[9] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[10] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[11] = 1 - 0.1 * ondo_keisu / 0.5; // PCS1, pcs2, pcs3 선택 정보 double pcs1_henkankouritsu = pwrGnrSimReq.getPcsCnvEff1() / 100; // 변환 효율 if(pcs1_henkankouritsu == 0){ pcs1_henkankouritsu = 1; } double pcs1_youryou = pwrGnrSimReq.getPcsSpecification1(); // 용량 (예시 4.4) double pcs2_henkankouritsu = (pwrGnrSimReq.getPcsCnvEff2() / 100); if(pcs2_henkankouritsu == 0){ pcs2_henkankouritsu = 1; } double pcs2_youryou = pwrGnrSimReq.getPcsSpecification2(); double pcs3_henkankouritsu = pwrGnrSimReq.getPcsCnvEff3() / 100; if(pcs3_henkankouritsu == 0){ pcs3_henkankouritsu = 1; } double pcs3_youryou = pwrGnrSimReq.getPcsSpecification3(); double pcs_amp1 = pwrGnrSimReq.getPcsAmp1(); double pcs_amp2 = pwrGnrSimReq.getPcsAmp2(); double pcs_amp3 = pwrGnrSimReq.getPcsAmp3(); int pcs1_number = pwrGnrSimReq.getPcsInput1(); int pcs2_number = pwrGnrSimReq.getPcsInput2(); int pcs3_number = pwrGnrSimReq.getPcsInput3(); // pcs1과 pcs2, pcs3의 실효 변환 효율 중 낮은 값을 사용 double[] values = {pcs1_henkankouritsu, pcs2_henkankouritsu, pcs3_henkankouritsu}; // double pcs_henkankouritsu = // Arrays.stream(values) // .filter(value -> value > 0.0) // 0.0 제외 // .min() // .orElse(0.0); // 모두 0.0인 경우 기본값 반환 double pcs_henkankouritsu = Math.min(pcs1_henkankouritsu,pcs2_henkankouritsu); // pcs 총 용량 계산 double pcs_youryou_total = (pcs1_youryou * pcs1_number) + (pcs2_youryou * pcs2_number) + (pcs3_youryou * pcs3_number); // 적재율 [실수] 계산 double sekisairitsu = module_youryou_total / (pcs_youryou_total * 1000); //過積載率によるスクリーニング if(sekisairitsu > 3){ log.error("Too many PV_modules or too few pcs capacity"); return null; } // double pcs_sekisairitsu = 0; //new // if(sekisairitsu >= pcs_sekisairitsu/100){ // log.error("Too many PV_modules or too few pcs capacity"); // return null; // } if(pcs_youryou_total < 4 && sekisairitsu > 2){ log.error("Too many PV_modules or too few pcs capacity"); return null; } // boolean pcs_daidenryu = false; //new // //大電流モジュール対応可否判定 // if(module_amp > 12.5 && pcs_daidenryu){ // log.error("This PV module is a high current model and cannot be combined with this PCS") // return null; // } // 피크 컷 및 발전량 배열 선언 //double[] hatsudenryou_all = new double[13]; // 피크 컷 없음 발전량(적설 고려 없음) // double[] hatsudenryou_all_snow = new double[13]; // 피크 컷 없음 발전량(적설 고려 있음) double[] hatsudenryou_peakcut_all = new double[13]; // 피크 컷 있음 발전량(적설 고려 없음) double[] hatsudenryou_peakcut_all_snow = new double[13]; // 피크 컷 있음 발전량(적설 고려 있음) // 설치면 피크 컷 및 발전량 배열 선언 double[][] hatsudenryou_peakcutShow = new double[roofLength][12]; double[][] hatsudenryou_peakcut = new double[roofLength][12]; // 전체 설치면의 피크 컷 있음 발전량 double[] peakcutParam = new double[3]; Arrays.fill(peakcutParam, 1.0); int simulationPointCode = pwrGnrSimReq.getSimulationPointCode(); int m = 0; for (int i = 0; i < roofLength; i++) { double angle = pwrGnrSimReq.getKoubai()[i]; double direction = pwrGnrSimReq.getHoui()[i]; // 설치면 발전량 계산 if (!Double.isNaN(angle) && angle > 0 && angle <= (Math.PI / 2) ) { if(!Double.isNaN(direction) && direction >= (-Math.PI) && direction <= (Math.PI)){ // 설치면의 사면 일사량 계산 double[] shamen_nissha = this.shamenNissharyou2(angle, direction, simulationPointCode); double[] peakcut_param = this.peakcut2(simulationPointCode,angle,direction,sekisairitsu,module_amp, pcs_amp1, pcs_henkankouritsu); Map northReductionParam = this.mathNorthReduction(angle, direction); double northReduction; if (northReductionParam.get("direction") == 1.0) { northReduction = 1.0; } else { northReduction = northReductionParam.get("direction") * northReductionParam.get("angle"); } hatsudenryou_peakcut[m] = new double[12]; hatsudenryou_peakcutShow[m] = new double[12]; for(int k = 0 ; k < 12 ; k++){ // Calculate the power generation for the current month double monthlyPower = shamen_nissha[k] * days[k] * ondo_sonshitsu[k] * pcs_henkankouritsu * ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i]) * (module1_north_reduction ? northReduction : 1.0) + (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i]) * (module2_north_reduction ? northReduction : 1.0) + (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i]) * (module3_north_reduction ? northReduction : 1.0)) / 1000.0 * module_system_loss * peakcut_param[2]; // Store the result in the arrays hatsudenryou_peakcut[m][k] = monthlyPower; // Monthly power without snow hatsudenryou_peakcutShow[m][k] = monthlyPower * (1.0 - snow.get(k)); // Monthly power with snow hatsudenryou_peakcut_all[k] += hatsudenryou_peakcut[m][k]; hatsudenryou_peakcut_all_snow[k] += hatsudenryou_peakcutShow[m][k]; // 계산 후 반올림 처리 hatsudenryou_peakcut_all[k] = (int) Math.round(hatsudenryou_peakcut_all[k]); hatsudenryou_peakcut_all_snow[k] =(int) Math.round(hatsudenryou_peakcut_all_snow[k]); } m++; } } } // 마지막에 총합계 추가 hatsudenryou_peakcut_all[12] = Arrays.stream(hatsudenryou_peakcut_all).sum(); hatsudenryou_peakcut_all_snow[12] = Arrays.stream(hatsudenryou_peakcut_all_snow).sum(); PwrGnrSimResponse pwrGnrSimRes = new PwrGnrSimResponse(); pwrGnrSimRes.setHatsudenryouPeakcutAll(formatAndPrintArray(hatsudenryou_peakcut_all)); pwrGnrSimRes.setHatsudenryouPeakcutAllSnow(formatAndPrintArray(hatsudenryou_peakcut_all_snow)); return pwrGnrSimRes; } private Map mathNorthReduction(double angle, double direction) { // 각도 감소값 배열 [angle, reduction] 쌍 double[][] northReductionAngle = { {0, 1.0}, {1, 0.9994}, {2, 0.9988}, {3, 0.9982}, {4, 0.9976}, {5, 0.997}, {6, 0.9964}, {7, 0.9958}, {8, 0.9952}, {9, 0.9946}, {10, 0.994}, {11, 0.9934}, {12, 0.9928}, {13, 0.9922}, {14, 0.9916}, {15, 0.991}, {16, 0.9904}, {17, 0.9898}, {18, 0.9892}, {19, 0.9886}, {20, 0.988}, {21, 0.9874}, {22, 0.9868}, {23, 0.9862}, {24, 0.9856}, {25, 0.985}, {26, 0.9844}, {27, 0.9838}, {28, 0.9832}, {29, 0.9826}, {30, 0.982}, {31, 0.9814}, {32, 0.9808}, {33, 0.9802}, {34, 0.9796}, {35, 0.979}, {36, 0.9784}, {37, 0.9778}, {38, 0.9772}, {39, 0.9766}, {40, 0.976}, {41, 0.9754}, {42, 0.9748}, {43, 0.9742}, {44, 0.9736}, {45, 0.973} }; // 방향 감소값 배열 [direction, reduction] 쌍 double[][] northReductionDirection = { {135, 0.99}, {136, 0.989511407046165}, {137, 0.989023409367582}, {138, 0.988536601514253}, {139, 0.988051576586559}, {140, 0.987568925512663}, {141, 0.987089236328551}, {142, 0.986613093461605}, {143, 0.986141077018562}, {144, 0.985673762078751}, {145, 0.985211717993441}, {146, 0.984755507692177}, {147, 0.984305686996939}, {148, 0.983862803944953}, {149, 0.983427398120998}, {150, 0.983}, {151, 0.982581130300735}, {152, 0.98217129935141}, {153, 0.981771006467905}, {154, 0.981380739345441}, {155, 0.981000973464388}, {156, 0.980632171510976}, {157, 0.980274782813574}, {158, 0.979929242795259}, {159, 0.979595972443316}, {160, 0.979275377796334}, {161, 0.978967849449506}, {162, 0.978673762078751}, {163, 0.978393473984229}, {164, 0.97812732665381}, {165, 0.977875644347018}, {166, 0.977638733699975}, {167, 0.977416883351812}, {168, 0.977210363593004}, {169, 0.977019426036065}, {170, 0.976844303308997}, {171, 0.976685208771868}, {172, 0.976542336256863}, {173, 0.976415859832136}, {174, 0.976305933589727}, {175, 0.976212691457829}, {176, 0.976136247037618}, {177, 0.976076693464844}, {178, 0.976034103296362}, {179, 0.976008528421733}, {180, 0.976} }; // 각도 계산 (라디안 → 도) double angleDeg = Math.toDegrees(angle); angleDeg = Math.abs(angleDeg); angleDeg = Math.ceil(angleDeg); // 방향 계산 (라디안 → 도) double directionDeg = Math.toDegrees(direction); directionDeg = Math.abs(directionDeg); directionDeg = Math.round(directionDeg); // 각도 감소값 찾기 double angleReduction = 1.0; for (double[] entry : northReductionAngle) { if (angleDeg == entry[0]) { angleReduction = entry[1]; break; } } // 방향 감소값 찾기 double directionReduction = 1.0; for (double[] entry : northReductionDirection) { if (directionDeg == entry[0]) { directionReduction = entry[1]; break; } } Map result = new HashMap<>(); result.put("angle", angleReduction); result.put("direction", directionReduction); return result; } private T loadJsonArray(String path, TypeReference typeReference) throws IOException { ClassPathResource resource = new ClassPathResource(path); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(resource.getInputStream(), typeReference); } /** * 실제 계산로직 * * @param pwrGnrSimReq * @param roofLength * @return * @throws Exception */ public PwrGnrSimResponse calcResults(PwrGnrSimRequest pwrGnrSimReq, int roofLength) { int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 각 월별 일수 선언 // 모듈 1에 대한 데이터 double module_system_loss = pwrGnrSimReq.getModuleTempLoss1(); // 모듈 온도 손실 double ondo_keisu = pwrGnrSimReq.getModuleTempCoeff1(); // 모듈 온도 계수 // 각 월별 온도 손실 계산 double[] ondo_sonshitsu = new double[12]; ondo_sonshitsu[0] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[1] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[2] = 1 - 0.1 * ondo_keisu / 0.5; ondo_sonshitsu[3] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[4] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[5] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[6] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[7] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[8] = 1 - 0.2 * ondo_keisu / 0.5; ondo_sonshitsu[9] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[10] = 1 - 0.15 * ondo_keisu / 0.5; ondo_sonshitsu[11] = 1 - 0.1 * ondo_keisu / 0.5; // PCS1, pcs2, pcs3 선택 정보 double pcs1_henkankouritsu = pwrGnrSimReq.getPcsCnvEff1() / 100; // 변환 효율 double pcs1_youryou = pwrGnrSimReq.getPcsSpecification1(); // 용량 (예시 4.4) double pcs2_henkankouritsu = pwrGnrSimReq.getPcsCnvEff2() / 100; double pcs2_youryou = pwrGnrSimReq.getPcsSpecification2(); double pcs3_henkankouritsu = pwrGnrSimReq.getPcsCnvEff3() / 100; double pcs3_youryou = pwrGnrSimReq.getPcsSpecification3(); // pcs1과 pcs2, pcs3의 실효 변환 효율 중 낮은 값을 사용 double[] values = {pcs1_henkankouritsu, pcs2_henkankouritsu, pcs3_henkankouritsu}; double pcs_henkankouritsu = Arrays.stream(values) .filter(value -> value > 0.0) // 0.0 제외 .min() .orElse(0.0); // 모두 0.0인 경우 기본값 반환 // pcs 총 용량 계산 double pcs_youryou_total = (pcs1_youryou * pwrGnrSimReq.getPcsInput1()) + (pcs2_youryou * pwrGnrSimReq.getPcsInput2()) + (pcs3_youryou * pwrGnrSimReq.getPcsInput3()); double module1_watt = pwrGnrSimReq.getModuleSpecification1(), module1_watt_flash = module1_watt + 2; // 모듈 1 출력 + 2w (플래시) double module2_watt = pwrGnrSimReq.getModuleSpecification2(), module2_watt_flash = module2_watt + 2; double module3_watt = pwrGnrSimReq.getModuleSpecification3(), module3_watt_flash = module3_watt + 2; int module1_number_total = 0; int module2_number_total = 0; int module3_number_total = 0; for (int i = 0; i < roofLength; i++) { if (!Double.isNaN(pwrGnrSimReq.getKoubai()[i]) && !Double.isNaN(pwrGnrSimReq.getHoui()[i])) { // 설치면 기울기와 방위가 입력되어 있으면 모듈의 매수를 취득 module1_number_total += pwrGnrSimReq.getModuleInput1()[i]; // amount module2_number_total += pwrGnrSimReq.getModuleInput2()[i]; module3_number_total += pwrGnrSimReq.getModuleInput3()[i]; } } // 모듈 1, 2, 3 총 용량 계산 double module1_youryou_total = module1_watt * module1_number_total; double module2_youryou_total = module2_watt * module2_number_total; double module3_youryou_total = module3_watt * module3_number_total; // 모듈 총 용량 계산 double module_youryou_total = module1_youryou_total + module2_youryou_total + module3_youryou_total; // 적재율 [실수] 계산 double sekisairitsu = module_youryou_total / (pcs_youryou_total * 1000); // 피크 컷 및 발전량 배열 선언 double[] hatsudenryou_all = new double[13]; // 피크 컷 없음 발전량(적설 고려 없음) double[] hatsudenryou_all_snow = new double[13]; // 피크 컷 없음 발전량(적설 고려 있음) double[] hatsudenryou_peakcut_all = new double[13]; // 피크 컷 있음 발전량(적설 고려 없음) double[] hatsudenryou_peakcut_all_snow = new double[13]; // 피크 컷 있음 발전량(적설 고려 있음) if (sekisairitsu <= 3) { // 설치면 피크 컷 및 발전량 배열 선언 double[][] hatsudenryou = new double[roofLength][12]; // 전체 설치면의 피크 컷 없음 발전량 double[][] hatsudenryou_peakcut = new double[roofLength][12]; // 전체 설치면의 피크 컷 있음 발전량 int simulationPointNumber = pwrGnrSimReq.getSimulationPointNumber(); // 우기일 사량 관측 지점의 경우, 계산을 하지 않는다 , 이리 오모테 섬, 이시가키 섬, 오하라, 하 테루 간] if (simulationPointNumber != 361 && simulationPointNumber != 362 && simulationPointNumber != 363 && simulationPointNumber != 364 && simulationPointNumber != 365 && simulationPointNumber != 827 && simulationPointNumber != 828 && simulationPointNumber != 829 && simulationPointNumber != 830 && simulationPointNumber != 831 && simulationPointNumber != 832 && simulationPointNumber != 833 && simulationPointNumber != 834 && simulationPointNumber != 835 && simulationPointNumber != 836 && simulationPointNumber != 837) { for (int i = 0; i < roofLength; i++) { // 설치면 발전량 계산 if (!Double.isNaN(pwrGnrSimReq.getKoubai()[i]) && !Double.isNaN(pwrGnrSimReq.getHoui()[i])) { // 설치면의 사면 일사량 계산 double[] shamen_nissha = this.shamenNissharyou( pwrGnrSimReq.getKoubai()[i], pwrGnrSimReq.getHoui()[i], simulationPointNumber); double[] peakcut = this.peakcut( simulationPointNumber, pwrGnrSimReq.getKoubai()[i], pwrGnrSimReq.getHoui()[i], sekisairitsu, pwrGnrSimReq.getModuleAmp1(), pwrGnrSimReq.getPcsAmp1(), pcs_henkankouritsu); for (int j = 0; j < 12; j++) { // 피크 컷 없음 발전량 계산 // shamen_nissha 조금 다름 hatsudenryou[i][j] = shamen_nissha[j] * days[j] * ondo_sonshitsu[j] // 0.931 * pcs_henkankouritsu // 0.97 * ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i]) + (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i]) + (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i])) / 1000 * module_system_loss // 0.99 * peakcut[0]; // 피크 컷 있음 발전량 계산 hatsudenryou_peakcut[i][j] = shamen_nissha[j] * days[j] * ondo_sonshitsu[j] * pcs_henkankouritsu * ((module1_watt_flash * pwrGnrSimReq.getModuleInput1()[i]) + (module2_watt_flash * pwrGnrSimReq.getModuleInput2()[i]) + (module3_watt_flash * pwrGnrSimReq.getModuleInput3()[i])) / 1000 * module_system_loss * peakcut[1]; } } } // 설치면 1~4의 합계 발전량 for (int j = 0; j < 12; j++) { for (int i = 0; i < roofLength; i++) { hatsudenryou_all[j] += hatsudenryou[i][j]; // hatsudenryou[i][j]); hatsudenryou_peakcut_all[j] += hatsudenryou_peakcut[i][j]; } hatsudenryou_all_snow[j] = hatsudenryou_all[j] * (1 - (snow[(simulationPointNumber * 12) + j])); hatsudenryou_peakcut_all_snow[j] = hatsudenryou_peakcut_all[j] * (1 - (snow[(simulationPointNumber * 12) + j])); // 계산 후 반올림 처리 hatsudenryou_all[j] = (int) Math.round(hatsudenryou_all[j]); hatsudenryou_peakcut_all[j] = (int) Math.round(hatsudenryou_peakcut_all[j]); hatsudenryou_all_snow[j] = (int)Math.round(hatsudenryou_all_snow[j]); hatsudenryou_peakcut_all_snow[j] =(int) Math.round(hatsudenryou_peakcut_all_snow[j]); } } // 마지막에 총합계 추가 hatsudenryou_all[12] = Arrays.stream(hatsudenryou_all).sum(); hatsudenryou_all_snow[12] = Arrays.stream(hatsudenryou_all_snow).sum(); hatsudenryou_peakcut_all[12] = Arrays.stream(hatsudenryou_peakcut_all).sum(); hatsudenryou_peakcut_all_snow[12] = Arrays.stream(hatsudenryou_peakcut_all_snow).sum(); } // 결과 확인용 //System.err.println("hatsudenryou_all 결과 ::: " + Arrays.toString(hatsudenryou_all)); // log.debug("hatsudenryou_all ::: " + Arrays.toString(hatsudenryou_all)); // System.err.println("hatsudenryou_all_snow 결과 ::: " + // Arrays.toString(hatsudenryou_all_snow)); // System.err.println( // "hatsudenryou_peakcut_all 결과 ::: " + Arrays.toString(hatsudenryou_peakcut_all)); // System.err.println( // "hatsudenryou_peakcut_all_snow 결과 ::: " + // Arrays.toString(hatsudenryou_peakcut_all_snow)); PwrGnrSimResponse pwrGnrSimRes = new PwrGnrSimResponse(); pwrGnrSimRes.setHatsudenryouAll(formatAndPrintArray(hatsudenryou_all)); pwrGnrSimRes.setHatsudenryouAllSnow(formatAndPrintArray(hatsudenryou_all_snow)); pwrGnrSimRes.setHatsudenryouPeakcutAll(formatAndPrintArray(hatsudenryou_peakcut_all)); pwrGnrSimRes.setHatsudenryouPeakcutAllSnow(formatAndPrintArray(hatsudenryou_peakcut_all_snow)); return pwrGnrSimRes; } // 배열 변환 및 포맷 처리 메서드 private String[] formatAndPrintArray(double[] doubleArray) { return Arrays.stream(doubleArray) .mapToInt(d -> (int) d) // double -> int 변환 .mapToObj(i -> String.format("%,d", i)) .toArray(String[]::new); // String[]로 변환 } // 설치면의 사면 일사량 계산 public double[] peakcut( int simulationPointNumber, double koubai, double houi, double sekisairitsu, double moduleAmp1, double pcsAmp1, double pcsHenkankouritsu) { // koubai[rad]을 [도]로 변환 _아래 계산식도 변경된 값으로 계산됨. koubai = (koubai / (2 * Math.PI)) * 360; // 방위각 0도 설치각에서의 전력 피크 컷 double wattPeakcutAboutDeg0 = computePowerPeakCutDeg0(simulationPointNumber, sekisairitsu, koubai); // 방위각 90도 설치각에서의 전력 피크 컷 double watt_peakcut_about_deg90 = computePowerPeakCutDeg90(simulationPointNumber, sekisairitsu, koubai); double matrix_multiply1 = (1 * wattPeakcutAboutDeg0) + (-1 * watt_peakcut_about_deg90); double matrix_multiply2 = (-1 * wattPeakcutAboutDeg0) + (2 * watt_peakcut_about_deg90); // houi[rad]을 [도]로 변환 houi = (houi / (2 * Math.PI)) * 360; // 電力ピークカット double watt_peakcut_result = ((matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2) < 0.1) ? 0.1 : (matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2); //////////// // pcs1과 모듈 1의 전류차 구하기 용도 boolean pcsAmpFound = true; // pcs_ampr 값은 항상있을거라 true // pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0) double amp_peakcut_result = 1; // 디폴트 : pcs1이 없거나 pcs1과모듈1dml wjsfbckrk 0 보다 작은경우 if (pcsAmpFound) { // pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0) double amp_delta = moduleAmp1 - pcsAmp1; if (amp_delta >= 0) { double amp_peakcut_about_deg0 = computeAmpPeakCutDeg0(simulationPointNumber, koubai, amp_delta); double amp_peakcut_about_deg90 = computeAmpPeakCutDeg90(simulationPointNumber, koubai, amp_delta); double matrix_multiply3 = (1 * amp_peakcut_about_deg0) + (-1 * amp_peakcut_about_deg90); double matrix_multiply4 = (-1 * amp_peakcut_about_deg0) + (2 * amp_peakcut_about_deg90); // 전류 피크 컷 amp_peakcut_result = 1 - (((matrix_multiply3 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply4) < 0.001) ? 0 : (matrix_multiply3 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply4)); } } ////////////// // 전력 피크 컷과 전류 피크 컷의 최소값 계산 if (sekisairitsu >= 1.25) { sekisairitsu *= 100; int sekisairitsuCeil = (int) Math.ceil(sekisairitsu / 5.0) * 5; double pcsEfficiency = (pcsHenkankouritsu * 1000 <= 985) ? Math.ceil(pcsHenkankouritsu * 1000 / 5) * 5 : 985; int i = 1, j = 1; while (i < peakcut_by_pcs_data.length && peakcut_by_pcs_data[i][0] != sekisairitsuCeil) i++; while (j < peakcut_by_pcs_data[0].length && peakcut_by_pcs_data[0][j] != pcsEfficiency) j++; double peakcutByPcs = peakcut_by_pcs_data[i][j]; double peakcutByPcsKijun = peakcut_by_pcs_data[i][10]; double peakcutByPcsResult = peakcutByPcsKijun - peakcutByPcs; watt_peakcut_result = ((100 - watt_peakcut_result + peakcutByPcsResult) / 100 < 1) ? ((100 - watt_peakcut_result + peakcutByPcsResult) / 100) : 1.0; } else { watt_peakcut_result = 1.0; } // 피크컷이 없는 경우, 발전량에 곱하는 피크컷 계수 double[] peakcutResult = new double[2]; peakcutResult[0] = amp_peakcut_result; peakcutResult[1] = Math.min(watt_peakcut_result, amp_peakcut_result); log.debug("peakcutResult ::: {}", peakcutResult); return peakcutResult; } public double[] peakcut2( int simulationPointCode,double koubai, double houi,double sekisairitsu, double moduleAmp1, double pcsAmp1, double pcsHenkankouritsu) throws IOException { List> peakcut_by_pcs_data = loadJsonArray("template/pwrGnrSimulation/peakcut_db/peakcut_by_pcs_data.json", new TypeReference>>() {} ); // koubai[rad]을 [도]로 변환 _아래 계산식도 변경된 값으로 계산됨. koubai = (koubai / (2 * Math.PI)) * 360; // 방위각 0도 설치각에서의 전력 피크 컷 double watt_peakcut_about_deg0 = tashounenDataDeg0(simulationPointCode, sekisairitsu, koubai); double watt_peakcut_about_deg90 = tashounenDataDeg90(simulationPointCode, sekisairitsu, koubai); double matrix_multiply1 = (1 * watt_peakcut_about_deg0)+(-1 * watt_peakcut_about_deg90); double matrix_multiply2 = (-1 * watt_peakcut_about_deg0)+(2 * watt_peakcut_about_deg90); // houi[rad]을 [도]로 변환 houi = (houi / (2 * Math.PI)) * 360; //電力ピークカット double watt_peakcut_result = ((matrix_multiply1 * (Math.cos(houi * Math.PI/180) + 1) + matrix_multiply2) < 0.1) ? 0.1:(matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2); //////////// // pcs1과 모듈 1의 전류차 구하기 용도 boolean pcsAmpFound = true; // pcs_ampr 값은 항상있을거라 true // pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0) double amp_peakcut_result = 1; // 디폴트 : pcs1이 없거나 pcs1과모듈1dml wjsfbckrk 0 보다 작은경우 if (pcsAmpFound) { // pcs1과 모듈 1의 전류차(pcs의 입력 전류 데이터가 없는 경우는 0) double amp_delta = moduleAmp1 - pcsAmp1; if (amp_delta >= 0) { double amp_peakcut_about_deg0 = ampPeakcutDataDeg0(simulationPointCode, koubai, amp_delta); double amp_peakcut_about_deg90 = ampPeakcutDataDeg90(simulationPointCode, koubai, amp_delta); matrix_multiply1 = (1 * amp_peakcut_about_deg0) + (-1 * amp_peakcut_about_deg90); matrix_multiply2 = (-1 * amp_peakcut_about_deg0) + (2 * amp_peakcut_about_deg90); //電流ピークカット amp_peakcut_result = 1-(((matrix_multiply1 * (Math.cos(houi * Math.PI / 180) + 1) + matrix_multiply2) < 0.001) ? 0:(matrix_multiply1 * (Math.cos(houi*Math.PI/180) + 1) + matrix_multiply2)); } } // 전력 피크 컷과 전류 피크 컷의 최소값 계산 if (sekisairitsu >= 1.25) { sekisairitsu *= 100; int sekisairitsu_ceil = (int) Math.ceil(sekisairitsu / 5.0) * 5; double pcs_henkankouritsu = (pcsHenkankouritsu * 1000 <= 985) ? Math.ceil(pcsHenkankouritsu * 1000 / 5) * 5 : 985; int i; for (i = 1; i < 37; i++) { // pcs効率によるピークカットデータベースから積載率の一致する行番号取得 if (sekisairitsu_ceil == peakcut_by_pcs_data.get(i).get(0)) { break; } } int j; for (j = 1; j < 15; j++) { // pcs効率によるピークカットデータベースから積載率の一致する列番号取得 if (pcs_henkankouritsu == peakcut_by_pcs_data.get(0).get(j)) { break; } } double peakcut_by_pcs = peakcut_by_pcs_data.get(i).get(j); // ピークカット率を代入 double peakcut_by_pcs_kijun = peakcut_by_pcs_data.get(i).get(10); // 基準ピークカット率(変換効率0.965)を代入 double peakcut_by_pcs_result = peakcut_by_pcs_kijun - peakcut_by_pcs; // ピークカット率と基準ピークカット率の差分を代入 if(((100 - watt_peakcut_result + peakcut_by_pcs_result) /100) < 1){ watt_peakcut_result = ((100 - watt_peakcut_result+peakcut_by_pcs_result)/100); }else{ watt_peakcut_result = 1; } } else { watt_peakcut_result = 1.0; } // 피크컷이 없는 경우, 발전량에 곱하는 피크컷 계수 double[] peakcut_result = new double[3]; peakcut_result[0] = amp_peakcut_result; //ピークカットなしの場合、発電量に乗算するピークカット係数 peakcut_result[1] = watt_peakcut_result; //ピークカットなしの場合、発電量に乗算するピークカット係数 peakcut_result[2] = Math.min(watt_peakcut_result,amp_peakcut_result); //ピークカットありの場合、発電量に乗算するピークカット係数 log.debug("peakcutResult ::: {}", peakcut_result); return peakcut_result; } private double ampPeakcutDataDeg90(int simulationPointCode, double koubai, double amp_delta) throws IOException { List amp_peakcut_data_deg90 = loadJsonArray("template/pwrGnrSimulation/peakcut_db/amp_peakcut_data_deg90/" + simulationPointCode + ".json", new TypeReference>() {} ); // 초기 설정 double[] x = {5, 15, 20, 30, 40, 50}; double[] z2 = new double[6]; double[] y = new double[6]; // 데이터 초기화 - y 값 계산 (amp_delta의 다항식 계산) for (int i = 0; i < 6; i++) { int idx = i * 5; y[i] = amp_peakcut_data_deg90.get(idx) * Math.pow(amp_delta, 4) + amp_peakcut_data_deg90.get(idx + 1) * Math.pow(amp_delta, 3) + amp_peakcut_data_deg90.get(idx + 2) * Math.pow(amp_delta, 2) + amp_peakcut_data_deg90.get(idx + 3) * amp_delta + amp_peakcut_data_deg90.get(idx + 4); } // 최소 제곱법을 위한 행렬 A와 w 초기화 double[][] w = new double[6][7]; double[][] A = new double[6][6]; // A 행렬 설정 (x 값을 사용하여 다항식 계수 생성) for (int i1 = 0; i1 < 6; i1++) { A[i1][4] = x[i1]; A[i1][5] = 1.0; double x1 = A[i1][4]; double x2 = x1; for (int i2 = 3; i2 >= 0; i2--) { x2 *= x1; A[i1][i2] = x2; } } // w 행렬 초기화 for (int i1 = 0; i1 < 6; i1++) { for (int i2 = 0; i2 < 6; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } // w의 마지막 열 값 설정 for (int i1 = 0; i1 < 6; i1++) { w[i1][6] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][6] += A[i2][i1] * y[i2]; } } // 가우스 소거법을 통해 연립방정식 해 구하기 for (int i1 = 0; i1 < 6; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 6; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 7; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 7; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 6; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 7; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } // z2에 계수 저장 for (int i1 = 0; i1 < 6; i1++) { z2[i1] = w[i1][6]; } return z2[0] * Math.pow(koubai, 5) + z2[1] * Math.pow(koubai, 4) + z2[2] * Math.pow(koubai, 3) + z2[3] * Math.pow(koubai, 2) + z2[4] * koubai + z2[5]; } private double ampPeakcutDataDeg0(int simulationPointCode, double koubai, double amp_delta) throws IOException { List amp_peakcut_data_deg0 = loadJsonArray("template/pwrGnrSimulation/peakcut_db/amp_peakcut_data_deg0/" + simulationPointCode + ".json", new TypeReference>() {} ); double[] x = {5, 15, 20, 30, 40, 50}; double[] z2 = new double[6]; double[] y = new double[6]; for (int i = 0; i < 6; i++) { int idx = i * 5; y[i] = amp_peakcut_data_deg0.get(idx) * Math.pow(amp_delta, 4) + amp_peakcut_data_deg0.get(idx + 1) * Math.pow(amp_delta, 3) + amp_peakcut_data_deg0.get(idx + 2) * Math.pow(amp_delta, 2) + amp_peakcut_data_deg0.get(idx + 3) * amp_delta + amp_peakcut_data_deg0.get(idx + 4); } double[][] w = new double[6][7]; double[][] A = new double[6][6]; for (int i1 = 0; i1 < 6; i1++) { A[i1][4] = x[i1]; A[i1][5] = 1.0; double x1 = A[i1][4]; double x2 = x1; for (int i2 = 3; i2 >= 0; i2--) { x2 *= x1; A[i1][i2] = x2; } } for (int i1 = 0; i1 < 6; i1++) { for (int i2 = 0; i2 < 6; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } for (int i1 = 0; i1 < 6; i1++) { w[i1][6] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][6] += A[i2][i1] * y[i2]; } } for (int i1 = 0; i1 < 6; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 6; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 7; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 7; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 6; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 7; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } for (int i1 = 0; i1 < 6; i1++) { z2[i1] = w[i1][6]; } return z2[0] * Math.pow(koubai, 5) + z2[1] * Math.pow(koubai, 4) + z2[2] * Math.pow(koubai, 3) + z2[3] * Math.pow(koubai, 2) + z2[4] * koubai + z2[5]; } private double tashounenDataDeg90(int simulationPointCode, double sekisairitsu, double koubai) throws IOException { List tashounen_data_deg90 = loadJsonArray("template/pwrGnrSimulation/peakcut_db/tashounen_data_deg90/" + simulationPointCode + ".json", new TypeReference>() {} ); double[] x = {1, 5, 15, 20, 30, 40}; // y 배열 초기화 및 값 할당 double[] y = new double[6]; for (int i = 0; i < 6; i++) { int idx = i * 6; y[i] = tashounen_data_deg90.get(idx) * Math.pow(sekisairitsu, 5) + tashounen_data_deg90.get(idx + 1) * Math.pow(sekisairitsu, 4) + tashounen_data_deg90.get(idx + 2) * Math.pow(sekisairitsu, 3) + tashounen_data_deg90.get(idx + 3) * Math.pow(sekisairitsu, 2) + tashounen_data_deg90.get(idx + 4) * sekisairitsu + tashounen_data_deg90.get(idx + 5); } // w 행렬 초기화 double[][] w = new double[4][5]; // A 행렬 초기화 double[][] A = new double[6][4]; for (int i = 0; i < 6; i++) { A[i][2] = x[i]; A[i][3] = 1.0; double x1 = A[i][2]; double x2 = x1; for (int j = 1; j >= 0; j--) { x2 *= x1; A[i][j] = x2; } } // w[i1][i2] 계산 for (int i1 = 0; i1 < 4; i1++) { for (int i2 = 0; i2 < 4; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } // w[i1][4] 계산 for (int i1 = 0; i1 < 4; i1++) { w[i1][4] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][4] += A[i2][i1] * y[i2]; } } // 가우스 소거법 적용 for (int i1 = 0; i1 < 4; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 4; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 5; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 5; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 4; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 5; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } // 결과 z1 배열에 저장 double[] z1 = new double[4]; for (int i1 = 0; i1 < 4; i1++) { z1[i1] = w[i1][4]; } // 출력 결과 확인 // System.out.println("Fitted coefficients:"); // for (double coef : z1) { // System.out.println(coef); // } return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3]; } private double tashounenDataDeg0(int simulationPointCode, double sekisairitsu, double koubai) throws IOException { List tashounen_data_deg0 = loadJsonArray("template/pwrGnrSimulation/peakcut_db/tashounen_data_deg0/" + simulationPointCode + ".json", new TypeReference>() {} ); // 0度近似曲線係数 double[] x = {1, 5, 15, 20, 30, 40}; double[] y = new double[6]; double x1, x2, y1, y2; int m1; double[] z1 = new double[4]; // 적재율과 다조년 데이터(방위 0도)에서 y를 대입 for (int i = 0; i < 6; i++) { int idx = i * 6; y[i] = tashounen_data_deg0.get(idx) * Math.pow(sekisairitsu, 5) + tashounen_data_deg0.get(idx + 1) * Math.pow(sekisairitsu, 4) + tashounen_data_deg0.get(idx + 2) * Math.pow(sekisairitsu, 3) + tashounen_data_deg0.get(idx + 3) * Math.pow(sekisairitsu, 2) + tashounen_data_deg0.get(idx + 4) * sekisairitsu + tashounen_data_deg0.get(idx + 5); } // 3차 최소 제곱법 double[][] w = new double[4][5]; double[][] A = new double[6][4]; for (int i = 0; i < 6; i++) { A[i][2] = x[i]; A[i][3] = 1.0; x1 = A[i][2]; x2 = x1; for (int j = 1; j >= 0; j--) { x2 *= x1; A[i][j] = x2; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { w[i][j] = 0.0; for (int k = 0; k < 6; k++) { w[i][j] += A[k][i] * A[k][j]; } } } for (int i = 0; i < 4; i++) { w[i][4] = 0.0; for (int j = 0; j < 6; j++) { w[i][4] += A[j][i] * y[j]; } } for (int i = 0; i < 4; i++) { y1 = 0.0; m1 = i + 1; int m2Index = 0; for (int j = i; j < 4; j++) { y2 = Math.abs(w[j][i]); if (y1 < y2) { y1 = y2; m2Index = j; } } for (int j = i; j < 5; j++) { double temp = w[i][j]; w[i][j] = w[m2Index][j]; w[m2Index][j] = temp; } y1 = 1.0 / w[i][i]; for (int j = m1; j < 5; j++) { w[i][j] *= y1; } for (int j = 0; j < 4; j++) { if (j != i) { for (int k = m1; k < 5; k++) { w[j][k] -= w[j][i] * w[i][k]; } } } } // z1에 3차, 2차, 1차 계수와 절편을 대입 for (int i = 0; i < 4; i++) { z1[i] = w[i][4]; } // 방위각 90도 설치각에서의 전력 피크 컷 return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3]; //電力ピークカット(3次) } /** 0도 방위 전력 피크 컷을 계산하는 방법 */ private double computePowerPeakCutDeg0( int simulationPointNumber, double sekisairitsu, double koubai) { // 0度近似曲線係数 double[] x = {1, 5, 15, 20, 30, 40}; double[] y = new double[6]; double x1, x2, y1, y2; int m1; double[] z1 = new double[4]; // 적재율과 다조년 데이터(방위 0도)에서 y를 대입 for (int i = 0; i < 6; i++) { int idx = i * 6; y[i] = tashounen_data_deg0[simulationPointNumber][idx] * Math.pow(sekisairitsu, 5) + tashounen_data_deg0[simulationPointNumber][idx + 1] * Math.pow(sekisairitsu, 4) + tashounen_data_deg0[simulationPointNumber][idx + 2] * Math.pow(sekisairitsu, 3) + tashounen_data_deg0[simulationPointNumber][idx + 3] * Math.pow(sekisairitsu, 2) + tashounen_data_deg0[simulationPointNumber][idx + 4] * sekisairitsu + tashounen_data_deg0[simulationPointNumber][idx + 5]; } // 3차 최소 제곱법 double[][] w = new double[4][5]; double[][] A = new double[6][4]; for (int i = 0; i < 6; i++) { A[i][2] = x[i]; A[i][3] = 1.0; x1 = A[i][2]; x2 = x1; for (int j = 1; j >= 0; j--) { x2 *= x1; A[i][j] = x2; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { w[i][j] = 0.0; for (int k = 0; k < 6; k++) { w[i][j] += A[k][i] * A[k][j]; } } } for (int i = 0; i < 4; i++) { w[i][4] = 0.0; for (int j = 0; j < 6; j++) { w[i][4] += A[j][i] * y[j]; } } for (int i = 0; i < 4; i++) { y1 = 0.0; m1 = i + 1; int m2Index = 0; for (int j = i; j < 4; j++) { y2 = Math.abs(w[j][i]); if (y1 < y2) { y1 = y2; m2Index = j; } } for (int j = i; j < 5; j++) { double temp = w[i][j]; w[i][j] = w[m2Index][j]; w[m2Index][j] = temp; } y1 = 1.0 / w[i][i]; for (int j = m1; j < 5; j++) { w[i][j] *= y1; } for (int j = 0; j < 4; j++) { if (j != i) { for (int k = m1; k < 5; k++) { w[j][k] -= w[j][i] * w[i][k]; } } } } // z1에 3차, 2차, 1차 계수와 절편을 대입 for (int i = 0; i < 4; i++) { z1[i] = w[i][4]; } // 방위각 90도 설치각에서의 전력 피크 컷 return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3]; } /** 전류 피크 컷(5차) 90도 근사 곡선 계수 */ private double computeAmpPeakCutDeg90( int simulationPointNumber, double koubai, double amp_delta) { // 초기 설정 double[] x = {5, 15, 20, 30, 40, 50}; double[] z2 = new double[6]; double[] y = new double[6]; // 데이터 초기화 - y 값 계산 (amp_delta의 다항식 계산) for (int i = 0; i < 6; i++) { int idx = i * 5; y[i] = amp_peakcut_data_deg90[simulationPointNumber][idx] * Math.pow(amp_delta, 4) + amp_peakcut_data_deg90[simulationPointNumber][idx + 1] * Math.pow(amp_delta, 3) + amp_peakcut_data_deg90[simulationPointNumber][idx + 2] * Math.pow(amp_delta, 2) + amp_peakcut_data_deg90[simulationPointNumber][idx + 3] * amp_delta + amp_peakcut_data_deg90[simulationPointNumber][idx + 4]; } // 최소 제곱법을 위한 행렬 A와 w 초기화 double[][] w = new double[6][7]; double[][] A = new double[6][6]; // A 행렬 설정 (x 값을 사용하여 다항식 계수 생성) for (int i1 = 0; i1 < 6; i1++) { A[i1][4] = x[i1]; A[i1][5] = 1.0; double x1 = A[i1][4]; double x2 = x1; for (int i2 = 3; i2 >= 0; i2--) { x2 *= x1; A[i1][i2] = x2; } } // w 행렬 초기화 for (int i1 = 0; i1 < 6; i1++) { for (int i2 = 0; i2 < 6; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } // w의 마지막 열 값 설정 for (int i1 = 0; i1 < 6; i1++) { w[i1][6] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][6] += A[i2][i1] * y[i2]; } } // 가우스 소거법을 통해 연립방정식 해 구하기 for (int i1 = 0; i1 < 6; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 6; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 7; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 7; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 6; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 7; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } // z2에 계수 저장 for (int i1 = 0; i1 < 6; i1++) { z2[i1] = w[i1][6]; } return z2[0] * Math.pow(koubai, 5) + z2[1] * Math.pow(koubai, 4) + z2[2] * Math.pow(koubai, 3) + z2[3] * Math.pow(koubai, 2) + z2[4] * koubai + z2[5]; } /** 전류 피크 컷(5차) 0도 근사 곡선 계수 */ private double computeAmpPeakCutDeg0(int simulationPointNumber, double koubai, double amp_delta) { double[] x = {5, 15, 20, 30, 40, 50}; double[] z2 = new double[6]; double[] y = new double[6]; for (int i = 0; i < 6; i++) { int idx = i * 5; y[i] = amp_peakcut_data_deg0[simulationPointNumber][idx] * Math.pow(amp_delta, 4) + amp_peakcut_data_deg0[simulationPointNumber][idx + 1] * Math.pow(amp_delta, 3) + amp_peakcut_data_deg0[simulationPointNumber][idx + 2] * Math.pow(amp_delta, 2) + amp_peakcut_data_deg0[simulationPointNumber][idx + 3] * amp_delta + amp_peakcut_data_deg0[simulationPointNumber][idx + 4]; } double[][] w = new double[6][7]; double[][] A = new double[6][6]; for (int i1 = 0; i1 < 6; i1++) { A[i1][4] = x[i1]; A[i1][5] = 1.0; double x1 = A[i1][4]; double x2 = x1; for (int i2 = 3; i2 >= 0; i2--) { x2 *= x1; A[i1][i2] = x2; } } for (int i1 = 0; i1 < 6; i1++) { for (int i2 = 0; i2 < 6; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } for (int i1 = 0; i1 < 6; i1++) { w[i1][6] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][6] += A[i2][i1] * y[i2]; } } for (int i1 = 0; i1 < 6; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 6; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 7; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 7; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 6; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 7; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } for (int i1 = 0; i1 < 6; i1++) { z2[i1] = w[i1][6]; } return z2[0] * Math.pow(koubai, 5) + z2[1] * Math.pow(koubai, 4) + z2[2] * Math.pow(koubai, 3) + z2[3] * Math.pow(koubai, 2) + z2[4] * koubai + z2[5]; } /** 0도 방위 전력 피크 컷을 계산하는 방법 */ private double computePowerPeakCutDeg90( int simulationPointNumber, double sekisairitsu, double koubai) { // 데이터 초기화 double[] x = {1, 5, 15, 20, 30, 40}; // y 배열 초기화 및 값 할당 double[] y = new double[6]; for (int i = 0; i < 6; i++) { int idx = i * 6; y[i] = tashounen_data_deg90[simulationPointNumber][idx] * Math.pow(sekisairitsu, 5) + tashounen_data_deg90[simulationPointNumber][idx + 1] * Math.pow(sekisairitsu, 4) + tashounen_data_deg90[simulationPointNumber][idx + 2] * Math.pow(sekisairitsu, 3) + tashounen_data_deg90[simulationPointNumber][idx + 3] * Math.pow(sekisairitsu, 2) + tashounen_data_deg90[simulationPointNumber][idx + 4] * sekisairitsu + tashounen_data_deg90[simulationPointNumber][idx + 5]; } // w 행렬 초기화 double[][] w = new double[4][5]; // A 행렬 초기화 double[][] A = new double[6][4]; for (int i = 0; i < 6; i++) { A[i][2] = x[i]; A[i][3] = 1.0; double x1 = A[i][2]; double x2 = x1; for (int j = 1; j >= 0; j--) { x2 *= x1; A[i][j] = x2; } } // w[i1][i2] 계산 for (int i1 = 0; i1 < 4; i1++) { for (int i2 = 0; i2 < 4; i2++) { w[i1][i2] = 0.0; for (int i3 = 0; i3 < 6; i3++) { w[i1][i2] += A[i3][i1] * A[i3][i2]; } } } // w[i1][4] 계산 for (int i1 = 0; i1 < 4; i1++) { w[i1][4] = 0.0; for (int i2 = 0; i2 < 6; i2++) { w[i1][4] += A[i2][i1] * y[i2]; } } // 가우스 소거법 적용 for (int i1 = 0; i1 < 4; i1++) { double y1 = 0.0; int m1 = i1 + 1; int m2 = 0; for (int i2 = i1; i2 < 4; i2++) { double y2 = Math.abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } for (int i2 = i1; i2 < 5; i2++) { y1 = w[i1][i2]; w[i1][i2] = w[m2][i2]; w[m2][i2] = y1; } y1 = 1.0 / w[i1][i1]; for (int i2 = m1; i2 < 5; i2++) { w[i1][i2] *= y1; } for (int i2 = 0; i2 < 4; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < 5; i3++) { w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } // 결과 z1 배열에 저장 double[] z1 = new double[4]; for (int i1 = 0; i1 < 4; i1++) { z1[i1] = w[i1][4]; } // 출력 결과 확인 // System.out.println("Fitted coefficients:"); // for (double coef : z1) { // System.out.println(coef); // } return (z1[0] * Math.pow(koubai, 3)) + (z1[1] * Math.pow(koubai, 2)) + (z1[2] * koubai) + z1[3]; } // 설치면의 사면 일사량 계산 public double[] shamenNissharyou(double koubai, double houi, int simulationPointNumber) { double[] shamenNissha = new double[12]; // 경사면 일사량 대입을 위한 빈 배열 변수 생성 int i = simulationPointNumber * 12; // 일사량 관측 지점의 행 번호로부터 데이터베이스의 일치하는 행을 산출 // (例:波照間は836行目→データベースでは836×12=10032行目から10044行目までが1月~12月) if (koubai == 0) { // 경사가 0이면 경사 일사량 = 수평면 일사 for (int m = 0; m < 12; m++) { shamenNissha[m] = suiheimennissha[i]; i++; } } else { // 기울기가 0이 아니면 경사 일사량 계산 // 경사면 일사량 (직접, 산란, 지면 반사)의 도출을위한 빈 배열 변수 생성 double[][] cosTheta = new double[12][26]; double[][] rb = new double[12][26]; double[][] io = new double[12][26]; double[][] I = new double[12][26]; double[][] id = new double[12][26]; double[][] Isby = new double[12][26]; double[][] Ibby = new double[12][26]; double[][] Irby = new double[12][26]; // 시간 double[] timeParam = { 100, 100, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5 }; double IsbyMath = 0; double[] IsbyTotal = new double[12]; double IbbyMath = 0; double[] IbbyTotal = new double[12]; double IrbyMath = 0; double[] IrbyTotal = new double[12]; i = simulationPointNumber * 12; // Reset i // System.err.println( " i >>>>>>>>> " + i); // 매월 각 시간의 각 파라미터 계산 for (int m = 0; m < 12; m++) { // System.err.println( " " ); // System.err.println( " m >>>>>>>>> " + m); for (int j = 0; j < 26; j++) { // System.err.println( " i >>>>>>>>> " + i); // System.err.println( " j >>>>>>>>> " + j); // System.err.println( " latlng[i] >>>>>>>>> " + latlng[i]); // System.err.println( " latlng[i][0] >>>>>>>>> " + latlng[i][0]); // System.err.println( " taiyousekii[i] >>>>>>>>> " + taiyousekii[i]); // System.err.println( " jikakudo[i][j] >>>>>>>>> " + jikakudo[i][j]); // System.err.println( " koubai>>>>>>>>> " + koubai); // System.err.println( " houi>>>>>>>>> " + houi); // System.err.println( " 결과>>>>>>>>> " + Math.max(0, (Math.sin(latlng[i][0]) * // Math.cos(koubai) - Math.cos(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi)) * // Math.sin(taiyousekii[i]) // + (Math.cos(latlng[i][0]) * Math.cos(koubai) + Math.sin(latlng[i][0]) * // Math.sin(koubai) * Math.cos(houi)) * Math.cos(taiyousekii[i]) * // Math.cos(jikakudo[i][j]) // + Math.cos(taiyousekii[i]) * Math.sin(koubai) * Math.sin(houi) * // Math.sin(jikakudo[i][j]))); // System.err.println( " " ); cosTheta[m][j] = Math.max( 0, (Math.sin(latlng[i][0]) * Math.cos(koubai) - Math.cos(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi)) * Math.sin(taiyousekii[i]) + (Math.cos(latlng[i][0]) * Math.cos(koubai) + Math.sin(latlng[i][0]) * Math.sin(koubai) * Math.cos(houi)) * Math.cos(taiyousekii[i]) * Math.cos(jikakudo[i][j]) + Math.cos(taiyousekii[i]) * Math.sin(koubai) * Math.sin(houi) * Math.sin(jikakudo[i][j])); rb[m][j] = (cosThetaZ[i][j] == 0) ? 0 : cosTheta[m][j] / cosThetaZ[i][j]; io[m][j] = ion[i] * cosThetaZ[i][j]; I[m][j] = suiheimennissha[i] * rtx[i][j]; id[m][j] = Math.min(sanrannissha[i] * rdx[i][j], I[m][j]); Isby[m][j] = Math.max( 0, (io[m][j] == 0) ? 0 : id[m][j] * (((I[m][j] - id[m][j]) / io[m][j]) * rb[m][j] + (1 - (I[m][j] - id[m][j]) / io[m][j]) * (1 + Math.cos(koubai)) / 2)); Ibby[m][j] = Math.max(0, (I[m][j] - id[m][j]) * rb[m][j]); Irby[m][j] = I[m][j] * jimenhansharitsu[i] * ((1 - Math.cos(koubai)) / 2); } i++; } // Reset i i = simulationPointNumber * 12; // 직접, 산란 계산 for (int m = 0; m < 12; m++) { for (int j = 0; j < 26; j++) { if (timeParam[j] > (Math.ceil(hinode_nichibotsu[i][0] * 2.0) / 2) && timeParam[j] < (Math.floor(hinode_nichibotsu[i][1] * 2.0) / 2)) { IsbyMath += Isby[m][j]; IbbyMath += Ibby[m][j]; } } IsbyMath += ((((Math.ceil(hinode_nichibotsu[i][0]) - 0.5) < hinode_nichibotsu[i][0]) ? 0 : Isby[m][0]) + (((Math.floor(hinode_nichibotsu[i][1]) + 0.5) > hinode_nichibotsu[i][1]) ? 0 : Isby[m][1])); IsbyTotal[m] = IsbyMath; IbbyMath += (((Math.ceil(hinode_nichibotsu[i][0]) - 0.5) < hinode_nichibotsu[i][0]) ? 0 : Ibby[m][0]) + (((Math.floor(hinode_nichibotsu[i][1]) + 0.5) > hinode_nichibotsu[i][1]) ? 0 : Ibby[m][1]); IbbyTotal[m] = IbbyMath; IsbyMath = 0; IbbyMath = 0; i++; } // Reset i i = simulationPointNumber * 12; // 지면 반사 계산 for (int m = 0; m < 12; m++) { for (int j = 0; j < 26; j++) { // 배열 범위에 데이터가 없어서 추가했음. if ((j + 2 < rd[i].length) && (rd[i][j + 2] > ((jikakudo[i][0] >= 0) ? Math.ceil(jikakudo[i][0] * 2.0) / 2 : Math.floor(jikakudo[i][0] * 2.0) / 2)) && (rd[i][j + 2] < ((jikakudo[i][1] >= 0) ? Math.floor(jikakudo[i][1] * 2.0) / 2 : Math.ceil(jikakudo[i][1] * 2.0) / 2))) { IrbyMath += Irby[m][j + 2]; } } IrbyMath += (((jikakudo[i][0] >= 0) ? Math.ceil(jikakudo[i][0]) - 0.5 : Math.floor(jikakudo[i][0]) - 0.5) < jikakudo[i][0] ? 0 : Irby[m][0]) + (((jikakudo[i][1] >= 0) ? Math.floor(jikakudo[i][1]) + 0.5 : Math.ceil(jikakudo[i][1]) + 0.5) > jikakudo[i][1] ? 0 : Irby[m][1]); IrbyTotal[m] = IrbyMath; IrbyMath = 0; i++; } log.debug(Arrays.toString(IsbyTotal)); // 직달, 산란, 지면 반사를 매월 합계 for (int m = 0; m < 12; m++) { shamenNissha[m] = IsbyTotal[m] + IbbyTotal[m] + IrbyTotal[m]; } } log.debug("shamenNissha ::: {}", shamenNissha); return shamenNissha; } public double[] shamenNissharyou2(double koubai, double houi, int simulationPointCode) throws IOException { List> cosThetaZ = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/cosθz/" + simulationPointCode + ".json", new TypeReference>>() {} ); List> hinode_nichibotsu = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/hinode_nichibotsu/" + simulationPointCode + ".json", new TypeReference>>() {} ); List> jikakudo = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/jikakudo/" + simulationPointCode + ".json", new TypeReference>>() {} ); List jimenhansharitsu = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/jimenhansharitsu/" + simulationPointCode + ".json", new TypeReference>() {} ); List latlng = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/latlng/" + simulationPointCode + ".json", new TypeReference>() {} ); List> rd = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/rd/" + simulationPointCode + ".json", new TypeReference>>() {} ); List> rdx = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/rdx/" + simulationPointCode + ".json", new TypeReference>>() {} ); List> rtx = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/rtx/" + simulationPointCode + ".json", new TypeReference>>() {} ); List sanrannissha = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/sanrannissha/" + simulationPointCode + ".json", new TypeReference>() {} ); List suiheimennissha = loadJsonArray("template/pwrGnrSimulation/shamen_nissha_db/suiheimennissha/" + simulationPointCode + ".json", new TypeReference>() {} ); double[] taiyousekii = new double[]{-0.364097062,-0.223037136,-0.03680526,0.168291101,0.327203218,0.399562561,0.371583056,0.247059216,0.059440186,-0.146278793,-0.316089477,-0.396166795}; double[] ion = new double[]{1.426094061,1.414593394,1.396093029,1.372838185,1.351972937,1.339772275,1.337619965,1.348593875,1.368280812,1.391546004,1.412321395,1.424077899}; double[] shamen_nissha = new double[12]; // 경사면 일사량 대입을 위한 빈 배열 변수 생성 int i = simulationPointCode * 12; // 일사량 관측 지점의 행 번호로부터 데이터베이스의 일치하는 행을 산출 // (例:波照間は836行目→データベースでは836×12=10032行目から10044行目までが1月~12月) if (koubai == 0) { // 경사가 0이면 경사 일사량 = 수평면 일사 shamen_nissha = suiheimennissha.stream() .mapToDouble(Double::doubleValue) .toArray(); } else { // 기울기가 0이 아니면 경사 일사량 계산 // 경사면 일사량 (직접, 산란, 지면 반사)의 도출을위한 빈 배열 변수 생성 double[][] cosTheta = new double[12][26]; double[][] rb = new double[12][26]; double[][] io = new double[12][26]; double[][] I = new double[12][26]; double[][] id = new double[12][26]; double[][] Isby = new double[12][26]; double[][] Ibby = new double[12][26]; double[][] Irby = new double[12][26]; // 시간 double[] time_param = { 100, 100, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5 }; double Isby_math = 0; double[] Isby_total = new double[12]; double Ibby_math = 0; double[] Ibby_total = new double[12]; double Irby_math = 0; double[] Irby_total = new double[12]; // 매월 각 시간의 각 파라미터 계산 for (int m = 0; m < 12; m++) { for (int j = 0; j < 26; j++) { cosTheta[m][j] = Math.max( 0, (Math.sin(latlng.get(0)) * Math.cos(koubai) - Math.cos(latlng.get(0)) * Math.sin(koubai) * Math.cos(houi)) * Math.sin(taiyousekii[m]) + (Math.cos(latlng.get(0)) * Math.cos(koubai) + Math.sin(latlng.get(0)) * Math.sin(koubai) * Math.cos(houi)) * Math.cos(taiyousekii[m]) * Math.cos(jikakudo.get(m).get(j)) + Math.cos(taiyousekii[m]) * Math.sin(koubai) * Math.sin(houi) * Math.sin(jikakudo.get(m).get(j))); rb[m][j] = (cosThetaZ.get(m).get(j) == 0) ? 0 : cosTheta[m][j] / cosThetaZ.get(m).get(j); io[m][j] = ion[m] * cosThetaZ.get(m).get(j); I[m][j] = suiheimennissha.get(m) * rtx.get(m).get(j); id[m][j] = Math.min(sanrannissha.get(m) * rdx.get(m).get(j), I[m][j]); Isby[m][j] = Math.max( 0, (io[m][j] == 0) ? 0 : id[m][j] * (((I[m][j] - id[m][j]) / io[m][j]) * rb[m][j] + (1 - (I[m][j] - id[m][j]) / io[m][j]) * (1 + Math.cos(koubai)) / 2)); Ibby[m][j] = Math.max(0, (I[m][j] - id[m][j]) * rb[m][j]); Irby[m][j] = I[m][j] * jimenhansharitsu.get(m) * ((1 - Math.cos(koubai)) / 2); } } // 直達、散乱の計算 Direct, scattering calculation for (int m = 0; m < 12; m++) { for (int j = 0; j < 26; j++) { if (time_param[j] > (Math.ceil(hinode_nichibotsu.get(m).get(0) * 2) / 2) && time_param[j] < (Math.floor(hinode_nichibotsu.get(m).get(1) * 2) / 2)) { Isby_math += Isby[m][j]; Ibby_math += Ibby[m][j]; } ; } ; Isby_math = Isby_math + (((Math.ceil(hinode_nichibotsu.get(m).get(0)) - 0.5) < hinode_nichibotsu.get(m).get(0)) ? 0 : Isby[m][0]) + (((Math.floor(hinode_nichibotsu.get(m).get(1)) + 0.5) > hinode_nichibotsu.get(m).get(1)) ? 0 : Isby[m][1]); Isby_total[m] = Isby_math; Ibby_math = Ibby_math + (((Math.ceil(hinode_nichibotsu.get(m).get(0)) - 0.5) < hinode_nichibotsu.get(m).get(0)) ? 0 : Ibby[m][0]) + (((Math.floor(hinode_nichibotsu.get(m).get(1)) + 0.5) > hinode_nichibotsu.get(m).get(1)) ? 0 : Ibby[m][1]); Ibby_total[m] = Ibby_math; Isby_math = 0; Ibby_math = 0; } ; // 地面反射の計算 Calculating ground reflection for (int m = 0; m < 12; m++) { for (int j = 0; j < 26; j++) { if (j + 2 < rd.get(m).size()) { //추가 java에러 if ((rd.get(m).get(j + 2) //java에러 > ((jikakudo.get(m).get(0) >= 0) ? (Math.ceil(jikakudo.get(m).get(0) * 2) / 2) : (Math.floor(jikakudo.get(m).get(0) * 2) / 2))) && (rd.get(m).get(j + 2) < ((jikakudo.get(m).get(1) >= 0) ? (Math.floor(jikakudo.get(m).get(1) * 2) / 2) : (Math.ceil(jikakudo.get(m).get(1) * 2) / 2)))) { Irby_math += Irby[m][j + 2]; } } ; } ; Irby_math = Irby_math + ((((jikakudo.get(m).get(0) >= 0) ? (Math.ceil(jikakudo.get(m).get(0)) - 0.5) : (Math.floor(jikakudo.get(m).get(0)) - 0.5)) < jikakudo.get(m).get(0)) ? 0 : Irby[m][0]) + ((((jikakudo.get(m).get(1) >= 0) ? (Math.floor(jikakudo.get(m).get(1)) + 0.5) : (Math.ceil(jikakudo.get(m).get(1)) + 0.5)) > jikakudo.get(m).get(1)) ? 0 : Irby[m][1]); Irby_total[m] = Irby_math; Irby_math = 0; } ; // 直達、散乱、地面反射を各月ごとに合計 //Total direct, scattering, and ground reflections for each month for (int m = 0; m < 12; m++) { shamen_nissha[m] = Isby_total[m] + Ibby_total[m] + Irby_total[m]; } } log.debug("shamen_nissha ::: {}", shamen_nissha); return shamen_nissha; } public double[] readValuesFromFile(String fileName) { List values = new ArrayList<>(); try (InputStream inputStream = PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = br.readLine()) != null) { // 줄을 , 기준으로 분리하여 배열에 추가 String[] parts = line.split(","); for (String part : parts) { values.add(Double.parseDouble(part.trim())); } } } catch (IOException e) { e.printStackTrace(); } // List를 배열로 변환하여 반환 return values.stream().mapToDouble(Double::doubleValue).toArray(); } public String[][] readValuesFromFileString2(String fileName) { List values = new ArrayList<>(); StringBuilder sb = new StringBuilder(); try (InputStream inputStream = PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } String content = sb.toString(); content = content.substring(1, content.length() - 1); // 시작과 끝의 중괄호 제거 String[] arrays = content.split("\\}\\s*,\\s*\\{"); for (String innerArray : arrays) { innerArray = innerArray.replaceAll("[{}]", "").trim(); String[] elements = innerArray.split(","); String[] row = new String[elements.length]; for (int i = 0; i < elements.length; i++) { String trimmedValue = elements[i].trim(); trimmedValue = trimmedValue.replace("\"", ""); row[i] = trimmedValue.isEmpty() ? "" : trimmedValue; } values.add(row); } return values.toArray(new String[0][]); } public double[][] readValuesFromFile2(String fileName) { List values = new ArrayList<>(); StringBuilder sb = new StringBuilder(); try (InputStream inputStream = PwrGnrSimService.class.getClassLoader().getResourceAsStream(fileName); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } String content = sb.toString(); content = content.substring(1, content.length() - 1); // 시작과 끝의 중괄호 제거 String[] arrays = content.split("\\},\\{"); for (String innerArray : arrays) { innerArray = innerArray.replaceAll("[{}]", "").trim(); String[] elements = innerArray.split(","); // 각 요소를 쉼표로 분리 double[] row = new double[elements.length]; for (int i = 0; i < elements.length; i++) { String trimmedValue = elements[i].trim(); if (trimmedValue.isEmpty()) { row[i] = 0.0; // 빈 문자열인 경우 기본값 설정 } else { row[i] = Double.parseDouble(trimmedValue); } } values.add(row); } // List을 double[][]로 변환하여 반환 return values.toArray(new double[0][]); } public PwrGnrSimGuideResponse selectPwrGnrSimulationGuideInfo() throws Exception { PwrGnrSimGuideResponse guideResponse = new PwrGnrSimGuideResponse(); String strResponse = interfaceQsp.callApi(HttpMethod.GET, qspUrl + qspSimulationGuideInfUrl, null); if (!"".equals(strResponse)) { ObjectMapper om = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); guideResponse = om.readValue(strResponse, PwrGnrSimGuideResponse.class); } return guideResponse; } public Document pwrGnrSimPdfHtml(Document doc, PwrGnrSimResponse data) throws IOException, QcastException { String[] pwrGnrSimList = data.getFrcPwrGnrList(); Element elm; // 상단 요약정보 elm = doc.getElementById("objectNo"); elm.text( StringUtils.defaultString(data.getObjectNo() + " (Plan No : " + data.getPlanNo() + ")")); elm = doc.getElementById("drawingEstimateCreateDate"); elm.text(StringUtils.defaultString(data.getDrawingEstimateCreateDate())); elm = doc.getElementById("prefName"); elm.text(StringUtils.defaultString(data.getPrefName())); elm = doc.getElementById("areaName"); elm.text(StringUtils.defaultString(data.getAreaName())); elm = doc.getElementById("capacity"); elm.text(StringUtils.defaultString(data.getCapacity()) + " kW"); elm = doc.getElementById("anlFrcsGnrt"); elm.text(StringUtils.defaultString(String.valueOf(pwrGnrSimList[12]))); elm = doc.getElementById("snowfall"); elm.text(StringUtils.defaultString(data.getSnowfall()) + " cm"); elm = doc.getElementById("standardWindSpeedId"); elm.text(StringUtils.defaultString(data.getStandardWindSpeedId())); boolean isUnchanged = Arrays.stream(pwrGnrSimList).allMatch(value -> value == "00"); if (!isUnchanged) { // 변경된값 확인 int[] onlyData = Arrays.stream(Arrays.copyOfRange(pwrGnrSimList, 0, pwrGnrSimList.length - 1)) .map(s -> s.replace(",", "")) // 쉼표 제거 .mapToInt(Integer::parseInt) // 정수로 변환 .toArray(); int referenceValue = 300; // table 높이 int orgMaxValue = Arrays.stream(onlyData) .max() .orElseThrow(() -> new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, "배열이 없음")); int maxValue = roundUpAuto(orgMaxValue / 8); // chart y축 StringBuilder sb = new StringBuilder(); for (int i = 8; i >= 1; i--) { sb.append(""); sb.append("") .append(maxValue * i) .append(""); sb.append(""); } sb.append(""); sb.append("0"); elm = doc.getElementById("htmlY"); elm.append(sb.toString()); // chart 데이터 String[] color = { "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc", "#0066cc" }; StringBuilder sb2 = new StringBuilder(); for (int i = 0; i < onlyData.length; i++) { double scaledValues = ((double) onlyData[i] / (maxValue * 8)) * referenceValue; sb2.append(""); sb2.append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("
"); sb2.append( "
"); sb2.append(""); } elm = doc.getElementById("htmlX"); elm.append(sb2.toString()); } // 예측발전량 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 13; i++) { sb.append("" + StringUtils.defaultString(pwrGnrSimList[i]) + ""); } elm = doc.getElementById("frcPwrGnrList_detail"); elm.append(sb.toString()); // 모듈 list if (data.getRoofModuleList() != null && data.getRoofModuleList().size() > 0) { sb = new StringBuilder(); for (int i = 0; i < data.getRoofModuleList().size(); i++) { PwrGnrSimRoofResponse listItem = data.getRoofModuleList().get(i); sb.append(""); sb.append("" + StringUtils.defaultString(listItem.getRoofSurface()) + ""); sb.append("" + StringUtils.defaultString(listItem.getSlopeAngleTxt()) + ""); sb.append("" + StringUtils.defaultString(listItem.getAzimuth()) + ""); sb.append("" + StringUtils.defaultString(listItem.getItemNo()) + ""); sb.append("" + StringUtils.defaultString(listItem.getAmount()) + ""); sb.append(""); } elm = doc.getElementById("roofModuleList_detail"); elm.append(sb.toString()); } // pcs list if (data.getPcsList() != null && data.getPcsList().size() > 0) { sb = new StringBuilder(); for (int i = 0; i < data.getPcsList().size(); i++) { PwrGnrSimRoofResponse listItem = data.getPcsList().get(i); sb.append(""); sb.append("" + StringUtils.defaultString(listItem.getItemNo()) + ""); sb.append("" + StringUtils.defaultString(listItem.getAmount()) + ""); sb.append(""); } elm = doc.getElementById("pcsList_detail"); elm.append(sb.toString()); } elm = doc.getElementById("guideInfo"); elm.append(data.getGuideInfo()); return doc; } public int roundUpAuto(double value) { int length = (int) Math.log10(value) + 1; // 숫자의 자리수를 계산 int place = (int) Math.pow(10, length - 1); // 자리수에 따른 올림 기준(10, 100, 1000 등) return (int) Math.ceil(value / place) * place; } public void excelDownload( HttpServletRequest request, HttpServletResponse response, EstimateRequest estimateRequest) throws Exception { // Validation if (StringUtils.isEmpty(estimateRequest.getObjectNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Object No")); } if (StringUtils.isEmpty(estimateRequest.getPlanNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Plan No")); } try { EstimateResponse estimateResponse = new EstimateResponse(); estimateResponse.setObjectNo(estimateRequest.getObjectNo()); estimateResponse.setPlanNo(estimateRequest.getPlanNo()); // file Name 명이 없는경우 if (estimateRequest.getFileName() == null || "".equals(estimateRequest.getFileName())) { estimateRequest.setFileName( estimateResponse.getObjectNo() + "_" + new SimpleDateFormat("yyyyMMdd").format(new Date())); } // 발전시뮬레이션 계산 PwrGnrSimRequest pwrGnrSimRequest = new PwrGnrSimRequest(); pwrGnrSimRequest.setObjectNo(estimateResponse.getObjectNo()); pwrGnrSimRequest.setPlanNo(estimateResponse.getPlanNo()); PwrGnrSimResponse pwrGnrSimResponse = this.selectPwrGnrSimulation(pwrGnrSimRequest); pwrGnrSimResponse.setPwrGnrSimType(estimateRequest.getPwrGnrSimType()); // 발전시뮬레이션 타입에 따른 list 셋팅 if ("A".equals(estimateRequest.getPwrGnrSimType())) { pwrGnrSimResponse.setFrcPwrGnrList(pwrGnrSimResponse.getHatsudenryouAll()); } else if ("B".equals(estimateRequest.getPwrGnrSimType())) { pwrGnrSimResponse.setFrcPwrGnrList(pwrGnrSimResponse.getHatsudenryouAllSnow()); } else if ("C".equals(estimateRequest.getPwrGnrSimType())) { pwrGnrSimResponse.setFrcPwrGnrList(pwrGnrSimResponse.getHatsudenryouPeakcutAll()); } else if ("D".equals(estimateRequest.getPwrGnrSimType())) { pwrGnrSimResponse.setFrcPwrGnrList(pwrGnrSimResponse.getHatsudenryouPeakcutAllSnow()); } pwrGnrSimResponse.setIntFrcPwrGnrList( Arrays.stream(pwrGnrSimResponse.getFrcPwrGnrList()) .map(s -> s.replace(",", "")) // , 제거 .mapToInt(Integer::parseInt) // 문자열을 int로 변환 .toArray()); if (pwrGnrSimResponse != null) { try { // 발전시뮬레이션 안내사항 조회 PwrGnrSimGuideResponse pwrGnrSimGuideInfo = this.selectPwrGnrSimulationGuideInfo(); if (pwrGnrSimGuideInfo != null) { pwrGnrSimResponse.setGuideInfo(pwrGnrSimGuideInfo.getData()); } } catch (Exception e) { } } estimateResponse.setPwrGnrSim(pwrGnrSimResponse); if ("PDF".equals(estimateRequest.getSchDownload())) { // PDF 다운로드 String[] arrSection = {"div.section3"}; String templateFilePath = "pdf_download_simulation_detail_template.html"; // 템플릿 html 조회 Document doc = PdfUtil.getPdfDoc(request, templateFilePath); // 발전시뮬레이션 pdf Html 생성 doc = this.pwrGnrSimPdfHtml(doc, pwrGnrSimResponse); // pdf 다운로드 PdfUtil.pdfDownload(request, response, doc, estimateRequest.getFileName(), null, true); } else { // 지붕면 목록에서 8개로 자름 List roofModuleList8 = estimateResponse.getPwrGnrSim().getRoofModuleList(); if (roofModuleList8.size() < 9) { // 9개 미만인 경우, 나머지 아이템을 빈 값으로 채움 for (int k = roofModuleList8.size(); k < 8; k++) { PwrGnrSimRoofResponse emptyRoof = new PwrGnrSimRoofResponse(); roofModuleList8.add(emptyRoof); } }else { // 9개 이상인 경우, 9개로 자름 roofModuleList8 = roofModuleList8.subList(0, 8); } estimateResponse.setRoofModuleList8(roofModuleList8); //pcs list 3개 List pcsList3 = estimateResponse.getPwrGnrSim().getPcsList(); if (pcsList3.size() < 4) { // 4개 미만인 경우, 나머지 아이템을 빈 값으로 채움 for (int k = pcsList3.size(); k < 4; k++) { PwrGnrSimRoofResponse emptyPcs = new PwrGnrSimRoofResponse(); pcsList3.add(emptyPcs); } } else { // 4개 이상인 경우, 4개로 자름 pcsList3 = pcsList3.subList(0, 3); } estimateResponse.setPcsList3(pcsList3); String excelTemplateName = "excel_download_simulation_detail_template.xlsx"; ExcelUtil excelUtil = new ExcelUtil(); excelUtil.download( request, response, excelUtil.convertVoToMap(estimateResponse), null, estimateRequest.getFileName(), excelTemplateName); } } catch (Exception e) { e.printStackTrace(); throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR); } } }